Adam Ierymenko 5 years ago
parent
commit
b55f98b813
2 changed files with 172 additions and 110 deletions
  1. 37 51
      node/Identity.hpp
  2. 135 59
      node/Packet.hpp

+ 37 - 51
node/Identity.hpp

@@ -52,30 +52,35 @@ public:
 	};
 
 	ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
-	ZT_ALWAYS_INLINE Identity(const char *str)
-	{
-		if (!fromString(str))
-			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
-	}
+	ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(&this->_priv),sizeof(this->_priv)); }
+
+	/**
+	 * Construct identity from string
+	 *
+	 * If the identity is not basically valid (no deep checking is done) the result will
+	 * be a null identity.
+	 *
+	 * @param str Identity in canonical string format
+	 */
+	ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); }
+
 	template<unsigned int C>
 	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() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
 
 	/**
-	 * @return Identity type
+	 * @return Identity type (undefined if identity is null or invalid)
 	 */
 	ZT_ALWAYS_INLINE Type type() const { return _type; }
 
 	/**
 	 * Generate a new identity (address, key pair)
 	 *
-	 * This is a time consuming operation.
+	 * This is a time consuming operation taking up to 5-10 seconds on some slower systems.
 	 *
 	 * @param t Type of identity to generate
 	 */
@@ -94,10 +99,12 @@ public:
 	ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; }
 
 	/**
+	 * This generates a SHA384 hash of this identity's keys.
+	 *
 	 * @param h Buffer to receive SHA384 of public key(s)
-	 * @param includePrivate If true, hash private key(s) too
+	 * @param includePrivate If true, hash private key(s) as well
 	 */
-	ZT_ALWAYS_INLINE bool hash(uint8_t h[48],const bool includePrivate) const
+	ZT_ALWAYS_INLINE bool hash(uint8_t h[48],const bool includePrivate = false) const
 	{
 		switch(_type) {
 
@@ -142,8 +149,10 @@ public:
 
 				case P384:
 					if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
+						// When signing with P384 we also hash the C25519 public key as an
+						// extra measure to ensure that only this identity can verify.
 						uint8_t h[48];
-						SHA384(h,data,len);
+						SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
 						ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
 						return ZT_ECC384_SIGNATURE_SIZE;
 					}
@@ -165,15 +174,18 @@ public:
 	ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
 	{
 		switch(_type) {
+
 			case C25519:
 				return C25519::verify(_pub.c25519,data,len,sig,siglen);
+
 			case P384:
 				if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
 					uint8_t h[48];
-					SHA384(h,data,len);
+					SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
 					return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
 				}
 				break;
+
 		}
 		return false;
 	}
@@ -193,6 +205,7 @@ public:
 		uint8_t h[64];
 		if (_hasPrivate) {
 			if (_type == C25519) {
+
 				if ((id._type == C25519)||(id._type == P384)) {
 					// If we are a C25519 key we can agree with another C25519 key or with only the
 					// C25519 portion of a type 1 P-384 key.
@@ -201,12 +214,10 @@ public:
 					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
 					return true;
 				}
+
 			} else if (_type == P384) {
+
 				if (id._type == P384) {
-					// Perform key agreement over both curves for the same reason that C25519 public
-					// keys are included in P-384 signature inputs: to bind the keys together so
-					// that a type 1 identity with the same C25519 public key (and therefore address)
-					// but a different P-384 key will not work.
 					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
 					ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
 					SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
@@ -219,6 +230,7 @@ public:
 					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
 					return true;
 				}
+
 			}
 		}
 		return false;
@@ -229,28 +241,6 @@ public:
 	 */
 	ZT_ALWAYS_INLINE const Address &address() const { return _address; }
 
