Browse Source

cleanup and docs

Adam Ierymenko 6 years ago
parent
commit
274b2682d6
2 changed files with 59 additions and 60 deletions
  1. 16 10
      node/AES.hpp
  2. 43 50
      node/Utils.cpp

+ 16 - 10
node/AES.hpp

@@ -153,11 +153,19 @@ public:
 	/**
 	 * Perform AES-GMAC-SIV encryption
 	 *
-	 * This is an AES mode built from GMAC and AES-CTR that is similar to the
-	 * various SIV (synthetic IV) modes for AES and is resistant to nonce
-	 * re-use. It's specifically tweaked for ZeroTier's packet structure with
-	 * a 64-bit IV (extended to 96 bits by including packet size and other info)
-	 * and a 64-bit auth tag.
+	 * This is basically AES-CMAC-SIV but with GMAC in place of CMAC after
+	 * GMAC is run through AES as a keyed hash to make it behave like a
+	 * proper PRF.
+	 *
+	 * See: https://github.com/miscreant/meta/wiki/AES-SIV
+	 *
+	 * The advantage is that this can be described in terms of FIPS and NSA
+	 * ceritifable primitives that are present in FIPS-compliant crypto
+	 * modules.
+	 *
+	 * The extra AES-ECB (keyed hash) encryption of the AES-CTR IV prior
+	 * to use makes the IV itself a secret. This is not strictly necessary
+	 * but comes at little cost.
 	 *
 	 * @param k1 GMAC key
 	 * @param k2 GMAC auth tag keyed hash key
@@ -180,7 +188,7 @@ public:
 		uint8_t ctrIv[16];
 #endif
 
-		// Extend packet IV to 96-bit message IV using direction byte and message length
+		// GMAC IV is 64-bit packet IV followed by other packet attributes to extend to 96 bits
 #ifndef __GNUC__
 		for(unsigned int i=0;i<8;++i) miv[i] = iv[i];
 #else
@@ -191,18 +199,16 @@ public:
 		miv[10] = (uint8_t)(len >> 8);
 		miv[11] = (uint8_t)len;
 
-		// Compute AES[k2](GMAC[k1](miv,plaintext))
+		// Compute auth TAG: AES-ECB[k2](GMAC[k1](miv,plaintext))[0:8]
 		k1.gmac(miv,in,len,ctrIv);
 		k2.encrypt(ctrIv,ctrIv); // ECB mode encrypt step is because GMAC is not a PRF
-
-		// Auth tag for packet is first 64 bits of AES(GMAC) (rest is discarded)
 #ifdef ZT_NO_TYPE_PUNNING
 		for(unsigned int i=0;i<8;++i) tag[i] = ctrIv[i];
 #else
 		*((uint64_t *)tag) = *((uint64_t *)ctrIv);
 #endif
 
-		// Create synthetic CTR IV from keyed hash of tag and message IV
+		// Create synthetic CTR IV: AES-ECB[k3](TAG | MIV[0:4] | (MIV[4:8] XOR MIV[8:12]))
 #ifndef __GNUC__
 		for(unsigned int i=0;i<4;++i) ctrIv[i+8] = miv[i];
 		for(unsigned int i=4;i<8;++i) ctrIv[i+8] = miv[i] ^ miv[i+4];

+ 43 - 50
node/Utils.cpp

@@ -146,69 +146,62 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 	static uint64_t randomState[4];
 	static uint8_t randomBuf[16384];
 	static unsigned long randomPtr = sizeof(randomBuf);
-#ifdef __WINDOWS__
-	static HCRYPTPROV cryptProvider = NULL;
-#endif
 
-	Mutex::Lock _l(globalLock);
-
-	/* Just for posterity we Salsa20 encrypt the result of whatever system
-	 * CSPRNG we use. There have been several bugs at the OS or OS distribution
-	 * level in the past that resulted in systematically weak or predictable
-	 * keys due to random seeding problems. This mitigates that by grabbing
-	 * a bit of extra entropy and further randomizing the result,and comes
-	 * at almost no cost and with no real downside if the random source is
-	 * good. */
-	if (unlikely(!initialized)) {
-#ifdef __WINDOWS__
-		if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
-			exit(1);
-		}
-		if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomState),(BYTE *)randomState)) {
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
-			exit(1);
-		}
-		if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
-			exit(1);
-		}
-#else
-		int devURandomFd = ::open("/dev/urandom",O_RDONLY);
-		if (devURandomFd < 0) {
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
-			exit(1);
-		}
-		if ((int)::read(devURandomFd,randomState,sizeof(randomState)) != (int)sizeof(randomState)) {
-			::close(devURandomFd);
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n");
-			exit(1);
-		}
-		if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
-			::close(devURandomFd);
-			fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n");
-			exit(1);
-		}
-		close(devURandomFd);
-#endif
-		initialized = true;
-	}
+	Mutex::Lock gl(globalLock);
 
 	for(unsigned int i=0;i<bytes;++i) {
 		if (randomPtr >= sizeof(randomBuf)) {
 			randomPtr = 0;
 
-			for(unsigned int k=0;k<4;++k) {
-				if (++randomState[k])
-					break;
+			if (unlikely(!initialized)) {
+				initialized = true;
+#ifdef __WINDOWS__
+				HCRYPTPROV cryptProvider = NULL;
+				if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
+					exit(1);
+				}
+				if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomState),(BYTE *)randomState)) {
+					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
+					exit(1);
+				}
+				if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
+					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
+					exit(1);
+				}
+				CryptReleaseContext(cryptProvider,0);
+#else
+				int devURandomFd = ::open("/dev/urandom",O_RDONLY);
+				if (devURandomFd < 0) {
+					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
+					exit(1);
+				}
+				if ((int)::read(devURandomFd,randomState,sizeof(randomState)) != (int)sizeof(randomState)) {
+					::close(devURandomFd);
+					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n");
+					exit(1);
+				}
+				if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
+					::close(devURandomFd);
+					fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n");
+					exit(1);
+				}
+				close(devURandomFd);
+#endif
+				randomState[0] ^= (uint64_t)time(nullptr);
+				randomState[1] ^= (uint64_t)((uintptr_t)buf); // XOR in some other entropy just in case the system random source is wonky
 			}
 
 			uint8_t h[48];
+			for(unsigned int k=0;k<4;++k) {
+				if (++randomState[k] != 0)
+					break;
+			}
 			HMACSHA384((const uint8_t *)randomState,randomBuf,sizeof(randomBuf),h);
-
 			AES c(h);
 			c.ctr(h + 32,randomBuf,sizeof(randomBuf),randomBuf);
 		}
+
 		((uint8_t *)buf)[i] = randomBuf[randomPtr++];
 	}
 }