Certificates, etc... work in progress.

This commit is contained in:
Adam Ierymenko 2020-06-13 21:41:39 -07:00
parent 808ab715d9
commit f447608d6b
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
24 changed files with 1502 additions and 865 deletions

View file

@ -25,7 +25,7 @@ central-controller-docker:
docker build -t registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP} -f controller/central-docker/Dockerfile .
clean:
rm -rf ${BUILDDIR} cmake-build-*
rm -rf ${BUILDDIR}
distclean:
rm -rf ${BUILDDIR} cmake-build-*
rm -rf ${BUILDDIR}

View file

@ -34,7 +34,8 @@ Global Options:
Commands:
help Show this help
version Print version
service Start node (see below)
service [-options] Start node (see below)
-d Fork into background (Unix only)
status Show node status and configuration
join [-options] <network> Join a virtual network
-a <token> Token to submit to controller
@ -56,10 +57,8 @@ Commands:
locator <locator> Explicitly update peer locator
roots List root peers
root [command] - Root management commands
add <identity> [endpoint] Designate a peer as a root
remove <address> Un-designate a peer as a root
subscribe <url> [<key hash>] Subscribe to a set of roots
unsubscribe <url | key hash> Unsubscribe from a set of roots
add <identity | url> [endpoint] Add a root or a root set
remove <address | url | serial> Remove a root or root set
set [option] [value] - Get or set a core config option
port <port> Primary P2P port
secondaryport <port/0> Secondary P2P port (0 to disable)

View file

@ -15,8 +15,11 @@ package cli
import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"path"
"strconv"
"syscall"
"zerotier/pkg/zerotier"
@ -28,16 +31,20 @@ func Service(basePath, authToken string, args []string) {
os.Exit(1)
}
pidPath := path.Join(basePath, "zerotier.pid")
_ = ioutil.WriteFile(pidPath, []byte(strconv.FormatInt(int64(os.Getpid()), 10)), 0644)
node, err := zerotier.NewNode(basePath)
if err != nil {
fmt.Println("FATAL: error initializing node: " + err.Error())
os.Exit(1)
} else {
osSignalChannel := make(chan os.Signal, 2)
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGSTOP)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPIPE, syscall.SIGHUP)
<-osSignalChannel
node.Close()
}
osSignalChannel := make(chan os.Signal, 2)
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2)
<-osSignalChannel
node.Close()
os.Exit(0)
_ = os.Remove(pidPath)
os.Exit(1)
}

44
core/Blob.hpp Normal file
View file

@ -0,0 +1,44 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_BLOB_HPP
#define ZT_BLOB_HPP
#include "Constants.hpp"
#include "Utils.hpp"
namespace ZeroTier {
/**
* Container for arbitrary bytes for use in collections
*
* @tparam S Size of container in bytes
*/
template<unsigned int S>
struct Blob
{
uint8_t data[S];
ZT_INLINE Blob() { Utils::zero<S>(data); }
ZT_INLINE bool operator==(const Blob &b) const noexcept { return (memcmp(data,b.data,S) == 0); }
ZT_INLINE bool operator!=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) != 0); }
ZT_INLINE bool operator<(const Blob &b) const noexcept { return (memcmp(data,b.data,S) < 0); }
ZT_INLINE bool operator>(const Blob &b) const noexcept { return (memcmp(data,b.data,S) > 0); }
ZT_INLINE bool operator<=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) <= 0); }
ZT_INLINE bool operator>=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) >= 0); }
};
} // namespace ZeroTier
#endif

View file