-	/**
-	 * Attempt to generate an older type identity from a newer type
-	 *
-	 * @param dest Destination to fill with downgraded identity
-	 * @param toType Desired identity type
-	 */
-	ZT_ALWAYS_INLINE bool downgrade(Identity &dest,const Type toType)
-	{
-		if (_type == toType) {
-			return true;
-		} else if ((_type == P384)&&(toType == C25519)) {
-			dest._address = _address;
-			dest._type = C25519;
-			dest._hasPrivate = _hasPrivate;
-			memcpy(dest._pub.c25519,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-			if (_hasPrivate)
-				memcpy(dest._priv.c25519,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
-			return true;
-		}
-		return false;
-	}
-
 	/**
 	 * Serialize this identity (binary)
 	 *
@@ -284,7 +274,7 @@ public:
 				} else {
 					b.append((uint8_t)0);
 				}
-				b.append((uint16_t)0); // size of additional fields
+				b.append((uint16_t)0); // size of additional fields (should have included such a thing in v0!)
 				break;
 
 		}
@@ -358,7 +348,7 @@ public:
 	 *
 	 * @param includePrivate If true, include private key (if it exists)
 	 * @param buf Buffer to store string
-	 * @return ASCII string representation of identity
+	 * @return ASCII string representation of identity (pointer to buf)
 	 */
 	char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
 
@@ -382,12 +372,9 @@ public:
 	{
 		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,sizeof(_pub)) == 0);
-				default:
-					return false;
+				case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
+				// case P384:
+				default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
 			}
 		}
 		return false;
@@ -401,10 +388,9 @@ public:
 				return true;
 			if (_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,sizeof(_pub)) < 0);
+					case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
+					// case P384:
+					default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
 				}
 			}
 		}

+ 135 - 59
node/Packet.hpp

@@ -424,10 +424,12 @@ public:
 		 *   <[8] timestamp for determining latency>
 		 *   <[...] binary serialized identity (see Identity)>
 		 *   <[...] physical destination address of packet>
-		 *   [... begin encrypted section ...]
+		 *   [... begin encrypted region ...]
 		 *   <[2] 16-bit reserved field, always 0>
 		 *   <[2] 16-bit length of locator>
 		 *   <[...] locator for this node>
+		 *   <[2] 16-bit length of meta-data dictionary>
+		 *   <[...] meta-data dictionary>
 		 *
 		 * HELLO is sent in the clear as it is how peers share their identity
 		 * public keys.
@@ -441,6 +443,9 @@ public:
 		 * very sensitive, but hiding the locator and other meta-data slightly
 		 * improves privacy.
 		 *
+		 * The 16-bit zero after encryption starts is for backward compatibility
+		 * with pre-2.0 nodes.
+		 *
 		 * OK payload:
 		 *   <[8] HELLO timestamp field echo>
 		 *   <[1] protocol version>
@@ -578,6 +583,20 @@ public:
 		 */
 		VERB_ECHO = 0x08,
 
+		/**
+		 * Announce interest in multicast group(s):
+		 *   <[8] 64-bit network ID>
+		 *   <[6] multicast Ethernet address>
+		 *   <[4] multicast additional distinguishing information (ADI)>
+		 *   [... additional tuples of network/address/adi ...]
+		 *
+		 * LIKEs may be sent to any peer, though a good implementation should
+		 * restrict them to peers on the same network they're for and to network
+		 * controllers and root servers. In the current network, root servers
+		 * will provide the service of final multicast cache.
+		 */
+		VERB_MULTICAST_LIKE = 0x09,
+
 		/**
 		 * Network credentials push:
 		 *   [<[...] one or more certificates of membership>]
@@ -682,6 +701,73 @@ public:
 		 */
 		VERB_NETWORK_CONFIG = 0x0c,
 
