Browse Source

More multicast work, add a signature in identity for safety margin, cleanup.

Adam Ierymenko 6 years ago
parent
commit
787277d282
6 changed files with 103 additions and 74 deletions
  1. 2 2
      node/Constants.hpp
  2. 17 6
      node/Identity.cpp
  3. 24 29
      node/Identity.hpp
  4. 0 29
      node/Multicaster.cpp
  5. 28 5
      node/Multicaster.hpp
  6. 32 3
      node/Packet.hpp

+ 2 - 2
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

+ 17 - 6
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 {

+ 24 - 29
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<void *>(this),0,sizeof(Identity)); }
-	ZT_ALWAYS_INLINE Identity(const Identity &id) { memcpy(reinterpret_cast<void *>(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<unsigned int C>
-	inline Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
+	ZT_ALWAYS_INLINE Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
 
 	ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
 
 	/**
 	 * Set identity to NIL value (all zero)
 	 */
-	ZT_ALWAYS_INLINE void zero() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
-
-	ZT_ALWAYS_INLINE Identity &operator=(const Identity &id)
-	{
-		memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity));
-		return *this;
-	}
+	ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast<void *>(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<unsigned int C>
-	inline void serialize(Buffer<C> &b,bool includePrivate = false) const
+	ZT_ALWAYS_INLINE void serialize(Buffer<C> &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<unsigned int C>
-	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+	ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer<C> &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;
 };
 

+ 0 - 29
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,

+ 28 - 5
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

+ 32 - 3
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