1/* 2 * Counter with CBC-MAC (CCM) with AES 3 * 4 * Copyright (c) 2010-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 17static void xor_aes_block(u8 *dst, const u8 *src) 18{ 19 u32 *d = (u32 *) dst; 20 u32 *s = (u32 *) src; 21 *d++ ^= *s++; 22 *d++ ^= *s++; 23 *d++ ^= *s++; 24 *d++ ^= *s++; 25} 26 27 28static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce, 29 const u8 *aad, size_t aad_len, size_t plain_len, 30 u8 *x) 31{ 32 u8 aad_buf[2 * AES_BLOCK_SIZE]; 33 u8 b[AES_BLOCK_SIZE]; 34 35 /* Authentication */ 36 /* B_0: Flags | Nonce N | l(m) */ 37 b[0] = aad_len ? 0x40 : 0 /* Adata */; 38 b[0] |= (((M - 2) / 2) /* M' */ << 3); 39 b[0] |= (L - 1) /* L' */; 40 os_memcpy(&b[1], nonce, 15 - L); 41 WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); 42 43 wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE); 44 aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ 45 46 if (!aad_len) 47 return; 48 49 WPA_PUT_BE16(aad_buf, aad_len); 50 os_memcpy(aad_buf + 2, aad, aad_len); 51 os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); 52 53 xor_aes_block(aad_buf, x); 54 aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ 55 56 if (aad_len > AES_BLOCK_SIZE - 2) { 57 xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); 58 /* X_3 = E(K, X_2 XOR B_2) */ 59 aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x); 60 } 61} 62 63 64static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x) 65{ 66 size_t last = len % AES_BLOCK_SIZE; 67 size_t i; 68 69 for (i = 0; i < len / AES_BLOCK_SIZE; i++) { 70 /* X_i+1 = E(K, X_i XOR B_i) */ 71 xor_aes_block(x, data); 72 data += AES_BLOCK_SIZE; 73 aes_encrypt(aes, x, x); 74 } 75 if (last) { 76 /* XOR zero-padded last block */ 77 for (i = 0; i < last; i++) 78 x[i] ^= *data++; 79 aes_encrypt(aes, x, x); 80 } 81} 82 83 84static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a) 85{ 86 /* A_i = Flags | Nonce N | Counter i */ 87 a[0] = L - 1; /* Flags = L' */ 88 os_memcpy(&a[1], nonce, 15 - L); 89} 90 91 92static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out, 93 u8 *a) 94{ 95 size_t last = len % AES_BLOCK_SIZE; 96 size_t i; 97 98 /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ 99 for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { 100 WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); 101 /* S_i = E(K, A_i) */ 102 aes_encrypt(aes, a, out); 103 xor_aes_block(out, in); 104 out += AES_BLOCK_SIZE; 105 in += AES_BLOCK_SIZE; 106 } 107 if (last) { 108 WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); 109 aes_encrypt(aes, a, out); 110 /* XOR zero-padded last block */ 111 for (i = 0; i < last; i++) 112 *out++ ^= *in++; 113 } 114} 115 116 117static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth) 118{ 119 size_t i; 120 u8 tmp[AES_BLOCK_SIZE]; 121 122 wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M); 123 /* U = T XOR S_0; S_0 = E(K, A_0) */ 124 WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); 125 aes_encrypt(aes, a, tmp); 126 for (i = 0; i < M; i++) 127 auth[i] = x[i] ^ tmp[i]; 128 wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); 129} 130 131 132static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t) 133{ 134 size_t i; 135 u8 tmp[AES_BLOCK_SIZE]; 136 137 wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); 138 /* U = T XOR S_0; S_0 = E(K, A_0) */ 139 WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); 140 aes_encrypt(aes, a, tmp); 141 for (i = 0; i < M; i++) 142 t[i] = auth[i] ^ tmp[i]; 143 wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M); 144} 145 146 147/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ 148int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, 149 size_t M, const u8 *plain, size_t plain_len, 150 const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth) 151{ 152 const size_t L = 2; 153 void *aes; 154 u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; 155 156 if (aad_len > 30 || M > AES_BLOCK_SIZE) 157 return -1; 158 159 aes = aes_encrypt_init(key, key_len); 160 if (aes == NULL) 161 return -1; 162 163 aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x); 164 aes_ccm_auth(aes, plain, plain_len, x); 165 166 /* Encryption */ 167 aes_ccm_encr_start(L, nonce, a); 168 aes_ccm_encr(aes, L, plain, plain_len, crypt, a); 169 aes_ccm_encr_auth(aes, M, x, a, auth); 170 171 aes_encrypt_deinit(aes); 172 173 return 0; 174} 175 176 177/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ 178int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, 179 size_t M, const u8 *crypt, size_t crypt_len, 180 const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain) 181{ 182 const size_t L = 2; 183 void *aes; 184 u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; 185 u8 t[AES_BLOCK_SIZE]; 186 187 if (aad_len > 30 || M > AES_BLOCK_SIZE) 188 return -1; 189 190 aes = aes_encrypt_init(key, key_len); 191 if (aes == NULL) 192 return -1; 193 194 /* Decryption */ 195 aes_ccm_encr_start(L, nonce, a); 196 aes_ccm_decr_auth(aes, M, a, auth, t); 197 198 /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ 199 aes_ccm_encr(aes, L, crypt, crypt_len, plain, a); 200 201 aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x); 202 aes_ccm_auth(aes, plain, crypt_len, x); 203 204 aes_encrypt_deinit(aes); 205 206 if (os_memcmp(x, t, M) != 0) { 207 wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch"); 208 return -1; 209 } 210 211 return 0; 212} 213