shithub: riscv

Download patch

ref: aa6673fcfbe3bc41078487f4ef5d5aea459cd953
parent: 7ff779ff52b3da9c3b4cfee38cd90088ac65d6c3
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Tue Mar 22 22:45:35 EDT 2016

add portable AES-GCM (Galois/Counter Mode) implementation to libsec and devtls

--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -27,7 +27,7 @@
 	ulong	ekey[4*(AESmaxrounds + 1)];	/* encryption key */
 	ulong	dkey[4*(AESmaxrounds + 1)];	/* decryption key */
 	uchar	ivec[AESbsize];			/* initialization vector */
-	uchar	mackey[3 * AESbsize];		/* 3 XCBC mac 96 keys */
+	uchar	mackey[3 * AESbsize];	/* 3 XCBC mac 96 keys */
 };
 
 /* block ciphers */
@@ -40,6 +40,20 @@
 
 void	setupAESXCBCstate(AESstate *s);
 uchar*	aesXCBCmac(uchar *p, int len, AESstate *s);
+
+typedef struct AESGCMstate AESGCMstate;
+struct AESGCMstate
+{
+	AESstate;
+
+	ulong	H[4];
+	ulong	M[16][256][4];
+};
+
+void	setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen);
+void	aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen);
+void	aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s);
+int	aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s);
 
 /*
  * Blowfish Definitions
--- a/sys/man/2/aes
+++ b/sys/man/2/aes
@@ -1,6 +1,6 @@
 .TH AES 2
 .SH NAME
-setupAESstate, aesCBCencrypt, aesCBCdecrypt, setupAESXCBCstate, aesXCBCmac - advanced encryption standard (rijndael)
+setupAESstate, aesCBCencrypt, aesCBCdecrypt, setupAESXCBCstate, aesXCBCmac, setupAESGCMstate - advanced encryption standard (rijndael)
 .SH SYNOPSIS
 .B #include <u.h>
 .br
@@ -32,6 +32,18 @@
 .PP
 .B
 void	aesXCBCmac(uchar *p, int len, AESstate *s)
+.PP
+.B
+void	setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen)
+.PP
+.B
+void	aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen)
+.PP
+.B
+void	aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+.PP
+.B
+int	aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
 .SH DESCRIPTION
 AES (a.k.a. Rijndael) has replaced DES as the preferred
 block cipher.
@@ -46,10 +58,27 @@
 and
 .I aesCBCdecrypt
 implement cipher-block-chaining encryption.
-.I setupAESXCBCstate
+.I SetupAESXCBCstate
 and
 .I aesXCBCmac
 implement AES XCBC message authentication, per RFC 3566.
+.IR SetupAESGCMstate ,
+.IR aesgcm_setiv ,
+.I aesgcm_encrypt
+and
+.I aesgcm_decrypt
+implement Galois/Counter Mode (GCM) authenticated encryption with associated data (AEAD).
+Before encryption or decryption, a new initialization vector (nonce) has to be set with
+.I aesgcm_setiv
+or by calling
+.I setupAESGCMstate
+with non-zero
+.I iv
+and
+.I ivlen
+arguments.
+Aesgcm_decrypt returns zero when authentication and decryption where successfull and
+non-zero otherwise.
 All ciphering is performed in place.
 .I Keybytes
 should be 16, 24, or 32.
--- a/sys/src/9/port/devtls.c
+++ b/sys/src/9/port/devtls.c
@@ -88,11 +88,12 @@
 	int		(*unpad)(uchar*, int, int);
 	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
 
-	int		(*aead_enc)(Secret*, uchar*, int, uchar*, int);
-	int		(*aead_dec)(Secret*, uchar*, int, uchar*, int);
+	int		(*aead_enc)(Secret*, uchar*, int, uchar*, uchar*, int);
+	int		(*aead_dec)(Secret*, uchar*, int, uchar*, uchar*, int);
 
 	int		block;		/* encryption block len, 0 if none */
 	int		maclen;
