13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * Galois/Counter Mode (GCM) and GMAC with AES
33ff40c12SJohn Marino  *
43ff40c12SJohn Marino  * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
53ff40c12SJohn Marino  *
63ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
73ff40c12SJohn Marino  * See README for more details.
83ff40c12SJohn Marino  */
93ff40c12SJohn Marino 
103ff40c12SJohn Marino #include "includes.h"
113ff40c12SJohn Marino 
123ff40c12SJohn Marino #include "common.h"
133ff40c12SJohn Marino #include "aes.h"
143ff40c12SJohn Marino #include "aes_wrap.h"
153ff40c12SJohn Marino 
inc32(u8 * block)163ff40c12SJohn Marino static void inc32(u8 *block)
173ff40c12SJohn Marino {
183ff40c12SJohn Marino 	u32 val;
193ff40c12SJohn Marino 	val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4);
203ff40c12SJohn Marino 	val++;
213ff40c12SJohn Marino 	WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val);
223ff40c12SJohn Marino }
233ff40c12SJohn Marino 
243ff40c12SJohn Marino 
xor_block(u8 * dst,const u8 * src)253ff40c12SJohn Marino static void xor_block(u8 *dst, const u8 *src)
263ff40c12SJohn Marino {
273ff40c12SJohn Marino 	u32 *d = (u32 *) dst;
283ff40c12SJohn Marino 	u32 *s = (u32 *) src;
293ff40c12SJohn Marino 	*d++ ^= *s++;
303ff40c12SJohn Marino 	*d++ ^= *s++;
313ff40c12SJohn Marino 	*d++ ^= *s++;
323ff40c12SJohn Marino 	*d++ ^= *s++;
333ff40c12SJohn Marino }
343ff40c12SJohn Marino 
353ff40c12SJohn Marino 
shift_right_block(u8 * v)363ff40c12SJohn Marino static void shift_right_block(u8 *v)
373ff40c12SJohn Marino {
383ff40c12SJohn Marino 	u32 val;
393ff40c12SJohn Marino 
403ff40c12SJohn Marino 	val = WPA_GET_BE32(v + 12);
413ff40c12SJohn Marino 	val >>= 1;
423ff40c12SJohn Marino 	if (v[11] & 0x01)
433ff40c12SJohn Marino 		val |= 0x80000000;
443ff40c12SJohn Marino 	WPA_PUT_BE32(v + 12, val);
453ff40c12SJohn Marino 
463ff40c12SJohn Marino 	val = WPA_GET_BE32(v + 8);
473ff40c12SJohn Marino 	val >>= 1;
483ff40c12SJohn Marino 	if (v[7] & 0x01)
493ff40c12SJohn Marino 		val |= 0x80000000;
503ff40c12SJohn Marino 	WPA_PUT_BE32(v + 8, val);
513ff40c12SJohn Marino 
523ff40c12SJohn Marino 	val = WPA_GET_BE32(v + 4);
533ff40c12SJohn Marino 	val >>= 1;
543ff40c12SJohn Marino 	if (v[3] & 0x01)
553ff40c12SJohn Marino 		val |= 0x80000000;
563ff40c12SJohn Marino 	WPA_PUT_BE32(v + 4, val);
573ff40c12SJohn Marino 
583ff40c12SJohn Marino 	val = WPA_GET_BE32(v);
593ff40c12SJohn Marino 	val >>= 1;
603ff40c12SJohn Marino 	WPA_PUT_BE32(v, val);
613ff40c12SJohn Marino }
623ff40c12SJohn Marino 
633ff40c12SJohn Marino 
643ff40c12SJohn Marino /* Multiplication in GF(2^128) */
gf_mult(const u8 * x,const u8 * y,u8 * z)653ff40c12SJohn Marino static void gf_mult(const u8 *x, const u8 *y, u8 *z)
663ff40c12SJohn Marino {
673ff40c12SJohn Marino 	u8 v[16];
683ff40c12SJohn Marino 	int i, j;
693ff40c12SJohn Marino 
703ff40c12SJohn Marino 	os_memset(z, 0, 16); /* Z_0 = 0^128 */
713ff40c12SJohn Marino 	os_memcpy(v, y, 16); /* V_0 = Y */
723ff40c12SJohn Marino 
733ff40c12SJohn Marino 	for (i = 0; i < 16; i++) {
743ff40c12SJohn Marino 		for (j = 0; j < 8; j++) {
753ff40c12SJohn Marino 			if (x[i] & BIT(7 - j)) {
763ff40c12SJohn Marino 				/* Z_(i + 1) = Z_i XOR V_i */
773ff40c12SJohn Marino 				xor_block(z, v);
783ff40c12SJohn Marino 			} else {
793ff40c12SJohn Marino 				/* Z_(i + 1) = Z_i */
803ff40c12SJohn Marino 			}
813ff40c12SJohn Marino 
823ff40c12SJohn Marino 			if (v[15] & 0x01) {
833ff40c12SJohn Marino 				/* V_(i + 1) = (V_i >> 1) XOR R */
843ff40c12SJohn Marino 				shift_right_block(v);
853ff40c12SJohn Marino 				/* R = 11100001 || 0^120 */
863ff40c12SJohn Marino 				v[0] ^= 0xe1;
873ff40c12SJohn Marino 			} else {
883ff40c12SJohn Marino 				/* V_(i + 1) = V_i >> 1 */
893ff40c12SJohn Marino 				shift_right_block(v);
903ff40c12SJohn Marino 			}
913ff40c12SJohn Marino 		}
923ff40c12SJohn Marino 	}
933ff40c12SJohn Marino }
943ff40c12SJohn Marino 
953ff40c12SJohn Marino 
ghash_start(u8 * y)963ff40c12SJohn Marino static void ghash_start(u8 *y)
973ff40c12SJohn Marino {
983ff40c12SJohn Marino 	/* Y_0 = 0^128 */
993ff40c12SJohn Marino 	os_memset(y, 0, 16);
1003ff40c12SJohn Marino }
1013ff40c12SJohn Marino 
1023ff40c12SJohn Marino 
ghash(const u8 * h,const u8 * x,size_t xlen,u8 * y)1033ff40c12SJohn Marino static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y)
1043ff40c12SJohn Marino {
1053ff40c12SJohn Marino 	size_t m, i;
1063ff40c12SJohn Marino 	const u8 *xpos = x;
1073ff40c12SJohn Marino 	u8 tmp[16];
1083ff40c12SJohn Marino 
1093ff40c12SJohn Marino 	m = xlen / 16;
1103ff40c12SJohn Marino 
1113ff40c12SJohn Marino 	for (i = 0; i < m; i++) {
1123ff40c12SJohn Marino 		/* Y_i = (Y^(i-1) XOR X_i) dot H */
1133ff40c12SJohn Marino 		xor_block(y, xpos);
1143ff40c12SJohn Marino 		xpos += 16;
1153ff40c12SJohn Marino 
1163ff40c12SJohn Marino 		/* dot operation:
1173ff40c12SJohn Marino 		 * multiplication operation for binary Galois (finite) field of
1183ff40c12SJohn Marino 		 * 2^128 elements */
1193ff40c12SJohn Marino 		gf_mult(y, h, tmp);
1203ff40c12SJohn Marino 		os_memcpy(y, tmp, 16);
1213ff40c12SJohn Marino 	}
1223ff40c12SJohn Marino 
1233ff40c12SJohn Marino 	if (x + xlen > xpos) {
1243ff40c12SJohn Marino 		/* Add zero padded last block */
1253ff40c12SJohn Marino 		size_t last = x + xlen - xpos;
1263ff40c12SJohn Marino 		os_memcpy(tmp, xpos, last);
1273ff40c12SJohn Marino 		os_memset(tmp + last, 0, sizeof(tmp) - last);
1283ff40c12SJohn Marino 
1293ff40c12SJohn Marino 		/* Y_i = (Y^(i-1) XOR X_i) dot H */
1303ff40c12SJohn Marino 		xor_block(y, tmp);
1313ff40c12SJohn Marino 
1323ff40c12SJohn Marino 		/* dot operation:
1333ff40c12SJohn Marino 		 * multiplication operation for binary Galois (finite) field of
1343ff40c12SJohn Marino 		 * 2^128 elements */
1353ff40c12SJohn Marino 		gf_mult(y, h, tmp);
1363ff40c12SJohn Marino 		os_memcpy(y, tmp, 16);
1373ff40c12SJohn Marino 	}
1383ff40c12SJohn Marino 
1393ff40c12SJohn Marino 	/* Return Y_m */
1403ff40c12SJohn Marino }
1413ff40c12SJohn Marino 
1423ff40c12SJohn Marino 
aes_gctr(void * aes,const u8 * icb,const u8 * x,size_t xlen,u8 * y)1433ff40c12SJohn Marino static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y)
1443ff40c12SJohn Marino {
1453ff40c12SJohn Marino 	size_t i, n, last;
1463ff40c12SJohn Marino 	u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
1473ff40c12SJohn Marino 	const u8 *xpos = x;
1483ff40c12SJohn Marino 	u8 *ypos = y;
1493ff40c12SJohn Marino 
1503ff40c12SJohn Marino 	if (xlen == 0)
1513ff40c12SJohn Marino 		return;
1523ff40c12SJohn Marino 
1533ff40c12SJohn Marino 	n = xlen / 16;
1543ff40c12SJohn Marino 
1553ff40c12SJohn Marino 	os_memcpy(cb, icb, AES_BLOCK_SIZE);
1563ff40c12SJohn Marino 	/* Full blocks */
1573ff40c12SJohn Marino 	for (i = 0; i < n; i++) {
1583ff40c12SJohn Marino 		aes_encrypt(aes, cb, ypos);
1593ff40c12SJohn Marino 		xor_block(ypos, xpos);
1603ff40c12SJohn Marino 		xpos += AES_BLOCK_SIZE;
1613ff40c12SJohn Marino 		ypos += AES_BLOCK_SIZE;
1623ff40c12SJohn Marino 		inc32(cb);
1633ff40c12SJohn Marino 	}
1643ff40c12SJohn Marino 
1653ff40c12SJohn Marino 	last = x + xlen - xpos;
1663ff40c12SJohn Marino 	if (last) {
1673ff40c12SJohn Marino 		/* Last, partial block */
1683ff40c12SJohn Marino 		aes_encrypt(aes, cb, tmp);
1693ff40c12SJohn Marino 		for (i = 0; i < last; i++)
1703ff40c12SJohn Marino 			*ypos++ = *xpos++ ^ tmp[i];
1713ff40c12SJohn Marino 	}
1723ff40c12SJohn Marino }
1733ff40c12SJohn Marino 
1743ff40c12SJohn Marino 
aes_gcm_init_hash_subkey(const u8 * key,size_t key_len,u8 * H)1753ff40c12SJohn Marino static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H)
1763ff40c12SJohn Marino {
1773ff40c12SJohn Marino 	void *aes;
1783ff40c12SJohn Marino 
1793ff40c12SJohn Marino 	aes = aes_encrypt_init(key, key_len);
1803ff40c12SJohn Marino 	if (aes == NULL)
1813ff40c12SJohn Marino 		return NULL;
1823ff40c12SJohn Marino 
1833ff40c12SJohn Marino 	/* Generate hash subkey H = AES_K(0^128) */
1843ff40c12SJohn Marino 	os_memset(H, 0, AES_BLOCK_SIZE);
1853ff40c12SJohn Marino 	aes_encrypt(aes, H, H);
1863ff40c12SJohn Marino 	wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH",
1873ff40c12SJohn Marino 			H, AES_BLOCK_SIZE);
1883ff40c12SJohn Marino 	return aes;
1893ff40c12SJohn Marino }
1903ff40c12SJohn Marino 
1913ff40c12SJohn Marino 
aes_gcm_prepare_j0(const u8 * iv,size_t iv_len,const u8 * H,u8 * J0)1923ff40c12SJohn Marino static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0)
1933ff40c12SJohn Marino {
1943ff40c12SJohn Marino 	u8 len_buf[16];
1953ff40c12SJohn Marino 
1963ff40c12SJohn Marino 	if (iv_len == 12) {
1973ff40c12SJohn Marino 		/* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
1983ff40c12SJohn Marino 		os_memcpy(J0, iv, iv_len);
1993ff40c12SJohn Marino 		os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len);
2003ff40c12SJohn Marino 		J0[AES_BLOCK_SIZE - 1] = 0x01;
2013ff40c12SJohn Marino 	} else {
2023ff40c12SJohn Marino 		/*
2033ff40c12SJohn Marino 		 * s = 128 * ceil(len(IV)/128) - len(IV)
2043ff40c12SJohn Marino 		 * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
2053ff40c12SJohn Marino 		 */
2063ff40c12SJohn Marino 		ghash_start(J0);
2073ff40c12SJohn Marino 		ghash(H, iv, iv_len, J0);
2083ff40c12SJohn Marino 		WPA_PUT_BE64(len_buf, 0);
2093ff40c12SJohn Marino 		WPA_PUT_BE64(len_buf + 8, iv_len * 8);
2103ff40c12SJohn Marino 		ghash(H, len_buf, sizeof(len_buf), J0);
2113ff40c12SJohn Marino 	}
2123ff40c12SJohn Marino }
2133ff40c12SJohn Marino 
2143ff40c12SJohn Marino 
aes_gcm_gctr(void * aes,const u8 * J0,const u8 * in,size_t len,u8 * out)2153ff40c12SJohn Marino static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len,
2163ff40c12SJohn Marino 			 u8 *out)
2173ff40c12SJohn Marino {
2183ff40c12SJohn Marino 	u8 J0inc[AES_BLOCK_SIZE];
2193ff40c12SJohn Marino 
2203ff40c12SJohn Marino 	if (len == 0)
2213ff40c12SJohn Marino 		return;
2223ff40c12SJohn Marino 
2233ff40c12SJohn Marino 	os_memcpy(J0inc, J0, AES_BLOCK_SIZE);
2243ff40c12SJohn Marino 	inc32(J0inc);
2253ff40c12SJohn Marino 	aes_gctr(aes, J0inc, in, len, out);
2263ff40c12SJohn Marino }
2273ff40c12SJohn Marino 
2283ff40c12SJohn Marino 
aes_gcm_ghash(const u8 * H,const u8 * aad,size_t aad_len,const u8 * crypt,size_t crypt_len,u8 * S)2293ff40c12SJohn Marino static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len,
2303ff40c12SJohn Marino 			  const u8 *crypt, size_t crypt_len, u8 *S)
2313ff40c12SJohn Marino {
2323ff40c12SJohn Marino 	u8 len_buf[16];
2333ff40c12SJohn Marino 
2343ff40c12SJohn Marino 	/*
2353ff40c12SJohn Marino 	 * u = 128 * ceil[len(C)/128] - len(C)
2363ff40c12SJohn Marino 	 * v = 128 * ceil[len(A)/128] - len(A)
2373ff40c12SJohn Marino 	 * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
2383ff40c12SJohn Marino 	 * (i.e., zero padded to block size A || C and lengths of each in bits)
2393ff40c12SJohn Marino 	 */
2403ff40c12SJohn Marino 	ghash_start(S);
2413ff40c12SJohn Marino 	ghash(H, aad, aad_len, S);
2423ff40c12SJohn Marino 	ghash(H, crypt, crypt_len, S);
2433ff40c12SJohn Marino 	WPA_PUT_BE64(len_buf, aad_len * 8);
2443ff40c12SJohn Marino 	WPA_PUT_BE64(len_buf + 8, crypt_len * 8);
2453ff40c12SJohn Marino 	ghash(H, len_buf, sizeof(len_buf), S);
2463ff40c12SJohn Marino 
2473ff40c12SJohn Marino 	wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16);
2483ff40c12SJohn Marino }
2493ff40c12SJohn Marino 
2503ff40c12SJohn Marino 
2513ff40c12SJohn Marino /**
2523ff40c12SJohn Marino  * aes_gcm_ae - GCM-AE_K(IV, P, A)
2533ff40c12SJohn Marino  */
aes_gcm_ae(const u8 * key,size_t key_len,const u8 * iv,size_t iv_len,const u8 * plain,size_t plain_len,const u8 * aad,size_t aad_len,u8 * crypt,u8 * tag)2543ff40c12SJohn Marino int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
2553ff40c12SJohn Marino 	       const u8 *plain, size_t plain_len,
2563ff40c12SJohn Marino 	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag)
2573ff40c12SJohn Marino {
2583ff40c12SJohn Marino 	u8 H[AES_BLOCK_SIZE];
2593ff40c12SJohn Marino 	u8 J0[AES_BLOCK_SIZE];
2603ff40c12SJohn Marino 	u8 S[16];
2613ff40c12SJohn Marino 	void *aes;
2623ff40c12SJohn Marino 
2633ff40c12SJohn Marino 	aes = aes_gcm_init_hash_subkey(key, key_len, H);
2643ff40c12SJohn Marino 	if (aes == NULL)
2653ff40c12SJohn Marino 		return -1;
2663ff40c12SJohn Marino 
2673ff40c12SJohn Marino 	aes_gcm_prepare_j0(iv, iv_len, H, J0);
2683ff40c12SJohn Marino 
2693ff40c12SJohn Marino 	/* C = GCTR_K(inc_32(J_0), P) */
2703ff40c12SJohn Marino 	aes_gcm_gctr(aes, J0, plain, plain_len, crypt);
2713ff40c12SJohn Marino 
2723ff40c12SJohn Marino 	aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
2733ff40c12SJohn Marino 
2743ff40c12SJohn Marino 	/* T = MSB_t(GCTR_K(J_0, S)) */
2753ff40c12SJohn Marino 	aes_gctr(aes, J0, S, sizeof(S), tag);
2763ff40c12SJohn Marino 
2773ff40c12SJohn Marino 	/* Return (C, T) */
2783ff40c12SJohn Marino 
2793ff40c12SJohn Marino 	aes_encrypt_deinit(aes);
2803ff40c12SJohn Marino 
2813ff40c12SJohn Marino 	return 0;
2823ff40c12SJohn Marino }
2833ff40c12SJohn Marino 
2843ff40c12SJohn Marino 
2853ff40c12SJohn Marino /**
2863ff40c12SJohn Marino  * aes_gcm_ad - GCM-AD_K(IV, C, A, T)
2873ff40c12SJohn Marino  */
aes_gcm_ad(const u8 * key,size_t key_len,const u8 * iv,size_t iv_len,const u8 * crypt,size_t crypt_len,const u8 * aad,size_t aad_len,const u8 * tag,u8 * plain)2883ff40c12SJohn Marino int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
2893ff40c12SJohn Marino 	       const u8 *crypt, size_t crypt_len,
2903ff40c12SJohn Marino 	       const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain)
2913ff40c12SJohn Marino {
2923ff40c12SJohn Marino 	u8 H[AES_BLOCK_SIZE];
2933ff40c12SJohn Marino 	u8 J0[AES_BLOCK_SIZE];
2943ff40c12SJohn Marino 	u8 S[16], T[16];
2953ff40c12SJohn Marino 	void *aes;
2963ff40c12SJohn Marino 
2973ff40c12SJohn Marino 	aes = aes_gcm_init_hash_subkey(key, key_len, H);
2983ff40c12SJohn Marino 	if (aes == NULL)
2993ff40c12SJohn Marino 		return -1;
3003ff40c12SJohn Marino 
3013ff40c12SJohn Marino 	aes_gcm_prepare_j0(iv, iv_len, H, J0);
3023ff40c12SJohn Marino 
3033ff40c12SJohn Marino 	/* P = GCTR_K(inc_32(J_0), C) */
3043ff40c12SJohn Marino 	aes_gcm_gctr(aes, J0, crypt, crypt_len, plain);
3053ff40c12SJohn Marino 
3063ff40c12SJohn Marino 	aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S);
3073ff40c12SJohn Marino 
3083ff40c12SJohn Marino 	/* T' = MSB_t(GCTR_K(J_0, S)) */
3093ff40c12SJohn Marino 	aes_gctr(aes, J0, S, sizeof(S), T);
3103ff40c12SJohn Marino 
3113ff40c12SJohn Marino 	aes_encrypt_deinit(aes);
3123ff40c12SJohn Marino 
313*a1157835SDaniel Fojt 	if (os_memcmp_const(tag, T, 16) != 0) {
3143ff40c12SJohn Marino 		wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
3153ff40c12SJohn Marino 		return -1;
3163ff40c12SJohn Marino 	}
3173ff40c12SJohn Marino 
3183ff40c12SJohn Marino 	return 0;
3193ff40c12SJohn Marino }
3203ff40c12SJohn Marino 
3213ff40c12SJohn Marino 
aes_gmac(const u8 * key,size_t key_len,const u8 * iv,size_t iv_len,const u8 * aad,size_t aad_len,u8 * tag)3223ff40c12SJohn Marino int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
3233ff40c12SJohn Marino 	     const u8 *aad, size_t aad_len, u8 *tag)
3243ff40c12SJohn Marino {
3253ff40c12SJohn Marino 	return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL,
3263ff40c12SJohn Marino 			  tag);
3273ff40c12SJohn Marino }
328