diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index da4ba7129..7adac1194 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -278,6 +278,32 @@ extern "C" { /* ----------------------------------------------------------------------------------------------------------------- */ + +/** + * Identity type codes + */ +enum ZT_Identity_Type +{ + /* These values must be the same as in Identity.hpp in the core. */ + ZT_IDENTITY_TYPE_C25519 = 0, + ZT_IDENTITY_TYPE_P384 = 1 +}; + +/** + * A ZeroTier identity (opaque) + */ +typedef void ZT_Identity; + +/** + * Full identity fingerprint with address and 384-bit hash of public key(s) + */ +ZT_PACKED_STRUCT(struct _ZT_Fingerprint +{ + uint64_t address; + uint8_t hash[48]; +}); +typedef struct _ZT_Fingerprint ZT_Fingerprint; + /** * Credential type IDs */ @@ -460,8 +486,7 @@ _ZT_TRACE_EVENT_STRUCT_END() * move around on the network. */ _ZT_TRACE_EVENT_STRUCT_START(VL1_RESETTING_PATHS_IN_SCOPE) - uint64_t reporter; /* ZeroTier address that triggered the reset */ - uint8_t reporterIdentityHash[48]; /* full identity hash of triggering node's identity */ + ZT_Fingerprint reporter; /* node that triggered the reset */ struct ZT_TraceEventPathAddress from; /* physical origin of triggering packet */ struct ZT_TraceEventPathAddress oldExternal; /* previous detected external address */ struct ZT_TraceEventPathAddress newExternal; /* new detected external address */ @@ -475,14 +500,12 @@ _ZT_TRACE_EVENT_STRUCT_END() * we might hear of them. A node tries a path by sending a trial message to it. */ _ZT_TRACE_EVENT_STRUCT_START(VL1_TRYING_NEW_PATH) - uint64_t address; /* short address of node we're trying to reach */ - uint8_t identityHash[48]; /* identity hash of node we're trying to reach */ + ZT_Fingerprint peer; /* node we're trying to reach */ struct ZT_TraceEventPathAddress physicalAddress; /* physical address being tried */ struct ZT_TraceEventPathAddress triggerAddress; /* physical origin of triggering packet */ uint64_t triggeringPacketId; /* packet ID of triggering packet */ uint8_t triggeringPacketVerb; /* packet verb of triggering packet */ - uint64_t triggeredByAddress; /* short address of node triggering attempt */ - uint8_t triggeredByIdentityHash[48]; /* full identity hash of node triggering attempt */ + ZT_Fingerprint triggeringPeer; /* peer that triggered attempt */ uint8_t reason; /* ZT_TraceTryingNewPathReason */ _ZT_TRACE_EVENT_STRUCT_END() @@ -491,8 +514,7 @@ _ZT_TRACE_EVENT_STRUCT_END() */ _ZT_TRACE_EVENT_STRUCT_START(VL1_LEARNED_NEW_PATH) uint64_t packetId; /* packet ID of confirming packet */ - uint64_t address; /* short address of learned peer */ - uint8_t identityHash[48]; /* full identity hash of learned peer */ + ZT_Fingerprint peer; /* peer on other side of new path */ struct ZT_TraceEventPathAddress physicalAddress; /* physical address learned */ struct ZT_TraceEventPathAddress replaced; /* if non-empty, an older address that was replaced */ _ZT_TRACE_EVENT_STRUCT_END() @@ -506,8 +528,7 @@ _ZT_TRACE_EVENT_STRUCT_END() _ZT_TRACE_EVENT_STRUCT_START(VL1_INCOMING_PACKET_DROPPED) uint64_t packetId; /* packet ID of failed packet */ uint64_t networkId; /* VL2 network ID or 0 if unrelated to a network or unknown */ - uint64_t address; /* short address that sent packet */ - uint8_t identityHash[48]; /* full identity hash of sending node */ + ZT_Fingerprint peer; /* peer that sent packet */ struct ZT_TraceEventPathAddress physicalAddress; /* physical origin address of packet */ uint8_t hops; /* hop count of packet */ uint8_t verb; /* packet verb */ @@ -535,7 +556,7 @@ _ZT_TRACE_EVENT_STRUCT_START(VL2_INCOMING_FRAME_DROPPED) uint64_t networkId; /* VL2 network ID */ uint64_t sourceMac; /* 48-bit source MAC */ uint64_t destMac; /* 48-bit destination MAC */ - uint64_t address; /* short address of sending peer */ + ZT_Fingerprint sender; /* sending peer */ struct ZT_TraceEventPathAddress physicalAddress; /* physical source address of packet */ uint8_t hops; /* hop count of packet */ uint16_t frameLength; /* length of frame in bytes */ @@ -582,7 +603,7 @@ _ZT_TRACE_EVENT_STRUCT_END() */ _ZT_TRACE_EVENT_STRUCT_START(VL2_CREDENTIAL_REJECTED) uint64_t networkId; /* VL2 network ID */ - uint64_t address; /* short address of sender */ + ZT_Fingerprint peer; /* sending peer */ uint32_t credentialId; /* credential ID */ int64_t credentialTimestamp; /* credential timestamp */ uint8_t credentialType; /* credential type */ @@ -728,21 +749,6 @@ enum ZT_Event ZT_EVENT_USER_MESSAGE = 6 }; -/** - * Identity type codes - */ -enum ZT_Identity_Type -{ - /* These values must be the same as in Identity.hpp in the core. */ - ZT_IDENTITY_TYPE_C25519 = 0, - ZT_IDENTITY_TYPE_P384 = 1 -}; - -/** - * A ZeroTier identity (opaque) - */ -typedef void ZT_Identity; - /** * User message used with ZT_EVENT_USER_MESSAGE * @@ -1343,11 +1349,6 @@ typedef struct */ const ZT_Identity *identity; - /** - * SHA384 hash of identity public key(s) - */ - uint8_t identityHash[48]; - /** * Remote major version or -1 if not known */ @@ -2141,15 +2142,6 @@ ZT_SDK_API int ZT_Identity_hasPrivate(const ZT_Identity *id); */ ZT_SDK_API uint64_t ZT_Identity_address(const ZT_Identity *id); -/** - * Compute a hash of this identity's public keys (or both public and private if includePrivate is true) - * - * @param id Identity to query - * @param h Buffer for 384-bit hash - * @param includePrivate If true include private keys if any - */ -ZT_SDK_API void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate); - /** * Delete an identity and free associated memory * diff --git a/node/Fingerprint.hpp b/node/Fingerprint.hpp index 4e443550a..1ed230b45 100644 --- a/node/Fingerprint.hpp +++ b/node/Fingerprint.hpp @@ -16,11 +16,14 @@ #include "Constants.hpp" #include "TriviallyCopyable.hpp" +#include "Address.hpp" #include namespace ZeroTier { +class Identity; + /** * Container for 384-bit identity hashes * @@ -33,39 +36,32 @@ namespace ZeroTier { */ class Fingerprint : public TriviallyCopyable { + friend class Identity; + public: - ZT_ALWAYS_INLINE Fingerprint() noexcept {} + /** + * Create an empty/nil fingerprint + */ + ZT_ALWAYS_INLINE Fingerprint() noexcept { memoryZero(this); } + + ZT_ALWAYS_INLINE Address address() const noexcept { return Address(_fp.address); } + ZT_ALWAYS_INLINE const uint8_t *hash() const noexcept { return _fp.hash; } + ZT_ALWAYS_INLINE void setZTFingerprint(ZT_Fingerprint *fp) const noexcept { memcpy(fp,&_fp,sizeof(ZT_Fingerprint)); } ZT_ALWAYS_INLINE void zero() noexcept { memoryZero(this); } + ZT_ALWAYS_INLINE unsigned long hashCode() const noexcept { return _fp.address; } - ZT_ALWAYS_INLINE uint8_t *data() noexcept { return reinterpret_cast(_h); } - ZT_ALWAYS_INLINE const uint8_t *data() const noexcept { return reinterpret_cast(_h); } + ZT_ALWAYS_INLINE operator bool() const noexcept { return (_fp.address != 0); } - ZT_ALWAYS_INLINE uint8_t operator[](const unsigned int i) const noexcept { return reinterpret_cast(_h)[i]; } - ZT_ALWAYS_INLINE uint8_t &operator[](const unsigned int i) noexcept { return reinterpret_cast(_h)[i]; } - - static constexpr unsigned int size() noexcept { return 48; } - - ZT_ALWAYS_INLINE unsigned long hashCode() const noexcept { return _h[0]; } - - ZT_ALWAYS_INLINE operator bool() const noexcept - { - for(unsigned int i=0;i<(384 / (sizeof(unsigned long) * 8));++i) { - if (_h[i] != 0) - return true; - } - return false; - } - - ZT_ALWAYS_INLINE bool operator==(const Fingerprint &h) const noexcept { return std::equal(_h,_h + (384 / (sizeof(unsigned long) * 8)),h._h); } + ZT_ALWAYS_INLINE bool operator==(const Fingerprint &h) const noexcept { return ((_fp.address == h._fp.address)&&(memcmp(_fp.hash,h._fp.hash,ZT_IDENTITY_HASH_SIZE) == 0)); } ZT_ALWAYS_INLINE bool operator!=(const Fingerprint &h) const noexcept { return !(*this == h); } - ZT_ALWAYS_INLINE bool operator<(const Fingerprint &h) const noexcept { return std::lexicographical_compare(_h,_h + (384 / (sizeof(unsigned long) * 8)),h._h,h._h + (384 / (sizeof(unsigned long) * 8))); } + ZT_ALWAYS_INLINE bool operator<(const Fingerprint &h) const noexcept { return ((_fp.address < h._fp.address) || ((_fp.address == h._fp.address)&&(memcmp(_fp.hash,h._fp.hash,ZT_IDENTITY_HASH_SIZE) < 0))); } ZT_ALWAYS_INLINE bool operator>(const Fingerprint &h) const noexcept { return (h < *this); } ZT_ALWAYS_INLINE bool operator<=(const Fingerprint &h) const noexcept { return !(h < *this); } ZT_ALWAYS_INLINE bool operator>=(const Fingerprint &h) const noexcept { return !(*this < h); } private: - unsigned long _h[384 / (sizeof(unsigned long) * 8)]; + ZT_Fingerprint _fp; }; } // namespace ZeroTier diff --git a/node/Identity.cpp b/node/Identity.cpp index 98f1c7915..bde09e1b0 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -111,8 +111,7 @@ bool Identity::generate(const Type t) Utils::storeBigEndian(_pub.t1mimc52,mimc52Delay(&_pub,sizeof(_pub) - sizeof(_pub.t1mimc52),ZT_V1_IDENTITY_MIMC52_VDF_ROUNDS_BASE)); // Compute SHA384 fingerprint hash of keys and MIMC output and generate address directly from it. - _computeHash(); - _address.setTo(_fp.data()); + _computeHash(); // this sets the address for P384 if (!_address.isReserved()) break; } @@ -140,7 +139,7 @@ bool Identity::locallyValidate() const noexcept } case P384: - if (_address == Address(_fp.data())) { + if (_address == Address(_fp.hash())) { // The most significant 8 bits of the MIMC proof included with v1 identities can be used to store a multiplier // that can indicate that more work than the required minimum has been performed. Right now this is never done // but it could have some use in the future. There is no harm in doing it, and we'll accept any round count @@ -445,10 +444,8 @@ int Identity::unmarshal(const uint8_t *data,const int len) noexcept _fp.zero(); _hasPrivate = false; - if (len < (ZT_ADDRESS_LENGTH + 1)) + if (len < (1 + ZT_ADDRESS_LENGTH)) return -1; - _address.setTo(data); - unsigned int privlen; switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) { @@ -457,6 +454,7 @@ int Identity::unmarshal(const uint8_t *data,const int len) noexcept return -1; memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN); + _address.setTo(data); _computeHash(); privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN]; @@ -477,8 +475,8 @@ int Identity::unmarshal(const uint8_t *data,const int len) noexcept return -1; memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); - _computeHash(); - if (_address != Address(_fp.data())) // for v1 we can sanity check this here, but this isn't a full validate + _computeHash(); // this sets the address for P384 + if (_address != Address(data)) // sanity check address in data stream return -1; privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE]; @@ -507,11 +505,14 @@ void Identity::_computeHash() break; case C25519: - SHA384(_fp.data(),_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + _fp._fp.address = _address.toInt(); + SHA384(_fp._fp.hash,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); break; case P384: - SHA384(_fp.data(),&_pub,sizeof(_pub)); + SHA384(_fp._fp.hash,&_pub,sizeof(_pub)); + _address.setTo(reinterpret_cast(_fp._fp.hash)); + _fp._fp.address = _address.toInt(); break; } } @@ -601,13 +602,6 @@ uint64_t ZT_Identity_address(const ZT_Identity *id) return reinterpret_cast(id)->address().toInt(); } -void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate) -{ - if (includePrivate) - reinterpret_cast(id)->hashWithPrivate(h); - else memcpy(h,reinterpret_cast(id)->fingerprint().data(),ZT_IDENTITY_HASH_SIZE); -} - ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id) { if (id) diff --git a/node/Membership.cpp b/node/Membership.cpp index 4597dc907..fd636d11d 100644 --- a/node/Membership.cpp +++ b/node/Membership.cpp @@ -130,13 +130,13 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme { const int64_t newts = com.timestamp(); if (newts <= _comRevocationThreshold) { - RR->t->credentialRejected(tPtr,0xd9992121,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED); + RR->t->credentialRejected(tPtr,0xd9992121,com.networkId(),sourcePeerIdentity.address(),sourcePeerIdentity,com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED); return ADD_REJECTED; } const int64_t oldts = _com.timestamp(); if (newts < oldts) { - RR->t->credentialRejected(tPtr,0xd9928192,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST); + RR->t->credentialRejected(tPtr,0xd9928192,com.networkId(),sourcePeerIdentity.address(),sourcePeerIdentity,com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST); return ADD_REJECTED; } if ((newts == oldts)&&(_com == com)) @@ -144,13 +144,13 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme switch(com.verify(RR,tPtr)) { default: - RR->t->credentialRejected(tPtr,0x0f198241,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); + RR->t->credentialRejected(tPtr,0x0f198241,com.networkId(),sourcePeerIdentity.address(),sourcePeerIdentity,com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); return Membership::ADD_REJECTED; case Credential::VERIFY_OK: _com = com; return ADD_ACCEPTED_NEW; case Credential::VERIFY_BAD_SIGNATURE: - RR->t->credentialRejected(tPtr,0xbaf0aaaa,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED); + RR->t->credentialRejected(tPtr,0xbaf0aaaa,com.networkId(),sourcePeerIdentity.address(),sourcePeerIdentity,com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED); return ADD_REJECTED; case Credential::VERIFY_NEED_IDENTITY: return ADD_DEFERRED_FOR_WHOIS; @@ -171,7 +171,7 @@ static ZT_ALWAYS_INLINE Membership::AddCredentialResult _addCredImpl( C *rc = remoteCreds.get(cred.id()); if (rc) { if (rc->timestamp() > cred.timestamp()) { - RR->t->credentialRejected(tPtr,0x40000001,nconf.networkId,sourcePeerIdentity.address(),cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST); + RR->t->credentialRejected(tPtr,0x40000001,nconf.networkId,sourcePeerIdentity.address(),sourcePeerIdentity,cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST); return Membership::ADD_REJECTED; } if (*rc == cred) @@ -180,13 +180,13 @@ static ZT_ALWAYS_INLINE Membership::AddCredentialResult _addCredImpl( const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id())); if ((rt)&&(*rt >= cred.timestamp())) { - RR->t->credentialRejected(tPtr,0x24248124,nconf.networkId,sourcePeerIdentity.address(),cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED); + RR->t->credentialRejected(tPtr,0x24248124,nconf.networkId,sourcePeerIdentity.address(),sourcePeerIdentity,cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED); return Membership::ADD_REJECTED; } switch(cred.verify(RR,tPtr)) { default: - RR->t->credentialRejected(tPtr,0x01feba012,nconf.networkId,sourcePeerIdentity.address(),cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); + RR->t->credentialRejected(tPtr,0x01feba012,nconf.networkId,sourcePeerIdentity.address(),sourcePeerIdentity,cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); return Membership::ADD_REJECTED; case 0: if (!rc) @@ -206,7 +206,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme int64_t *rt; switch(rev.verify(RR,tPtr)) { default: - RR->t->credentialRejected(tPtr,0x938fffff,nconf.networkId,sourcePeerIdentity.address(),rev.id(),0,ZT_CREDENTIAL_TYPE_REVOCATION,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); + RR->t->credentialRejected(tPtr,0x938fffff,nconf.networkId,sourcePeerIdentity.address(),sourcePeerIdentity,rev.id(),0,ZT_CREDENTIAL_TYPE_REVOCATION,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); return ADD_REJECTED; case 0: { const ZT_CredentialType ct = rev.typeBeingRevoked(); @@ -228,7 +228,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme } return ADD_ACCEPTED_REDUNDANT; default: - RR->t->credentialRejected(tPtr,0x0bbbb1a4,nconf.networkId,sourcePeerIdentity.address(),rev.id(),0,ZT_CREDENTIAL_TYPE_REVOCATION,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); + RR->t->credentialRejected(tPtr,0x0bbbb1a4,nconf.networkId,sourcePeerIdentity.address(),sourcePeerIdentity,rev.id(),0,ZT_CREDENTIAL_TYPE_REVOCATION,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID); return ADD_REJECTED; } } diff --git a/node/Network.cpp b/node/Network.cpp index afd0832a8..d15e12570 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1010,7 +1010,7 @@ int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToD try { if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id)) return 0; // invalid config that is not for us or not for this network - if ((!Utils::allZero(nconf.issuedToIdentityHash,ZT_IDENTITY_HASH_SIZE))&&(memcmp(nconf.issuedToIdentityHash,RR->identity.fingerprint().data(),ZT_IDENTITY_HASH_SIZE) != 0)) + if ((!Utils::allZero(nconf.issuedToFingerprintHash,ZT_IDENTITY_HASH_SIZE))&&(memcmp(nconf.issuedToFingerprintHash,RR->identity.fingerprint().hash(),ZT_IDENTITY_HASH_SIZE) != 0)) return 0; // full identity hash is present and does not match if (_config == nconf) diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 2d94a9d12..9d2ae89f2 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -32,7 +32,7 @@ bool NetworkConfig::toDictionary(Dictionary &d,bool includeLegacy) const d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta); d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision); d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString((char *)tmp)); - d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO_IDENTITY_HASH,this->issuedToIdentityHash,ZT_IDENTITY_HASH_SIZE); + d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO_IDENTITY_HASH,this->issuedToFingerprintHash,ZT_IDENTITY_HASH_SIZE); d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags); d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit); d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint16_t)this->type); @@ -122,9 +122,9 @@ bool NetworkConfig::fromDictionary(const Dictionary &d) this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0); const std::vector *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO_IDENTITY_HASH]); if (blob->size() == ZT_IDENTITY_HASH_SIZE) { - memcpy(this->issuedToIdentityHash,blob->data(),ZT_IDENTITY_HASH_SIZE); + memcpy(this->issuedToFingerprintHash,blob->data(),ZT_IDENTITY_HASH_SIZE); } else { - memset(this->issuedToIdentityHash,0,ZT_IDENTITY_HASH_SIZE); + memset(this->issuedToFingerprintHash,0,ZT_IDENTITY_HASH_SIZE); } if (!this->issuedTo) return false; diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index f232400ac..6845a3085 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -280,7 +280,7 @@ struct NetworkConfig : TriviallyCopyable * If this field is all zero it is treated as undefined since old controllers * do not set it. */ - uint8_t issuedToIdentityHash[ZT_IDENTITY_HASH_SIZE]; + uint8_t issuedToFingerprintHash[ZT_IDENTITY_HASH_SIZE]; /** * Flags (64-bit) diff --git a/node/Node.cpp b/node/Node.cpp index 1bbe1fd86..732ce0129 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -111,7 +111,10 @@ Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64 stateObjectPut(tPtr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr)); } - RR->identity.hashWithPrivate(RR->localCacheSymmetricKey); + uint8_t tmph[ZT_IDENTITY_HASH_SIZE]; + RR->identity.hashWithPrivate(tmph); + RR->localCacheSymmetric.init(tmph); + Utils::burn(tmph,sizeof(tmph)); // This constructs all the components of the ZeroTier core within a single contiguous memory container, // which reduces memory fragmentation and may improve cache locality. @@ -483,7 +486,6 @@ ZT_PeerList *Node::peers() const p->address = (*pi)->address().toInt(); identities[pl->peerCount] = (*pi)->identity(); // need to make a copy in case peer gets deleted p->identity = &identities[pl->peerCount]; - memcpy(p->identityHash,(*pi)->identity().fingerprint().data(),ZT_IDENTITY_HASH_SIZE); if ((*pi)->remoteVersionKnown()) { p->versionMajor = (int)(*pi)->remoteVersionMajor(); p->versionMinor = (int)(*pi)->remoteVersionMinor(); diff --git a/node/Peer.cpp b/node/Peer.cpp index dc1615060..8bb000acb 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -123,7 +123,7 @@ void Peer::received( RR->t->learnedNewPath(tPtr,0x582fabdd,packetId,_id,path->address(),old); } else { if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,path->localSocket(),path->address())) { - RR->t->tryingNewPath(tPtr,0xb7747ddd,_id,path->address(),path->address(),packetId,(uint8_t)verb,_id.address(),_id.fingerprint().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH); + RR->t->tryingNewPath(tPtr,0xb7747ddd,_id,path->address(),path->address(),packetId,(uint8_t)verb,_id,ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH); path->sent(now,sendHELLO(tPtr,path->localSocket(),path->address(),now)); } } @@ -135,11 +135,11 @@ path_check_done: InetAddress addr; if ((_bootstrap.type() == Endpoint::TYPE_INETADDR_V4)||(_bootstrap.type() == Endpoint::TYPE_INETADDR_V6)) { - RR->t->tryingNewPath(tPtr,0x0a009444,_id,_bootstrap.inetAddr(),InetAddress::NIL,0,0,0,nullptr,ZT_TRACE_TRYING_NEW_PATH_REASON_BOOTSTRAP_ADDRESS); + RR->t->tryingNewPath(tPtr,0x0a009444,_id,_bootstrap.inetAddr(),InetAddress::NIL,0,0,Identity::NIL,ZT_TRACE_TRYING_NEW_PATH_REASON_BOOTSTRAP_ADDRESS); sendHELLO(tPtr,-1,_bootstrap.inetAddr(),now); } if (RR->node->externalPathLookup(tPtr,_id,-1,addr)) { if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,-1,addr)) { - RR->t->tryingNewPath(tPtr,0x84a10000,_id,_bootstrap.inetAddr(),InetAddress::NIL,0,0,0,nullptr,ZT_TRACE_TRYING_NEW_PATH_REASON_EXPLICITLY_SUGGESTED_ADDRESS); + RR->t->tryingNewPath(tPtr,0x84a10000,_id,_bootstrap.inetAddr(),InetAddress::NIL,0,0,Identity::NIL,ZT_TRACE_TRYING_NEW_PATH_REASON_EXPLICITLY_SUGGESTED_ADDRESS); sendHELLO(tPtr,-1,addr,now); } } @@ -485,14 +485,23 @@ void Peer::alarm(void *tPtr,const int64_t now) int Peer::marshal(uint8_t data[ZT_PEER_MARSHAL_SIZE_MAX]) const noexcept { - RWMutex::RLock l(_lock); - data[0] = 0; // serialized peer version - int s = _id.marshal(data + 1,false); + // For faster unmarshaling on large nodes the long-term secret key is cached. It's + // encrypted with a symmetric key derived from a hash of the local node's identity + // secrets, so the local node's address is also included. That way the unmarshal + // code can check this address and not use this cached key if the local identity has + // changed. In that case agreement must be executed again. + RR->identity.address().copyTo(data + 1); + RR->localCacheSymmetric.encrypt(_key,data + 6); + RR->localCacheSymmetric.encrypt(_key + 16,data + 22); + + RWMutex::RLock l(_lock); + + int s = _id.marshal(data + 38,false); if (s <= 0) return s; - int p = 1 + s; + int p = s + 38; s = _locator.marshal(data + p); if (s <= 0) return s; @@ -520,17 +529,26 @@ int Peer::marshal(uint8_t data[ZT_PEER_MARSHAL_SIZE_MAX]) const noexcept int Peer::unmarshal(const uint8_t *restrict data,const int len) noexcept { int p; + bool mustRecomputeSecret; { RWMutex::Lock l(_lock); - if ((len <= 1) || (data[0] != 0)) + if ((len <= 38) || (data[0] != 0)) return -1; - int s = _id.unmarshal(data + 1,len - 1); + if (Address(data + 1) == RR->identity.address()) { + RR->localCacheSymmetric.decrypt(data + 6,_key); + RR->localCacheSymmetric.decrypt(data + 22,_key + 16); + mustRecomputeSecret = false; + } else { + mustRecomputeSecret = true; // can't use cached key if local identity has changed + } + + int s = _id.unmarshal(data + 38,len - 38); if (s <= 0) return s; - p = 1 + s; + p = s + 38; s = _locator.unmarshal(data + p,len - p); if (s <= 0) return s; @@ -542,6 +560,7 @@ int Peer::unmarshal(const uint8_t *restrict data,const int len) noexcept if ((p + 10) > len) return -1; + _vProto = Utils::loadBigEndian(data + p); p += 2; _vMajor = Utils::loadBigEndian(data + p); @@ -551,12 +570,16 @@ int Peer::unmarshal(const uint8_t *restrict data,const int len) noexcept _vRevision = Utils::loadBigEndian(data + p); p += 2; p += 2 + (int)Utils::loadBigEndian(data + p); + if (p > len) return -1; } - if (!RR->identity.agree(_id,_key)) - return -1; + if (mustRecomputeSecret) { + if (!RR->identity.agree(_id,_key)) + return -1; + } + _incomingProbe = Protocol::createProbe(_id,RR->identity,_key); return p; diff --git a/node/Peer.hpp b/node/Peer.hpp index 634f65309..894a90a67 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -33,7 +33,7 @@ #include // version, identity, locator, bootstrap, version info, length of any additional fields -#define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + ZT_INETADDRESS_MARSHAL_SIZE_MAX + (2*4) + 2) +#define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_ADDRESS_LENGTH + ZT_PEER_SECRET_KEY_LENGTH + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + ZT_INETADDRESS_MARSHAL_SIZE_MAX + (2*4) + 2) namespace ZeroTier { diff --git a/node/Protocol.cpp b/node/Protocol.cpp index 66afc96d2..ba2f9feb1 100644 --- a/node/Protocol.cpp +++ b/node/Protocol.cpp @@ -33,8 +33,8 @@ std::atomic _s_packetIdCtr((uint64_t)time(nullptr) << 32U); uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept { uint8_t tmp[ZT_IDENTITY_HASH_SIZE + ZT_IDENTITY_HASH_SIZE]; - memcpy(tmp,sender.fingerprint().data(),ZT_IDENTITY_HASH_SIZE); - memcpy(tmp + ZT_IDENTITY_HASH_SIZE,recipient.fingerprint().data(),ZT_IDENTITY_HASH_SIZE); + memcpy(tmp,sender.fingerprint().hash(),ZT_IDENTITY_HASH_SIZE); + memcpy(tmp + ZT_IDENTITY_HASH_SIZE,recipient.fingerprint().hash(),ZT_IDENTITY_HASH_SIZE); uint64_t hash[6]; SHA384(hash,tmp,sizeof(tmp),key,ZT_PEER_SECRET_KEY_LENGTH); return hash[0]; diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 4dec6532d..bc9019cee 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -17,6 +17,7 @@ #include "Constants.hpp" #include "Utils.hpp" #include "Identity.hpp" +#include "AES.hpp" namespace ZeroTier { @@ -57,7 +58,6 @@ public: ZT_ALWAYS_INLINE ~RuntimeEnvironment() { Utils::burn(secretIdentityStr,sizeof(secretIdentityStr)); - Utils::burn(localCacheSymmetricKey,sizeof(localCacheSymmetricKey)); } // Node instance that owns this RuntimeEnvironment @@ -81,9 +81,8 @@ public: char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH]; char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH]; - // A hash of this node identity's public and private keys that is used as - // a secret key to encrypt locally cached sensitive information. - uint8_t localCacheSymmetricKey[ZT_IDENTITY_HASH_SIZE]; + // AES keyed with a hash of this node's identity secret keys for local cache encryption at rest (where needed). + AES localCacheSymmetric; }; } // namespace ZeroTier diff --git a/node/Tests.cpp b/node/Tests.cpp index 29d523a94..080869a7d 100644 --- a/node/Tests.cpp +++ b/node/Tests.cpp @@ -285,7 +285,7 @@ extern "C" const char *ZTT_general() ZT_T_ASSERT(sizeof(sockaddr_in) <= sizeof(InetAddress)); ZT_T_ASSERT(sizeof(sockaddr_in6) <= sizeof(InetAddress)); ZT_T_ASSERT(sizeof(sockaddr) <= sizeof(InetAddress)); - ZT_T_ASSERT(sizeof(Fingerprint) == 48); + ZT_T_ASSERT(sizeof(Fingerprint) == sizeof(ZT_Fingerprint)); ZT_T_PRINTF("OK" ZT_EOL_S); } diff --git a/node/Topology.hpp b/node/Topology.hpp index decb430ba..e817bf071 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -351,7 +351,7 @@ private: #endif return h + (uint64_t)Utils::ntoh(reinterpret_cast(&r)->sin6_port) + (uint64_t)l; } else { - return Utils::hashString(reinterpret_cast(&r),sizeof(InetAddress)) + (uint64_t)l; + return Utils::fnv1a32(reinterpret_cast(&r),sizeof(InetAddress)) + (uint64_t)l; } } diff --git a/node/Trace.cpp b/node/Trace.cpp index a92ddac49..f513dae7a 100644 --- a/node/Trace.cpp +++ b/node/Trace.cpp @@ -109,24 +109,19 @@ void Trace::_tryingNewPath( const InetAddress &triggerAddress, const uint64_t triggeringPacketId, const uint8_t triggeringPacketVerb, - const uint64_t triggeredByAddress, - const uint8_t *triggeredByIdentityHash, + const Identity &triggeringPeer, const ZT_TraceTryingNewPathReason reason) { ZT_TraceEvent_VL1_TRYING_NEW_PATH ev; ev.evSize = ZT_CONST_TO_BE_UINT16(sizeof(ev)); ev.evType = ZT_CONST_TO_BE_UINT16(ZT_TRACE_VL1_TRYING_NEW_PATH); ev.codeLocation = Utils::hton(codeLocation); - ev.address = Utils::hton(trying.address().toInt()); - memcpy(ev.identityHash,trying.fingerprint().data(),48); + trying.fingerprint().setZTFingerprint(&ev.peer); physicalAddress.forTrace(ev.physicalAddress); triggerAddress.forTrace(ev.triggerAddress); ev.triggeringPacketId = triggeringPacketId; ev.triggeringPacketVerb = triggeringPacketVerb; - ev.triggeredByAddress = Utils::hton(triggeredByAddress); - if (triggeredByIdentityHash) - memcpy(ev.triggeredByIdentityHash,triggeredByIdentityHash,ZT_IDENTITY_HASH_SIZE); - else memset(ev.triggeredByIdentityHash,0,ZT_IDENTITY_HASH_SIZE); + triggeringPeer.fingerprint().setZTFingerprint(&ev.triggeringPeer); ev.reason = (uint8_t)reason; RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev); } @@ -144,8 +139,7 @@ void Trace::_learnedNewPath( ev.evType = ZT_CONST_TO_BE_UINT16(ZT_TRACE_VL1_LEARNED_NEW_PATH); ev.codeLocation = Utils::hton(codeLocation); ev.packetId = packetId; // packet IDs are kept in big-endian - ev.address = Utils::hton(peerIdentity.address().toInt()); - memcpy(ev.identityHash,peerIdentity.fingerprint().data(),ZT_IDENTITY_HASH_SIZE); + peerIdentity.fingerprint().setZTFingerprint(&ev.peer); physicalAddress.forTrace(ev.physicalAddress); replaced.forTrace(ev.replaced); @@ -169,13 +163,7 @@ void Trace::_incomingPacketDropped( ev.codeLocation = Utils::hton(codeLocation); ev.packetId = packetId; // packet IDs are kept in big-endian ev.networkId = Utils::hton(networkId); - if (peerIdentity) { - ev.address = Utils::hton(peerIdentity.address().toInt()); - memcpy(ev.identityHash,peerIdentity.fingerprint().data(),ZT_IDENTITY_HASH_SIZE); - } else { - ev.address = 0; - memset(ev.identityHash,0,ZT_IDENTITY_HASH_SIZE); - } + peerIdentity.fingerprint().setZTFingerprint(&ev.peer); physicalAddress.forTrace(ev.physicalAddress); ev.hops = hops; ev.verb = verb; @@ -238,7 +226,7 @@ void Trace::_incomingNetworkFrameDropped( ev.networkId = Utils::hton(networkId); ev.sourceMac = Utils::hton(sourceMac.toInt()); ev.destMac = Utils::hton(destMac.toInt()); - ev.address = Utils::hton(peerIdentity.address().toInt()); + peerIdentity.fingerprint().setZTFingerprint(&ev.sender); physicalAddress.forTrace(ev.physicalAddress); ev.hops = hops; ev.frameLength = Utils::hton(frameLength); @@ -325,6 +313,7 @@ void Trace::_credentialRejected( const uint32_t codeLocation, const uint64_t networkId, const Address &address, + const Identity &identity, const uint32_t credentialId, const int64_t credentialTimestamp, const uint8_t credentialType, @@ -335,7 +324,12 @@ void Trace::_credentialRejected( ev.evType = ZT_CONST_TO_BE_UINT16(ZT_TRACE_VL2_NETWORK_FILTER); ev.codeLocation = Utils::hton(codeLocation); ev.networkId = Utils::hton(networkId); - ev.address = Utils::hton(address.toInt()); + if (identity) { + identity.fingerprint().setZTFingerprint(&ev.peer); + } else { + ev.peer.address = address.toInt(); + memset(ev.peer.hash,0,sizeof(ev.peer.hash)); + } ev.credentialId = Utils::hton(credentialId); ev.credentialTimestamp = Utils::hton(credentialTimestamp); ev.credentialType = credentialType; diff --git a/node/Trace.hpp b/node/Trace.hpp index 19e55d635..acc82a3cc 100644 --- a/node/Trace.hpp +++ b/node/Trace.hpp @@ -119,11 +119,10 @@ public: const InetAddress &triggerAddress, uint64_t triggeringPacketId, uint8_t triggeringPacketVerb, - uint64_t triggeredByAddress, - const uint8_t *triggeredByIdentityHash, + const Identity &triggeringPeer, ZT_TraceTryingNewPathReason reason) { - if (_vl1) _tryingNewPath(tPtr,codeLocation,trying,physicalAddress,triggerAddress,triggeringPacketId,triggeringPacketVerb,triggeredByAddress,triggeredByIdentityHash,reason); + if (_vl1) _tryingNewPath(tPtr,codeLocation,trying,physicalAddress,triggerAddress,triggeringPacketId,triggeringPacketVerb,triggeringPeer,reason); } ZT_ALWAYS_INLINE void learnedNewPath( @@ -239,12 +238,13 @@ public: const uint32_t codeLocation, uint64_t networkId, const Address &address, + const Identity &identity, uint32_t credentialId, int64_t credentialTimestamp, uint8_t credentialType, ZT_TraceCredentialRejectionReason reason) { - if (_vl2) _credentialRejected(tPtr,codeLocation,networkId,address,credentialId,credentialTimestamp,credentialType,reason); + if (_vl2) _credentialRejected(tPtr,codeLocation,networkId,address,identity,credentialId,credentialTimestamp,credentialType,reason); } private: @@ -264,8 +264,7 @@ private: const InetAddress &triggerAddress, uint64_t triggeringPacketId, uint8_t triggeringPacketVerb, - uint64_t triggeredByAddress, - const uint8_t *triggeredByIdentityHash, + const Identity &triggeringPeer, ZT_TraceTryingNewPathReason reason); void _learnedNewPath( void *tPtr, @@ -336,6 +335,7 @@ private: uint32_t codeLocation, uint64_t networkId, const Address &address, + const Identity &identity, uint32_t credentialId, int64_t credentialTimestamp, uint8_t credentialType, diff --git a/node/Utils.hpp b/node/Utils.hpp index 70585201b..50c643c94 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -258,24 +258,20 @@ static ZT_ALWAYS_INLINE unsigned long long hexStrToU64(const char *s) noexcept } /** - * Calculate a non-cryptographic hash of a byte string + * Compute 32-bit FNV-1a checksum * - * @param key Key to hash - * @param len Length in bytes - * @return Non-cryptographic hash suitable for use in a hash table + * See: http://www.isthe.com/chongo/tech/comp/fnv/ + * + * @param data Data to checksum + * @param len Length of data + * @return FNV1a checksum */ -static ZT_ALWAYS_INLINE unsigned long hashString(const void *restrict key,const unsigned int len) noexcept +static ZT_ALWAYS_INLINE uint32_t fnv1a32(const void *const data,const unsigned int len) noexcept { - const uint8_t *p = reinterpret_cast(key); - unsigned long h = 0; - for (unsigned int i=0;i> 6U); - } - h += (h << 3U); - h ^= (h >> 11U); - h += (h << 15U); + uint32_t h = 0x811c9dc5; + const uint32_t p = 0x01000193; + for(unsigned int i=0;i(data)[i]) * p; return h; } diff --git a/node/VL1.cpp b/node/VL1.cpp index 176aa2ff5..f86f4938b 100644 --- a/node/VL1.cpp +++ b/node/VL1.cpp @@ -823,7 +823,7 @@ bool VL1::_RENDEZVOUS(void *tPtr,const SharedPtr &path,const SharedPtrcontact(tPtr,Endpoint(atAddr),now,false); - RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().fingerprint().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); + RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->identity(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); } break; case 255: { @@ -835,7 +835,7 @@ bool VL1::_RENDEZVOUS(void *tPtr,const SharedPtr &path,const SharedPtrcontact(tPtr,ep,now,false); - RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().fingerprint().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); + RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->identity(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); break; default: break; @@ -969,7 +969,7 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr &path,const Shared } if (a) { - RR->t->tryingNewPath(tPtr,0xa5ab1a43,peer->identity(),a,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().fingerprint().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RECEIVED_PUSH_DIRECT_PATHS); + RR->t->tryingNewPath(tPtr,0xa5ab1a43,peer->identity(),a,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->identity(),ZT_TRACE_TRYING_NEW_PATH_REASON_RECEIVED_PUSH_DIRECT_PATHS); } ptr += (int)addrRecordLen;