+	int		recivlen;
 	void		*enckey;
 	uchar		mackey[MaxMacLen];
 };
@@ -246,8 +247,10 @@
 static int	des3dec(Secret *sec, uchar *buf, int n);
 static int	aesenc(Secret *sec, uchar *buf, int n);
 static int	aesdec(Secret *sec, uchar *buf, int n);
-static int	ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *data, int len);
-static int	ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *data, int len);
+static int	ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int	ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int	aesgcm_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
+static int	aesgcm_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len);
 static int	noenc(Secret *sec, uchar *buf, int n);
 static int	sslunpad(uchar *buf, int n, int block);
 static int	tlsunpad(uchar *buf, int n, int block);
@@ -821,14 +824,16 @@
 if(tr->debug) pdump(unpad_len, p, "decrypted:");
 		}
 
+		ivlen = sec->recivlen;
 		if(tr->version >= TLS11Version){
-			ivlen = sec->block;
-			len -= ivlen;
-			if(len < 0)
-				rcvError(tr, EDecodeError, "runt record message");
-			unpad_len -= ivlen;
-			p += ivlen;
+			if(ivlen == 0)
+				ivlen = sec->block;
 		}
+		len -= ivlen;
+		if(len < 0)
+			rcvError(tr, EDecodeError, "runt record message");
+		unpad_len -= ivlen;
+		p += ivlen;
 
 		if(unpad_len >= sec->maclen)
 			len = unpad_len - sec->maclen;
@@ -837,7 +842,7 @@
 		put16(header+3, len);
 		aadlen = (*tr->packAAD)(in->seq++, header, aad);
 		if(sec->aead_dec != nil) {
-			len = (*sec->aead_dec)(sec, aad, aadlen, p, unpad_len);
+			len = (*sec->aead_dec)(sec, aad, aadlen, p - ivlen, p, unpad_len);
 			if(len < 0)
 				rcvError(tr, EBadRecordMac, "record mac mismatch");
 		} else {
@@ -1299,8 +1304,11 @@
 		if(sec != nil){
 			maclen = sec->maclen;
 			pad = maclen + sec->block;
-			if(tr->version >= TLS11Version)
-				ivlen = sec->block;
+			ivlen = sec->recivlen;
+			if(tr->version >= TLS11Version){
+				if(ivlen == 0)
+					ivlen = sec->block;
+			}
 		}
 		n = BLEN(bb);
 		if(n > MaxRecLen){
@@ -1326,12 +1334,12 @@
 		put16(p+3, n);
 
 		if(sec != nil){
-			if(ivlen > 0)
-				randfill(p + RecHdrLen, ivlen);
 			aadlen = (*tr->packAAD)(out->seq++, p, aad);
 			if(sec->aead_enc != nil)
-				n = (*sec->aead_enc)(sec, aad, aadlen, p + RecHdrLen + ivlen, n) + ivlen;
+				n = (*sec->aead_enc)(sec, aad, aadlen, p + RecHdrLen, p + RecHdrLen + ivlen, n) + ivlen;
 			else {
+				if(ivlen > 0)
+					randfill(p + RecHdrLen, ivlen);
 				packMac(sec, aad, aadlen, p + RecHdrLen + ivlen, n, p + RecHdrLen + ivlen + n);
 				n = (*sec->enc)(sec, p + RecHdrLen, ivlen + n + maclen);
 			}
@@ -1527,6 +1535,23 @@
 }
 
 static void
+initaesgcmkey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
+{
+	s->enckey = smalloc(sizeof(AESGCMstate));
+	s->enc = noenc;
+	s->dec = noenc;
+	s->mac = nomac;
+	s->aead_enc = aesgcm_aead_enc;
+	s->aead_dec = aesgcm_aead_dec;
+	s->block = 0;
+	s->maclen = 16;
+	s->recivlen = 8;
+	memmove(s->mackey, iv, ea->ivlen);
+	randfill(s->mackey + ea->ivlen, s->recivlen);
+	setupAESGCMstate(s->enckey, p, ea->keylen, nil, 0);
+}
+
+static void
 initclearenc(Encalg *, Secret *s, uchar *, uchar *)
 {
 	s->enc = noenc;
@@ -1543,6 +1568,8 @@
 	{ "aes_256_cbc", 256/8, 16, initAESkey },
 	{ "ccpoly64_aead", 256/8, 0, initccpolykey },
 	{ "ccpoly96_aead", 256/8, 96/8, initccpolykey },
+	{ "aes_128_gcm_aead", 128/8, 4, initaesgcmkey },
+	{ "aes_256_gcm_aead", 256/8, 4, initaesgcmkey },
 	{ 0 }
 };
 
@@ -1697,6 +1724,7 @@
 		if(!tos->mac || !tos->enc || !tos->dec
 		|| !toc->mac || !toc->enc || !toc->dec)
 			error("missing algorithm implementations");
+
 		if(strtol(cb->f[3], nil, 0) == 0){
 			tr->in.new = tos;
 			tr->out.new = toc;
@@ -2155,8 +2183,9 @@
 }
 
 static int
-ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *data, int len)
+ccpoly_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
 {
+	USED(reciv);
 	ccpoly_aead_setiv(sec, aad);
 	ccpoly_encrypt(data, len, aad, aadlen, data+len, sec->enckey);
 	return len + sec->maclen;
@@ -2163,8 +2192,9 @@
 }
 
 static int
-ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *data, int len)
+ccpoly_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
 {
+	USED(reciv);
 	len -= sec->maclen;
 	if(len < 0)
 		return -1;
@@ -2173,6 +2203,37 @@
 		return -1;
 	return len;
 }
