Browse Source

Crypto work, packet work

Adam Ierymenko 6 years ago
parent
commit
3a21fdc304
5 changed files with 110 additions and 66 deletions
  1. 71 0
      node/AES.hpp
  2. 8 8
      node/ECC384.hpp
  3. 27 22
      node/Identity.hpp
  4. 1 33
      node/IncomingPacket.cpp
  5. 3 3
      node/Packet.hpp

+ 71 - 0
node/AES.hpp

@@ -155,6 +155,77 @@ public:
 #endif
 	}
 
+	/**
+	 * Encrypt with AES256-GCM-DDS
+	 *
+	 * DDS stands for Data Dependent Scramble and refers to our scheme for nonce
+	 * duplication resistance.
+	 *
+	 * @param iv IV (usually random)
+	 * @param in Input plaintext
+	 * @param inlen Length of plaintext
+	 * @param assoc Associated data that won't be encrypted
+	 * @param assoclen Length of associated data
+	 * @param out Output ciphertext buffer (must be at least inlen in size)
+	 * @param combinedTag Buffer to receive 128-bit encrypted combined IV and MAC
+	 */
+	inline void gcmDdsEncrypt(const uint64_t iv,const void *in,unsigned int inlen,const void *assoc,unsigned int assoclen,void *out,uint64_t combinedTag[2])
+	{
+		// Make 12-byte GCM IV (use combinedTag as tmp buffer)
+		combinedTag[0] = iv;
+		((uint8_t *)combinedTag)[8] = (uint8_t)(inlen >> 16);
+		((uint8_t *)combinedTag)[9] = (uint8_t)(inlen >> 8);
+		((uint8_t *)combinedTag)[10] = (uint8_t)inlen;
+		((uint8_t *)combinedTag)[11] = (uint8_t)assoclen;
+
+		// Encrypt data and store 64-bit tag/MAC code in second 64 bits of combinedTag.
+		gcmEncrypt((const uint8_t *)combinedTag,in,inlen,assoc,assoclen,out,((uint8_t *)&(combinedTag[1])),8);
+
+		// Encrypt combinedTag once to get scramble key
+		encrypt((const uint8_t *)combinedTag,(uint8_t *)combinedTag);
+
+		// Scramble ciphertext
+		scramble((const uint8_t *)combinedTag,out,inlen,out);
+
+		// Encrypt combinedTag again to get masked tag to include with message
+		encrypt((const uint8_t *)combinedTag,(uint8_t *)combinedTag);
+	}
+
+	/**
+	 * Decrypt with AES256-GCM-DDS
+	 *
+	 * @param combinedTag Encrypted combined tag
+	 * @param in Input ciphertext
+	 * @param inlen Length of ciphertext
+	 * @param assoc Associated data that wasn't encrypted
+	 * @param assoclen Length of associated data
+	 * @param out Output plaintext buffer (must be at least inlen in size)
+	 * @return True if GCM authentication check succeeded (if false, discard packet)
+	 */
+	inline bool gcmDdsDecrypt(const uint64_t combinedTag[2],const void *in,unsigned int inlen,const void *assoc,unsigned int assoclen,void *out)
+	{
+		uint64_t tmp[2],gcmIv[2];
+
+		// Decrypt combinedTag to get scramble key
+		decrypt((const uint8_t *)combinedTag,(uint8_t *)tmp);
+
+		// Unscramble ciphertext
+		unscramble((const uint8_t *)tmp,in,inlen,out);
+
+		// Decrypt combinedTag again to get original IV and AES-GCM MAC
+		decrypt((const uint8_t *)tmp,(uint8_t *)tmp);
+
+		// Make 12-byte GCM IV
+		gcmIv[0] = tmp[0];
+		((uint8_t *)gcmIv)[8] = (uint8_t)(inlen >> 16);
+		((uint8_t *)gcmIv)[9] = (uint8_t)(inlen >> 8);
+		((uint8_t *)gcmIv)[10] = (uint8_t)inlen;
+		((uint8_t *)gcmIv)[11] = (uint8_t)assoclen;
+
+		// Perform GCM decryption and authentication
+		return gcmDecrypt((const uint8_t *)gcmIv,out,inlen,assoc,assoclen,out,(const uint8_t *)&(tmp[1]),8);
+	}
+
 private:
 	static const uint32_t Te0[256];
 	static const uint32_t Te1[256];

+ 8 - 8
node/ECC384.hpp

@@ -63,7 +63,7 @@ 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
  */