+		/**
+		 * Request endpoints for multicast distribution:
+		 *   <[8] 64-bit network ID>
+		 *   <[1] flags>
+		 *   <[6] MAC address of multicast group being queried>
+		 *   <[4] 32-bit ADI for multicast group being queried>
+		 *   <[4] 32-bit requested max number of multicast peers>
+		 *
+		 * This message asks a peer for additional known endpoints that have
+		 * LIKEd a given multicast group. It's sent when the sender wishes
+		 * to send multicast but does not have the desired number of recipient
+		 * peers.
+		 *
+		 * OK response payload: (multiple OKs can be generated)
+		 *   <[8] 64-bit network ID>
+		 *   <[6] MAC address of multicast group being queried>
+		 *   <[4] 32-bit ADI for multicast group being queried>
+		 *   <[4] 32-bit total number of known members in this multicast group>
+		 *   <[2] 16-bit number of members enumerated in this packet>
+		 *   <[...] series of 5-byte ZeroTier addresses of enumerated members>
+		 *
+		 * ERROR is not generated; queries that return no response are dropped.
+		 */
+		VERB_MULTICAST_GATHER = 0x0d,
+
+		/** *** DEPRECATED ***
+		 * Multicast frame:
+		 *   <[8] 64-bit network ID>
+		 *   <[1] flags>
+		 *  [<[4] 32-bit implicit gather limit>]
+		 *  [<[6] source MAC>]
+		 *   <[6] destination MAC (multicast address)>
+		 *   <[4] 32-bit multicast ADI (multicast address extension)>
+		 *   <[2] 16-bit ethertype>
+		 *   <[...] ethernet payload>
+		 *
+		 * Flags:
+		 *   0x01 - Network certificate of membership attached (DEPRECATED)
+		 *   0x02 - Implicit gather limit field is present
+		 *   0x04 - Source MAC is specified -- otherwise it's computed from sender
+		 *   0x08 - Please replicate (sent to multicast replicators)
+		 *
+		 * OK and ERROR responses are optional. OK may be generated if there are
+		 * implicit gather results or if the recipient wants to send its own
+		 * updated certificate of network membership to the sender. ERROR may be
+		 * generated if a certificate is needed or if multicasts to this group
+		 * are no longer wanted (multicast unsubscribe).
+		 *
+		 * OK response payload:
+		 *   <[8] 64-bit network ID>
+		 *   <[6] MAC address of multicast group>
+		 *   <[4] 32-bit ADI for multicast group>
+		 *   <[1] flags>
+		 *  [<[...] network certificate of membership (DEPRECATED)>]
+		 *  [<[...] implicit gather results if flag 0x01 is set>]
+		 *
+		 * OK flags (same bits as request flags):
+		 *   0x01 - OK includes certificate of network membership (DEPRECATED)
+		 *   0x02 - OK includes implicit gather results
+		 *
+		 * ERROR response payload:
+		 *   <[8] 64-bit network ID>
+		 *   <[6] multicast group MAC>
+		 *   <[4] 32-bit multicast group ADI>
+		 */
+		VERB_MULTICAST_FRAME = 0x0e,
+
 		/**
 		 * Push of potential endpoints for direct communication:
 		 *   <[2] 16-bit number of paths>
@@ -784,77 +870,67 @@ public:
 		VERB_REMOTE_TRACE = 0x15,
 
 		/**
-		 * Multipurpose VL2 network multicast:
-		 *   <[5] start of range of addresses for propagation>
-		 *   <[5] end of range of addresses for propagation>
-		 *   <[1] 8-bit propagation depth / hops or 0xff to not propagate>
-		 *   <[1] 8-bit length of bloom filter in 256-byte/2048-bit chunks>
-		 *   <[...] propagation bloom filter>
-		 *   [... start of signed portion ...]
-		 *   <[8] 64-bit timestamp>
+		 * Peer-to-peer propagated multicast packet:
+		 *   <[128] 1024-bit bloom filter>
+		 *   <[2] 16-bit perturbation coefficient to minimize bloom collisions>
+		 *   <[5] 40-bit start of range of recipient addresses>
+		 *   <[5] 40-bit end of range of recipient addresses>
+		 *   [... begin signed portion ...]
+		 *   <[1] 8-bit flags>
+		 *   <[5] 40-bit ZeroTier address of sender>
 		 *   <[8] 64-bit network ID>
-		 *   <[5] 40-bit address of sender>
-		 *   <[2] 16-bit length of multicast payload>
-		 *   [... start multicast payload ...]
-		 *   <[1] 8-bit payload type>
-		 *   [... end multicast payload and signed portion ...]
-		 *   <[2] 16-bit length of signature or 0 if not present>
-		 *   <[...] signature of signed portion>
-		 *
-		 * Payload type 0x00: multicast frame:
 		 *   <[6] MAC address of multicast group>
-		 *   <[4] 32-bit ADI of multicast group>
-		 *   <[6] 48-bit source MAC of packet or all 0 if from sender>
+		 *   <[4] 32-bit ADI for multicast group>
+		 *   <[6] MAC address of sender>
 		 *   <[2] 16-bit ethertype>
+		 *   <[2] 16-bit length of ethernet payload>
 		 *   <[...] ethernet payload>
+		 *   [... end signed portion ...]
+		 *   <[2] 16-bit length of signature or 0 if unsigned>
+		 *  [<[...] optional signature of multicast>]
 		 *
-		 * Payload type 0x01: multicast subscribe:
-		 *   <[2] 16-bit number of multicast group IDs to subscribe>
-		 *   <[...] series of 32-bit multicast group IDs>
-		 *
-		 * Payload type 0x02: multicast unsubscribe:
-		 *   <[2] 16-bit number of multicast group IDs to unsubscribe>
-		 *   <[...] series of 32-bit multicast group IDs>
-		 *
-		 * This is the common packet structure for VL2 network-level multicasts
-		 * and is used for multicast frames, multicast group subscribe and
-		 * unsubscribe, and could be used in the future for other purposes such
-		 * as credential propagation or diagnostics.
-		 *
-		 * The header contains an address range, bloom filter, and depth/hop
-		 * counter. The bloom filter tracks which nodes have seen this multicast,
-		 * with bits being set prior to send. The range allows the total set of
-		 * subscribers to be partitioned in the case of huge networks that would
-		 * saturate the bloom filter or have collisions. The propagation depth
-		 * allows propagation to stop at some maximum value, and the value 0xff
-		 * can be used to indicate that further propagation is not desired.
-		 *
-		 * Logic connected to the parsing of the multicast payload will determine
-		 * whether or not and to whom this multicast is propagated. Subscribe and
-		 * unsubscribe messages are propagated to online nodes up to a maximum
-		 * depth, while frames have the added constraint of being propagated only
-		 * to nodes that subscribe to the target multicast group.
+		 * This packet contains a multicast that is to be peer-to-peer replicated.
+		 * The range of recipient addresses is a subset of the global list of
+		 * subscribers to this multicast group. As the packet is propagated bits
+		 * in the bloom filter will be set. The sender may attempt to select a
+		 * perturbation coefficient to prevent collisions within the selected
+		 * recipient range.
 		 */
-		VERB_VL2_MULTICAST = 0x16,
+		VERB_MULTICAST = 0x16,
 
 		/**
 		 * Negotiate a new ephemeral key:
-		 *   <[8] first 64 bits of SHA-384 of currently known key for destination>
-		 *   <[...] ephemeral key for sender>
+		 *   <[48] SHA384 of ephemeral key we currently have for recipient>
+		 *  [<[...] sender's ephemeral key>]
 		 *
-		 * If the 64-bit hash of the currently known key sent by the sender does
-		 * not match the key the destination is currently using, the destination
-		 * will send its own REKEY after sending OK to ensure that keys are up to
-		 * date on both sides. This causes either side sending REKEY to trigger
-		 * an automatic two-way handshake. Either side may therefore rekey at
-		 * any time, though a rate limit should be in effect to prevent flooding.
+		 * REKEY is used to negotiate ephemeral keys. The first byte is a step
+		 * number from 0 to 2. Here's a new session initiated by Alice:
 		 *
-		 * OK payload:
-		 *   <[8] first 64 bits of SHA-384 of received ephemeral key>
+		 * Alice: REKEY[0x000...,AliceKey]        -> Bob
+		 * Bob:   REKEY[SHA384(AliceKey),BobKey]  -> Alice
+		 * Alice: REKEY[SHA384(BobKey),(omitted)] -> Bob
+		 *
+		 * REKEY messages will continue until both sides have acknowledged each
+		 * others' keys. Either Alice or Bob can send REKEY to negotiate a new
+		 * ephemeral key pair at any time.
+		 *
+		 * OK isn't used because this is an ongoing handshake until both sides
+		 * agree on a key. REKEY triggers a REKEY in reply if the hash for the
+		 * recipient's ephemeral public key doesn't match the ephemeral key it
+		 * wants to use.
 		 */
-		VERB_REKEY = 0x17
+		VERB_REKEY = 0x17,
 
-		// TODO: legacy multicast message types must be supported
+		/**
+		 * Encapsulate a full ZeroTier packet in another:
+		 *   <[...] raw encapsulated packet>
+		 *
+		 * Encapsulation exists to enable secure relaying as opposed to the usual
+		 * "dumb" relaying. The latter is faster but secure relaying has roles
+		 * where endpoint privacy is desired. Multiply nested ENCAP packets
+		 * could allow ZeroTier to act as an onion router.
+		 */
+		VERB_ENCAP = 0x18
 
 		// protocol max: 0x1f
 	};