+
+static int
+aesgcm_aead_enc(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
+{
+	uchar iv[12];
+	int i;
+
+	memmove(iv, sec->mackey, 4+8);
+	for(i=0; i<8; i++) iv[4+i] ^= aad[i];
+	memmove(reciv, iv+4, 8);
+	aesgcm_setiv(sec->enckey, iv, 12);
+	aesgcm_encrypt(data, len, aad, aadlen, data+len, sec->enckey);
+	return len + sec->maclen;
+}
+
+static int
+aesgcm_aead_dec(Secret *sec, uchar *aad, int aadlen, uchar *reciv, uchar *data, int len)
+{
+	uchar iv[12];
+
+	len -= sec->maclen;
+	if(len < 0)
+		return -1;
+	memmove(iv, sec->mackey, 4);
+	memmove(iv+4, reciv, 8);
+	aesgcm_setiv(sec->enckey, iv, 12);
+	if(aesgcm_decrypt(data, len, aad, aadlen, data+len, sec->enckey) != 0)
+		return -1;
+	return len;
+}
+
 
 static DigestState*
 nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *)
--- /dev/null
+++ b/sys/src/libsec/port/aes_gcm.c
@@ -1,0 +1,200 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+static void
+load128(uchar b[16], ulong W[4])
+{
+	W[0] = (ulong)b[15] | (ulong)b[14]<<8 | (ulong)b[13]<<16 | (ulong)b[12]<<24;
+	W[1] = (ulong)b[11] | (ulong)b[10]<<8 | (ulong)b[ 9]<<16 | (ulong)b[ 8]<<24;
+	W[2] = (ulong)b[ 7] | (ulong)b[ 6]<<8 | (ulong)b[ 5]<<16 | (ulong)b[ 4]<<24;
+	W[3] = (ulong)b[ 3] | (ulong)b[ 2]<<8 | (ulong)b[ 1]<<16 | (ulong)b[ 0]<<24;
+}
+
+static void
+store128(ulong W[4], uchar b[16])
+{
+	b[15] = W[0], b[14] = W[0]>>8, b[13] = W[0]>>16, b[12] = W[0]>>24;
+	b[11] = W[1], b[10] = W[1]>>8, b[ 9] = W[1]>>16, b[ 8] = W[1]>>24;
+	b[ 7] = W[2], b[ 6] = W[2]>>8, b[ 5] = W[2]>>16, b[ 4] = W[2]>>24;
+	b[ 3] = W[3], b[ 2] = W[3]>>8, b[ 1] = W[3]>>16, b[ 0] = W[3]>>24;
+}
+
+static void
+gfmul(ulong X[4], ulong Y[4], ulong Z[4])
+{
+	long m, i;
+
+	Z[0] = Z[1] = Z[2] = Z[3] = 0;
+	for(i=127; i>=0; i--){
+		m = ((long)Y[i>>5] << 31-(i&31)) >> 31;
+		Z[0] ^= X[0] & m;
+		Z[1] ^= X[1] & m;
+		Z[2] ^= X[2] & m;
+		Z[3] ^= X[3] & m;
+		m = ((long)X[0]<<31) >> 31;
+		X[0] = X[0]>>1 | X[1]<<31;
+		X[1] = X[1]>>1 | X[2]<<31;
+		X[2] = X[2]>>1 | X[3]<<31;
+		X[3] = X[3]>>1 ^ (0xE1000000 & m);
+	}
+}
+
+static void
+prepareM(ulong H[4], ulong M[16][256][4])
+{
+	ulong X[4], i, j;
+
+	for(i=0; i<16; i++){
+		for(j=0; j<256; j++){
+			X[0] = X[1] = X[2] = X[3] = 0;
+			X[i>>2] = j<<((i&3)<<3);
+			gfmul(X, H, M[i][j]);
+		}
+	}
+}
+
+static void
+ghash1(AESGCMstate *s, ulong X[4], ulong Y[4])
+{
+	ulong *Xi, i;
+
+	X[0] ^= Y[0], X[1] ^= Y[1], X[2] ^= Y[2], X[3] ^= Y[3];
+	if(0){
+		gfmul(X, s->H, Y);
+		return;
+	}
+
+	Y[0] = Y[1] = Y[2] = Y[3] = 0;
+	for(i=0; i<16; i++){
+		Xi = s->M[i][(X[i>>2]>>((i&3)<<3))&0xFF];
+		Y[0] ^= Xi[0];
+		Y[1] ^= Xi[1];
+		Y[2] ^= Xi[2];
+		Y[3] ^= Xi[3];
+	}
+}
+
+static void
+ghashn(AESGCMstate *s, uchar *dat, ulong len, ulong Y[4])
+{
+	uchar tmp[16];
+	ulong X[4];
+
+	while(len >= 16){
+		load128(dat, X);
+		ghash1(s, X, Y);
+		dat += 16, len -= 16;
+	}
+	if(len > 0){
+		memmove(tmp, dat, len);
+		memset(tmp+len, 0, 16-len);
+		load128(tmp, X);
+		ghash1(s, X, Y);
+	}
+}
+
+static ulong
+aesxctr1(AESstate *s, uchar ctr[AESbsize], uchar *dat, ulong len)
+{
+	uchar tmp[AESbsize];
+	ulong i;
+
+	aes_encrypt(s->ekey, s->rounds, ctr, tmp);
+	if(len > AESbsize)
+		len = AESbsize;
+	for(i=0; i<len; i++)
+		dat[i] ^= tmp[i];
+	return len;
+}
+
+static void
+aesxctrn(AESstate *s, uchar *dat, ulong len)
+{
+	uchar ctr[AESbsize];
+	ulong i;
+
+	memmove(ctr, s->ivec, AESbsize);
+	while(len > 0){
+		for(i=AESbsize-1; i>=AESbsize-4; i--)
+			if(++ctr[i] != 0)
+				break;
+
+		if(aesxctr1(s, ctr, dat, len) < AESbsize)
+			break;
+		dat += AESbsize;
+		len -= AESbsize;
+	}
+}
+
+void
+aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen)
+{
+	if(ivlen == 96/8){
+		memmove(s->ivec, iv, ivlen);
+		memset(s->ivec+ivlen, 0, AESbsize-ivlen);
+		s->ivec[AESbsize-1] = 1;
+	} else {
+		ulong L[4], Y[4] = {0};
+
+		ghashn(s, iv, ivlen, Y);
+		L[0] = ivlen << 3;
+		L[1] = ivlen >> 29;
+		L[2] = L[3] = 0;
+		ghash1(s, L, Y);
+		store128(Y, s->ivec);
+	}
+}
+
+void
+setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen)
+{
+	setupAESstate(s, key, keylen, nil);
+
+	memset(s->mackey, 0, AESbsize);
+	aes_encrypt(s->ekey, s->rounds, s->mackey, s->mackey);
+	load128(s->mackey, s->H);
+	prepareM(s->H, s->M);
+
+	if(iv != nil && ivlen > 0)
+		aesgcm_setiv(s, iv, ivlen);
+}
+
+void
+aesgcm_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+{
+	ulong L[4], Y[4] = {0};
+
+	ghashn(s, aad, naad, Y);
+	aesxctrn(s, dat, ndat);
+	ghashn(s, dat, ndat, Y);
+	L[0] = ndat << 3;
+	L[1] = ndat >> 29;
+	L[2] = naad << 3;
+	L[3] = naad >> 29;
+	ghash1(s, L, Y);
+	store128(Y, tag);
+	aesxctr1(s, s->ivec, tag, 16);
+}
+
+int
+aesgcm_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], AESGCMstate *s)
+{
+	ulong L[4], Y[4] = {0};
+	uchar tmp[16];
+
+	ghashn(s, aad, naad, Y);
+	ghashn(s, dat, ndat, Y);
+	L[0] = ndat << 3;
+	L[1] = ndat >> 29;
+	L[2] = naad << 3;
+	L[3] = naad >> 29;
+	ghash1(s, L, Y);
+	store128(Y, tmp);
+	aesxctr1(s, s->ivec, tmp, 16);
+	if(tsmemcmp(tag, tmp, 16) != 0)
+		return -1;
+	aesxctrn(s, dat, ndat);
+	return 0;
+}
--- /dev/null
+++ b/sys/src/libsec/port/aesgcmtest.c
@@ -1,0 +1,314 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <mp.h>
+
+typedef struct Test Test;
+struct Test
+{
+	char *K;
+	char *P;
+	char *A;
+	char *IV;
+	char *T;
+};
+
+Test tests[] = {
+	{	/* Test Case 1 */
+		"00000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+
+		"58E2FCCEFA7E3061367F1D57A4E7455A"
+	},
+	{	/* Test Case 2 */
+		"00000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+
+		"AB6E47D42CEC13BDF53A67B21257BDDF",
+	},
+	{	/* Test Case 3 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+
+		"4D5C2AF327CD64A62CF35ABD2BA6FAB4"
+	},
+	{	/* Test Case 4 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbaddecaf888",
+
+		"5BC94FBC3221A5DB94FAE95AE7121A47"
+	},
+	{	/* Test Case 5 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbad",
+
+		"3612D2E79E3B0785561BE14AACA2FCCB"
+	},
+	{	/* Test Case 6 */
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"9313225df88406e555909c5aff5269aa"
+		"6a7a9538534f7da1e4c303d2a318a728"
+		"c3c0c95156809539fcf0e2429a6b5254"
+		"16aedbf5a0de6a57a637b39b",
+
+		"619CC5AEFFFE0BFA462AF43C1699D050"
+	},
+	{	/* Test Case 7 */
+		"00000000000000000000000000000000"
+		"0000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+
+		"CD33B28AC773F74BA00ED1F312572435"
+	},
+	{	/* Test Case 8 */
+		"00000000000000000000000000000000"
+		"0000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+
+		"2FF58D80033927AB8EF4D4587514F0FB"
+	},
+	{	/* Test Case 9 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+
+		"9924A7C8587336BFB118024DB8674A14"
+	},
+	{	/* Test Case 10 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbaddecaf888",
+
+		"2519498E80F1478F37BA55BD6D27618C"
+	},
+	{	/* Test Case 11 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbad",
+
+		"65DCC57FCF623A24094FCCA40D3533F8"
+	},
+	{	/* Test Case 12 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"9313225df88406e555909c5aff5269aa"
+		"6a7a9538534f7da1e4c303d2a318a728"
+		"c3c0c95156809539fcf0e2429a6b5254"
+		"16aedbf5a0de6a57a637b39b",
+
+		"DCF566FF291C25BBB8568FC3D376A6D9"
+	},
+	{	/* Test Case 13 */
+		"00000000000000000000000000000000"
+		"00000000000000000000000000000000",
+		"",
+		"",
+		"000000000000000000000000",
+
+		"530F8AFBC74536B9A963B4F1C4CB738B"
+	},
+	{	/* Test Case 14 */
+		"00000000000000000000000000000000"
+		"00000000000000000000000000000000",
+		"00000000000000000000000000000000",
+		"",
+		"000000000000000000000000",
+
+		"D0D1C8A799996BF0265B98B5D48AB919"
+	},
+	{	/* Test Case 15 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b391aafd255",
+		"",
+		"cafebabefacedbaddecaf888",
+
+		"B094DAC5D93471BDEC1A502270E3CC6C"
+	},
+	{	/* Test Case 16 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbaddecaf888",
+
+		"76FC6ECE0F4E1768CDDF8853BB2D551B"
+	},
+	{	/* Test Case 17 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"cafebabefacedbad",
+
+		"3A337DBF46A792C45E454913FE2EA8F2"
+	},
+	{	/* Test Case 18 */
+		"feffe9928665731c6d6a8f9467308308"
+		"feffe9928665731c6d6a8f9467308308",
+		"d9313225f88406e5a55909c5aff5269a"
+		"86a7a9531534f7da2e4c303d8a318a72"
+		"1c3c0c95956809532fcf0e2449a6b525"
+		"b16aedf5aa0de657ba637b39",
+		"feedfacedeadbeeffeedfacedeadbeef"
+		"abaddad2",
+		"9313225df88406e555909c5aff5269aa"
+		"6a7a9538534f7da1e4c303d2a318a728"
+		"c3c0c95156809539fcf0e2429a6b5254"
+		"16aedbf5a0de6a57a637b39b",
+
+		"A44A8266EE1C8EB0C8B5D4CF5AE9F19A"
+	},
+};
+
+int
+parsehex(char *s, uchar *h, char *l)
+{
+	char *e;
+	mpint *m;
+	int n;
+
+	n = strlen(s);
+	if(n == 0)
+		return 0;
+	assert((n & 1) == 0);
+	n >>= 1;
+	e = nil;
+	m = strtomp(s, &e, 16, nil);
+	if(m == nil || *e != '\0')
+		abort();
+	mptober(m, h, n);
+	if(l != nil)
+		print("%s = %.*H\n", l, n, h);
+	return n;
+}
+
+void
+runtest(Test *t)
+{
+	AESGCMstate s;
+	uchar key[1024], plain[1024], aad[1024], iv[1024], tag[16], tmp[16];
+	int nkey, nplain, naad, niv;
+
+	nkey = parsehex(t->K, key, "K");
+	nplain = parsehex(t->P, plain, "P");
+	naad = parsehex(t->A, aad, "A");
+	niv = parsehex(t->IV, iv, "IV");
+
+	setupAESGCMstate(&s, key, nkey, iv, niv);
+	aesgcm_encrypt(plain, nplain, aad, naad, tag, &s);
+	print("C = %.*H\n", nplain, plain);
+	print("T = %.*H\n", 16, tag);
+
+	parsehex(t->T, tmp, nil);
+	assert(memcmp(tmp, tag, 16) == 0);
+}
+
+void
+perftest(void)
+{
+	AESGCMstate s;
+	static uchar zeros[16];
+	uchar buf[1024*1024], tag[16];
+	vlong now;
+	int i, delta;
+
+	now = nsec();
+	for(i=0; i<100; i++){
+		memset(buf, 0, sizeof(buf));
+		if(1){
+			setupAESGCMstate(&s, zeros, 16, zeros, 12);
+			aesgcm_encrypt(buf, sizeof(buf), nil, 0, tag, &s);
+		} else {
+			setupAESstate(&s, zeros, 16, zeros);
+			aesCBCencrypt(buf, sizeof(buf), &s);
+		}
+	}
+	delta = (nsec() - now) / 1000000000LL;
+	fprint(2, "%ds = %d/s\n", delta, i*sizeof(buf) / delta);
+}
+
+void
+main(int argc, char **argv)
+{
+	int i;
+
+	fmtinstall('H', encodefmt);
+
+	ARGBEGIN {
+	case 'p':
+		perftest();
+		exits(nil);
+	} ARGEND;
+
+	for(i=0; i<nelem(tests); i++){
+		print("Test Case %d\n", i+1);
+		runtest(&tests[i]);
+		print("\n");
+	}
+}
--- a/sys/src/libsec/port/mkfile
+++ b/sys/src/libsec/port/mkfile
@@ -3,7 +3,7 @@
 LIB=/$objtype/lib/libsec.a
 
 CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\