@ -2,13 +2,14 @@ cmake_minimum_required (VERSION 2.8)
project(zt_core)
configure_file(
version.h.in
version.h
version.h.in
version.h
)
set(core_headers
zerotier.h
Address.hpp
Blob.hpp
Buf.hpp
C25519.hpp
Capability.hpp
@ -24,6 +25,7 @@ set(core_headers
Expect.hpp
FCV.hpp
Fingerprint.hpp
IdentificationCertificate.hpp
Identity.hpp
InetAddress.hpp
Locator.hpp
@ -67,6 +69,7 @@ set(core_src
Dictionary.cpp
ECC384.cpp
Endpoint.cpp
IdentificationCertificate.cpp
Identity.cpp
InetAddress.cpp
Locator.cpp

View file

@ -12,56 +12,64 @@
/****/
#include "Dictionary.hpp"
#include "SHA512.hpp"
namespace ZeroTier {
Dictionary::Dictionary()
{}
Dictionary::~Dictionary()
{}
Vector< uint8_t > &Dictionary::operator[](const char *k)
{
if (k)
return m_entries[s_key(k)];
else return m_entries[""];
}
Vector<uint8_t> &Dictionary::operator[](const char *k)
const Vector< uint8_t > &Dictionary::operator[](const char *k) const
{
return m_entries[s_key(k)];
}
const Vector<uint8_t> &Dictionary::operator[](const char *k) const
{
static const Vector<uint8_t> s_emptyEntry;
SortedMap< String, Vector<uint8_t> >::const_iterator e(m_entries.find(s_key(k)));
return (e == m_entries.end()) ? s_emptyEntry : e->second;
static const Vector< uint8_t > s_emptyEntry;
if (k) {
SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(s_key(k)));
return (e == m_entries.end()) ? s_emptyEntry : e->second;
} else {
SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(""));
return (e == m_entries.end()) ? s_emptyEntry : e->second;
}
}
void Dictionary::add(const char *k, bool v)
{
Vector<uint8_t> &e = (*this)[k];
Vector< uint8_t > &e = (*this)[k];
e.resize(2);
e[0] = (uint8_t) (v ? '1' : '0');
e[0] = (uint8_t)(v ? '1' : '0');
e[1] = 0;
}
void Dictionary::add(const char *k, const Address &v)
{
Vector<uint8_t> &e = (*this)[k];
Vector< uint8_t > &e = (*this)[k];
e.resize(ZT_ADDRESS_STRING_SIZE_MAX);
v.toString((char *) e.data());
v.toString((char *)e.data());
}
void Dictionary::add(const char *k, const char *v)
{
if ((v) && (*v)) {
Vector<uint8_t> &e = (*this)[k];
Vector< uint8_t > &e = (*this)[k];
e.clear();
while (*v)
e.push_back((uint8_t) *(v++));
e.push_back((uint8_t)*(v++));
}
}
void Dictionary::add(const char *k, const void *data, unsigned int len)
{
Vector<uint8_t> &e = (*this)[k];
Vector< uint8_t > &e = (*this)[k];
if (len != 0) {
e.assign((const uint8_t *) data, (const uint8_t *) data + len);
e.assign((const uint8_t *)data, (const uint8_t *)data + len);
} else {
e.clear();
}
@ -69,9 +77,9 @@ void Dictionary::add(const char *k, const void *data, unsigned int len)
bool Dictionary::getB(const char *k, bool dfl) const
{
const Vector<uint8_t> &e = (*this)[k];
const Vector< uint8_t > &e = (*this)[k];
if (!e.empty()) {
switch ((char) e[0]) {
switch ((char)e[0]) {
case '1':
case 't':
case 'T':
@ -89,15 +97,15 @@ uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const
{
uint8_t tmp[18];
uint64_t v = dfl;
const Vector<uint8_t> &e = (*this)[k];
const Vector< uint8_t > &e = (*this)[k];
if (!e.empty()) {
if (e.back() != 0) {
const unsigned long sl = e.size();
Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl);
tmp[17] = 0;
return Utils::unhex((const char *) tmp);
return Utils::unhex((const char *)tmp);
}
return Utils::unhex((const char *) e.data());
return Utils::unhex((const char *)e.data());
}
return v;
}
@ -106,13 +114,13 @@ char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const
{
if (cap == 0) // sanity check
return v;
const Vector<uint8_t> &e = (*this)[k];
const Vector< uint8_t > &e = (*this)[k];
unsigned int i = 0;
const unsigned int last = cap - 1;
for (;;) {
if ((i == last) || (i >= (unsigned int)e.size()))
break;
v[i] = (char) e[i];
v[i] = (char)e[i];
++i;
}
v[i] = 0;
@ -124,16 +132,14 @@ void Dictionary::clear()
m_entries.clear();
}
void Dictionary::encode(Vector<uint8_t> &out, const bool omitSignatureFields) const
void Dictionary::encode(Vector< uint8_t > &out) const
{
out.clear();
for (SortedMap< String, Vector<uint8_t> >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) {
if ((!omitSignatureFields) || ((ti->first != ZT_DICTIONARY_SIGNATURE_KEY))) {
s_appendKey(out, ti->first.data());
for (Vector<uint8_t>::const_iterator i(ti->second.begin());i != ti->second.end();++i)
s_appendValueByte(out, *i);
out.push_back((uint8_t) '\n');
}
for (SortedMap< String, Vector< uint8_t > >::const_iterator ti(m_entries.begin()); ti != m_entries.end(); ++ti) {
s_appendKey(out, ti->first.data());
for (Vector< uint8_t >::const_iterator i(ti->second.begin()); i != ti->second.end(); ++i)
s_appendValueByte(out, *i);
out.push_back((uint8_t)'\n');
}
out.push_back(0);
}
@ -142,9 +148,9 @@ bool Dictionary::decode(const void *data, unsigned int len)
{
clear();
String k;
Vector<uint8_t> *v = nullptr;
Vector< uint8_t > *v = nullptr;
bool escape = false;
for (unsigned int di = 0;di < len;++di) {
for (unsigned int di = 0; di < len; ++di) {
uint8_t c = reinterpret_cast<const uint8_t *>(data)[di];
if (!c) break;
if (v) {
@ -168,7 +174,7 @@ bool Dictionary::decode(const void *data, unsigned int len)
break;
}
} else {
if (c == (uint8_t) '\n') {
if (c == (uint8_t)'\n') {
k.clear();
v = nullptr;
} else if (c == 92) { // backslash
@ -180,7 +186,7 @@ bool Dictionary::decode(const void *data, unsigned int len)
} else {
if ((c < 33) || (c > 126) || (c == 92)) {
return false;
} else if (c == (uint8_t) '=') {
} else if (c == (uint8_t)'=') {
k.push_back(0);
v = &m_entries[k];
} else if (k.size() < 7) {
@ -193,59 +199,4 @@ bool Dictionary::decode(const void *data, unsigned int len)
return true;
}
void Dictionary::sign(
const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE],
const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE],
const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE])
{
Vector<uint8_t> buf;
encode(buf, true);
uint8_t c25519Signature[ZT_C25519_SIGNATURE_LEN];
C25519::sign(c25519PrivateKey, c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature);
uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE];
static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE,"size mismatch");
SHA384(hbuf, buf.data(), (unsigned int)buf.size());
uint8_t p384Signature[ZT_ECC384_SIGNATURE_SIZE];
ECC384ECDSASign(p384PrivateKey, hbuf, p384Signature);
SHA384(hbuf, c25519PublicKey, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, p384PublicKey, ZT_ECC384_PUBLIC_KEY_SIZE);
Dictionary signature;
signature["kh"].assign(hbuf, hbuf + ZT_SHA384_DIGEST_SIZE);
signature["ed25519"].assign(c25519Signature, c25519Signature + ZT_C25519_SIGNATURE_LEN);
signature["p384"].assign(p384Signature, p384Signature + ZT_ECC384_SIGNATURE_SIZE);
signature.encode((*this)[ZT_DICTIONARY_SIGNATURE_KEY], true);
}
bool Dictionary::verify(
const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const
{
try {
const Vector< uint8_t > &data = (*this)[ZT_DICTIONARY_SIGNATURE_KEY];
if (data.empty())
return false;
Dictionary signature;
if (!signature.decode(data.data(), (unsigned int)data.size()))
return false;
const Vector< uint8_t > &p384Signature = signature["p384"];
const Vector< uint8_t > &c25519Signature = signature["ed25519"];
if ((p384Signature.size() != ZT_ECC384_SIGNATURE_SIZE) || (c25519Signature.size() != ZT_C25519_SIGNATURE_LEN))
return false;
Vector< uint8_t > buf;
encode(buf, true);
if (C25519::verify(c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature.data(), (unsigned int)c25519Signature.size())) {
uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE];
SHA384(hbuf, buf.data(), (unsigned int)buf.size());
return ECC384ECDSAVerify(p384PublicKey, hbuf, p384Signature.data());
}
} catch ( ... ) {}
return false;
}
} // namespace ZeroTier

View file

@ -19,10 +19,6 @@
#include "Address.hpp"
#include "Buf.hpp"
#include "Containers.hpp"
#include "C25519.hpp"
#include "ECC384.hpp"
#define ZT_DICTIONARY_SIGNATURE_KEY "@S"
namespace ZeroTier {
@ -46,10 +42,10 @@ class Identity;
class Dictionary
{
public:
typedef SortedMap< String, Vector < uint8_t > >
::const_iterator const_iterator;
typedef SortedMap< String, Vector < uint8_t > >::const_iterator const_iterator;
Dictionary();
~Dictionary();
/**
* Get a reference to a value
@ -90,12 +86,17 @@ public:
* @param k Key to set
* @param v Integer to set, will be cast to uint64_t and stored as hex
*/
template< typename I >
ZT_INLINE void add(const char *const k, I v)
{
char buf[17];
add(k, Utils::hex((uint64_t)(v), buf));
}
ZT_INLINE void add(const char *const k, const uint64_t v)
{ char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); }
/**
* Add an integer as a hexadecimal string value
*
* @param k Key to set
* @param v Integer to set, will be cast to uint64_t and stored as hex
*/
ZT_INLINE void add(const char *const k, const int64_t v)
{ char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); }
/**
* Add an address in 10-digit hex string format
@ -145,6 +146,7 @@ public:
/**
* Get an object supporting the marshal/unmarshal interface pattern
*
* @tparam T Object type (inferred)
* @param k Key to look up
* @param obj Object to unmarshal() into
* @return True if unmarshal was successful
@ -158,6 +160,27 @@ public:
return (obj.unmarshal(d.data(), (unsigned int)d.size()) > 0);
}
/**
* Add an object supporting the marshal/unmarshal interface pattern
*
* @tparam T Object type (inferred)
* @param k Key to add
* @param obj Object to marshal() into vector
* @return True if successful
*/
template< typename T >
ZT_INLINE bool addO(const char *k, T &obj)
{
uint8_t tmp[4096];
static_assert(sizeof(tmp) >= T::marshalSizeMax(),"buffer too small");
int l = obj.marshal(tmp);
if (l > 0) {
(*this)[k].assign(tmp, tmp + l);
return true;
}
return false;
}
/**
* Erase all entries in dictionary
*/
@ -179,9 +202,8 @@ public:
* Encode to a string in the supplied vector
*
* @param out String encoded dictionary
* @param omitSignatureFields If true omit the signature fields from encoding (default: false)
*/
void encode(Vector <uint8_t> &out, bool omitSignatureFields = false) const;
void encode(Vector <uint8_t> &out) const;
/**
* Decode a string encoded dictionary
@ -195,34 +217,6 @@ public:
*/
bool decode(const void *data, unsigned int len);
/**
* Sign this dictionary with both an Ed25519 key and a NIST P-384 key.
*
* This is currently used just for signing root sets for the root subscribe
* feature. It uses both key types for more crypto cowbell.
*
* @param c25519PrivateKey Curve25519 combined key (C25519 and ed25519), though only ed25519 part is used
* @param c25519PublicKey Public part of Curve25519 combined key
* @param p384PrivateKey NIST P-384 private key
* @param p384PublicKey NIST P-384 public key
*/
void sign(
const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE],
const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE],
const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]);
/**
* Verify this dictionary's signature
*
* @param c25519PublicKey Curve25519 public key
* @param p384PublicKey P-384 public key
* @return True if signatures are valid
*/
bool verify(
const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const;
/**
* Append a key=value pair to a buffer (vector or FCV)
*
@ -376,6 +370,19 @@ public:
return mlen;
}
static ZT_INLINE char *arraySubscript(char buf[256],const char *name,const unsigned long sub) noexcept
{
for(unsigned int i=0;i<(256 - 17);++i) {
if ((buf[i] = name[i]) == 0) {
buf[i++] = '#';
Utils::hex(sub, buf + i);
return buf;
}
}
buf[0] = 0;
return buf;
}
private:
template< typename V >
ZT_INLINE static void s_appendValueByte(V &out, const uint8_t c)

View file

@ -0,0 +1,260 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#include "IdentificationCertificate.hpp"
#include "SHA512.hpp"
namespace ZeroTier {
IdentificationCertificate &IdentificationCertificate::operator=(const ZT_IdentificationCertificate &apiCert)
{
Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert);
m_identities.clear();
m_locators.clear();
m_nodes.clear();
m_networks.clear();
return *this;
}
IdentificationCertificate &IdentificationCertificate::operator=(const IdentificationCertificate &cert)
{
Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, (const ZT_IdentificationCertificate *)(&cert));
m_identities.clear();
m_locators.clear();
m_nodes.clear();
m_networks.clear();
this->subject.nodeCount = 0;
this->subject.networkCount = 0;
if (cert.issuer) {
m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
this->issuer = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
}
for (unsigned int i = 0; i < cert.subject.nodeCount; ++i) {
if (cert.subject.nodes[i].locator)
addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.nodes[i].identity), *reinterpret_cast<const Locator *>(cert.subject.nodes[i].locator));
else if (cert.subject.nodes[i].identity)
addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.nodes[i].identity));
}
for (unsigned int i = 0; i < cert.subject.networkCount; ++i)
addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller);
return *this;
}
ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id)
{
m_nodes.resize(++this->subject.nodeCount);
this->subject.nodes = m_nodes.data();
m_identities.push_back(id);
m_nodes.back().identity = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
m_nodes.back().locator = nullptr;
return &(m_nodes.back());
}
ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id, const Locator &loc)
{
ZT_IdentificationCertificate_Node *n = addSubjectNode(id);
m_locators.push_back(loc);
n->locator = reinterpret_cast<ZT_Locator *>(&(m_locators.back()));
return n;
}
ZT_IdentificationCertificate_Network *IdentificationCertificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller)
{
m_networks.resize(++this->subject.networkCount);
this->subject.networks = m_networks.data();
m_networks.back().id = id;
Utils::copy< sizeof(ZT_Fingerprint) >(&(m_networks.back().controller), &controller);
return &(m_networks.back());
}
Vector< uint8_t > IdentificationCertificate::encode(const bool omitSignature) const
{
char tmp[256];
Vector< uint8_t > enc;
Dictionary d;
d.add("v", (uint64_t)this->version);
d.add("mP", (uint64_t)this->maxPathLength);
d.add("f", this->flags);
d.add("v0", this->validity[0]);
d.add("v1", this->validity[1]);
d.add("s.n[]", (uint64_t)this->subject.nodeCount);
for (unsigned int i = 0; i < this->subject.nodeCount; ++i) {
d.addO(Dictionary::arraySubscript(tmp, "s.n[].i", i), *reinterpret_cast<const Identity *>(this->subject.nodes[i].identity));
if (this->subject.nodes[i].locator)
d.addO(Dictionary::arraySubscript(tmp, "s.n[].l", i), *reinterpret_cast<const Locator *>(this->subject.nodes[i].locator));
}
d.add("s.nw[]", (uint64_t)this->subject.networkCount);
for (unsigned int i = 0; i < this->subject.networkCount; ++i) {
d.add(Dictionary::arraySubscript(tmp, "s.nw[].i", i), this->subject.networks[i].id);
Fingerprint fp(this->subject.networks[i].controller);
d.addO(Dictionary::arraySubscript(tmp, "s.nw[].c", i), fp);
}
d.add("s.n.c", this->subject.name.country);
d.add("s.n.o", this->subject.name.organization);
d.add("s.n.u", this->subject.name.unit);
d.add("s.n.l", this->subject.name.locality);
d.add("s.n.p", this->subject.name.province);
d.add("s.n.sA", this->subject.name.streetAddress);
d.add("s.n.pC", this->subject.name.postalCode);
d.add("s.n.cN", this->subject.name.commonName);
d.add("s.n.sN", this->subject.name.serialNo);
d.add("s.n.e", this->subject.name.email);
d.add("s.n.ur", this->subject.name.url);
if (this->issuer)
d.addO("i", *reinterpret_cast<const Identity *>(this->issuer));
d.add("iN.c", this->issuerName.country);
d.add("iN.o", this->issuerName.organization);
d.add("iN.u", this->issuerName.unit);
d.add("iN.l", this->issuerName.locality);
d.add("iN.p", this->issuerName.province);
d.add("iN.sA", this->issuerName.streetAddress);
d.add("iN.pC", this->issuerName.postalCode);
d.add("iN.cN", this->issuerName.commonName);
d.add("iN.sN", this->issuerName.serialNo);
d.add("iN.e", this->issuerName.email);
d.add("iN.ur", this->issuerName.url);
if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature)))
d["si"].assign(this->signature, this->signature + this->signatureSize);
d.encode(enc);
return enc;
}
bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
{
char tmp[256];
Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this);
m_identities.clear();
m_locators.clear();
m_nodes.clear();
m_networks.clear();
Dictionary d;
if (!d.decode(data.data(), (unsigned int)data.size()))
return false;
this->version = (unsigned int)d.getUI("v");
this->maxPathLength = (unsigned int)d.getUI("mP");
this->flags = d.getUI("f");
this->validity[0] = (int64_t)d.getUI("v0");
this->validity[1] = (int64_t)d.getUI("v1");
unsigned int cnt = (unsigned int)d.getUI("s.n[]");
for (unsigned int i = 0; i < cnt; ++i) {
const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, "s.n[].i", i)];
if (identityData.empty())
return false;
Identity id;
if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0)
return false;
const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, "s.n[].l", i)];
if (!locatorData.empty()) {
Locator loc;
if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0)
return false;
this->addSubjectNode(id, loc);
} else {
this->addSubjectNode(id);
}
}
cnt = (unsigned int)d.getUI("s.nw[]");
for (unsigned int i = 0; i < cnt; ++i) {
const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, "s.nw[].i", i));
if (nwid == 0)
return false;
const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, "s.nw[].c", i)];
if (fingerprintData.empty())
return false;
Fingerprint fp;
if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0)
return false;
this->addSubjectNetwork(nwid, fp);
}
d.getS("s.n.c", this->subject.name.country, sizeof(this->subject.name.country));
d.getS("s.n.o", this->subject.name.organization, sizeof(this->subject.name.organization));
d.getS("s.n.u", this->subject.name.unit, sizeof(this->subject.name.unit));
d.getS("s.n.l", this->subject.name.locality, sizeof(this->subject.name.locality));
d.getS("s.n.p", this->subject.name.province, sizeof(this->subject.name.province));
d.getS("s.n.sA", this->subject.name.streetAddress, sizeof(this->subject.name.streetAddress));
d.getS("s.n.pC", this->subject.name.postalCode, sizeof(this->subject.name.postalCode));
d.getS("s.n.cN", this->subject.name.commonName, sizeof(this->subject.name.commonName));
d.getS("s.n.sN", this->subject.name.serialNo, sizeof(this->subject.name.serialNo));
d.getS("s.n.e", this->subject.name.email, sizeof(this->subject.name.email));
d.getS("s.n.ur", this->subject.name.url, sizeof(this->subject.name.url));
const Vector< uint8_t > &issuerData = d["i"];
if (!issuerData.empty()) {
Identity id;
if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) {
m_identities.push_back(id);
this->issuer = reinterpret_cast<const Identity *>(&(m_identities.back()));
}
}
d.getS("iN.c", this->issuerName.country, sizeof(this->issuerName.country));
d.getS("iN.o", this->issuerName.organization, sizeof(this->issuerName.organization));
d.getS("iN.u", this->issuerName.unit, sizeof(this->issuerName.unit));
d.getS("iN.l", this->issuerName.locality, sizeof(this->issuerName.locality));
d.getS("iN.p", this->issuerName.province, sizeof(this->issuerName.province));
d.getS("iN.sA", this->issuerName.streetAddress, sizeof(this->issuerName.streetAddress));
d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode));
d.getS("iN.cN", this->issuerName.commonName, sizeof(this->issuerName.commonName));
d.getS("iN.sN", this->issuerName.serialNo, sizeof(this->issuerName.serialNo));
d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email));
d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url));
const Vector< uint8_t > &sig = d["si"];
if (sig.size() > sizeof(this->signature))
return false;
Utils::copy(this->signature, sig.data(), (unsigned int)sig.size());
this->signatureSize = (unsigned int)sig.size();
Vector< uint8_t > enc(encode(true));
SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
return true;
}
bool IdentificationCertificate::sign(const Identity &issuer)
{
Vector< uint8_t > enc(encode(true));
SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
return (this->signatureSize = issuer.sign(enc.data(), (unsigned int)enc.size(), this->signature, sizeof(this->signature))) > 0;
}
bool IdentificationCertificate::verify() const
{
if (this->issuer) {
Vector< uint8_t > enc(encode(true));
return reinterpret_cast<const Identity *>(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize);
}
return false;
}
} // namespace ZeroTier

View file

@ -0,0 +1,135 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_IDENTIFICATIONCERTIFICATE_HPP
#define ZT_IDENTIFICATIONCERTIFICATE_HPP
#include "Constants.hpp"
#include "SHA512.hpp"
#include "C25519.hpp"
#include "ECC384.hpp"
#include "SharedPtr.hpp"
#include "Identity.hpp"
#include "Locator.hpp"
#include "Dictionary.hpp"
#include "Utils.hpp"
#include "Containers.hpp"
namespace ZeroTier {
/**
* Certificate identifying the real world owner of an identity or network.
*
* This is a wrapper around the straight C ZT_IdentificationCertificate and
* handles allocating memory for objects and disposing of it on GC. If filling
* out a ZT_IdentificationCertificate structure, identities and other objects
* should be attached via the addXXX() methods rather than by directly setting
* the pointers in the C structure.
*
* If identities and similar objects are NOT added via the addXXX() methods,
* this will not take care of de-allocating them when destroyed.
*
* The serialNo field is filled in automatically by sign() and decode(), so
* it can be left undefined when building certificates.
*/
class IdentificationCertificate : public ZT_IdentificationCertificate
{
public:
ZT_INLINE IdentificationCertificate() noexcept
{ Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this); }
ZT_INLINE IdentificationCertificate(const ZT_IdentificationCertificate &apiCert)
{ Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert); }
ZT_INLINE IdentificationCertificate(const IdentificationCertificate &cert)
{ *this = cert; }
IdentificationCertificate &operator=(const ZT_IdentificationCertificate &apiCert);
IdentificationCertificate &operator=(const IdentificationCertificate &cert);
/**
* Add a subject node/identity without a locator
*
* @param id Identity
* @return Pointer to C struct
*/
ZT_IdentificationCertificate_Node *addSubjectNode(const Identity &id);
/**
* Add a subject node/identity with a locator
*
* @param id Identity
* @param loc Locator signed by identity (signature is NOT checked here)
* @return Pointer to C struct
*/
ZT_IdentificationCertificate_Node *addSubjectNode(const Identity &id, const Locator &loc);
/**
* Add a subject network
*
* @param id Network ID
* @param controller Network controller's full fingerprint
* @return Pointer to C struct
*/
ZT_IdentificationCertificate_Network *addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller);
/**
* Marshal this certificate in binary form
*
* The internal encoding used here is Dictionary to permit easy
* extensibility.
*
* @param omitSignature If true omit the signature field (for signing and verification, default is false)
* @return Marshaled certificate
*/
Vector< uint8_t > encode(bool omitSignature = false) const;
/**
* Decode this certificate from marshaled bytes.
*
* @param data Marshalled certificate
* @return True if input is valid and was unmarshalled (signature is NOT checked)
*/
bool decode(const Vector< uint8_t > &data);
/**
* Sign this certificate (and also fill in serialNo).
*
* @param issuer Issuer identity (must have secret key)
* @return True on success
*/
bool sign(const Identity &issuer);
/**
* Verify certificate signature against the issuer contained therein
*
* @return True if certificate is signed and signature is valid
*/
bool verify() const;
private:
// These hold any identity or locator objects that are owned by and should
// be deleted with this certificate. Lists are used so the pointers never
// change.
List< Identity > m_identities;
List< Locator > m_locators;
// These are stored in a vector because the memory needs to be contiguous.
Vector< ZT_IdentificationCertificate_Node > m_nodes;
Vector< ZT_IdentificationCertificate_Network > m_networks;
};
} // namespace ZeroTier
#endif

