TON of refactoring, moon/planet is DEAD, ECC P-384 is integrated (but not enabled), and multicast work and cleanup. Whew.

This commit is contained in:
Adam Ierymenko 2019-07-17 10:52:08 -05:00
parent 640bbaabbf
commit fe2215df00
36 changed files with 556 additions and 1844 deletions

View file

@ -98,7 +98,6 @@ public:
* @param keylen Number of key bytes to generate
*/
static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen) { agree(mine.priv,their,keybuf,keylen); }
/**
* Sign a message with a sender's key pair
@ -120,7 +119,6 @@ public:
* @param signature Buffer to fill with signature -- MUST be 96 bytes in length
*/
static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature) { sign(mine.priv,mine.pub,msg,len,signature); }
/**
* Sign a message with a sender's key pair

View file

@ -34,6 +34,23 @@
namespace ZeroTier {
bool Capability::sign(const Identity &from,const Address &to)
{
try {
for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
if (!(_custody[i].to)) {
Buffer<(sizeof(Capability) * 2)> tmp;
this->serialize(tmp,true);
_custody[i].to = to;
_custody[i].from = from.address();
_custody[i].signatureLength = from.sign(tmp.data(),tmp.size(),_custody[i].signature,sizeof(_custody[i].signature));
return true;
}
}
} catch ( ... ) {}
return false;
}
int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
{
try {
@ -57,7 +74,7 @@ int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
const Identity id(RR->topology->getIdentity(tPtr,_custody[c].from));
if (id) {
if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature,_custody[c].signatureLength))
return -1;
} else {
RR->sw->requestWhois(tPtr,RR->node->now(),_custody[c].from);

View file

@ -154,22 +154,7 @@ public:
* @param to Recipient of this signature
* @return True if signature successful and chain of custody appended
*/
inline bool sign(const Identity &from,const Address &to)
{
try {
for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
if (!(_custody[i].to)) {
Buffer<(sizeof(Capability) * 2)> tmp;
this->serialize(tmp,true);
_custody[i].to = to;
_custody[i].from = from.address();
_custody[i].signature = from.sign(tmp.data(),tmp.size());
return true;
}
}
} catch ( ... ) {}
return false;
}
bool sign(const Identity &from,const Address &to);
/**
* Verify this capability's chain of custody and signatures
@ -409,9 +394,9 @@ public:
if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) {
_custody[i].to.appendTo(b);
_custody[i].from.appendTo(b);
b.append((uint8_t)1); // 1 == Ed25519 signature
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
b.append(_custody[i].signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)1);
b.append((uint16_t)_custody[i].signatureLength);
b.append(_custody[i].signature,_custody[i].signatureLength);
} else {
b.append((unsigned char)0,ZT_ADDRESS_LENGTH); // zero 'to' terminates chain
break;
@ -454,10 +439,11 @@ public:
_custody[i].to = to;
_custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
_custody[i].signatureLength = b.template at<uint16_t>(p);
if (_custody[i].signatureLength > sizeof(_custody[i].signature))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
p += 2;
memcpy(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
memcpy(_custody[i].signature,b.field(p,_custody[i].signatureLength),_custody[i].signatureLength); p += _custody[i].signatureLength;
} else {
p += 2 + b.template at<uint16_t>(p);
}
@ -489,7 +475,8 @@ private:
struct {
Address to;
Address from;
C25519::Signature signature;
unsigned int signatureLength;
uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE];
} _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
};

View file

@ -84,7 +84,7 @@ std::string CertificateOfMembership::toString() const
if (_signedBy) {
s.push_back(':');
s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp));
s.append(Utils::hex(_signature,_signatureLength,tmp));
}
return s;
@ -92,9 +92,9 @@ std::string CertificateOfMembership::toString() const
void CertificateOfMembership::fromString(const char *s)
{
_qualifierCount = 0;
_signedBy.zero();
memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
_qualifierCount = 0;
_signatureLength = 0;
if (!*s)
return;
@ -145,8 +145,7 @@ void CertificateOfMembership::fromString(const char *s)
colonAt = 0;
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
if (colonAt) {
if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN)
_signedBy.zero();
_signatureLength = Utils::unhex(s,colonAt,_signature,sizeof(_signature));
} else {
_signedBy.zero();
}
@ -208,7 +207,7 @@ bool CertificateOfMembership::sign(const Identity &with)
}
try {
_signature = with.sign(buf,ptr * sizeof(uint64_t));
_signatureLength = with.sign(buf,ptr * sizeof(uint64_t),_signature,sizeof(_signature));
_signedBy = with.address();
return true;
} catch ( ... ) {
@ -235,7 +234,7 @@ int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) con
buf[ptr++] = Utils::hton(_qualifiers[i].value);
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
}
return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
return (id.verify(buf,ptr * sizeof(uint64_t),_signature,_signatureLength) ? 0 : -1);
}
} // namespace ZeroTier

View file

@ -113,7 +113,8 @@ public:
* Create an empty certificate of membership
*/
CertificateOfMembership() :
_qualifierCount(0) {}
_qualifierCount(0),
_signatureLength(0) {}
/**
* Create from required fields common to all networks
@ -135,7 +136,7 @@ public:
_qualifiers[2].value = issuedTo.toInt();
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
_qualifierCount = 3;
memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
_signatureLength = 0;
}
/**
@ -279,8 +280,13 @@ public:
b.append(_qualifiers[i].maxDelta);
}
_signedBy.appendTo(b);
if (_signedBy)
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
if ((_signedBy)&&(_signatureLength == 96)) {
// UGLY: Ed25519 signatures in ZT are 96 bytes (64 + 32 bytes of hash).
// P-384 signatures are also 96 bytes, praise the horned one. That means
// we don't need to include a length. If we ever do we will need a new
// serialized object version, but only for those with length != 96.
b.append(_signature,96);
}
}
template<unsigned int C>
@ -288,8 +294,9 @@ public:
{
unsigned int p = startAt;
_qualifierCount = 0;
_signedBy.zero();
_qualifierCount = 0;
_signatureLength = 0;
if (b[p++] != 1)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
@ -316,8 +323,10 @@ public:
p += ZT_ADDRESS_LENGTH;
if (_signedBy) {
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
p += ZT_C25519_SIGNATURE_LEN;
// See "UGLY" comment in serialize()...
_signatureLength = 96;
memcpy(_signature,b.field(p,96),96);
p += 96;
}
return (p - startAt);
@ -329,13 +338,15 @@ public:
return false;
if (_qualifierCount != c._qualifierCount)
return false;
if (_signatureLength != c._signatureLength)
return false;
for(unsigned int i=0;i<_qualifierCount;++i) {
const _Qualifier &a = _qualifiers[i];
const _Qualifier &b = c._qualifiers[i];
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
return false;
}
return (memcmp(_signature.data,c._signature.data,ZT_C25519_SIGNATURE_LEN) == 0);
return (memcmp(_signature,c._signature,_signatureLength) == 0);
}
inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
@ -352,7 +363,8 @@ private:
Address _signedBy;
_Qualifier _qualifiers[ZT_NETWORK_COM_MAX_QUALIFIERS];
unsigned int _qualifierCount;
C25519::Signature _signature;
unsigned int _signatureLength;
uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
};
} // namespace ZeroTier

View file

@ -34,6 +34,32 @@
namespace ZeroTier {
void CertificateOfOwnership::addThing(const InetAddress &ip)
{
if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
if (ip.ss_family == AF_INET) {
_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
++_thingCount;
} else if (ip.ss_family == AF_INET6) {
_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
++_thingCount;
}
}
bool CertificateOfOwnership::sign(const Identity &signer)
{
if (signer.hasPrivate()) {
Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
_signedBy = signer.address();
this->serialize(tmp,true);
_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
return true;
}
return false;
}
int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
{
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
@ -46,7 +72,7 @@ int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) cons
try {
Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
this->serialize(tmp,true);
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
} catch ( ... ) {
return -1;
}

View file

@ -107,19 +107,7 @@ public:
return this->_owns(THING_MAC_ADDRESS,tmp,6);
}
inline void addThing(const InetAddress &ip)
{
if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
if (ip.ss_family == AF_INET) {
_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
++_thingCount;
} else if (ip.ss_family == AF_INET6) {
_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
++_thingCount;
}
}
void addThing(const InetAddress &ip);
inline void addThing(const MAC &mac)
{
@ -133,17 +121,7 @@ public:
* @param signer Signing identity, must have private key
* @return True if signature was successful
*/
inline bool sign(const Identity &signer)
{
if (signer.hasPrivate()) {
Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
_signedBy = signer.address();
this->serialize(tmp,true);
_signature = signer.sign(tmp.data(),tmp.size());
return true;
}
return false;
}
bool sign(const Identity &signer);
/**
* @param RR Runtime environment to allow identity lookup for signedBy
@ -170,9 +148,9 @@ public:
_issuedTo.appendTo(b);
_signedBy.appendTo(b);
if (!forSign) {
b.append((uint8_t)1); // 1 == Ed25519
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)1);
b.append((uint16_t)_signatureLength); // length of signature
b.append(_signature,_signatureLength);
}
b.append((uint16_t)0); // length of additional fields, currently 0
@ -203,10 +181,11 @@ public:
_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
_signatureLength = b.template at<uint16_t>(p);
if (_signatureLength > sizeof(_signature))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
p += 2;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
memcpy(_signature,b.field(p,_signatureLength),_signatureLength); p += _signatureLength;
} else {
p += 2 + b.template at<uint16_t>(p);
}
@ -236,7 +215,8 @@ private:
uint8_t _thingValues[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS][ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE];
Address _issuedTo;
Address _signedBy;
C25519::Signature _signature;
unsigned int _signatureLength;
uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
};
} // namespace ZeroTier

View file

@ -613,6 +613,11 @@
*/
#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
/**
* Size of a buffer to store either a C25519 or an ECC P-384 signature
*/
#define ZT_SIGNATURE_BUFFER_SIZE 96
/**
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
*/

View file

@ -61,12 +61,46 @@
namespace ZeroTier {
/**
* Generate a NIST P-384 key pair
*
* @param pub Buffer to receive point compressed public key
* @param priv Buffer to receiver private key
*/
void ECC384GenerateKey(uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE]);
/**
* Sign a hash with a NIST P-384 private key
*
* The hash must be 48 bytes in size and is typically the first 48 bytes
* of a SHA512 hash or something similar. Extra bytes of course are ignored.
*
* @param priv Private key
* @param hash 48-byte hash
* @param sig Buffer to receive signature
*/
void ECC384ECDSASign(const uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE],const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],uint8_t sig[ZT_ECC384_SIGNATURE_SIZE]);
/**
* Verify a signature
*
* @param pub Public key
* @param hash 48-byte hash (usually first 48 bytes of SHA512(msg))
* @param sig Signature to check
* @return True if signature is valid
*/
bool ECC384ECDSAVerify(const uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],const uint8_t sig[ZT_ECC384_SIGNATURE_SIZE]);
/**
* Perform ECDH key agreement
*
* The secret generated here is the raw 48-byte result of ECDH.
* It's typically hashed prior to use.
*
* @param theirPub Remote public key
* @param ourPriv Local private key
* @param secret Buffer to receive 48-byte secret
*/
bool ECC384ECDH(const uint8_t theirPub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t ourPriv[ZT_ECC384_PRIVATE_KEY_SIZE],uint8_t secret[ZT_ECC384_SHARED_SECRET_SIZE]);
} // namespace ZeroTier

View file

