Browse Source

Fully implement the packet encryption/decryption algorithms.

Adam Ierymenko 6 years ago
parent
commit
52f7f6e6cf
2 changed files with 132 additions and 4 deletions
  1. 119 1
      node/AES.hpp
  2. 13 3
      selftest.cpp

+ 119 - 1
node/AES.hpp

@@ -90,7 +90,7 @@ public:
 #endif
 #endif
 	}
 	}
 
 
-	inline void ctr(const uint8_t iv[16],const void *in,const unsigned int len,void *out) const
+	inline void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const
 	{
 	{
 #ifdef ZT_AES_AESNI
 #ifdef ZT_AES_AESNI
 		if (likely(HW_ACCEL)) {
 		if (likely(HW_ACCEL)) {
@@ -98,6 +98,120 @@ public:
 			return;
 			return;
 		}
 		}
 #endif
 #endif
+
+		uint64_t ctr[2],cenc[2];
+		memcpy(ctr,iv,16);
+		uint64_t bctr = Utils::ntoh(ctr[1]);
+
+		const uint8_t *i = (const uint8_t *)in;
+		uint8_t *o = (uint8_t *)out;
+		while (len >= 16) {
+			_encryptSW((const uint8_t *)ctr,(uint8_t *)cenc);
+			ctr[1] = Utils::hton(++bctr);
+			for(unsigned int k=0;k<16;++k)
+				*(o++) = *(i++) ^ ((uint8_t *)cenc)[k];
+			len -= 16;
+		}
+
+		if (len) {
+			_encryptSW((const uint8_t *)ctr,(uint8_t *)cenc);
+			for(unsigned int k=0;k<len;++k)
+				*(o++) = *(i++) ^ ((uint8_t *)cenc)[k];
+		}
+	}
+
+	/**
+	 * Perform AES-256-GMAC-CTR encryption
+	 *
+	 * This mode combines the two standard modes AES256-GMAC and AES256-CTR to
+	 * yield a mode similar to AES256-GCM-SIV that is resistant to accidental
+	 * message IV duplication.
+	 *
+	 * @param iv 64-bit message IV
+	 * @param in Message plaintext
+	 * @param len Length of plaintext
+	 * @param out Output buffer to receive ciphertext
+	 * @param tag Output buffer to receive 64-bit authentication tag
+	 */
+	inline void ztGmacCtrEncrypt(const uint8_t iv[8],const void *in,unsigned int len,void *out,uint8_t tag[8])
+	{
+		uint8_t ctrIv[16],gmacIv[12];
+
+		// (1) Compute AES256-GMAC(in) using a 96-bit IV constructed from
+		// the 64-bit supplied IV and the message size.
+#ifdef ZT_NO_TYPE_PUNNING
+		for(unsigned int i=0;i<8;++i) gmacIv[i] = iv[i];
+#else
+		*((uint64_t *)gmacIv) = *((const uint64_t *)iv);
+#endif
+		gmacIv[8] = (uint8_t)(len >> 24);
+		gmacIv[9] = (uint8_t)(len >> 16);
+		gmacIv[10] = (uint8_t)(len >> 8);
+		gmacIv[11] = (uint8_t)len;
+		gmac(gmacIv,in,len,ctrIv);
+
+		// (2) The first 64 bits of GMAC output are the auth tag. Create
+		// a secret synthetic AES256-CTR IV by encrypting these and the
+		// original supplied IV.
+#ifdef ZT_NO_TYPE_PUNNING
+		for(unsigned int i=0;i<8;++i) tag[i] = ctrIv[i];
+		for(unsigned int i=0;i<8;++i) ctrIv[i+8] = iv[i];
+#else
+		*((uint64_t *)tag) = *((const uint64_t *)ctrIv);
+		*((uint64_t *)(ctrIv + 8)) = *((const uint64_t *)iv);
+#endif
+		encrypt(ctrIv,ctrIv);
+
+		// (3) Encrypt input using AES256-CTR
+		ctr(ctrIv,in,len,out);
+	}
+
+	/**
+	 * Decrypt a message encrypted with AES-256-GMAC-CTR and check its authenticity
+	 *
+	 * @param iv 64-bit message IV
+	 * @param in Message ciphertext
+	 * @param len Length of ciphertext
+	 * @param out Output buffer to receive plaintext
+	 * @param tag Authentication tag supplied with message
+	 * @return True if authentication tags match and message appears authentic
+	 */
+	inline bool ztGmacCtrDecrypt(const uint8_t iv[8],const void *in,unsigned int len,void *out,const uint8_t tag[8])
+	{
+		uint8_t ctrIv[16],gmacOut[16],gmacIv[12];
+
+		// (1) Re-create the original secret synthetic AES256-CTR IV.
+#ifdef ZT_NO_TYPE_PUNNING
+		for(unsigned int i=0;i<8;++i) ctrIv[i] = tag[i];
+		for(unsigned int i=0;i<8;++i) ctrIv[i+8] = iv[i];
+#else
+		*((uint64_t *)ctrIv) = *((const uint8_t *)tag);
+		*((uint64_t *)(ctrIv + 8)) = *((const uint64_t *)iv);
+#endif
+		encrypt(ctrIv,ctrIv);
+
+		// (2) Decrypt input using AES256-CTR
+		ctr(ctrIv,in,len,out);
+
+		// (3) Compute AES256-GMAC(out) using the re-created 96-bit
+		// GMAC IV built from the message IV and the message size.
+#ifdef ZT_NO_TYPE_PUNNING
+		for(unsigned int i=0;i<8;++i) gmacIv[i] = iv[i];
+#else
+		*((uint64_t *)gmacIv) = *((const uint64_t *)iv);
+#endif
+		gmacIv[8] = (uint8_t)(len >> 24);
+		gmacIv[9] = (uint8_t)(len >> 16);
+		gmacIv[10] = (uint8_t)(len >> 8);
+		gmacIv[11] = (uint8_t)len;
+		gmac(gmacIv,out,len,gmacOut);
+
+		// (4) Compare first 64 bits of GMAC output with tag.
+#ifdef ZT_NO_TYPE_PUNNING
+		return Utils::secureEq(gmacOut,tag,8);
+#else
+		return (*((const uint64_t *)gmacOut) == *((const uint64_t *)tag));
+#endif
 	}
 	}
 
 
 private:
 private:
@@ -561,11 +675,13 @@ private:
 		__m128i h2 = _k.ni.hhh;
 		__m128i h2 = _k.ni.hhh;
 		__m128i h3 = _k.ni.hh;
 		__m128i h3 = _k.ni.hh;
 		__m128i h4 = _k.ni.h;
 		__m128i h4 = _k.ni.h;
+
 		__m128i y = _mm_setzero_si128();
 		__m128i y = _mm_setzero_si128();
 		const __m128i *ab = (const __m128i *)in;
 		const __m128i *ab = (const __m128i *)in;
 		unsigned int blocks = len / 16;
 		unsigned int blocks = len / 16;
 		unsigned int pblocks = blocks - (blocks % 4);
 		unsigned int pblocks = blocks - (blocks % 4);
 		unsigned int rem = len % 16;
 		unsigned int rem = len % 16;
+
 		for (unsigned int i=0;i<pblocks;i+=4) {
 		for (unsigned int i=0;i<pblocks;i+=4) {
 			__m128i d1 = _mm_loadu_si128(ab + i + 0);
 			__m128i d1 = _mm_loadu_si128(ab + i + 0);
 			__m128i d2 = _mm_loadu_si128(ab + i + 1);
 			__m128i d2 = _mm_loadu_si128(ab + i + 1);
@@ -574,8 +690,10 @@ private:
 			y = _mm_xor_si128(y,d1);
 			y = _mm_xor_si128(y,d1);
 			y = _mult4xor_aesni(h1,h2,h3,h4,y,d2,d3,d4);
 			y = _mult4xor_aesni(h1,h2,h3,h4,y,d2,d3,d4);
 		}
 		}
+
 		for (unsigned int i=pblocks;i<blocks;++i)
 		for (unsigned int i=pblocks;i<blocks;++i)
 			y = _ghash_aesni(_k.ni.h,y,_mm_loadu_si128(ab + i));
 			y = _ghash_aesni(_k.ni.h,y,_mm_loadu_si128(ab + i));