@@ -71,10 +71,10 @@ void ECC384GenerateKey(uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],uint8_t priv[ZT_EC
 
 /**
  * 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.
- * 
+ *
+ * The hash must be 48 bytes in size. If it's longer only the first 48
+ * bytes are used.
+ *
  * @param priv Private key
  * @param hash 48-byte hash
  * @param sig Buffer to receive signature
@@ -83,7 +83,7 @@ void ECC384ECDSASign(const uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE],const uint8_
 
 /**
  * Verify a signature
- * 
+ *
  * @param pub Public key
  * @param hash 48-byte hash (usually first 48 bytes of SHA512(msg))
  * @param sig Signature to check
@@ -93,10 +93,10 @@ bool ECC384ECDSAVerify(const uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_
 
 /**
  * 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

+ 27 - 22
node/Identity.hpp

@@ -218,31 +218,36 @@ public:
 		uint8_t rawkey[128];
 		uint8_t h[64];
 		if (_hasPrivate) {
-			switch(_type) {
-
-				case C25519:
+			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.
 					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
 					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
-					memcpy(key,h,32);
+					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
 					return true;
-
-				case 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);
-						for(unsigned int i=0;i<32;++i)
-							key[i] = h[i];
-						for(unsigned int i=0;i<16;++i)
-							key[i] ^= h[32+i];
-						return true;
-					}
-					return false;
-
+				}
+			} 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);
+					for(unsigned int i=0;i<32;++i)
+						key[i] = h[i];
+					for(unsigned int i=0;i<16;++i)
+						key[i] ^= h[32+i];
+					return true;
+				} else if (id._type == C25519) {
+					// If the other identity is a C25519 identity we can agree using only that type.
+					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
+					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+					return true;
+				}
 			}
 		}
 		return false;

+ 1 - 33
node/IncomingPacket.cpp

@@ -41,7 +41,6 @@
 #include "NetworkController.hpp"
 #include "SelfAwareness.hpp"
 #include "Salsa20.hpp"
-#include "SHA512.hpp"
 #include "Node.hpp"
 #include "CertificateOfMembership.hpp"
 #include "Capability.hpp"
@@ -377,38 +376,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
 	outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
-
-	if (protoVersion >= 5) {
-		_path->address().serialize(outp);
-	} else {
-		/* LEGACY COMPATIBILITY HACK:
-		 *
-		 * For a while now (since 1.0.3), ZeroTier has recognized changes in
-		 * its network environment empirically by examining its external network
-		 * address as reported by trusted peers. In versions prior to 1.1.0
-		 * (protocol version < 5), they did this by saving a snapshot of this
-		 * information (in SelfAwareness.hpp) keyed by reporting device ID and
-		 * address type.
-		 *
-		 * This causes problems when clustering is combined with symmetric NAT.
-		 * Symmetric NAT remaps ports, so different endpoints in a cluster will
-		 * report back different exterior addresses. Since the old code keys
-		 * this by device ID and not sending physical address and compares the
-		 * entire address including port, it constantly thinks its external
-		 * surface is changing and resets connections when talking to a cluster.
-		 *
-		 * In new code we key by sending physical address and device and we also
-		 * take the more conservative position of only interpreting changes in
-		 * IP address (neglecting port) as a change in network topology that
-		 * necessitates a reset. But we can make older clients work here by
-		 * nulling out the port field. Since this info is only used for empirical
-		 * detection of link changes, it doesn't break anything else.
-		 */
-		InetAddress tmpa(_path->address());
-		tmpa.setPort(0);
-		tmpa.serialize(outp);
-	}
-
+	_path->address().serialize(outp);
 	outp.armor(peer->key(),true);
 	_path->send(RR,tPtr,outp.data(),outp.size(),now);
 

+ 3 - 3
node/Packet.hpp

@@ -455,7 +455,7 @@ public:
 		 *   <[8] timestamp for determining latency>
 		 *   <[...] binary serialized identity (see Identity)>
 		 *   <[...] physical destination address of packet>
-		 * 
+		 *
 		 * HELLO is sent in the clear as it is how peers share their identity
 		 * public keys.
 		 *
@@ -724,7 +724,7 @@ public:
 		 *
 		 * Flags:
 		 *   0x01 - COM is attached (DEPRECATED)
-		 * 
+		 *
 		 * More than one OK response can occur if the response is broken up across
 		 * multiple packets or if querying a clustered node.
 		 *
@@ -759,7 +759,7 @@ public:
 		 *   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
-		 * 
+		 *
 		 * Explicit recipient lists are used for peer to peer or hub and spoke
 		 * replication.
 		 *