View file

@ -64,7 +64,7 @@ public:
ZT_INLINE Identity() noexcept
{
Utils::memoryLock(this,sizeof(Identity));
Utils::memoryLock(this, sizeof(Identity));
memoryZero(this);
}
@ -78,25 +78,27 @@ public:
*/
explicit ZT_INLINE Identity(const char *str)
{
Utils::memoryLock(this,sizeof(Identity));
Utils::memoryLock(this, sizeof(Identity));
fromString(str);
}
ZT_INLINE ~Identity()
{
Utils::memoryUnlock(this,sizeof(Identity));
Utils::memoryUnlock(this, sizeof(Identity));
Utils::burn(reinterpret_cast<void *>(&this->m_priv), sizeof(this->m_priv));
}
/**
* Set identity to NIL value (all zero)
*/
ZT_INLINE void zero() noexcept { memoryZero(this); }
ZT_INLINE void zero() noexcept
{ memoryZero(this); }
/**
* @return Identity type (undefined if identity is null or invalid)
*/
ZT_INLINE Type type() const noexcept { return m_type; }
ZT_INLINE Type type() const noexcept
{ return m_type; }
/**
* Generate a new identity (address, key pair)
@ -122,17 +124,20 @@ public:
/**
* @return True if this identity contains a private key
*/
ZT_INLINE bool hasPrivate() const noexcept { return m_hasPrivate; }
ZT_INLINE bool hasPrivate() const noexcept
{ return m_hasPrivate; }
/**
* @return This identity's address
*/
ZT_INLINE Address address() const noexcept { return Address(m_fp.address); }
ZT_INLINE Address address() const noexcept
{ return Address(m_fp.address); }
/**
* @return Full fingerprint of this identity (address plus SHA384 of keys)
*/
ZT_INLINE const Fingerprint &fingerprint() const noexcept { return m_fp; }
ZT_INLINE const Fingerprint &fingerprint() const noexcept
{ return m_fp; }
/**
* Compute a hash of this identity's public and private keys.
@ -155,7 +160,7 @@ public:
* @param siglen Length of buffer
* @return Number of bytes actually written to sig or 0 on error
*/
unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const;
unsigned int sign(const void *data, unsigned int len, void *sig, unsigned int siglen) const;
/**
* Verify a message signature against this identity
@ -166,7 +171,7 @@ public:
* @param siglen Length of signature in bytes
* @return True if signature validates and data integrity checks
*/
bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const;
bool verify(const void *data, unsigned int len, const void *sig, unsigned int siglen) const;
/**
* Shortcut method to perform key agreement with another identity
@ -177,7 +182,7 @@ public:
* @param key Result parameter to fill with key bytes
* @return Was agreement successful?
*/
bool agree(const Identity &id,uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const;
bool agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const;
/**
* Serialize to a more human-friendly string
@ -186,8 +191,14 @@ public:
* @param buf Buffer to store string
* @return ASCII string representation of identity (pointer to buf)
*/
char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
ZT_INLINE String toString(const bool includePrivate = false) const { char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]; toString(includePrivate,buf); return String(buf); }
char *toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
ZT_INLINE String toString(const bool includePrivate = false) const
{
char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH];
toString(includePrivate, buf);
return String(buf);
}
/**
* Deserialize a human-friendly string
@ -203,20 +214,36 @@ public:
/**
* @return True if this identity contains something
*/
explicit ZT_INLINE operator bool() const noexcept { return (m_fp); }
explicit ZT_INLINE operator bool() const noexcept
{ return (m_fp); }
ZT_INLINE unsigned long hashCode() const noexcept { return m_fp.hashCode(); }
ZT_INLINE unsigned long hashCode() const noexcept
{ return m_fp.hashCode(); }
ZT_INLINE bool operator==(const Identity &id) const noexcept { return (m_fp == id.m_fp); }
ZT_INLINE bool operator!=(const Identity &id) const noexcept { return !(*this == id); }
ZT_INLINE bool operator<(const Identity &id) const noexcept { return (m_fp < id.m_fp); }
ZT_INLINE bool operator>(const Identity &id) const noexcept { return (id < *this); }
ZT_INLINE bool operator<=(const Identity &id) const noexcept { return !(id < *this); }
ZT_INLINE bool operator>=(const Identity &id) const noexcept { return !(*this < id); }
ZT_INLINE bool operator==(const Identity &id) const noexcept
{ return (m_fp == id.m_fp); }
static constexpr int marshalSizeMax() noexcept { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],bool includePrivate = false) const noexcept;
int unmarshal(const uint8_t *data,int len) noexcept;
ZT_INLINE bool operator!=(const Identity &id) const noexcept
{ return !(*this == id); }
ZT_INLINE bool operator<(const Identity &id) const noexcept
{ return (m_fp < id.m_fp); }
ZT_INLINE bool operator>(const Identity &id) const noexcept
{ return (id < *this); }
ZT_INLINE bool operator<=(const Identity &id) const noexcept
{ return !(id < *this); }
ZT_INLINE bool operator>=(const Identity &id) const noexcept
{ return !(*this < id); }
static constexpr int marshalSizeMax() noexcept
{ return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], bool includePrivate = false) const noexcept;
int unmarshal(const uint8_t *data, int len) noexcept;
private:
void m_computeHash();

