Adam Ierymenko 6 years ago
parent
commit
55f4f9aaeb
3 changed files with 88 additions and 78 deletions
  1. 25 15
      node/AES.hpp
  2. 9 14
      node/Utils.cpp
  3. 54 49
      selftest.cpp

+ 25 - 15
node/AES.hpp

@@ -41,14 +41,14 @@ public:
 	 */
 	static const bool HW_ACCEL;
 
-	inline AES() {}
-	inline AES(const uint8_t key[32]) { this->init(key); }
-	inline ~AES() { Utils::burn(&_k,sizeof(_k)); }
+	ZT_ALWAYS_INLINE AES() {}
+	ZT_ALWAYS_INLINE AES(const uint8_t key[32]) { this->init(key); }
+	ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); }
 
 	/**
 	 * Set (or re-set) this AES256 cipher's key
 	 */
-	inline void init(const uint8_t key[32])
+	ZT_ALWAYS_INLINE void init(const uint8_t key[32])
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(HW_ACCEL)) {
@@ -66,7 +66,7 @@ public:
 	 * @param in Input block
 	 * @param out Output block (can be same as input)
 	 */
-	inline void encrypt(const uint8_t in[16],uint8_t out[16]) const
+	ZT_ALWAYS_INLINE void encrypt(const uint8_t in[16],uint8_t out[16]) const
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(HW_ACCEL)) {
@@ -86,7 +86,7 @@ public:
 	 * @param len Length of input
 	 * @param out 128-bit authorization tag from GMAC
 	 */
-	inline void gmac(const uint8_t iv[12],const void *in,const unsigned int len,uint8_t out[16]) const
+	ZT_ALWAYS_INLINE void gmac(const uint8_t iv[12],const void *in,const unsigned int len,uint8_t out[16]) const
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(HW_ACCEL)) {
@@ -110,7 +110,7 @@ public:
 	 * @param len Length of input
 	 * @param out Output plaintext or ciphertext
 	 */
-	inline void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const
+	ZT_ALWAYS_INLINE void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const
 	{
 #ifdef ZT_AES_AESNI
 		if (likely(HW_ACCEL)) {
@@ -173,7 +173,7 @@ public:
 	 * @param out Output buffer to receive ciphertext
 	 * @param tag Output buffer to receive 64-bit authentication tag
 	 */
-	static inline void ztGmacCtrEncrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[12],const void *in,unsigned int len,void *out,uint8_t tag[8])
+	static ZT_ALWAYS_INLINE void ztGmacCtrEncrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[12],const void *in,const unsigned int len,void *out,uint8_t tag[8])
 	{
 		uint8_t ctrIv[16];
 
@@ -216,7 +216,7 @@ public:
 	 * @param tag Authentication tag supplied with message
 	 * @return True if authentication tags match and message appears authentic
 	 */
-	static inline bool ztGmacCtrDecrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[12],const void *in,unsigned int len,void *out,const uint8_t tag[8])
+	static ZT_ALWAYS_INLINE bool ztGmacCtrDecrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[12],const void *in,const unsigned int len,void *out,const uint8_t tag[8])
 	{
 		uint8_t ctrIv[16],gmacOut[16];
 
@@ -248,7 +248,9 @@ public:
 	}
 
 	/**
-	 * Use HMAC-SHA-384 as a PRF to generate four AES keys from one master
+	 * Use KBKDF with HMAC-SHA-384 to derive four sub-keys for AES-GMAC-CTR from a single master key
+	 *
+	 * See section 5.1 at https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf
 	 *
 	 * @param masterKey Master 256-bit key
 	 * @param k1 GMAC key
@@ -256,12 +258,20 @@ public:
 	 * @param k3 CTR IV masking (ECB encryption) key
 	 * @param k4 AES-CTR key
 	 */
