Dmitry Shmidt | 9bce59c | 2012-09-11 15:06:38 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Galois/Counter Mode (GCM) and GMAC with AES |
| 3 | * |
| 4 | * Copyright (c) 2012, Jouni Malinen <j@w1.fi> |
| 5 | * |
| 6 | * This software may be distributed under the terms of the BSD license. |
| 7 | * See README for more details. |
| 8 | */ |
| 9 | |
| 10 | #include "includes.h" |
| 11 | |
| 12 | #include "common.h" |
| 13 | #include "aes.h" |
| 14 | #include "aes_wrap.h" |
| 15 | |
| 16 | static void inc32(u8 *block) |
| 17 | { |
| 18 | u32 val; |
| 19 | val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4); |
| 20 | val++; |
| 21 | WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val); |
| 22 | } |
| 23 | |
| 24 | |
| 25 | static void xor_block(u8 *dst, const u8 *src) |
| 26 | { |
| 27 | u32 *d = (u32 *) dst; |
| 28 | u32 *s = (u32 *) src; |
| 29 | *d++ ^= *s++; |
| 30 | *d++ ^= *s++; |
| 31 | *d++ ^= *s++; |
| 32 | *d++ ^= *s++; |
| 33 | } |
| 34 | |
| 35 | |
| 36 | static void shift_right_block(u8 *v) |
| 37 | { |
| 38 | u32 val; |
| 39 | |
| 40 | val = WPA_GET_BE32(v + 12); |
| 41 | val >>= 1; |
| 42 | if (v[11] & 0x01) |
| 43 | val |= 0x80000000; |
| 44 | WPA_PUT_BE32(v + 12, val); |
| 45 | |
| 46 | val = WPA_GET_BE32(v + 8); |
| 47 | val >>= 1; |
| 48 | if (v[7] & 0x01) |
| 49 | val |= 0x80000000; |
| 50 | WPA_PUT_BE32(v + 8, val); |
| 51 | |
| 52 | val = WPA_GET_BE32(v + 4); |
| 53 | val >>= 1; |
| 54 | if (v[3] & 0x01) |
| 55 | val |= 0x80000000; |
| 56 | WPA_PUT_BE32(v + 4, val); |
| 57 | |
| 58 | val = WPA_GET_BE32(v); |
| 59 | val >>= 1; |
| 60 | WPA_PUT_BE32(v, val); |
| 61 | } |
| 62 | |
| 63 | |
| 64 | /* Multiplication in GF(2^128) */ |
| 65 | static void gf_mult(const u8 *x, const u8 *y, u8 *z) |
| 66 | { |
| 67 | u8 v[16]; |
| 68 | int i, j; |
| 69 | |
| 70 | os_memset(z, 0, 16); /* Z_0 = 0^128 */ |
| 71 | os_memcpy(v, y, 16); /* V_0 = Y */ |
| 72 | |
| 73 | for (i = 0; i < 16; i++) { |
| 74 | for (j = 0; j < 8; j++) { |
| 75 | if (x[i] & BIT(7 - j)) { |
| 76 | /* Z_(i + 1) = Z_i XOR V_i */ |
| 77 | xor_block(z, v); |
| 78 | } else { |
| 79 | /* Z_(i + 1) = Z_i */ |
| 80 | } |
| 81 | |
| 82 | if (v[15] & 0x01) { |
| 83 | /* V_(i + 1) = (V_i >> 1) XOR R */ |
| 84 | shift_right_block(v); |
| 85 | /* R = 11100001 || 0^120 */ |
| 86 | v[0] ^= 0xe1; |
| 87 | } else { |
| 88 | /* V_(i + 1) = V_i >> 1 */ |
| 89 | shift_right_block(v); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | |
| 96 | static void ghash_start(u8 *y) |
| 97 | { |
| 98 | /* Y_0 = 0^128 */ |
| 99 | os_memset(y, 0, 16); |
| 100 | } |
| 101 | |
| 102 | |
| 103 | static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y) |
| 104 | { |
| 105 | size_t m, i; |
| 106 | const u8 *xpos = x; |
| 107 | u8 tmp[16]; |
| 108 | |
| 109 | m = xlen / 16; |
| 110 | |
| 111 | for (i = 0; i < m; i++) { |
| 112 | /* Y_i = (Y^(i-1) XOR X_i) dot H */ |
| 113 | xor_block(y, xpos); |
| 114 | xpos += 16; |
| 115 | |
| 116 | /* dot operation: |
| 117 | * multiplication operation for binary Galois (finite) field of |
| 118 | * 2^128 elements */ |
| 119 | gf_mult(y, h, tmp); |
| 120 | os_memcpy(y, tmp, 16); |
| 121 | } |
| 122 | |
| 123 | if (x + xlen > xpos) { |
| 124 | /* Add zero padded last block */ |
| 125 | size_t last = x + xlen - xpos; |
| 126 | os_memcpy(tmp, xpos, last); |
| 127 | os_memset(tmp + last, 0, sizeof(tmp) - last); |
| 128 | |
| 129 | /* Y_i = (Y^(i-1) XOR X_i) dot H */ |
| 130 | xor_block(y, tmp); |
| 131 | |
| 132 | /* dot operation: |
| 133 | * multiplication operation for binary Galois (finite) field of |
| 134 | * 2^128 elements */ |
| 135 | gf_mult(y, h, tmp); |
| 136 | os_memcpy(y, tmp, 16); |
| 137 | } |
| 138 | |
| 139 | /* Return Y_m */ |
| 140 | } |
| 141 | |
| 142 | |
| 143 | static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y) |
| 144 | { |
| 145 | size_t i, n, last; |
| 146 | u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; |
| 147 | const u8 *xpos = x; |
| 148 | u8 *ypos = y; |
| 149 | |
| 150 | if (xlen == 0) |
| 151 | return; |
| 152 | |
| 153 | n = xlen / 16; |
| 154 | |
| 155 | os_memcpy(cb, icb, AES_BLOCK_SIZE); |
| 156 | /* Full blocks */ |
| 157 | for (i = 0; i < n; i++) { |
| 158 | aes_encrypt(aes, cb, ypos); |
| 159 | xor_block(ypos, xpos); |
| 160 | xpos += AES_BLOCK_SIZE; |
| 161 | ypos += AES_BLOCK_SIZE; |
| 162 | inc32(cb); |
| 163 | } |
| 164 | |
| 165 | last = x + xlen - xpos; |
| 166 | if (last) { |
| 167 | /* Last, partial block */ |
| 168 | aes_encrypt(aes, cb, tmp); |
| 169 | for (i = 0; i < last; i++) |
| 170 | *ypos++ = *xpos++ ^ tmp[i]; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | |
| 175 | static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H) |
| 176 | { |
| 177 | void *aes; |
| 178 | |
| 179 | aes = aes_encrypt_init(key, key_len); |
| 180 | if (aes == NULL) |
| 181 | return NULL; |
| 182 | |
| 183 | /* Generate hash subkey H = AES_K(0^128) */ |
| 184 | os_memset(H, 0, AES_BLOCK_SIZE); |
| 185 | aes_encrypt(aes, H, H); |
| 186 | wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH", |
| 187 | H, AES_BLOCK_SIZE); |
| 188 | return aes; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0) |
| 193 | { |
| 194 | u8 len_buf[16]; |
| 195 | |
| 196 | if (iv_len == 12) { |
| 197 | /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */ |
| 198 | os_memcpy(J0, iv, iv_len); |
| 199 | os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len); |
| 200 | J0[AES_BLOCK_SIZE - 1] = 0x01; |
| 201 | } else { |
| 202 | /* |
| 203 | * s = 128 * ceil(len(IV)/128) - len(IV) |
| 204 | * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64) |
| 205 | */ |
| 206 | ghash_start(J0); |
| 207 | ghash(H, iv, iv_len, J0); |
| 208 | WPA_PUT_BE64(len_buf, 0); |
| 209 | WPA_PUT_BE64(len_buf + 8, iv_len * 8); |
| 210 | ghash(H, len_buf, sizeof(len_buf), J0); |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | |
| 215 | static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len, |
| 216 | u8 *out) |
| 217 | { |
| 218 | u8 J0inc[AES_BLOCK_SIZE]; |
| 219 | |
| 220 | if (len == 0) |
| 221 | return; |
| 222 | |
| 223 | os_memcpy(J0inc, J0, AES_BLOCK_SIZE); |
| 224 | inc32(J0inc); |
| 225 | aes_gctr(aes, J0inc, in, len, out); |
| 226 | } |
| 227 | |
| 228 | |
| 229 | static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len, |
| 230 | const u8 *crypt, size_t crypt_len, u8 *S) |
| 231 | { |
| 232 | u8 len_buf[16]; |
| 233 | |
| 234 | /* |
| 235 | * u = 128 * ceil[len(C)/128] - len(C) |
| 236 | * v = 128 * ceil[len(A)/128] - len(A) |
| 237 | * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64) |
| 238 | * (i.e., zero padded to block size A || C and lengths of each in bits) |
| 239 | */ |
| 240 | ghash_start(S); |
| 241 | ghash(H, aad, aad_len, S); |
| 242 | ghash(H, crypt, crypt_len, S); |
| 243 | WPA_PUT_BE64(len_buf, aad_len * 8); |
| 244 | WPA_PUT_BE64(len_buf + 8, crypt_len * 8); |
| 245 | ghash(H, len_buf, sizeof(len_buf), S); |
| 246 | |
| 247 | wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16); |
| 248 | } |
| 249 | |
| 250 | |
| 251 | /** |
| 252 | * aes_gcm_ae - GCM-AE_K(IV, P, A) |
| 253 | */ |
| 254 | int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, |
| 255 | const u8 *plain, size_t plain_len, |
| 256 | const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag) |
| 257 | { |
| 258 | u8 H[AES_BLOCK_SIZE]; |
| 259 | u8 J0[AES_BLOCK_SIZE]; |
| 260 | u8 S[16]; |
| 261 | void *aes; |
| 262 | |
| 263 | aes = aes_gcm_init_hash_subkey(key, key_len, H); |
| 264 | if (aes == NULL) |
| 265 | return -1; |
| 266 | |
| 267 | aes_gcm_prepare_j0(iv, iv_len, H, J0); |
| 268 | |
| 269 | /* C = GCTR_K(inc_32(J_0), P) */ |
| 270 | aes_gcm_gctr(aes, J0, plain, plain_len, crypt); |
| 271 | |
| 272 | aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S); |
| 273 | |
| 274 | /* T = MSB_t(GCTR_K(J_0, S)) */ |
| 275 | aes_gctr(aes, J0, S, sizeof(S), tag); |
| 276 | |
| 277 | /* Return (C, T) */ |
| 278 | |
| 279 | aes_encrypt_deinit(aes); |
| 280 | |
| 281 | return 0; |
| 282 | } |
| 283 | |
| 284 | |
| 285 | /** |
| 286 | * aes_gcm_ad - GCM-AD_K(IV, C, A, T) |
| 287 | */ |
| 288 | int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, |
| 289 | const u8 *crypt, size_t crypt_len, |
| 290 | const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain) |
| 291 | { |
| 292 | u8 H[AES_BLOCK_SIZE]; |
| 293 | u8 J0[AES_BLOCK_SIZE]; |
| 294 | u8 S[16], T[16]; |
| 295 | void *aes; |
| 296 | |
| 297 | aes = aes_gcm_init_hash_subkey(key, key_len, H); |
| 298 | if (aes == NULL) |
| 299 | return -1; |
| 300 | |
| 301 | aes_gcm_prepare_j0(iv, iv_len, H, J0); |
| 302 | |
| 303 | /* P = GCTR_K(inc_32(J_0), C) */ |
| 304 | aes_gcm_gctr(aes, J0, crypt, crypt_len, plain); |
| 305 | |
| 306 | aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S); |
| 307 | |
| 308 | /* T' = MSB_t(GCTR_K(J_0, S)) */ |
| 309 | aes_gctr(aes, J0, S, sizeof(S), T); |
| 310 | |
| 311 | aes_encrypt_deinit(aes); |
| 312 | |
Dmitry Shmidt | c281702 | 2014-07-02 10:32:10 -0700 | [diff] [blame] | 313 | if (os_memcmp_const(tag, T, 16) != 0) { |
Dmitry Shmidt | 9bce59c | 2012-09-11 15:06:38 -0700 | [diff] [blame] | 314 | wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch"); |
| 315 | return -1; |
| 316 | } |
| 317 | |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | |
| 322 | int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, |
| 323 | const u8 *aad, size_t aad_len, u8 *tag) |
| 324 | { |
| 325 | return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL, |
| 326 | tag); |
| 327 | } |