+
 		if (rem) {
 		if (rem) {
 			__m128i last = _mm_setzero_si128();
 			__m128i last = _mm_setzero_si128();
 			memcpy(&last,ab + blocks,rem);
 			memcpy(&last,ab + blocks,rem);

+ 13 - 3
selftest.cpp

@@ -153,6 +153,11 @@ static const uint8_t AES_GMAC_VECTOR_0_IV[12] = { 0x2f, 0x9a, 0xd0, 0x12, 0xad,
 static const uint8_t AES_GMAC_VECTOR_0_IN[16] = { 0xdb, 0x98, 0xd9, 0x0d, 0x1b, 0x69, 0x5c, 0xdb, 0x74, 0x7a, 0x34, 0x3f, 0xbb, 0xc9, 0xf1, 0x41 };
 static const uint8_t AES_GMAC_VECTOR_0_IN[16] = { 0xdb, 0x98, 0xd9, 0x0d, 0x1b, 0x69, 0x5c, 0xdb, 0x74, 0x7a, 0x34, 0x3f, 0xbb, 0xc9, 0xf1, 0x41 };
 static const uint8_t AES_GMAC_VECTOR_0_OUT[16] = { 0xef, 0x06, 0xd5, 0x4d, 0xfd, 0x00, 0x02, 0x1d, 0x75, 0x27, 0xdf, 0xf2, 0x6f, 0xc9, 0xd4, 0x84 };
 static const uint8_t AES_GMAC_VECTOR_0_OUT[16] = { 0xef, 0x06, 0xd5, 0x4d, 0xfd, 0x00, 0x02, 0x1d, 0x75, 0x27, 0xdf, 0xf2, 0x6f, 0xc9, 0xd4, 0x84 };
 
 
+static const uint8_t AES_GMAC_VECTOR_1_KEY[32] = { 0x83,0xC0,0x93,0xB5,0x8D,0xE7,0xFF,0xE1,0xC0,0xDA,0x92,0x6A,0xC4,0x3F,0xB3,0x60,0x9A,0xC1,0xC8,0x0F,0xEE,0x1B,0x62,0x44,0x97,0xEF,0x94,0x2E,0x2F,0x79,0xA8,0x23 };
+static const uint8_t AES_GMAC_VECTOR_1_IV[12] = { 0x7C,0xFD,0xE9,0xF9,0xE3,0x37,0x24,0xC6,0x89,0x32,0xD6,0x12 };
+static const uint8_t AES_GMAC_VECTOR_1_IN[81] = { 0x84,0xC5,0xD5,0x13,0xD2,0xAA,0xF6,0xE5,0xBB,0xD2,0x72,0x77,0x88,0xE5,0x23,0x00,0x89,0x32,0xD6,0x12,0x7C,0xFD,0xE9,0xF9,0xE3,0x37,0x24,0xC6,0x08,0x00,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x00,0x05 };
+static const uint8_t AES_GMAC_VECTOR_1_OUT[16] = { 0x6E,0xE1,0x60,0xE8,0xFA,0xEC,0xA4,0xB3,0x6C,0x86,0xB2,0x34,0x92,0x0C,0xA9,0x75 };
+
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
 
 static int testCrypto()
 static int testCrypto()
@@ -182,6 +187,12 @@ static int testCrypto()
 			std::cout << "FAILED (test vector 0)" ZT_EOL_S;
 			std::cout << "FAILED (test vector 0)" ZT_EOL_S;
 			return -1;
 			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 *)hexbuf);
+		if (memcmp(hexbuf,AES_GMAC_VECTOR_1_OUT,16) != 0) {
+			std::cout << "FAILED (test vector 1)" ZT_EOL_S;
+			return -1;
+		}
 		std::cout << "OK" ZT_EOL_S << "  GMAC-AES-256 (benchmark): "; std::cout.flush();
 		std::cout << "OK" ZT_EOL_S << "  GMAC-AES-256 (benchmark): "; std::cout.flush();
 		int64_t start = OSUtils::now();
 		int64_t start = OSUtils::now();
 		for(unsigned long i=0;i<200000;++i) {
 		for(unsigned long i=0;i<200000;++i) {
@@ -200,11 +211,10 @@ static int testCrypto()
 		end = OSUtils::now();
 		end = OSUtils::now();
 		*dummy = buf2[0];
 		*dummy = buf2[0];
 		std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
 		std::cout << (((double)(200000 * sizeof(buf1)) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" ZT_EOL_S;
-		std::cout << "  GMAC-AES-256 and AES-256-CTR (benchmark): "; std::cout.flush();
+		std::cout << "  AES-256-GMAC-CTR (benchmark): "; std::cout.flush();
 		start = OSUtils::now();
 		start = OSUtils::now();
 		for(unsigned long i=0;i<200000;++i) {
 		for(unsigned long i=0;i<200000;++i) {
-			tv.gmac(AES_GMAC_VECTOR_0_IV,buf1,sizeof(buf1),(uint8_t *)hexbuf);
-			tv.ctr((const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2);
+			tv.ztGmacCtrEncrypt((const uint8_t *)hexbuf,buf1,sizeof(buf1),buf2,(uint8_t *)(hexbuf + 8));
 			hexbuf[0] = buf2[0];
 			hexbuf[0] = buf2[0];
 		}
 		}
 		end = OSUtils::now();
 		end = OSUtils::now();