View file

@ -119,9 +119,7 @@ Node::Node(
stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int)strlen(RR->publicIdentityStr));
}
// 2X hash our identity private key(s) to obtain a symmetric key for encrypting
// locally cached data at rest (as a defense in depth measure). This is not used
// for any network level encryption or authentication.
// Create a secret key for encrypting local data at rest.
uint8_t tmph[ZT_SHA384_DIGEST_SIZE];
RR->identity.hashWithPrivate(tmph);
SHA384(tmph, tmph, ZT_SHA384_DIGEST_SIZE);

View file

@ -194,7 +194,7 @@
#define ZT_PROTO_PACKET_FRAGMENT_PAYLOAD_START_AT ZT_PROTO_MIN_FRAGMENT_LENGTH
/**
* Header flag indicating that a packet is fragmented and more fragments should be expected
* Outer flag indicating that a packet is fragmented and this is just the head.
*/
#define ZT_PROTO_FLAG_FRAGMENTED 0x40U

View file

@ -145,7 +145,9 @@ static ZT_INLINE void p_salsaCrypt(p_SalsaState *const state, const uint8_t *m,
for (;;) {
if (likely(bytes >= 64)) {
#ifdef ZT_SALSA20_SSE
_mm_prefetch(m + 128, _MM_HINT_T0);
#endif
} else {
for (unsigned int i = 0;i < bytes;++i)
tmp[i] = m[i];

View file

@ -29,7 +29,6 @@ Topology::Topology(const RuntimeEnvironment *renv, void *tPtr) :
Identity id;
int l = id.unmarshal(dptr, drem);
if ((l > 0) && (id)) {
m_roots.insert(id);
ZT_SPEW("restored root %s", id.address().toString().c_str());
if ((drem -= l) <= 0)
break;
@ -189,25 +188,25 @@ void Topology::m_updateRootPeers(void *tPtr)
{
// assumes m_peers_l is locked for write
Vector< SharedPtr< Peer > > rp;
for (Set< Identity >::iterator r(m_roots.begin()); r != m_roots.end(); ++r) {
Map< Address, SharedPtr< Peer > >::iterator pp(m_peers.find(r->address()));
for (Map< Identity, Set< SubscriptionKeyHash > >::iterator r(m_roots.begin()); r != m_roots.end(); ++r) {
Map< Address, SharedPtr< Peer > >::iterator pp(m_peers.find(r->first.address()));
SharedPtr< Peer > p;
if (pp != m_peers.end())
p = pp->second;
if (!p)
m_loadCached(tPtr, r->address(), p);
m_loadCached(tPtr, r->first.address(), p);
if ((!p) || (p->identity() != *r)) {
if ((!p) || (p->identity() != r->first)) {
p.set(new Peer(RR));
p->init(*r);
m_peers[r->address()] = p;
p->init(r->first);
m_peers[r->first.address()] = p;
}
rp.push_back(p);
}
std::sort(rp.begin(), rp.end(), p_RootSortComparisonOperator());
m_rootPeers.swap(rp);
std::sort(m_rootPeers.begin(), m_rootPeers.end(), p_RootSortComparisonOperator());
}
} // namespace ZeroTier

View file

@ -25,6 +25,7 @@
#include "ScopedPtr.hpp"
#include "Fingerprint.hpp"
#include "Containers.hpp"
#include "Blob.hpp"
namespace ZeroTier {
@ -36,6 +37,11 @@ class RuntimeEnvironment;
class Topology
{
public:
/**
* Hash of public keys for signing a root set definition
*/
typedef Blob<ZT_SHA384_DIGEST_SIZE> RootSetId;
Topology(const RuntimeEnvironment *renv, void *tPtr);
/**
@ -237,7 +243,7 @@ private:
RWMutex m_peers_l; // locks m_peers, m_roots, and m_rootPeers
Map< uint64_t, SharedPtr< Path > > m_paths;
Map< Address, SharedPtr< Peer > > m_peers;
Set< Identity > m_roots;
Map< Identity, Set< SubscriptionKeyHash > > m_roots;
Vector< SharedPtr< Peer > > m_rootPeers;
};

View file

@ -132,14 +132,19 @@ char *decimal(unsigned long n, char s[24]) noexcept
char *hex(uint64_t i, char buf[17]) noexcept
{
if (i) {
char *p = buf + 16;
*p = 0;
while (i) {
*(--p) = HEXCHARS[i & 0xfU];
i >>= 4;
if (i != 0) {
char *p = nullptr;
for (int b = 60; b >= 0; b -= 4) {
const unsigned int nyb = (unsigned int)(i >> (unsigned int)b) & 0xfU;
if (p) {
*(p++) = HEXCHARS[nyb];
} else if (nyb != 0) {
p = buf;
*(p++) = HEXCHARS[nyb];
}
}
return p;
*p = 0;
return buf;
} else {
buf[0] = '0';
buf[1] = 0;
@ -267,20 +272,18 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept
close(devURandomFd);
#endif
// Mix in additional entropy from time, the address of 'buf', CPU RDRAND if present, etc.
randomState[0] += (uint64_t)time(nullptr);
randomState[1] += (uint64_t)((uintptr_t)buf);
#ifdef __UNIX_LIKE__
randomState[2] += (uint64_t)getpid();
randomState[3] += (uint64_t)getppid();
randomState[0] += (uint64_t)getpid();
randomState[1] += (uint64_t)getppid();
#endif
#ifdef ZT_ARCH_X64
if (CPUID.rdrand) {
// RDRAND is very slow on some chips, so only sample it a little bit for extra entropy.
uint64_t tmp = 0;
for (int k = 0; k < ZT_GETSECURERANDOM_STATE_SIZE; ++k) {
_rdrand64_step((unsigned long long *)&tmp);
randomState[k] ^= tmp;
}
_rdrand64_step((unsigned long long *)&tmp);
randomState[2] ^= tmp;
_rdrand64_step((unsigned long long *)&tmp);
randomState[3] ^= tmp;
}
#endif
}
@ -289,6 +292,9 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept
// replacing the first 64 bytes with this hash, and then re-initializing
// AES with the first 32 bytes.
randomByteCounter = 0;
randomState[4] += (uint64_t)((uintptr_t)buf);
randomState[5] += (uint64_t)bytes;
randomState[6] += (uint64_t)time(nullptr);
SHA512(randomState, randomState, sizeof(randomState));
randomGen.init(randomState);
}

View file

@ -144,9 +144,6 @@ char *decimal(unsigned long n, char s[24]) noexcept;
/**
* Convert an unsigned integer into hex
*
* The returned pointer won't point to the start of 'buf', since
* hex writing is done in reverse order.
*
* @param i Any unsigned integer
* @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur.
* @return Pointer to s containing hex string with trailing zero byte

View file

@ -276,6 +276,181 @@ typedef void ZT_Identity;
*/
typedef void ZT_Locator;
/**
* Full identity fingerprint with address and 384-bit hash of public key(s)
*/
typedef struct
{
/**
* Short address (only least significant 40 bits are used)
*/
uint64_t address;
/**
* 384-bit hash of identity public key(s)
*/
uint8_t hash[48];
} ZT_Fingerprint;
/**
* Maximum length of string fields in identification certificates
*/
#define ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH 127
/**
* Maximum length of a signature
*/
#define ZT_IDENTIFICATION_CERTIFICATE_MAX_SIGNATURE_SIZE 256
/**
* Information about a real world entity.
*/
typedef struct
{
char country[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char organization[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char unit[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char locality[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char province[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char streetAddress[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char postalCode[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char commonName[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char serialNo[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char email[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
char url[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
} ZT_IdentificationCertificate_Name;
/**
* Identity and optional locator for a node
*/
typedef struct
{
/**
* Identity (never NULL)
*/
const ZT_Identity *identity;
/**
* Locator or NULL if not specified
*/
const ZT_Locator *locator;
} ZT_IdentificationCertificate_Node;
/**
* ID and primary controller for a network
*/
typedef struct
{
/**
* Network ID
*/
uint64_t id;
/**
* Full fingerprint of primary controller
*/
ZT_Fingerprint controller;
} ZT_IdentificationCertificate_Network;
/**
* Identification certificate subject
*/
typedef struct
{
/**
* Identities and optional locators of nodes
*/
ZT_IdentificationCertificate_Node *nodes;
/**
* Number of nodes
*/
unsigned int nodeCount;
/**
* Networks owned by this entity
*/
ZT_IdentificationCertificate_Network *networks;
/**
* Number of networks
*/
unsigned int networkCount;
/**
* Information about owner of items.
*/
ZT_IdentificationCertificate_Name name;
} ZT_IdentificationCertificate_Subject;
/**
* Identification certificate
*
* This is designed so it could be converted to/from an X509 format
* for interoperability with X509 systems. OCSP could be implemented
* too, though it would probably require the development of an OCSP
* proxy server that queried the issuer via the ZeroTier protocol.
*/
typedef struct
{
/**
* Serial number, a SHA384 hash of this certificate.
*/
uint8_t serialNo[48];
/**
* Certificate version
*/
unsigned int version;
/**
* Maximum path length from this certificate toward further certificates.
*
* Subjects may sign other certificates whose path lengths are less than
* this value. A value of zero indicates that no identification certificates
* may be signed (not a CA).
*/
unsigned int maxPathLength;
/**
* Flags (for future use, currently zero).
*
* This could be used to implement key usage flags similar to X509 if
* these are needed.
*/
uint64_t flags;
/**
* Valid time range: not before, not after.
*/
int64_t validity[2];
/**
* Subject of certificate
*/
ZT_IdentificationCertificate_Subject subject;
/**
* Issuer node identity and public key(s).
*/
const ZT_Identity *issuer;
/**
* Issuer information
*/
ZT_IdentificationCertificate_Name issuerName;
/**
* Signature by issuer (algorithm determined by identity type).
*/
uint8_t signature[ZT_IDENTIFICATION_CERTIFICATE_MAX_SIGNATURE_SIZE];
/**
* Size of signature in bytes.
*/
unsigned int signatureSize;
} ZT_IdentificationCertificate;
/**
* Credential type IDs
*/
@ -308,22 +483,6 @@ enum ZT_EndpointType
ZT_ENDPOINT_TYPE_IP_HTTP = 8 // IP/HTTP encapsulation
};
/**
* Full identity fingerprint with address and 384-bit hash of public key(s)
*/
typedef struct
{
/**
* Short address (only least significant 40 bits are used)
*/
uint64_t address;
/**
* 384-bit hash of identity public key(s)
*/
uint8_t hash[48];
} ZT_Fingerprint;
/**
* Flag indicating that VL1 tracing should be generated
*/

View file

@ -11,9 +11,41 @@
*/
/****/
#if !defined(_WIN32) && !defined(_WIN64)
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#endif
#include "zerotier_cgo.h"
int main() {
ZeroTierMain();
return 0;
int main(int argc,char **argv)
{
// Fork into background if run with 'service -d'. This is best done prior
// to launching the Go code, since Go likes to start thread pools and stuff
// that don't play nice with fork. This is obviously unsupported on Windows.
#if !defined(_WIN32) && !defined(_WIN64)
for(int i=1;i<argc;) {
if (strcmp(argv[i++], "service") == 0) {
for(;i<argc;) {
if (strcmp(argv[i++], "-d") == 0) {
long p = (long)fork();
if (p < 0) {
fprintf(stderr,"FATAL: fork() failed!\n");
return -1;
} else if (p > 0) {
return 0;
}
break;
}
}
break;
}
}
#endif
ZeroTierMain();
return 0;
}

View file

@ -47,7 +47,7 @@ var nullLogger = log.New(ioutil.Discard, "", 0)
const (
NetworkIDStringLength = 16
NEtworkIDLength = 8
NetworkIDLength = 8
AddressStringLength = 10
AddressLength = 5
@ -883,7 +883,7 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Poi
*((*uintptr)(dataP)) = 0
tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id)))
if found && len(tmp) > 0 {
cData := C.malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function
cData := C.ZT_malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function
if uintptr(cData) == 0 {
return -1
}

View file

@ -724,3 +724,6 @@ extern "C" int ZT_isTemporaryV6Address(const char *ifname, const struct sockaddr
return 0;
#endif
}
extern "C" void *ZT_malloc(unsigned long s)
{ return (void *)malloc((size_t)s); }

View file

@ -50,6 +50,8 @@ void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu);
int ZT_isTemporaryV6Address(const char *ifname,const struct sockaddr_storage *a);
void *ZT_malloc(unsigned long s);
#ifdef __cplusplus
}
#endif