diff --git a/node/Constants.hpp b/node/Constants.hpp index 9474338c1..0f4e9f662 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -186,9 +186,9 @@ #define ZT_DEFAULT_MTU 2800 /** - * Maximum number of packet fragments we'll support (protocol max: 16) + * Maximum number of packet fragments we'll support (protocol limit: 16) */ -#define ZT_MAX_PACKET_FRAGMENTS 7 +#define ZT_MAX_PACKET_FRAGMENTS 10 /** * Size of RX queue in packets diff --git a/node/Identity.cpp b/node/Identity.cpp index 0c3656d12..2f45cb12a 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -99,18 +99,29 @@ void Identity::generate(const Type t) } while (_address.isReserved()); delete [] genmem; - if (t == P384) + if (t == P384) { ECC384GenerateKey(_pub.p384,_priv.p384); + SHA384(digest,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + ECC384ECDSASign(_priv.p384,digest,_pub.p384s); + } } bool Identity::locallyValidate() const { + uint8_t digest[64]; + if (_address.isReserved()) return false; + if (_type == P384) { + // Check that the C25519 public key is blessed by the P-384 key. + SHA384(digest,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + if (!ECC384ECDSAVerify(_pub.p384,digest,_pub.p384s)) + return false; + } + char *genmem = nullptr; try { - uint8_t digest[64]; genmem = new char[ZT_IDENTITY_GEN_MEMORY]; _computeMemoryHardHash(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem); delete [] genmem; @@ -151,12 +162,12 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_ *(p++) = ':'; *(p++) = '1'; *(p++) = ':'; - int el = Utils::b32e((const uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); + int el = Utils::b32e((const uint8_t *)(&_pub),sizeof(_pub),p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); if (el <= 0) return nullptr; p += el; if ((_hasPrivate)&&(includePrivate)) { *(p++) = ':'; - el = Utils::b32e((const uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); + el = Utils::b32e((const uint8_t *)(&_priv),sizeof(_priv),p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); if (el <= 0) return nullptr; p += el; } @@ -218,7 +229,7 @@ bool Identity::fromString(const char *str) break; case P384: - if (Utils::b32d(f,(uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) != (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE)) { + if (Utils::b32d(f,(uint8_t *)(&_pub),sizeof(_pub)) != sizeof(_pub)) { _address.zero(); return false; } @@ -241,7 +252,7 @@ bool Identity::fromString(const char *str) break; case P384: - if (Utils::b32d(f,(uint8_t *)(&_priv),ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE) != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)) { + if (Utils::b32d(f,(uint8_t *)(&_priv),sizeof(_priv)) != sizeof(_priv)) { _address.zero(); return false; } else { diff --git a/node/Identity.hpp b/node/Identity.hpp index f447189dc..060b70a2c 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -48,33 +48,24 @@ public: enum Type { C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.x and 2.x, default) - P384 = ZT_CRYPTO_ALG_P384 // Type 1 -- NIST P-384 with linked Curve25519 and Ed25519 secondaries (2.x+) + P384 = ZT_CRYPTO_ALG_P384 // Type 1 -- NIST P-384 with linked Curve25519/Ed25519 secondaries (2.x+) }; ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast(this),0,sizeof(Identity)); } - ZT_ALWAYS_INLINE Identity(const Identity &id) { memcpy(reinterpret_cast(this),&id,sizeof(Identity)); } - - inline Identity(const char *str) + ZT_ALWAYS_INLINE Identity(const char *str) { if (!fromString(str)) throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE; } - template - inline Identity(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } + ZT_ALWAYS_INLINE Identity(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast(this),sizeof(Identity)); } /** * Set identity to NIL value (all zero) */ - ZT_ALWAYS_INLINE void zero() { Utils::burn(reinterpret_cast(this),sizeof(Identity)); } - - ZT_ALWAYS_INLINE Identity &operator=(const Identity &id) - { - memcpy(reinterpret_cast(this),&id,sizeof(Identity)); - return *this; - } + ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast(this),0,sizeof(Identity)); } /** * @return Identity type @@ -108,7 +99,7 @@ public: * @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length) * @return True on success, false if no private key */ - inline bool sha512PrivateKey(void *const sha) const + ZT_ALWAYS_INLINE bool sha512PrivateKey(void *const sha) const { if (_hasPrivate) { switch(_type) { @@ -116,7 +107,7 @@ public: SHA512(sha,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); return true; case P384: - SHA512(sha,&_priv,ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE); + SHA512(sha,&_priv,sizeof(_priv)); return true; } } @@ -131,7 +122,7 @@ public: * * @param h 128-bit buffer to receive hash (must be 16 bytes in size) */ - inline void publicKeyHash128(void *const h) const + ZT_ALWAYS_INLINE void publicKeyHash128(void *const h) const { uint8_t tmp[48]; switch(_type) { @@ -139,7 +130,7 @@ public: SHA384(tmp,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); break; case P384: - SHA384(tmp,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE); + SHA384(tmp,&_pub,sizeof(_pub)); break; } for(int i=0;i<16;++i) @@ -158,7 +149,7 @@ public: * @param siglen Length of buffer * @return Number of bytes actually written to sig or 0 on error */ - inline unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const + ZT_ALWAYS_INLINE unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const { if (_hasPrivate) { switch(_type) { @@ -171,8 +162,8 @@ public: case P384: if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { - // Signature is a hash of the message followed by the c25519/ed25519 type 0 - // identity public keys to ensure that the two public keys are not separable. + // Signature hash includes the C25519/Ed25519 public key after the message. + // This is an added guard against divorcing these two bound keys. uint8_t h[48]; SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); @@ -193,7 +184,7 @@ 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 *sig,unsigned int siglen) const + ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const { switch(_type) { case C25519: @@ -218,7 +209,7 @@ public: * @param key Result parameter to fill with key bytes * @return Was agreement successful? */ - inline bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const + ZT_ALWAYS_INLINE bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const { uint8_t rawkey[128]; uint8_t h[64]; @@ -269,7 +260,7 @@ public: * @param dest Destination to fill with downgraded identity * @param toType Desired identity type */ - inline bool downgrade(Identity &dest,const Type toType) + ZT_ALWAYS_INLINE bool downgrade(Identity &dest,const Type toType) { if ((_type == P384)&&(toType == C25519)) { dest._address = _address; @@ -289,7 +280,7 @@ public: * @throws std::out_of_range Buffer too small */ template - inline void serialize(Buffer &b,bool includePrivate = false) const + ZT_ALWAYS_INLINE void serialize(Buffer &b,bool includePrivate = false) const { _address.appendTo(b); switch(_type) { @@ -309,6 +300,7 @@ public: b.append((uint8_t)P384); b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); b.append(_pub.p384,ZT_ECC384_PUBLIC_KEY_SIZE); + b.append(_pub.p384s,ZT_ECC384_SIGNATURE_SIZE); if ((_hasPrivate)&&(includePrivate)) { b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)); b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); @@ -334,7 +326,7 @@ public: * @throws std::invalid_argument Serialized data invalid */ template - inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { _hasPrivate = false; unsigned int p = startAt; @@ -365,6 +357,8 @@ public: p += ZT_C25519_PUBLIC_KEY_LEN; memcpy(_pub.p384,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE); p += ZT_ECC384_PUBLIC_KEY_SIZE; + memcpy(_pub.p384s,b.field(p,ZT_ECC384_SIGNATURE_SIZE),ZT_ECC384_SIGNATURE_SIZE); + p += ZT_ECC384_SIGNATURE_SIZE; pkl = (unsigned int)b[p++]; if (pkl) { if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)) @@ -412,21 +406,21 @@ public: */ ZT_ALWAYS_INLINE operator bool() const { return (_address); } - inline bool operator==(const Identity &id) const + ZT_ALWAYS_INLINE bool operator==(const Identity &id) const { if ((_address == id._address)&&(_type == id._type)) { switch(_type) { case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0); case P384: - return (memcmp(&_pub,&id._pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) == 0); + return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0); default: return false; } } return false; } - inline bool operator<(const Identity &id) const + ZT_ALWAYS_INLINE bool operator<(const Identity &id) const { if (_address < id._address) return true; @@ -438,7 +432,7 @@ public: case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0); case P384: - return (memcmp(&_pub,&id._pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) < 0); + return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0); } } } @@ -462,6 +456,7 @@ private: ZT_PACKED_STRUCT(struct { // don't re-order these uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; + uint8_t p384s[ZT_ECC384_SIGNATURE_SIZE]; // signature of type 0 key with p384 }) _pub; }; diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 71f3bd32b..84d4925b7 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -33,35 +33,6 @@ Multicaster::Multicaster(const RuntimeEnvironment *renv) : Multicaster::~Multicaster() {} -void Multicaster::add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member) -{ - Mutex::Lock l(_groups_l); - _groups[Multicaster::Key(nwid,mg)].set(member,now); -} - -void Multicaster::addMultiple(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,const unsigned int totalKnown) -{ - Mutex::Lock l(_groups_l); - const uint8_t *a = (const uint8_t *)addresses; - Hashtable< Address,int64_t > &members = _groups[Multicaster::Key(nwid,mg)]; - while (count--) { - members.set(Address(a,ZT_ADDRESS_LENGTH),now); - a += ZT_ADDRESS_LENGTH; - } -} - -void Multicaster::remove(const uint64_t nwid,const MulticastGroup &mg,const Address &member) -{ - Mutex::Lock l(_groups_l); - const Multicaster::Key gk(nwid,mg); - Hashtable< Address,int64_t > *const members = _groups.get(gk); - if (members) { - members->erase(member); - if (members->empty()) - _groups.erase(gk); - } -} - void Multicaster::send( void *tPtr, int64_t now, diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index c4a023c5c..7942f3654 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -38,7 +38,7 @@ class Packet; class Network; /** - * Multicast database and outbound multicast handler + * Multicast database and outbound multicast logic */ class Multicaster { @@ -54,7 +54,11 @@ public: * @param mg Multicast group * @param member New member address */ - void add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member); + inline void add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member) + { + Mutex::Lock l(_groups_l); + _groups[Multicaster::Key(nwid,mg)].set(member,now); + } /** * Add multiple addresses from a binary array of 5-byte address fields @@ -69,7 +73,16 @@ public: * @param count Number of addresses * @param totalKnown Total number of known addresses as reported by peer */ - void addMultiple(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,const unsigned int totalKnown); + inline void addMultiple(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,const unsigned int totalKnown) + { + Mutex::Lock l(_groups_l); + const uint8_t *a = (const uint8_t *)addresses; + Hashtable< Address,int64_t > &members = _groups[Multicaster::Key(nwid,mg)]; + while (count--) { + members.set(Address(a,ZT_ADDRESS_LENGTH),now); + a += ZT_ADDRESS_LENGTH; + } + } /** * Remove a multicast group member (if present) @@ -78,7 +91,17 @@ public: * @param mg Multicast group * @param member Member to unsubscribe */ - void remove(const uint64_t nwid,const MulticastGroup &mg,const Address &member); + inline void remove(const uint64_t nwid,const MulticastGroup &mg,const Address &member) + { + Mutex::Lock l(_groups_l); + const Multicaster::Key gk(nwid,mg); + Hashtable< Address,int64_t > *const members = _groups.get(gk); + if (members) { + members->erase(member); + if (members->empty()) + _groups.erase(gk); + } + } /** * Iterate over members of a multicast group until function returns false @@ -144,7 +167,7 @@ public: unsigned int len); /** - * Clean up and resort database + * Clean up database * * @param RR Runtime environment * @param now Current time diff --git a/node/Packet.hpp b/node/Packet.hpp index 260f36e17..e9ad2087e 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -710,17 +710,48 @@ public: * <[1] flags> * [<[...] network certificate of membership (DEPRECATED)>] * [<[4] 32-bit implicit gather limit (DEPRECATED)>] + * [<[5] ZeroTier address of originating sender (including w/0x08)>] + * [<[2] 16-bit bloom filter multiplier>] + * [<[2] 16-bit length of propagation bloom filter in bytes] + * [<[...] propagation bloom filter>] * [<[6] source MAC>] * <[6] destination MAC (multicast address)> * <[4] 32-bit multicast ADI (multicast address extension)> * <[2] 16-bit ethertype> * <[...] ethernet payload> + * [<[2] 16-bit length of signature>] + * [<[...] signature (algorithm depends on sender identity)>] * * Flags: * 0x01 - Network certificate of membership attached (DEPRECATED) * 0x02 - Implicit gather limit field is present (DEPRECATED) * 0x04 - Source MAC is specified -- otherwise it's computed from sender - * 0x08 - Explicit recipient list included for P2P/HS replication + * 0x08 - Propagation bloom filter is included + * 0x10 - Signature by sending identity is included + * + * Version 1.x only supports sender-side replication. Version 2.x also + * supports peer to peer and hub and spoke models. For that there is + * a new field: a bloom filter that tracks recipients by ZeroTier address. + * + * Bits in the bloom filter are set by multiplying the address by the + * indicated multiplier and then taking that modulo the number of bits + * in the filter. Both the length of the filter and this multiplier are + * variable and can be selected based on the sender's knowledge of + * the total recipient set to minimize the chance of collision, as a + * collision would result in a multicast not reaching one particular + * recipient. The algorithm for selecting these is not defined by the + * protocol. + * + * The ZeroTier address of the originating sender is also included + * before the bloom filter if flag bit 0x08 is set. + * + * Version 2.x also supports an optional signature of the packet's + * payload by the sending ZeroTier node. This can be used to validate + * multicasts propagated cooperatively, since unlike sender side + * replication the message MAC alone cannot be used for this. This + * imposes a non-trivial CPU cost on the sender and so it's optional. + * + * OK is not sent. * * ERROR_MULTICAST_STFU is generated if a recipient no longer wishes to * receive these multicasts. It's essentially a source quench. Its @@ -764,8 +795,6 @@ public: */ VERB_PUSH_DIRECT_PATHS = 0x10, - // 0x11 -- deprecated - /** * An acknowledgment of receipt of a series of recent packets from another * peer. This is used to calculate relative throughput values and to detect