-	static inline void initGmacCtrKeys(const uint8_t masterKey[32],AES &k1,AES &k2,AES &k3,AES &k4)
+	static ZT_ALWAYS_INLINE void initGmacCtrKeys(const uint8_t masterKey[32],AES &k1,AES &k2,AES &k3,AES &k4)
 	{
-		uint64_t kbuf[6];
-		for(uint8_t kno=0;kno<4;++kno) {
-			HMACSHA384(masterKey,&kno,1,(uint8_t *)kbuf);
-			k1.init((const uint8_t *)kbuf);
+		uint8_t kbuf[48];
+		uint8_t kbkdfMsg[16];
+		kbkdfMsg[0] = 0;    // key iterator, incremented for each key
+		for(unsigned int i=0;i<12;++i)
+			kbkdfMsg[i+1] = (uint8_t)("AES-GMAC-CTR"[i]); // KBKDF "label" indicating the use for these keys
+		kbkdfMsg[13] = 0;   // 0x00
+		kbkdfMsg[14] = 0;   // KBKDF "context", just 0 as it's not used in this protocol
+		kbkdfMsg[15] = 32;  // bits used in resulting key
+		while (kbkdfMsg[0] < 4) {
+			HMACSHA384(masterKey,&kbkdfMsg,sizeof(kbkdfMsg),kbuf);
+			k1.init(kbuf);
+			++kbkdfMsg[0];
 		}
 	}
 

+ 9 - 14
node/Utils.cpp

@@ -143,8 +143,8 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 {
 	static Mutex globalLock;
 	static bool initialized = false;
-	static uint64_t randomState[1024];
-	static uint8_t randomBuf[65536];
+	static uint64_t randomState[4];
+	static uint8_t randomBuf[16384];
 	static unsigned long randomPtr = sizeof(randomBuf);
 #ifdef __WINDOWS__
 	static HCRYPTPROV cryptProvider = NULL;
@@ -196,23 +196,18 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 
 	for(unsigned int i=0;i<bytes;++i) {
 		if (randomPtr >= sizeof(randomBuf)) {
-			for(unsigned int k=0;k<1024;++k) {
+			randomPtr = 0;
+
+			for(unsigned int k=0;k<4;++k) {
 				if (++randomState[k])
 					break;
 			}
 
-			uint8_t h[64];
-			SHA512(h,randomState,sizeof(randomState));
+			uint8_t h[48];
+			HMACSHA384((const uint8_t *)randomState,randomBuf,sizeof(randomBuf),h);
 
-			if (AES::HW_ACCEL) {
-				AES c(h);
-				c.ctr(h + 32,randomBuf,sizeof(randomBuf),randomBuf);
-			} else {
-				Salsa20 c(h,h + 32);
-				c.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
-			}
-
-			randomPtr = 0;
+			AES c(h);
+			c.ctr(h + 32,randomBuf,sizeof(randomBuf),randomBuf);
 		}
 		((uint8_t *)buf)[i] = randomBuf[randomPtr++];
 	}

+ 54 - 49
selftest.cpp

@@ -179,57 +179,62 @@ static int testCrypto()
 
 	{
 		std::cout << "[crypto] Testing and benchmarking AES-256..." ZT_EOL_S << "  AES-256 (test vectors): "; std::cout.flush();
-		AES tv(AES_TEST_VECTOR_0_KEY);
-		tv.encrypt(AES_TEST_VECTOR_0_IN,(uint8_t *)buf1);
-		if (memcmp(buf1,AES_TEST_VECTOR_0_OUT,16) != 0) {
-			std::cout << "FAILED (test vector 0 encrypt)" ZT_EOL_S;
-			return -1;
-		}
-		std::cout << "OK" ZT_EOL_S << "  GMAC-AES-256 (test vectors): "; std::cout.flush();
-		tv.init(AES_GMAC_VECTOR_0_KEY);
-		tv.gmac(AES_GMAC_VECTOR_0_IV,AES_GMAC_VECTOR_0_IN,sizeof(AES_GMAC_VECTOR_0_IN),(uint8_t *)buf2);
-		if (memcmp(buf2,AES_GMAC_VECTOR_0_OUT,16) != 0) {
-			std::cout << "FAILED (test vector 0) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S;
-			return -1;
-		}
-		tv.init(AES_GMAC_VECTOR_1_KEY);
-		tv.gmac(AES_GMAC_VECTOR_1_IV,AES_GMAC_VECTOR_1_IN,sizeof(AES_GMAC_VECTOR_1_IN),(uint8_t *)buf2);
-		if (memcmp(buf2,AES_GMAC_VECTOR_1_OUT,16) != 0) {
-			std::cout << "FAILED (test vector 1) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S;
-			return -1;
-		}
-		tv.init(AES_GMAC_VECTOR_2_KEY);
-		tv.gmac(AES_GMAC_VECTOR_2_IV,AES_GMAC_VECTOR_2_IN,sizeof(AES_GMAC_VECTOR_2_IN),(uint8_t *)buf2);
-		if (memcmp(buf2,AES_GMAC_VECTOR_2_OUT,16) != 0) {
-			std::cout << "FAILED (test vector 2) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S;
-			return -1;
-		}
-		std::cout << "OK" ZT_EOL_S << "  GMAC-AES-256 (benchmark): "; std::cout.flush();
-		int64_t start = OSUtils::now();
-		for(unsigned long i=0;i<200000;++i) {
-			tv.gmac((const uint8_t *)buf1,buf1,sizeof(buf1),(uint8_t *)buf1);
-		}
-		int64_t end = OSUtils::now();
-		*dummy = hexbuf[0];
-		std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
-		std::cout << "  AES-256-CTR (benchmark): "; std::cout.flush();
-		start = OSUtils::now();
-		for(unsigned long i=0;i<200000;++i) {
-			tv.ctr((const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2);
-			hexbuf[0] = buf2[0];
+		{
+			AES tv(AES_TEST_VECTOR_0_KEY);
+			tv.encrypt(AES_TEST_VECTOR_0_IN,(uint8_t *)buf1);
+			if (memcmp(buf1,AES_TEST_VECTOR_0_OUT,16) != 0) {
+				std::cout << "FAILED (test vector 0 encrypt)" ZT_EOL_S;
+				return -1;
+			}
+			std::cout << "OK" ZT_EOL_S << "  GMAC-AES-256 (test vectors): "; std::cout.flush();
+			tv.init(AES_GMAC_VECTOR_0_KEY);
+			tv.gmac(AES_GMAC_VECTOR_0_IV,AES_GMAC_VECTOR_0_IN,sizeof(AES_GMAC_VECTOR_0_IN),(uint8_t *)buf2);
+			if (memcmp(buf2,AES_GMAC_VECTOR_0_OUT,16) != 0) {
+				std::cout << "FAILED (test vector 0) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S;
+				return -1;
+			}
+			tv.init(AES_GMAC_VECTOR_1_KEY);
+			tv.gmac(AES_GMAC_VECTOR_1_IV,AES_GMAC_VECTOR_1_IN,sizeof(AES_GMAC_VECTOR_1_IN),(uint8_t *)buf2);
+			if (memcmp(buf2,AES_GMAC_VECTOR_1_OUT,16) != 0) {
+				std::cout << "FAILED (test vector 1) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S;
+				return -1;
+			}
+			tv.init(AES_GMAC_VECTOR_2_KEY);
+			tv.gmac(AES_GMAC_VECTOR_2_IV,AES_GMAC_VECTOR_2_IN,sizeof(AES_GMAC_VECTOR_2_IN),(uint8_t *)buf2);
+			if (memcmp(buf2,AES_GMAC_VECTOR_2_OUT,16) != 0) {
+				std::cout << "FAILED (test vector 2) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S;
+				return -1;
+			}
+			std::cout << "OK" ZT_EOL_S << "  GMAC-AES-256 (benchmark): "; std::cout.flush();
+			int64_t start = OSUtils::now();
+			for(unsigned long i=0;i<200000;++i) {
+				tv.gmac((const uint8_t *)buf1,buf1,sizeof(buf1),(uint8_t *)buf1);
+			}
+			int64_t end = OSUtils::now();
+			*dummy = hexbuf[0];
+			std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
+			std::cout << "  AES-256-CTR (benchmark): "; std::cout.flush();
+			start = OSUtils::now();
+			for(unsigned long i=0;i<200000;++i) {
+				tv.ctr((const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2);
+				hexbuf[0] = buf2[0];
+			}
+			end = OSUtils::now();
+			*dummy = buf2[0];
+			std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
 		}
-		end = OSUtils::now();
-		*dummy = buf2[0];
-		std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
-		std::cout << "  AES-256-GMAC-CTR (benchmark): "; std::cout.flush();
-		start = OSUtils::now();
-		for(unsigned long i=0;i<200000;++i) {
-			AES::ztGmacCtrEncrypt(tv,tv,tv,tv,(const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2,(uint8_t *)(hexbuf + 8));
-			hexbuf[0] = buf2[0];
+		{
+			std::cout << "  AES-256-GMAC-CTR (benchmark): "; std::cout.flush();
+			AES k1,k2,k3,k4;
+			AES::initGmacCtrKeys(AES_TEST_VECTOR_0_KEY,k1,k2,k3,k4);
+			int64_t start = OSUtils::now();
+			for(unsigned long i=0;i<200000;++i) {
+				AES::ztGmacCtrEncrypt(k1,k2,k3,k4,(const uint8_t *)hexbuf,buf1,sizeof(buf1),buf1,(uint8_t *)(hexbuf + 8));
+				*dummy = buf1[0];
+			}
+			int64_t end = OSUtils::now();
+			std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
 		}
-		end = OSUtils::now();
-		*dummy = buf2[0];
-		std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
 	}
 
 	{