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 . docker build -t registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP} -f controller/central-docker/Dockerfile .
clean: clean:
rm -rf ${BUILDDIR} cmake-build-* rm -rf ${BUILDDIR}
distclean: distclean:
rm -rf ${BUILDDIR} cmake-build-* rm -rf ${BUILDDIR}

View file

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

View file

@ -15,8 +15,11 @@ package cli
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/signal" "os/signal"
"path"
"strconv"
"syscall" "syscall"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
@ -28,16 +31,20 @@ func Service(basePath, authToken string, args []string) {
os.Exit(1) 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) node, err := zerotier.NewNode(basePath)
if err != nil { if err != nil {
fmt.Println("FATAL: error initializing node: " + err.Error()) 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) _ = os.Remove(pidPath)
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS) os.Exit(1)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2)
<-osSignalChannel
node.Close()
os.Exit(0)
} }

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) project(zt_core)
configure_file( configure_file(
version.h.in version.h.in
version.h version.h
) )
set(core_headers set(core_headers
zerotier.h zerotier.h
Address.hpp Address.hpp
Blob.hpp
Buf.hpp Buf.hpp
C25519.hpp C25519.hpp
Capability.hpp Capability.hpp
@ -24,6 +25,7 @@ set(core_headers
Expect.hpp Expect.hpp
FCV.hpp FCV.hpp
Fingerprint.hpp Fingerprint.hpp
IdentificationCertificate.hpp
Identity.hpp Identity.hpp
InetAddress.hpp InetAddress.hpp
Locator.hpp Locator.hpp
@ -67,6 +69,7 @@ set(core_src
Dictionary.cpp Dictionary.cpp
ECC384.cpp ECC384.cpp
Endpoint.cpp Endpoint.cpp
IdentificationCertificate.cpp
Identity.cpp Identity.cpp
InetAddress.cpp InetAddress.cpp
Locator.cpp Locator.cpp

View file

@ -12,56 +12,64 @@
/****/ /****/
#include "Dictionary.hpp" #include "Dictionary.hpp"
#include "SHA512.hpp"
namespace ZeroTier { namespace ZeroTier {
Dictionary::Dictionary() 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)]; static const Vector< uint8_t > s_emptyEntry;
} if (k) {
SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(s_key(k)));
const Vector<uint8_t> &Dictionary::operator[](const char *k) const return (e == m_entries.end()) ? s_emptyEntry : e->second;
{ } else {
static const Vector<uint8_t> s_emptyEntry; SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(""));
SortedMap< String, Vector<uint8_t> >::const_iterator e(m_entries.find(s_key(k))); return (e == m_entries.end()) ? s_emptyEntry : e->second;
return (e == m_entries.end()) ? s_emptyEntry : e->second; }
} }
void Dictionary::add(const char *k, bool v) void Dictionary::add(const char *k, bool v)
{ {
Vector<uint8_t> &e = (*this)[k]; Vector< uint8_t > &e = (*this)[k];
e.resize(2); e.resize(2);
e[0] = (uint8_t) (v ? '1' : '0'); e[0] = (uint8_t)(v ? '1' : '0');
e[1] = 0; e[1] = 0;
} }
void Dictionary::add(const char *k, const Address &v) 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); 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) void Dictionary::add(const char *k, const char *v)
{ {
if ((v) && (*v)) { if ((v) && (*v)) {
Vector<uint8_t> &e = (*this)[k]; Vector< uint8_t > &e = (*this)[k];
e.clear(); e.clear();
while (*v) 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) 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) { 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 { } else {
e.clear(); 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 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()) { if (!e.empty()) {
switch ((char) e[0]) { switch ((char)e[0]) {
case '1': case '1':
case 't': case 't':
case 'T': case 'T':
@ -89,15 +97,15 @@ uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const
{ {
uint8_t tmp[18]; uint8_t tmp[18];
uint64_t v = dfl; uint64_t v = dfl;
const Vector<uint8_t> &e = (*this)[k]; const Vector< uint8_t > &e = (*this)[k];
if (!e.empty()) { if (!e.empty()) {
if (e.back() != 0) { if (e.back() != 0) {
const unsigned long sl = e.size(); const unsigned long sl = e.size();
Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl); Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl);
tmp[17] = 0; 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; return v;
} }
@ -106,13 +114,13 @@ char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const
{ {
if (cap == 0) // sanity check if (cap == 0) // sanity check
return v; return v;
const Vector<uint8_t> &e = (*this)[k]; const Vector< uint8_t > &e = (*this)[k];
unsigned int i = 0; unsigned int i = 0;
const unsigned int last = cap - 1; const unsigned int last = cap - 1;
for (;;) { for (;;) {
if ((i == last) || (i >= (unsigned int)e.size())) if ((i == last) || (i >= (unsigned int)e.size()))
break; break;
v[i] = (char) e[i]; v[i] = (char)e[i];
++i; ++i;
} }
v[i] = 0; v[i] = 0;
@ -124,16 +132,14 @@ void Dictionary::clear()
m_entries.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(); out.clear();
for (SortedMap< String, Vector<uint8_t> >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) { 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());
s_appendKey(out, ti->first.data()); for (Vector< uint8_t >::const_iterator i(ti->second.begin()); i != ti->second.end(); ++i)
for (Vector<uint8_t>::const_iterator i(ti->second.begin());i != ti->second.end();++i) s_appendValueByte(out, *i);
s_appendValueByte(out, *i); out.push_back((uint8_t)'\n');
out.push_back((uint8_t) '\n');
}
} }
out.push_back(0); out.push_back(0);
} }
@ -142,9 +148,9 @@ bool Dictionary::decode(const void *data, unsigned int len)
{ {
clear(); clear();
String k; String k;
Vector<uint8_t> *v = nullptr; Vector< uint8_t > *v = nullptr;
bool escape = false; 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]; uint8_t c = reinterpret_cast<const uint8_t *>(data)[di];
if (!c) break; if (!c) break;
if (v) { if (v) {
@ -168,7 +174,7 @@ bool Dictionary::decode(const void *data, unsigned int len)
break; break;
} }
} else { } else {
if (c == (uint8_t) '\n') { if (c == (uint8_t)'\n') {
k.clear(); k.clear();
v = nullptr; v = nullptr;
} else if (c == 92) { // backslash } else if (c == 92) { // backslash
@ -180,7 +186,7 @@ bool Dictionary::decode(const void *data, unsigned int len)
} else { } else {
if ((c < 33) || (c > 126) || (c == 92)) { if ((c < 33) || (c > 126) || (c == 92)) {
return false; return false;
} else if (c == (uint8_t) '=') { } else if (c == (uint8_t)'=') {
k.push_back(0); k.push_back(0);
v = &m_entries[k]; v = &m_entries[k];
} else if (k.size() < 7) { } else if (k.size() < 7) {
@ -193,59 +199,4 @@ bool Dictionary::decode(const void *data, unsigned int len)
return true; 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 } // namespace ZeroTier

View file

@ -19,10 +19,6 @@
#include "Address.hpp" #include "Address.hpp"
#include "Buf.hpp" #include "Buf.hpp"
#include "Containers.hpp" #include "Containers.hpp"
#include "C25519.hpp"
#include "ECC384.hpp"
#define ZT_DICTIONARY_SIGNATURE_KEY "@S"
namespace ZeroTier { namespace ZeroTier {
@ -46,10 +42,10 @@ class Identity;
class Dictionary class Dictionary
{ {
public: public:
typedef SortedMap< String, Vector < uint8_t > > typedef SortedMap< String, Vector < uint8_t > >::const_iterator const_iterator;
::const_iterator const_iterator;
Dictionary(); Dictionary();
~Dictionary();
/** /**
* Get a reference to a value * Get a reference to a value
@ -90,12 +86,17 @@ public:
* @param k Key to set * @param k Key to set
* @param v Integer to set, will be cast to uint64_t and stored as hex * @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, const uint64_t v)
ZT_INLINE void add(const char *const k, I v) { char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); }
{
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 * Add an address in 10-digit hex string format
@ -145,6 +146,7 @@ public:
/** /**
* Get an object supporting the marshal/unmarshal interface pattern * Get an object supporting the marshal/unmarshal interface pattern
* *
* @tparam T Object type (inferred)
* @param k Key to look up * @param k Key to look up
* @param obj Object to unmarshal() into * @param obj Object to unmarshal() into
* @return True if unmarshal was successful * @return True if unmarshal was successful
@ -158,6 +160,27 @@ public:
return (obj.unmarshal(d.data(), (unsigned int)d.size()) > 0); 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 * Erase all entries in dictionary
*/ */
@ -179,9 +202,8 @@ public:
* Encode to a string in the supplied vector * Encode to a string in the supplied vector
* *
* @param out String encoded dictionary * @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 * Decode a string encoded dictionary
@ -195,34 +217,6 @@ public:
*/ */
bool decode(const void *data, unsigned int len); 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) * Append a key=value pair to a buffer (vector or FCV)
* *
@ -376,6 +370,19 @@ public:
return mlen; 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: private:
template< typename V > template< typename V >
ZT_INLINE static void s_appendValueByte(V &out, const uint8_t c) 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 ZT_INLINE Identity() noexcept
{ {
Utils::memoryLock(this,sizeof(Identity)); Utils::memoryLock(this, sizeof(Identity));
memoryZero(this); memoryZero(this);
} }
@ -78,25 +78,27 @@ public:
*/ */
explicit ZT_INLINE Identity(const char *str) explicit ZT_INLINE Identity(const char *str)
{ {
Utils::memoryLock(this,sizeof(Identity)); Utils::memoryLock(this, sizeof(Identity));
fromString(str); fromString(str);
} }
ZT_INLINE ~Identity() 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)); Utils::burn(reinterpret_cast<void *>(&this->m_priv), sizeof(this->m_priv));
} }
/** /**
* Set identity to NIL value (all zero) * 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) * @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) * Generate a new identity (address, key pair)
@ -122,17 +124,20 @@ public:
/** /**
* @return True if this identity contains a private key * @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 * @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) * @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. * Compute a hash of this identity's public and private keys.
@ -155,7 +160,7 @@ public:
* @param siglen Length of buffer * @param siglen Length of buffer
* @return Number of bytes actually written to sig or 0 on error * @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 * Verify a message signature against this identity
@ -166,7 +171,7 @@ public:
* @param siglen Length of signature in bytes * @param siglen Length of signature in bytes
* @return True if signature validates and data integrity checks * @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 * Shortcut method to perform key agreement with another identity
@ -177,7 +182,7 @@ public:
* @param key Result parameter to fill with key bytes * @param key Result parameter to fill with key bytes
* @return Was agreement successful? * @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 * Serialize to a more human-friendly string
@ -186,8 +191,14 @@ public:
* @param buf Buffer to store string * @param buf Buffer to store string
* @return ASCII string representation of identity (pointer to buf) * @return ASCII string representation of identity (pointer to buf)
*/ */
char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const; 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); }
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 * Deserialize a human-friendly string
@ -203,20 +214,36 @@ public:
/** /**
* @return True if this identity contains something * @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
ZT_INLINE bool operator!=(const Identity &id) const noexcept { return !(*this == id); } { return (m_fp == id.m_fp); }
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; } ZT_INLINE bool operator!=(const Identity &id) const noexcept
int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],bool includePrivate = false) const noexcept; { return !(*this == id); }
int unmarshal(const uint8_t *data,int len) noexcept;
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: private:
void m_computeHash(); 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)); 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 // Create a secret key for encrypting local data at rest.
// locally cached data at rest (as a defense in depth measure). This is not used
// for any network level encryption or authentication.
uint8_t tmph[ZT_SHA384_DIGEST_SIZE]; uint8_t tmph[ZT_SHA384_DIGEST_SIZE];
RR->identity.hashWithPrivate(tmph); RR->identity.hashWithPrivate(tmph);
SHA384(tmph, tmph, ZT_SHA384_DIGEST_SIZE); 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 #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 #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 (;;) { for (;;) {
if (likely(bytes >= 64)) { if (likely(bytes >= 64)) {
#ifdef ZT_SALSA20_SSE
_mm_prefetch(m + 128, _MM_HINT_T0); _mm_prefetch(m + 128, _MM_HINT_T0);
#endif
} else { } else {
for (unsigned int i = 0;i < bytes;++i) for (unsigned int i = 0;i < bytes;++i)
tmp[i] = m[i]; tmp[i] = m[i];

View file

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

View file

@ -25,6 +25,7 @@
#include "ScopedPtr.hpp" #include "ScopedPtr.hpp"
#include "Fingerprint.hpp" #include "Fingerprint.hpp"
#include "Containers.hpp" #include "Containers.hpp"
#include "Blob.hpp"
namespace ZeroTier { namespace ZeroTier {
@ -36,6 +37,11 @@ class RuntimeEnvironment;
class Topology class Topology
{ {
public: public:
/**
* Hash of public keys for signing a root set definition
*/
typedef Blob<ZT_SHA384_DIGEST_SIZE> RootSetId;
Topology(const RuntimeEnvironment *renv, void *tPtr); Topology(const RuntimeEnvironment *renv, void *tPtr);
/** /**
@ -237,7 +243,7 @@ private:
RWMutex m_peers_l; // locks m_peers, m_roots, and m_rootPeers RWMutex m_peers_l; // locks m_peers, m_roots, and m_rootPeers
Map< uint64_t, SharedPtr< Path > > m_paths; Map< uint64_t, SharedPtr< Path > > m_paths;
Map< Address, SharedPtr< Peer > > m_peers; Map< Address, SharedPtr< Peer > > m_peers;
Set< Identity > m_roots; Map< Identity, Set< SubscriptionKeyHash > > m_roots;
Vector< SharedPtr< Peer > > m_rootPeers; 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 char *hex(uint64_t i, char buf[17]) noexcept
{ {
if (i) { if (i != 0) {
char *p = buf + 16; char *p = nullptr;
*p = 0; for (int b = 60; b >= 0; b -= 4) {
while (i) { const unsigned int nyb = (unsigned int)(i >> (unsigned int)b) & 0xfU;
*(--p) = HEXCHARS[i & 0xfU]; if (p) {
i >>= 4; *(p++) = HEXCHARS[nyb];
} else if (nyb != 0) {
p = buf;
*(p++) = HEXCHARS[nyb];
}
} }
return p; *p = 0;
return buf;
} else { } else {
buf[0] = '0'; buf[0] = '0';
buf[1] = 0; buf[1] = 0;
@ -267,20 +272,18 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept
close(devURandomFd); close(devURandomFd);
#endif #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__ #ifdef __UNIX_LIKE__
randomState[2] += (uint64_t)getpid(); randomState[0] += (uint64_t)getpid();
randomState[3] += (uint64_t)getppid(); randomState[1] += (uint64_t)getppid();
#endif #endif
#ifdef ZT_ARCH_X64 #ifdef ZT_ARCH_X64
if (CPUID.rdrand) { if (CPUID.rdrand) {
// RDRAND is very slow on some chips, so only sample it a little bit for extra entropy.
uint64_t tmp = 0; uint64_t tmp = 0;
for (int k = 0; k < ZT_GETSECURERANDOM_STATE_SIZE; ++k) { _rdrand64_step((unsigned long long *)&tmp);
_rdrand64_step((unsigned long long *)&tmp); randomState[2] ^= tmp;
randomState[k] ^= tmp; _rdrand64_step((unsigned long long *)&tmp);
} randomState[3] ^= tmp;
} }
#endif #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 // replacing the first 64 bytes with this hash, and then re-initializing
// AES with the first 32 bytes. // AES with the first 32 bytes.
randomByteCounter = 0; 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)); SHA512(randomState, randomState, sizeof(randomState));
randomGen.init(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 * 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 i Any unsigned integer
* @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur. * @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 * @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; 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 * Credential type IDs
*/ */
@ -308,22 +483,6 @@ enum ZT_EndpointType
ZT_ENDPOINT_TYPE_IP_HTTP = 8 // IP/HTTP encapsulation 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 * 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" #include "zerotier_cgo.h"
int main() { int main(int argc,char **argv)
ZeroTierMain(); {
return 0; // 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 ( const (
NetworkIDStringLength = 16 NetworkIDStringLength = 16
NEtworkIDLength = 8 NetworkIDLength = 8
AddressStringLength = 10 AddressStringLength = 10
AddressLength = 5 AddressLength = 5
@ -883,7 +883,7 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Poi
*((*uintptr)(dataP)) = 0 *((*uintptr)(dataP)) = 0
tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id)))
if found && len(tmp) > 0 { 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 { if uintptr(cData) == 0 {
return -1 return -1
} }

View file

@ -724,3 +724,6 @@ extern "C" int ZT_isTemporaryV6Address(const char *ifname, const struct sockaddr
return 0; return 0;
#endif #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); int ZT_isTemporaryV6Address(const char *ifname,const struct sockaddr_storage *a);
void *ZT_malloc(unsigned long s);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif