From 274b2682d633b7cac0dd01d84b6a2bbba9593ddd Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 5 Sep 2019 15:09:20 -0700 Subject: [PATCH] cleanup and docs --- node/AES.hpp | 26 ++++++++------ node/Utils.cpp | 93 +++++++++++++++++++++++--------------------------- 2 files changed, 59 insertions(+), 60 deletions(-) diff --git a/node/AES.hpp b/node/AES.hpp index ca41cd313..2178a1c66 100644 --- a/node/AES.hpp +++ b/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]; diff --git a/node/Utils.cpp b/node/Utils.cpp index 4c27a0ade..03fde4493 100644 --- a/node/Utils.cpp +++ b/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= 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++]; } }