@ -35,14 +35,16 @@
#include "Salsa20.hpp"
#include "Utils.hpp"
namespace ZeroTier {
//////////////////////////////////////////////////////////////////////////////
// This is the memory-hard hash used for type 0 identities' addresses
// These can't be changed without a new identity type. They define the
// parameters of the hashcash hashing/searching algorithm.
#define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
#define ZT_IDENTITY_GEN_MEMORY 2097152
namespace ZeroTier {
// A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing
static inline void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
{
@ -94,23 +96,39 @@ struct _Identity_generate_cond
char *genmem;
};
void Identity::generate()
//////////////////////////////////////////////////////////////////////////////
void Identity::generate(const Type t)
{
unsigned char digest[64];
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
uint8_t digest[64];
switch(t) {
case C25519: {
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
C25519::Pair kp;
do {
kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
} while (_address.isReserved());
C25519::Pair kp;
do {
kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
} while (_address.isReserved());
_publicKey = kp.pub;
if (!_privateKey)
_privateKey = new C25519::Private();
*_privateKey = kp.priv;
memcpy(_k.t0.pub.data,kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
memcpy(_k.t0.priv.data,kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
_type = C25519;
_hasPrivate = true;
delete [] genmem;
} break;
delete [] genmem;
case P384: {
do {
ECC384GenerateKey(_k.t1.pub,_k.t1.priv);
SHA512::hash(digest,_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
_address.setTo(digest + 59,ZT_ADDRESS_LENGTH);
} while (_address.isReserved());
_type = P384;
_hasPrivate = true;
} break;
}
}
bool Identity::locallyValidate() const
@ -118,40 +136,70 @@ bool Identity::locallyValidate() const
if (_address.isReserved())
return false;
unsigned char digest[64];
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
_computeMemoryHardHash(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem;
switch(_type) {
case C25519: {
unsigned char digest[64];
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
_computeMemoryHardHash(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem;
unsigned char addrb[5];
_address.copyTo(addrb,5);
return (
(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
(digest[59] == addrb[0])&&
(digest[60] == addrb[1])&&
(digest[61] == addrb[2])&&
(digest[62] == addrb[3])&&
(digest[63] == addrb[4]));
} break;
unsigned char addrb[5];
_address.copyTo(addrb,5);
case P384: {
return true;
} break;
}
return (
(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
(digest[59] == addrb[0])&&
(digest[60] == addrb[1])&&
(digest[61] == addrb[2])&&
(digest[62] == addrb[3])&&
(digest[63] == addrb[4]));
return false;
}
char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
{
char *p = buf;
Utils::hex10(_address.toInt(),p);
p += 10;
*(p++) = ':';
*(p++) = '0';
*(p++) = ':';
Utils::hex(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,p);
p += ZT_C25519_PUBLIC_KEY_LEN * 2;
if ((_privateKey)&&(includePrivate)) {
*(p++) = ':';
Utils::hex(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN,p);
p += ZT_C25519_PRIVATE_KEY_LEN * 2;
switch(_type) {
case C25519: {
char *p = buf;
Utils::hex10(_address.toInt(),p);
p += 10;
*(p++) = ':';
*(p++) = '0';
*(p++) = ':';
Utils::hex(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,p);
p += ZT_C25519_PUBLIC_KEY_LEN * 2;
if ((_hasPrivate)&&(includePrivate)) {
*(p++) = ':';
Utils::hex(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN,p);
p += ZT_C25519_PRIVATE_KEY_LEN * 2;
}
*p = (char)0;
return buf;
}
case P384: {
char *p = buf;
Utils::hex10(_address.toInt(),p);
p += 10;
*(p++) = ':';
*(p++) = '1';
*(p++) = ':';
Utils::hex(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,p);
p += ZT_ECC384_PUBLIC_KEY_SIZE * 2;
if ((_hasPrivate)&&(includePrivate)) {
*(p++) = ':';
Utils::hex(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE,p);
p += ZT_ECC384_PRIVATE_KEY_SIZE * 2;
}
*p = (char)0;
return buf;
} break;
}
*p = (char)0;
return buf;
}
bool Identity::fromString(const char *str)
@ -166,8 +214,7 @@ bool Identity::fromString(const char *str)
return false;
}
delete _privateKey;
_privateKey = (C25519::Private *)0;
_hasPrivate = false;
int fno = 0;
char *saveptr = (char *)0;
@ -181,22 +228,49 @@ bool Identity::fromString(const char *str)
}
break;
case 1:
if ((f[0] != '0')||(f[1])) {
if ((f[0] == '0')&&(!f[1])) {
_type = C25519;
} else if ((f[0] == '1')&&(!f[1])) {
_type = P384;
} else {
_address.zero();
return false;
}
break;
case 2:
if (Utils::unhex(f,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
_address.zero();
return false;
switch(_type) {
case C25519:
if (Utils::unhex(f,_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
_address.zero();
return false;
}
break;
case P384:
if (Utils::unhex(f,_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) != ZT_ECC384_PUBLIC_KEY_SIZE) {
_address.zero();
return false;
}
break;
}
break;
case 3:
_privateKey = new C25519::Private();
if (Utils::unhex(f,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
_address.zero();
return false;
switch(_type) {
case C25519:
if (Utils::unhex(f,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
_address.zero();
return false;
} else {
_hasPrivate = true;
}
break;
case P384:
if (Utils::unhex(f,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE) != ZT_ECC384_PRIVATE_KEY_SIZE) {
_address.zero();
return false;
} else {
_hasPrivate = true;
}
break;
}
break;
default:

View file

@ -36,6 +36,7 @@
#include "C25519.hpp"
#include "Buffer.hpp"
#include "SHA512.hpp"
#include "ECC384.hpp"
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
@ -54,61 +55,45 @@ namespace ZeroTier {
class Identity
{
public:
Identity() :
_privateKey((C25519::Private *)0)
enum Type
{
}
C25519 = 0, // Curve25519 and Ed25519
P384 = 1 // NIST P-384 ECDH and ECDSA
};
Identity(const Identity &id) :
_address(id._address),
_publicKey(id._publicKey),
_privateKey((id._privateKey) ? new C25519::Private(*(id._privateKey)) : (C25519::Private *)0)
{
}
Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
Identity(const Identity &id) { memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity)); }
Identity(const char *str) :
_privateKey((C25519::Private *)0)
Identity(const char *str)
{
if (!fromString(str))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
template<unsigned int C>
Identity(const Buffer<C> &b,unsigned int startAt = 0) :
_privateKey((C25519::Private *)0)
{
deserialize(b,startAt);
}
Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
~Identity()
{
if (_privateKey) {
Utils::burn(_privateKey,sizeof(C25519::Private));
delete _privateKey;
}
}
~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
inline Identity &operator=(const Identity &id)
{
_address = id._address;
_publicKey = id._publicKey;
if (id._privateKey) {
if (!_privateKey)
_privateKey = new C25519::Private();
*_privateKey = *(id._privateKey);
} else {
delete _privateKey;
_privateKey = (C25519::Private *)0;
}
memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity));
return *this;
}
/**
* @return Identity type
*/
inline Type type() const { return _type; }
/**
* Generate a new identity (address, key pair)
*
* This is a time consuming operation.
*
* @param t Type of identity to generate
*/
void generate();
void generate(const Type t);
/**
* Check the validity of this identity's pairing of key to address
@ -120,7 +105,7 @@ public:
/**
* @return True if this identity contains a private key
*/
inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
inline bool hasPrivate() const { return _hasPrivate; }
/**
* Compute the SHA512 hash of our private key (if we have one)
@ -130,9 +115,15 @@ public:
*/
inline bool sha512PrivateKey(void *sha) const
{
if (_privateKey) {
SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
return true;
if (_hasPrivate) {
switch(_type) {
case C25519:
SHA512::hash(sha,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
return true;
case P384:
SHA512::hash(sha,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
return true;
}
}
return false;
}
@ -140,14 +131,34 @@ public:
/**
* Sign a message with this identity (private key required)
*
* The signature buffer should be large enough for the largest
* signature, which is currently 96 bytes.
*
* @param data Data to sign
* @param len Length of data
* @param sig Buffer to receive signature
* @param siglen Length of buffer
* @return Number of bytes actually written to sig or 0 on error
*/
inline C25519::Signature sign(const void *data,unsigned int len) const
inline unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
{
if (_privateKey)
return C25519::sign(*_privateKey,_publicKey,data,len);
throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
uint8_t h[64];
if (!_hasPrivate)
return 0;
switch(_type) {
case C25519:
if (siglen < ZT_C25519_SIGNATURE_LEN)
return 0;
C25519::sign(_k.t0.priv,_k.t0.pub,data,len,sig);
return ZT_C25519_SIGNATURE_LEN;
case P384:
if (siglen < ZT_ECC384_SIGNATURE_SIZE)
return 0;
SHA512::hash(h,data,len);
ECC384ECDSASign(_k.t1.priv,h,(uint8_t *)sig);
return ZT_ECC384_SIGNATURE_SIZE;
}
return 0;
}
/**
@ -159,17 +170,20 @@ public:
* @param siglen Length of signature in bytes
* @return True if signature validates and data integrity checks
*/
inline bool verify(const void *data,unsigned int len,const void *signature,unsigned int siglen) const { return C25519::verify(_publicKey,data,len,signature,siglen); }
/**
* Verify a message signature against this identity
*
* @param data Data to check
* @param len Length of data
* @param signature Signature
* @return True if signature validates and data integrity checks
*/
inline bool verify(const void *data,unsigned int len,const C25519::Signature &signature) const { return C25519::verify(_publicKey,data,len,signature); }
inline bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
{
uint8_t h[64];
switch(_type) {
case C25519:
return C25519::verify(_k.t0.pub,data,len,sig,siglen);
case P384:
if (siglen != ZT_ECC384_SIGNATURE_SIZE)
return false;
SHA512::hash(h,data,len);
return ECC384ECDSAVerify(_k.t1.pub,h,(const uint8_t *)sig);
}
return false;
}
/**
* Shortcut method to perform key agreement with another identity
@ -183,9 +197,26 @@ public:
*/
inline bool agree(const Identity &id,void *key,unsigned int klen) const
{
if (_privateKey) {
C25519::agree(*_privateKey,id._publicKey,key,klen);
return true;
uint8_t ecc384RawSecret[ZT_ECC384_SHARED_SECRET_SIZE];
uint8_t h[64];
if (_hasPrivate) {
switch(_type) {
case C25519:
C25519::agree(_k.t0.priv,id._k.t0.pub,key,klen);
return true;
case P384:
ECC384ECDH(id._k.t1.pub,_k.t1.priv,ecc384RawSecret);
SHA512::hash(h,ecc384RawSecret,sizeof(ecc384RawSecret));
unsigned int hi = 0;
for(unsigned int i=0;i<klen;++i) {
if (hi == 64) {
hi = 0;
SHA512::hash(h,h,64);
}
((uint8_t *)key)[i] = h[hi++];
}
return true;
}
}
return false;
}
@ -206,12 +237,28 @@ public:
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
{
_address.appendTo(b);
b.append((uint8_t)0); // C25519/Ed25519 identity type
b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_privateKey)&&(includePrivate)) {
b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
} else b.append((unsigned char)0);
switch(_type) {
case C25519:
b.append((uint8_t)C25519);
b.append(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_hasPrivate)&&(includePrivate)) {
b.append((uint8_t)ZT_C25519_PRIVATE_KEY_LEN);
b.append(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
} else {
b.append((uint8_t)0);
}
break;
case P384:
b.append((uint8_t)P384);
b.append(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
if ((_hasPrivate)&&(includePrivate)) {
b.append((uint8_t)ZT_ECC384_PRIVATE_KEY_SIZE);
b.append(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
} else {
b.append((uint8_t)0);
}
break;
}
}
/**
@ -229,27 +276,47 @@ public:
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
delete _privateKey;
_privateKey = (C25519::Private *)0;
_hasPrivate = false;
unsigned int p = startAt;
unsigned int pkl;
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
p += ZT_ADDRESS_LENGTH;
if (b[p++] != 0)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
memcpy(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
p += ZT_C25519_PUBLIC_KEY_LEN;
unsigned int privateKeyLength = (unsigned int)b[p++];
if (privateKeyLength) {
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_privateKey = new C25519::Private();
memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
_type = (Type)b[p++];
switch(_type) {
case C25519:
memcpy(_k.t0.pub.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
p += ZT_C25519_PUBLIC_KEY_LEN;
pkl = (unsigned int)b[p++];
if (pkl) {
if (pkl != ZT_C25519_PRIVATE_KEY_LEN)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_hasPrivate = true;
memcpy(_k.t0.priv.data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
} else {
memset(_k.t0.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
_hasPrivate = false;
}
break;
case P384:
memcpy(_k.t0.pub.data,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
p += ZT_ECC384_PUBLIC_KEY_SIZE;
pkl = (unsigned int)b[p++];
if (pkl) {
if (pkl != ZT_ECC384_PRIVATE_KEY_SIZE)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_hasPrivate = true;
memcpy(_k.t1.priv,b.field(p,ZT_ECC384_PRIVATE_KEY_SIZE),ZT_ECC384_PRIVATE_KEY_SIZE);
p += ZT_ECC384_PRIVATE_KEY_SIZE;
} else {
memset(_k.t1.priv,0,ZT_ECC384_PRIVATE_KEY_SIZE);
_hasPrivate = false;
}
break;
default:
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
return (p - startAt);
@ -275,40 +342,64 @@ public:
*/
bool fromString(const char *str);
/**
* @return C25519 public key
*/
inline const C25519::Public &publicKey() const { return _publicKey; }
/**
* @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
*/
inline const C25519::Pair privateKeyPair() const
{
C25519::Pair pair;
pair.pub = _publicKey;
if (_privateKey)
pair.priv = *_privateKey;
else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
return pair;
}
/**
* @return True if this identity contains something
*/
inline operator bool() const { return (_address); }
inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
inline bool operator==(const Identity &id) const
{
if ((_address == id._address)&&(_type == id._type)) {
switch(_type) {
case C25519:
return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) == 0);
case P384:
return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) == 0);
default:
return false;
}
}
return false;
}
inline bool operator<(const Identity &id) const
{
if (_address < id._address)
return true;
if (_address == id._address) {
if ((int)_type < (int)id._type)
return true;
if (_type == id._type) {
switch(_type) {
case C25519:
return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) < 0);
case P384:
return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) < 0);
}
}
}
return false;
}
inline bool operator!=(const Identity &id) const { return !(*this == id); }
inline bool operator>(const Identity &id) const { return (id < *this); }
inline bool operator<=(const Identity &id) const { return !(id < *this); }
inline bool operator>=(const Identity &id) const { return !(*this < id); }
inline unsigned long hashCode() const { return (unsigned long)_address.toInt(); }
private:
Address _address;
C25519::Public _publicKey;
C25519::Private *_privateKey;
union {
struct {
C25519::Public pub;
C25519::Private priv;
} t0;
struct {
uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE];
uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE];
} t1;
} _k;
Type _type;
bool _hasPrivate;
};
} // namespace ZeroTier

View file

@ -43,7 +43,6 @@
#include "SelfAwareness.hpp"
#include "Salsa20.hpp"
#include "SHA512.hpp"
#include "World.hpp"
#include "Node.hpp"
#include "CertificateOfMembership.hpp"
#include "Capability.hpp"
@ -368,30 +367,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
}
// Get primary planet world ID and world timestamp if present
uint64_t planetWorldId = 0;
uint64_t planetWorldTimestamp = 0;
if ((ptr + 16) <= size()) {
planetWorldId = at<uint64_t>(ptr); ptr += 8;
planetWorldTimestamp = at<uint64_t>(ptr); ptr += 8;
}
std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
if (ptr < size()) {
// Remainder of packet, if present, is encrypted
cryptField(peer->key(),ptr,size() - ptr);
// Get moon IDs and timestamps if present
if ((ptr + 2) <= size()) {
const unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
for(unsigned int i=0;i<numMoons;++i) {
if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
ptr += 16;
}
}
}
// Send OK(HELLO) with an echo of the packet's timestamp and some of the same
// information about us: version, sent-to address, etc.
@ -435,25 +410,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
tmpa.serialize(outp);
}
const unsigned int worldUpdateSizeAt = outp.size();
outp.addSize(2); // make room for 16-bit size field
if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) {
RR->topology->planet().serialize(outp,false);
}
if (moonIdsAndTimestamps.size() > 0) {
std::vector<World> moons(RR->topology->moons());
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
for(std::vector< std::pair<uint64_t,uint64_t> >::const_iterator i(moonIdsAndTimestamps.begin());i!=moonIdsAndTimestamps.end();++i) {
if (i->first == m->id()) {
if (m->timestamp() > i->second)
m->serialize(outp,false);
break;
}
}
}
}
outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),now);
@ -489,22 +445,6 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
// Get reported external surface address if present
if (ptr < size())
ptr += externalSurfaceAddress.deserialize(*this,ptr);
// Handle planet or moon updates if present
if ((ptr + 2) <= size()) {
const unsigned int worldsLen = at<uint16_t>(ptr); ptr += 2;
if (RR->topology->shouldAcceptWorldUpdateFrom(peer->address())) {
const unsigned int endOfWorlds = ptr + worldsLen;
while (ptr < endOfWorlds) {
World w;
ptr += w.deserialize(*this,ptr);
RR->topology->addWorld(tPtr,w,false);
}
} else {
ptr += worldsLen;
}
}
if (!hops()) {
_path->updateLatency((unsigned int)latency,RR->node->now());
}
@ -575,8 +515,9 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
return true;
// TODO
//if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
// return true;
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_WHOIS);
@ -772,8 +713,8 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,c
SharedPtr<Network> network(RR->node->network(nwid));
if (network)
authorized = network->gate(tPtr,peer);
if (!authorized)
authorized = ((RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())));
//if (!authorized)
// authorized = ((RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())));
}
if (authorized)
RR->mc->add(tPtr,now,nwid,MulticastGroup(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14)),peer->address());
@ -982,7 +923,8 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
}
const int64_t now = RR->node->now();
if ((gatherLimit > 0)&&((trustEstablished)||(RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())))) {
//if ((gatherLimit > 0)&&((trustEstablished)||(RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())))) {
if (gatherLimit) {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
outp.append(packetId());

View file

@ -93,7 +93,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64
}
if (n <= 0) {
RR->identity.generate();
RR->identity.generate(Identity::C25519);
RR->identity.toString(false,RR->publicIdentityStr);
RR->identity.toString(true,RR->secretIdentityStr);
idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
@ -192,16 +192,17 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
// Closure used to ping upstream and active/online peers
class _PingPeersThatNeedPing
// Function object used to traverse the peer list, check peer status, and ping
// those that need pinging.
struct _PingPeersThatNeedPing
{
public:
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &alwaysContact,int64_t now) :
RR(renv),
_tPtr(tPtr),
_alwaysContact(alwaysContact),
_now(now),
_bestCurrentUpstream(RR->topology->getUpstreamPeer())
_bestCurrentUpstream(RR->topology->getUpstreamPeer()),
online(false)
{
}
@ -209,6 +210,7 @@ public:
{
const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address());
if (alwaysContactEndpoints) {
online |= p->isAlive(_now);
const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now);
bool contacted = (sent != 0);
@ -246,12 +248,13 @@ public:
}
}
private:
const RuntimeEnvironment *RR;
void *_tPtr;
Hashtable< Address,std::vector<InetAddress> > &_alwaysContact;
const int64_t _now;
const SharedPtr<Peer> _bestCurrentUpstream;
bool online;
};
ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
@ -265,38 +268,25 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64
try {
_lastPingCheck = now;
// Get designated VL1 upstreams
Hashtable< Address,std::vector<InetAddress> > alwaysContact;
RR->topology->getUpstreamsToContact(alwaysContact);
// Check last receive time on designated upstreams to see if we seem to be online
int64_t lastReceivedFromUpstream = 0;
{
Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
Address *upstreamAddress = (Address *)0;
std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
while (i.next(upstreamAddress,upstreamStableEndpoints)) {
SharedPtr<Peer> p(RR->topology->getPeerNoCache(*upstreamAddress));
if (p)
lastReceivedFromUpstream = std::max(p->lastReceive(),lastReceivedFromUpstream);
}
}
// Clean up any old local controller auth memorizations.
// Clean up any old local controller auth memoizations. This is an
// optimization for network controllers to know whether to accept
// or trust nodes without doing an extra cert check.
{
_localControllerAuthorizations_m.lock();
Hashtable< _LocalControllerAuth,int64_t >::Iterator i(_localControllerAuthorizations);
_LocalControllerAuth *k = (_LocalControllerAuth *)0;
int64_t *v = (int64_t *)0;
while (i.next(k,v)) {
if ((*v - now) > (ZT_NETWORK_AUTOCONF_DELAY * 3))
if ((*v - now) > (ZT_NETWORK_AUTOCONF_DELAY * 3)) {
_localControllerAuthorizations.erase(*k);
}
}
_localControllerAuthorizations_m.unlock();
}
// Get peers we should stay connected to according to network configs
// Also get networks and whether they need config so we only have to do one pass over networks
// (1) Get peers we should remain connected to and (2) get networks that need config.
Hashtable< Address,std::vector<InetAddress> > alwaysContact;
RR->topology->getUpstreamsToContact(alwaysContact);
std::vector< std::pair< SharedPtr<Network>,bool > > networkConfigNeeded;
{
Mutex::Lock l(_networks_m);
@ -331,7 +321,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64
// Update online status, post status change as event
const bool oldOnline = _online;
_online = (((now - lastReceivedFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amUpstream()));
_online = pfunc.online;
if (oldOnline != _online)
postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
} catch ( ... ) {
@ -425,18 +415,6 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
ZT_ResultCode Node::orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
{
RR->topology->addMoon(tptr,moonWorldId,Address(moonSeed));
return ZT_RESULT_OK;
}
ZT_ResultCode Node::deorbit(void *tptr,uint64_t moonWorldId)
{
RR->topology->removeMoon(tptr,moonWorldId);
return ZT_RESULT_OK;
}
uint64_t Node::address() const
{
return RR->identity.address().toInt();
@ -635,16 +613,6 @@ ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *
return ZT_RESULT_OK;
}
World Node::planet() const
{
return RR->topology->planet();
}
std::vector<World> Node::moons() const
{
return RR->topology->moons();
}
void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
{
_localControllerAuthorizations_m.lock();
@ -682,10 +650,11 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de
outp.append((uint32_t)totalSize);
outp.append((uint32_t)chunkIndex);
C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
uint8_t sig[256];
const unsigned int siglen = RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart,sig,sizeof(sig));
outp.append((uint8_t)1);
outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
outp.append((uint16_t)siglen);
outp.append(sig,siglen);
outp.compress();
RR->sw->send((void *)0,outp,true);
@ -879,24 +848,6 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
}
}
enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->orbit(tptr,moonWorldId,moonSeed);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(tptr,moonWorldId);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
uint64_t ZT_Node_address(ZT_Node *node)
{
return reinterpret_cast<ZeroTier::Node *>(node)->address();

View file

@ -54,8 +54,6 @@
namespace ZeroTier {
class World;
/**
* Implementation of Node object as defined in CAPI
*
@ -99,8 +97,6 @@ public:
ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
ZT_ResultCode orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
ZT_ResultCode deorbit(void *tptr,uint64_t moonWorldId);
uint64_t address() const;
void status(ZT_NodeStatus *status) const;
ZT_PeerList *peers() const;
@ -194,9 +190,6 @@ public:
uint64_t prng();
ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
World planet() const;
std::vector<World> moons() const;
inline const Identity &identity() const { return _RR.identity; }
/**

View file

@ -972,16 +972,6 @@ bool Packet::dearmor(const void *key)
}
}
void Packet::cryptField(const void *key,unsigned int start,unsigned int len)
{
uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
uint8_t iv[8];
for(int i=0;i<8;++i) iv[i] = data[i];
iv[7] &= 0xf8; // mask off least significant 3 bits of packet ID / IV since this is unset when this function gets called
Salsa20 s20(key,iv);
s20.crypt12(data + start,data + start,len);
}
bool Packet::compress()
{
char *const data = reinterpret_cast<char *>(unsafeData());

View file

@ -71,6 +71,7 @@
* 10 - 1.4.0 ... CURRENT
* + Multipath capability and load balancing
* + Peer-to-peer multicast replication (optional)
* + Old planet/moon stuff is DEAD!
*/
#define ZT_PROTO_VERSION 10
@ -532,22 +533,9 @@ public:
* <[8] timestamp for determining latency>
* <[...] binary serialized identity (see Identity)>
* <[...] physical destination address of packet>
* <[8] 64-bit world ID of current planet>
* <[8] 64-bit timestamp of current planet>
* [... remainder if packet is encrypted using cryptField() ...]
* <[2] 16-bit number of moons>
* [<[1] 8-bit type ID of moon>]
* [<[8] 64-bit world ID of moon>]
* [<[8] 64-bit timestamp of moon>]
* [... additional moon type/ID/timestamp tuples ...]
*
*
* HELLO is sent in the clear as it is how peers share their identity
* public keys. A few additional fields are sent in the clear too, but
* these are things that are public info or are easy to determine. As
* of 1.2.0 we have added a few more fields, but since these could have
* the potential to be sensitive we introduced the encryption of the
* remainder of the packet. See cryptField(). Packet MAC is still
* performed of course, so authentication occurs as normal.
* public keys.
*
* Destination address is the actual wire address to which the packet
* was sent. See InetAddress::serialize() for format.
@ -559,15 +547,10 @@ public:
* <[1] software minor version>
* <[2] software revision>
* <[...] physical destination address of packet>
* <[2] 16-bit length of world update(s) or 0 if none>
* [[...] updates to planets and/or moons]
*
* With the exception of the timestamp, the other fields pertain to the
* respondent who is sending OK and are not echoes.
*
* Note that OK is fully encrypted so no selective cryptField() of
* potentially sensitive fields is needed.
*
* ERROR has no payload.
*/
VERB_HELLO = 0x01,
@ -1268,21 +1251,6 @@ public:
*/
bool dearmor(const void *key);
/**
* Encrypt/decrypt a separately armored portion of a packet
*
* This is currently only used to mask portions of HELLO as an extra
* security precaution since most of that message is sent in the clear.
*
* This must NEVER be used more than once in the same packet, as doing
* so will result in re-use of the same key stream.
*
* @param key 32-byte key
* @param start Start of encrypted portion
* @param len Length of encrypted portion
*/
void cryptField(const void *key,unsigned int start,unsigned int len);
/**
* Attempt to compress payload if not already (must be unencrypted)
*

View file

@ -711,27 +711,6 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA
RR->identity.serialize(outp,false);
atAddress.serialize(outp);
outp.append((uint64_t)RR->topology->planetWorldId());
outp.append((uint64_t)RR->topology->planetWorldTimestamp());
const unsigned int startCryptedPortionAt = outp.size();
std::vector<World> moons(RR->topology->moons());
std::vector<uint64_t> moonsWanted(RR->topology->moonsWanted());
outp.append((uint16_t)(moons.size() + moonsWanted.size()));
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
outp.append((uint8_t)m->type());
outp.append((uint64_t)m->id());
outp.append((uint64_t)m->timestamp());
}
for(std::vector<uint64_t>::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) {
outp.append((uint8_t)World::TYPE_MOON);
outp.append(*m);
outp.append((uint64_t)0);
}
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
RR->node->expectReplyTo(outp.packetId());
if (atAddress) {

View file

@ -34,6 +34,18 @@
namespace ZeroTier {
bool Revocation::sign(const Identity &signer)
{
if (signer.hasPrivate()) {
Buffer<sizeof(Revocation) + 64> tmp;
_signedBy = signer.address();
this->serialize(tmp,true);
_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
return true;
}
return false;
}
int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
{
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
@ -46,7 +58,7 @@ int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
try {
Buffer<sizeof(Revocation) + 64> tmp;
this->serialize(tmp,true);
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
} catch ( ... ) {
return -1;
}

View file

@ -66,9 +66,9 @@ public:
_flags(0),
_target(),
_signedBy(),
_type(Credential::CREDENTIAL_TYPE_NULL)
_type(Credential::CREDENTIAL_TYPE_NULL),
_signatureLength(0)
{
memset(_signature.data,0,sizeof(_signature.data));
}
/**
@ -88,9 +88,9 @@ public:
_flags(fl),
_target(tgt),
_signedBy(),
_type(ct)
_type(ct),
_signatureLength(0)
{
memset(_signature.data,0,sizeof(_signature.data));
}
inline uint32_t id() const { return _id; }
@ -107,17 +107,7 @@ public:
* @param signer Signing identity, must have private key
* @return True if signature was successful
*/
inline bool sign(const Identity &signer)
{
if (signer.hasPrivate()) {
Buffer<sizeof(Revocation) + 64> tmp;
_signedBy = signer.address();
this->serialize(tmp,true);
_signature = signer.sign(tmp.data(),tmp.size());
return true;
}
return false;
}
bool sign(const Identity &signer);
/**
* Verify this revocation's signature
@ -145,9 +135,9 @@ public:
b.append((uint8_t)_type);
if (!forSign) {
b.append((uint8_t)1); // 1 == Ed25519 signature
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)1);
b.append((uint16_t)_signatureLength);
b.append(_signature,_signatureLength);
}
// This is the size of any additional fields, currently 0.
@ -175,11 +165,10 @@ public:
_type = (Credential::Type)b[p++];
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
p += 2;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
p += ZT_C25519_SIGNATURE_LEN;
} else throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_signatureLength = b.template at<uint16_t>(p);
if (_signatureLength > sizeof(_signature))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
memcpy(_signature,b.field(p,_signatureLength),_signatureLength);
} else {
p += 2 + b.template at<uint16_t>(p);
}
@ -200,7 +189,8 @@ private:
Address _target;
Address _signedBy;
Credential::Type _type;
C25519::Signature _signature;
unsigned int _signatureLength;
uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
};
} // namespace ZeroTier

View file

@ -92,9 +92,6 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
const Address destination(fragment.destination());
if (destination != RR->identity.address()) {
if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) )
return;
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
fragment.incrementHops();
@ -164,11 +161,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
return;
if (destination != RR->identity.address()) {
if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
return;
Packet packet(data,len);
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
packet.incrementHops();
SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);

View file

@ -34,6 +34,18 @@
namespace ZeroTier {
bool Tag::sign(const Identity &signer)
{
if (signer.hasPrivate()) {
Buffer<sizeof(Tag) + 64> tmp;
_signedBy = signer.address();
this->serialize(tmp,true);
_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
return true;
}
return false;
}
int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
{
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
@ -46,7 +58,7 @@ int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
try {
Buffer<(sizeof(Tag) * 2)> tmp;
this->serialize(tmp,true);
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
} catch ( ... ) {
return -1;
}

View file

@ -69,9 +69,9 @@ public:
_id(0),
_value(0),
_networkId(0),
_ts(0)
_ts(0),
_signatureLength(0)
{
memset(_signature.data,0,sizeof(_signature.data));
}
/**
@ -87,9 +87,9 @@ public:
_networkId(nwid),
_ts(ts),
_issuedTo(issuedTo),
_signedBy()
_signedBy(),
_signatureLength(0)
{
memset(_signature.data,0,sizeof(_signature.data));
}
inline uint32_t id() const { return _id; }
@ -105,17 +105,7 @@ public:
* @param signer Signing identity, must have private key
* @return True if signature was successful
*/
inline bool sign(const Identity &signer)
{
if (signer.hasPrivate()) {
Buffer<sizeof(Tag) + 64> tmp;
_signedBy = signer.address();
this->serialize(tmp,true);
_signature = signer.sign(tmp.data(),tmp.size());
return true;
}
return false;
}
bool sign(const Identity &signer);
/**
* Check this tag's signature
@ -139,9 +129,9 @@ public:
_issuedTo.appendTo(b);
_signedBy.appendTo(b);
if (!forSign) {
b.append((uint8_t)1); // 1 == Ed25519
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)1);
b.append((uint16_t)_signatureLength);
b.append(_signature,_signatureLength);
}
b.append((uint16_t)0); // length of additional fields, currently 0
@ -165,10 +155,11 @@ public:
_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
_signatureLength = b.template at<uint16_t>(p);
if (_signatureLength > sizeof(_signature))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
p += 2;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
memcpy(_signature,b.field(p,_signatureLength),_signatureLength); p += _signatureLength;
} else {
p += 2 + b.template at<uint16_t>(p);
}
@ -207,7 +198,8 @@ private:
int64_t _ts;
Address _issuedTo;
Address _signedBy;
C25519::Signature _signature;
unsigned int _signatureLength;
uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
};
} // namespace ZeroTier

View file

@ -35,57 +35,10 @@
namespace ZeroTier {
/*
* 2018-07-26 ZeroTier planet definition for the third planet of Sol:
*
* There are two roots, each of which is a cluster spread across multiple
* continents and providers. They are named Alice and Bob after the
* canonical example names used in cryptography.
*
* Alice:
*
* root-alice-ams-01: Amsterdam, Netherlands
* root-alice-joh-01: Johannesburg, South Africa
* root-alice-nyc-01: New York, New York, USA
* root-alice-arg-01: Buenos Aires, Argentina
* root-alice-sfo-01: San Francisco, California, USA
* root-alice-sgp-01: Singapore
*
* Bob:
*
* root-bob-dfw-01: Dallas, Texas, USA
* root-bob-fra-01: Frankfurt, Germany
* root-bob-par-01: Paris, France
* root-bob-syd-01: Sydney, Australia
* root-bob-tok-01: Tokyo, Japan
* root-bob-tor-01: Toronto, Canada
*/
#define ZT_DEFAULT_WORLD_LENGTH 634
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x64,0xd3,0x71,0xf0,0x58,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0xbf,0xfd,0xd5,0x32,0xf7,0x15,0x6e,0x02,0x6f,0xb9,0x01,0x0d,0xb5,0x7b,0x04,0xd8,0x3a,0xc5,0x17,0x39,0x04,0x36,0xfd,0x9d,0xc6,0x3d,0xa8,0xf3,0x8e,0x79,0xe7,0xc8,0x77,0x8d,0xcc,0x79,0xb8,0xab,0xc6,0x98,0x7c,0x9f,0x34,0x25,0x14,0xe1,0x2f,0xd7,0x97,0x11,0xec,0x34,0x4c,0x9f,0x0f,0xb4,0x85,0x0d,0x9b,0x11,0xd1,0xc2,0xce,0x00,0xc4,0x0a,0x13,0x4b,0xcb,0xc3,0xae,0x2e,0x16,0x00,0x4b,0xdc,0x90,0x5e,0x7e,0x9b,0x44,0x07,0x15,0x36,0x61,0x3c,0x64,0xaa,0xe9,0x46,0x78,0x3c,0xa7,0x18,0xc8,0xd8,0x02,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x7d,0x00,0x01,0x00,0x00,0x00,0x00,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x33,0xcc,0x08,0xf8,0xfa,0xcc,0x08,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x54,0x60,0x01,0x00,0xfc,0xcc,0x08,0x27,0x09,0x04,0x83,0xff,0x06,0x10,0x27,0x09,0x06,0x28,0x03,0xeb,0x80,0x00,0x00,0x00,0x0e,0x00,0x02,0x60,0x01,0x00,0xfc,0xcc,0x08,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x02,0x00,0xe0,0x01,0x08,0xfe,0xcc,0x08,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0xb7,0x40,0x01,0x08,0xfe,0xcc,0x08,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x6a,0x30,0x01,0x78,0x00,0xcd,0x08,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x64,0xcd,0x08,0x80,0x01,0xcd,0x08,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x26,0x70,0x01,0xfe,0x15,0xc4,0xf5,0x27,0x09};
Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
RR(renv),
_numConfiguredPhysicalPaths(0),
_amUpstream(false)
_numConfiguredPhysicalPaths(0)
{
uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
uint64_t idtmp[2];
idtmp[0] = 0; idtmp[1] = 0;
int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,idtmp,tmp,sizeof(tmp));
if (n > 0) {
try {
World cachedPlanet;
cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0);
addWorld(tPtr,cachedPlanet,false);
} catch ( ... ) {} // ignore invalid cached planets
}
World defaultPlanet;
{
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
}
addWorld(tPtr,defaultPlanet,false);
}
Topology::~Topology()
@ -158,224 +111,33 @@ Identity Topology::getIdentity(void *tPtr,const Address &zta)
SharedPtr<Peer> Topology::getUpstreamPeer()
{
const int64_t now = RR->node->now();
unsigned int bestq = ~((unsigned int)0);
const SharedPtr<Peer> *best = (const SharedPtr<Peer> *)0;
Mutex::Lock _l2(_peers_m);
Mutex::Lock _l1(_upstreams_m);
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
const SharedPtr<Peer> *p = _peers.get(*a);
if (p) {
const unsigned int q = (*p)->relayQuality(now);
if (q <= bestq) {
bestq = q;
best = p;
}
}
}
if (!best)
return SharedPtr<Peer>();
return *best;
return SharedPtr<Peer>();
}
bool Topology::isUpstream(const Identity &id) const
{
Mutex::Lock _l(_upstreams_m);
return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
}
bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const
{
Mutex::Lock _l(_upstreams_m);
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end())
return true;
for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
if (s->second == addr)
return true;
}
return false;
}
ZT_PeerRole Topology::role(const Address &ztaddr) const
{
Mutex::Lock _l(_upstreams_m);
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
if (i->identity.address() == ztaddr)
return ZT_PEER_ROLE_PLANET;
}
return ZT_PEER_ROLE_MOON;
}
return ZT_PEER_ROLE_LEAF;
}
bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const
{
Mutex::Lock _l(_upstreams_m);
// For roots the only permitted addresses are those defined. This adds just a little
// bit of extra security against spoofing, replaying, etc.
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
for(std::vector<World::Root>::const_iterator r(_planet.roots().begin());r!=_planet.roots().end();++r) {
if (r->identity.address() == ztaddr) {
if (r->stableEndpoints.size() == 0)
return false; // no stable endpoints specified, so allow dynamic paths
for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
if (ipaddr.ipsEqual(*e))
return false;
}
}
}
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
for(std::vector<World::Root>::const_iterator r(m->roots().begin());r!=m->roots().end();++r) {
if (r->identity.address() == ztaddr) {
if (r->stableEndpoints.size() == 0)
return false; // no stable endpoints specified, so allow dynamic paths
for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
if (ipaddr.ipsEqual(*e))
return false;
}
}
}
}
return true;
}
return false;
}
bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
{
if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON))
return false;
Mutex::Lock _l2(_peers_m);
Mutex::Lock _l1(_upstreams_m);
World *existing = (World *)0;
switch(newWorld.type()) {
case World::TYPE_PLANET:
existing = &_planet;
break;
case World::TYPE_MOON:
for(std::vector< World >::iterator m(_moons.begin());m!=_moons.end();++m) {
if (m->id() == newWorld.id()) {
existing = &(*m);
break;
}
}
break;
default:
return false;
}
if (existing) {
if (existing->shouldBeReplacedBy(newWorld))
*existing = newWorld;
else return false;
} else if (newWorld.type() == World::TYPE_MOON) {
if (alwaysAcceptNew) {
_moons.push_back(newWorld);
existing = &(_moons.back());
} else {
for(std::vector< std::pair<uint64_t,Address> >::iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
if (m->first == newWorld.id()) {
for(std::vector<World::Root>::const_iterator r(newWorld.roots().begin());r!=newWorld.roots().end();++r) {
if (r->identity.address() == m->second) {
_moonSeeds.erase(m);
_moons.push_back(newWorld);
existing = &(_moons.back());
break;
}
}
if (existing)
break;
}
}
}
if (!existing)
return false;
} else {
return false;
}
try {
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf;
existing->serialize(sbuf,false);
uint64_t idtmp[2];
idtmp[0] = existing->id(); idtmp[1] = 0;
RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,idtmp,sbuf.data(),sbuf.size());
} catch ( ... ) {}
_memoizeUpstreams(tPtr);
return true;
}
void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
{
char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
uint64_t idtmp[2];
idtmp[0] = id; idtmp[1] = 0;
int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,idtmp,tmp,sizeof(tmp));
if (n > 0) {
try {
World w;
w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n));
if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
addWorld(tPtr,w,true);
return;
}
} catch ( ... ) {}
}
if (seed) {
Mutex::Lock _l(_upstreams_m);
if (std::find(_moonSeeds.begin(),_moonSeeds.end(),std::pair<uint64_t,Address>(id,seed)) == _moonSeeds.end())
_moonSeeds.push_back(std::pair<uint64_t,Address>(id,seed));
}
}
void Topology::removeMoon(void *tPtr,const uint64_t id)
{
Mutex::Lock _l2(_peers_m);
Mutex::Lock _l1(_upstreams_m);
std::vector<World> nm;
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
if (m->id() != id) {
nm.push_back(*m);
} else {
uint64_t idtmp[2];
idtmp[0] = id; idtmp[1] = 0;
RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp);
}
}
_moons.swap(nm);
std::vector< std::pair<uint64_t,Address> > cm;
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
if (m->first != id)
cm.push_back(*m);
}
_moonSeeds.swap(cm);
_memoizeUpstreams(tPtr);
}
void Topology::doPeriodicTasks(void *tPtr,int64_t now)
{
{
Mutex::Lock _l1(_peers_m);
Mutex::Lock _l2(_upstreams_m);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) {
if (!(*p)->isAlive(now)) {
_savePeer(tPtr,*p);
_peers.erase(*a);
}
@ -394,39 +156,6 @@ void Topology::doPeriodicTasks(void *tPtr,int64_t now)
}
}
void Topology::_memoizeUpstreams(void *tPtr)
{
// assumes _upstreams_m and _peers_m are locked
_upstreamAddresses.clear();
_amUpstream = false;
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
if (i->identity == RR->identity) {
_amUpstream = true;
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
_upstreamAddresses.push_back(i->identity.address());
SharedPtr<Peer> &hp = _peers[i->identity.address()];
if (!hp)
hp = new Peer(RR,RR->identity,i->identity);
}
}
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
if (i->identity == RR->identity) {
_amUpstream = true;
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
_upstreamAddresses.push_back(i->identity.address());
SharedPtr<Peer> &hp = _peers[i->identity.address()];
if (!hp)
hp = new Peer(RR,RR->identity,i->identity);
}
}
}
std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
}
void Topology::_savePeer(void *tPtr,const SharedPtr<Peer> &peer)
{
try {

View file

@ -45,7 +45,6 @@
#include "Mutex.hpp"
#include "InetAddress.hpp"
#include "Hashtable.hpp"
#include "World.hpp"
namespace ZeroTier {
@ -136,12 +135,6 @@ public:
*/
bool isUpstream(const Identity &id) const;
/**
* @param addr Address to check
* @return True if we should accept a world update from this address
*/
bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
/**
* @param ztaddr ZeroTier address
* @return Peer role for this device
@ -171,29 +164,6 @@ public:
*/
inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
{
Mutex::Lock _l(_upstreams_m);
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
if (i->identity != RR->identity) {
std::vector<InetAddress> &ips = eps[i->identity.address()];
for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
if (std::find(ips.begin(),ips.end(),*j) == ips.end())
ips.push_back(*j);
}
}
}
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
if (i->identity != RR->identity) {
std::vector<InetAddress> &ips = eps[i->identity.address()];
for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
if (std::find(ips.begin(),ips.end(),*j) == ips.end())
ips.push_back(*j);
}
}
}
}
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m)
eps[m->second];
}
/**
@ -201,87 +171,9 @@ public:
*/
inline std::vector<Address> upstreamAddresses() const
{
Mutex::Lock _l(_upstreams_m);
return _upstreamAddresses;
return std::vector<Address>();
}
/**
* @return Current moons
*/
inline std::vector<World> moons() const
{
Mutex::Lock _l(_upstreams_m);
return _moons;
}
/**
* @return Moon IDs we are waiting for from seeds
*/
inline std::vector<uint64_t> moonsWanted() const
{
Mutex::Lock _l(_upstreams_m);
std::vector<uint64_t> mw;
for(std::vector< std::pair<uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
if (std::find(mw.begin(),mw.end(),s->first) == mw.end())
mw.push_back(s->first);
}
return mw;
}
/**
* @return Current planet
*/
inline World planet() const
{
Mutex::Lock _l(_upstreams_m);
return _planet;
}
/**
* @return Current planet's world ID
*/
inline uint64_t planetWorldId() const
{
return _planet.id(); // safe to read without lock, and used from within eachPeer() so don't lock
}
/**
* @return Current planet's world timestamp
*/
inline uint64_t planetWorldTimestamp() const
{
return _planet.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
}
/**
* Validate new world and update if newer and signature is okay
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param newWorld A new or updated planet or moon to learn
* @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
* @return True if it was valid and newer than current (or totally new for moons)
*/
bool addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew);
/**
* Add a moon
*
* This loads it from moons.d if present, and if not adds it to
* a list of moons that we want to contact.
*
* @param id Moon ID
* @param seed If non-NULL, an address of any member of the moon to contact
*/
void addMoon(void *tPtr,const uint64_t id,const Address &seed);
/**
* Remove a moon
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param id Moon's world ID
*/
void removeMoon(void *tPtr,const uint64_t id);
/**
* Clean and flush database
*/
@ -333,11 +225,6 @@ public:
return _peers.entries();
}
/**
* @return True if I am a root server in a planet or moon
*/
inline bool amUpstream() const { return _amUpstream; }
/**
* Get info about a path
*
@ -455,13 +342,6 @@ private:
Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
Mutex _paths_m;
World _planet;
std::vector<World> _moons;
std::vector< std::pair<uint64_t,Address> > _moonSeeds;
std::vector<Address> _upstreamAddresses;
bool _amUpstream;
Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
};
} // namespace ZeroTier

View file

@ -453,7 +453,7 @@ public:
out[6] = BASE32CHARS[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5];
out[7] = BASE32CHARS[(in[4] & 0x1f)];
}
static inline void base328to5(const char *const in,uint8_t *const out)
{
out[0] = ((BASE32BITS[(unsigned int)in[0]]) << 3) | (BASE32BITS[(unsigned int)in[1]] & 0x1C) >> 2;
@ -462,7 +462,7 @@ public:
out[3] = ((BASE32BITS[(unsigned int)in[4]] & 0x01) << 7) | (BASE32BITS[(unsigned int)in[5]]) << 2 | (BASE32BITS[(unsigned int)in[6]] & 0x18) >> 3;
out[4] = ((BASE32BITS[(unsigned int)in[6]] & 0x07) << 5) | (BASE32BITS[(unsigned int)in[7]]);
}
/**
* Hexadecimal characters 0-f
*/

View file

@ -1,284 +0,0 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
#ifndef ZT_WORLD_HPP
#define ZT_WORLD_HPP
#include <vector>
#include <string>
#include "Constants.hpp"
#include "InetAddress.hpp"
#include "Identity.hpp"
#include "Buffer.hpp"
#include "C25519.hpp"
/**
* Maximum number of roots (sanity limit, okay to increase)
*
* A given root can (through multi-homing) be distributed across any number of
* physical endpoints, but having more than one is good to permit total failure
* of one root or its withdrawal due to compromise without taking the whole net
* down.
*/
#define ZT_WORLD_MAX_ROOTS 4
/**
* Maximum number of stable endpoints per root (sanity limit, okay to increase)
*/
#define ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT 32
/**
* The (more than) maximum length of a serialized World
*/
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
/**
* World ID for Earth
*
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
* to the public network 8056c2e21c000001 of the same name. It was chosen
* from Earth's approximate distance from the sun in kilometers.
*/
#define ZT_WORLD_ID_EARTH 149604618
/**
* World ID for Mars -- for future use by SpaceX or others
*/
#define ZT_WORLD_ID_MARS 227883110
namespace ZeroTier {
/**
* A world definition (formerly known as a root topology)
*
* Think of a World as a single data center. Within this data center a set
* of distributed fault tolerant root servers provide stable anchor points
* for a peer to peer network that provides VLAN service. Updates to a world
* definition can be published by signing them with the previous revision's
* signing key, and should be very infrequent.
*
* The maximum data center size is approximately 2.5 cubic light seconds,
* since many protocols have issues with >5s RTT latencies.
*
* ZeroTier operates a World for Earth capable of encompassing the planet, its
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
* world ID for Mars and nearby space is defined but not yet used, and a test
* world ID is provided for testing purposes.
*/
class World
{
public:
/**
* World type -- do not change IDs
*/
enum Type
{
TYPE_NULL = 0,
TYPE_PLANET = 1, // Planets, of which there is currently one (Earth)
TYPE_MOON = 127 // Moons, which are user-created and many
};
/**
* Upstream server definition in world/moon
*/
struct Root
{
Identity identity;
std::vector<InetAddress> stableEndpoints;
inline bool operator==(const Root &r) const { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
inline bool operator!=(const Root &r) const { return (!(*this == r)); }
inline bool operator<(const Root &r) const { return (identity < r.identity); } // for sorting
};
/**
* Construct an empty / null World
*/
World() :
_id(0),
_ts(0),
_type(TYPE_NULL) {}
/**
* @return Root servers for this world and their stable endpoints
*/
inline const std::vector<World::Root> &roots() const { return _roots; }
/**
* @return World type: planet or moon
*/
inline Type type() const { return _type; }
/**
* @return World unique identifier
*/
inline uint64_t id() const { return _id; }
/**
* @return World definition timestamp
*/
inline uint64_t timestamp() const { return _ts; }
/**
* @return C25519 signature
*/
inline const C25519::Signature &signature() const { return _signature; }
/**
* @return Public key that must sign next update
*/
inline const C25519::Public &updatesMustBeSignedBy() const { return _updatesMustBeSignedBy; }
/**
* Check whether a world update should replace this one
*
* @param update Candidate update
* @return True if update is newer than current, matches its ID and type, and is properly signed (or if current is NULL)
*/
inline bool shouldBeReplacedBy(const World &update)
{
if ((_id == 0)||(_type == TYPE_NULL))
return true;
if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
update.serialize(tmp,true);
return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
}
return false;
}
/**
* @return True if this World is non-empty
*/
inline operator bool() const { return (_type != TYPE_NULL); }
template<unsigned int C>
inline void serialize(Buffer<C> &b,bool forSign = false) const
{
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
b.append((uint8_t)_type);
b.append((uint64_t)_id);
b.append((uint64_t)_ts);
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
if (!forSign)
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)_roots.size());
for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
r->identity.serialize(b);
b.append((uint8_t)r->stableEndpoints.size());
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
ep->serialize(b);
}
if (_type == TYPE_MOON)
b.append((uint16_t)0); // no attached dictionary (for future use)
if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
unsigned int p = startAt;
_roots.clear();
switch((Type)b[p++]) {
case TYPE_NULL: _type = TYPE_NULL; break; // shouldn't ever really happen in serialized data but it's not invalid
case TYPE_PLANET: _type = TYPE_PLANET; break;
case TYPE_MOON: _type = TYPE_MOON; break;
default:
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
_id = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
const unsigned int numRoots = (unsigned int)b[p++];
if (numRoots > ZT_WORLD_MAX_ROOTS)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int k=0;k<numRoots;++k) {
_roots.push_back(Root());
Root &r = _roots.back();
p += r.identity.deserialize(b,p);
unsigned int numStableEndpoints = b[p++];
if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
r.stableEndpoints.push_back(InetAddress());
p += r.stableEndpoints.back().deserialize(b,p);
}
}
if (_type == TYPE_MOON)
p += b.template at<uint16_t>(p) + 2;
return (p - startAt);
}
inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(memcmp(_updatesMustBeSignedBy.data,w._updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)&&(memcmp(_signature.data,w._signature.data,ZT_C25519_SIGNATURE_LEN) == 0)&&(_roots == w._roots)&&(_type == w._type)); }
inline bool operator!=(const World &w) const { return (!(*this == w)); }
/**
* Create a World object signed with a key pair
*
* @param t World type
* @param id World ID
* @param ts World timestamp / revision
* @param sk Key that must be used to sign the next future update to this world
* @param roots Roots and their stable endpoints
* @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
* @return Signed World object
*/
static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
{
World w;
w._id = id;
w._ts = ts;
w._type = t;
w._updatesMustBeSignedBy = sk;
w._roots = roots;
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
w.serialize(tmp,true);
w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
return w;
}
protected:
uint64_t _id;
uint64_t _ts;
Type _type;
C25519::Public _updatesMustBeSignedBy;
C25519::Signature _signature;
std::vector<Root> _roots;
};
} // namespace ZeroTier
#endif