-	aes.c blowfish.c \
+	aes.c aes_gcm.c blowfish.c \
 	hmac.c md5.c md5block.c md4.c sha1.c sha1block.c\
 	sha2_64.c sha2_128.c sha2block64.c sha2block128.c\
 	sha1pickle.c md5pickle.c\
@@ -60,4 +60,7 @@
 	$LD -o $target $prereq
 
 $O.chachatest: chachatest.$O
+	$LD -o $target $prereq
+
+$O.aesgcmtest: aesgcmtest.$O
 	$LD -o $target $prereq
--- a/sys/src/libsec/port/tlshand.c
+++ b/sys/src/libsec/port/tlshand.c
@@ -269,6 +269,23 @@
 	TLS_RSA_WITH_AES_128_CBC_SHA256		= 0X003C,
 	TLS_RSA_WITH_AES_256_CBC_SHA256		= 0X003D,
 	TLS_DHE_RSA_WITH_AES_128_CBC_SHA256	= 0X0067,
+
+	TLS_RSA_WITH_AES_128_GCM_SHA256		= 0x009C,
+	TLS_RSA_WITH_AES_256_GCM_SHA384		= 0x009D,
+	TLS_DHE_RSA_WITH_AES_128_GCM_SHA256	= 0x009E,
+	TLS_DHE_RSA_WITH_AES_256_GCM_SHA384	= 0x009F,
+	TLS_DH_RSA_WITH_AES_128_GCM_SHA256	= 0x00A0,
+	TLS_DH_RSA_WITH_AES_256_GCM_SHA384	= 0x00A1,
+	TLS_DHE_DSS_WITH_AES_128_GCM_SHA256	= 0x00A2,
+	TLS_DHE_DSS_WITH_AES_256_GCM_SHA384	= 0x00A3,
+	TLS_DH_DSS_WITH_AES_128_GCM_SHA256	= 0x00A4,
+	TLS_DH_DSS_WITH_AES_256_GCM_SHA384	= 0x00A5,
+	TLS_DH_anon_WITH_AES_128_GCM_SHA256	= 0x00A6,
+	TLS_DH_anon_WITH_AES_256_GCM_SHA384	= 0x00A7,
+
+	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D,
+	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256	= 0xC031,
+
 	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA	= 0XC013,
 	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA	= 0XC014,
 	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256	= 0xC027,
@@ -302,6 +319,11 @@
 	{"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},
 	{"ccpoly64_aead", "clear", 2*32, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305},
 
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+	{"aes_128_gcm_aead", "clear", 2*(16+4), TLS_RSA_WITH_AES_128_GCM_SHA256},
+
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
 	{"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
 	{"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
@@ -856,6 +878,7 @@
 isDHE(int tlsid)
 {
 	switch(tlsid){
+	case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
 	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
  	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
  	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
@@ -876,6 +899,9 @@
 
 	case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
 	case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
 
 	case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
 	case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: