]>
Commit | Line | Data |
---|---|---|
d318c534 JM |
1 | /* |
2 | * CTR with CBC-MAC Protocol (CCMP) | |
3 | * Copyright (c) 2010, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
d318c534 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "common/ieee802_11_defs.h" | |
13 | #include "crypto/aes.h" | |
14 | #include "wlantest.h" | |
15 | ||
16 | ||
17 | static void ccmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data, | |
18 | u8 *aad, size_t *aad_len, u8 *nonce) | |
19 | { | |
20 | u16 fc, stype, seq; | |
21 | int qos = 0, addr4 = 0; | |
22 | u8 *pos; | |
23 | ||
24 | nonce[0] = 0; | |
25 | ||
26 | fc = le_to_host16(hdr->frame_control); | |
27 | stype = WLAN_FC_GET_STYPE(fc); | |
28 | if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == | |
29 | (WLAN_FC_TODS | WLAN_FC_FROMDS)) | |
30 | addr4 = 1; | |
31 | ||
32 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { | |
33 | fc &= ~0x0070; /* Mask subtype bits */ | |
34 | if (stype & 0x08) { | |
35 | const u8 *qc; | |
36 | qos = 1; | |
37 | fc &= ~WLAN_FC_ORDER; | |
38 | qc = (const u8 *) (hdr + 1); | |
39 | if (addr4) | |
40 | qc += ETH_ALEN; | |
41 | nonce[0] = qc[0] & 0x0f; | |
42 | } | |
43 | } else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) | |
44 | nonce[0] |= 0x10; /* Management */ | |
45 | ||
46 | fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA); | |
47 | fc |= WLAN_FC_ISWEP; | |
48 | WPA_PUT_LE16(aad, fc); | |
49 | pos = aad + 2; | |
50 | os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN); | |
51 | pos += 3 * ETH_ALEN; | |
52 | seq = le_to_host16(hdr->seq_ctrl); | |
53 | seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */ | |
54 | WPA_PUT_LE16(pos, seq); | |
55 | pos += 2; | |
56 | ||
57 | os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2); | |
58 | pos += addr4 * ETH_ALEN; | |
59 | if (qos) { | |
ef44a08b | 60 | pos[0] &= ~0x70; |
d318c534 | 61 | if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */) |
ef44a08b | 62 | pos[0] &= ~0x80; |
d318c534 JM |
63 | pos++; |
64 | *pos++ = 0x00; | |
65 | } | |
66 | ||
67 | *aad_len = pos - aad; | |
68 | ||
69 | os_memcpy(nonce + 1, hdr->addr2, ETH_ALEN); | |
70 | nonce[7] = data[7]; /* PN5 */ | |
71 | nonce[8] = data[6]; /* PN4 */ | |
72 | nonce[9] = data[5]; /* PN3 */ | |
73 | nonce[10] = data[4]; /* PN2 */ | |
74 | nonce[11] = data[1]; /* PN1 */ | |
75 | nonce[12] = data[0]; /* PN0 */ | |
76 | } | |
77 | ||
78 | ||
79 | static void xor_aes_block(u8 *dst, const u8 *src) | |
80 | { | |
81 | u32 *d = (u32 *) dst; | |
82 | u32 *s = (u32 *) src; | |
83 | *d++ ^= *s++; | |
84 | *d++ ^= *s++; | |
85 | *d++ ^= *s++; | |
86 | *d++ ^= *s++; | |
87 | } | |
88 | ||
89 | ||
90 | u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, | |
91 | const u8 *data, size_t data_len, size_t *decrypted_len) | |
92 | { | |
93 | u8 aad[2 + 30], nonce[13]; | |
94 | size_t aad_len; | |
95 | u8 b[AES_BLOCK_SIZE], x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; | |
96 | void *aes; | |
97 | const u8 *m, *mpos, *mic; | |
98 | size_t mlen, last; | |
99 | int i; | |
100 | u8 *plain, *ppos; | |
101 | u8 t[8]; | |
102 | ||
103 | if (data_len < 8 + 8) | |
104 | return NULL; | |
105 | ||
71a7e936 | 106 | plain = os_malloc(data_len + AES_BLOCK_SIZE); |
d318c534 JM |
107 | if (plain == NULL) |
108 | return NULL; | |
109 | ||
110 | aes = aes_encrypt_init(tk, 16); | |
111 | if (aes == NULL) { | |
112 | os_free(plain); | |
113 | return NULL; | |
114 | } | |
115 | ||
116 | m = data + 8; | |
117 | mlen = data_len - 8 - 8; | |
118 | last = mlen % AES_BLOCK_SIZE; | |
119 | ||
120 | os_memset(aad, 0, sizeof(aad)); | |
121 | ccmp_aad_nonce(hdr, data, &aad[2], &aad_len, nonce); | |
122 | WPA_PUT_BE16(aad, aad_len); | |
123 | wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", &aad[2], aad_len); | |
124 | wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13); | |
125 | ||
126 | /* CCM: M=8 L=2, Adata=1, M' = (M-2)/2 = 3, L' = L-1 = 1 */ | |
127 | ||
128 | /* A_i = Flags | Nonce N | Counter i */ | |
129 | a[0] = 0x01; /* Flags = L' */ | |
130 | os_memcpy(&a[1], nonce, 13); | |
131 | ||
132 | /* Decryption */ | |
133 | ||
134 | mic = data + data_len - 8; | |
135 | wpa_hexdump(MSG_EXCESSIVE, "CCMP U", mic, 8); | |
136 | /* U = T XOR S_0; S_0 = E(K, A_0) */ | |
137 | WPA_PUT_BE16(&a[14], 0); | |
138 | aes_encrypt(aes, a, x); | |
139 | for (i = 0; i < 8; i++) | |
140 | t[i] = mic[i] ^ x[i]; | |
141 | wpa_hexdump(MSG_EXCESSIVE, "CCMP T", t, 8); | |
142 | ||
143 | /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ | |
144 | ppos = plain; | |
145 | mpos = m; | |
146 | for (i = 1; i <= mlen / AES_BLOCK_SIZE; i++) { | |
147 | WPA_PUT_BE16(&a[14], i); | |
148 | /* S_i = E(K, A_i) */ | |
149 | aes_encrypt(aes, a, ppos); | |
150 | xor_aes_block(ppos, mpos); | |
151 | ppos += AES_BLOCK_SIZE; | |
152 | mpos += AES_BLOCK_SIZE; | |
153 | } | |
154 | if (last) { | |
155 | WPA_PUT_BE16(&a[14], i); | |
156 | aes_encrypt(aes, a, ppos); | |
157 | /* XOR zero-padded last block */ | |
158 | for (i = 0; i < last; i++) | |
159 | *ppos++ ^= *mpos++; | |
160 | } | |
161 | wpa_hexdump(MSG_EXCESSIVE, "CCMP decrypted", plain, mlen); | |
162 | ||
163 | /* Authentication */ | |
164 | /* B_0: Flags | Nonce N | l(m) */ | |
165 | b[0] = 0x40 /* Adata */ | (3 /* M' */ << 3) | 1 /* L' */; | |
166 | os_memcpy(&b[1], nonce, 13); | |
167 | WPA_PUT_BE16(&b[14], mlen); | |
168 | ||
169 | wpa_hexdump(MSG_EXCESSIVE, "CCMP B_0", b, AES_BLOCK_SIZE); | |
170 | aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ | |
171 | ||
172 | wpa_hexdump(MSG_EXCESSIVE, "CCMP B_1", aad, AES_BLOCK_SIZE); | |
173 | xor_aes_block(aad, x); | |
174 | aes_encrypt(aes, aad, x); /* X_2 = E(K, X_1 XOR B_1) */ | |
175 | ||
176 | wpa_hexdump(MSG_EXCESSIVE, "CCMP B_2", &aad[AES_BLOCK_SIZE], | |
177 | AES_BLOCK_SIZE); | |
178 | xor_aes_block(&aad[AES_BLOCK_SIZE], x); | |
179 | aes_encrypt(aes, &aad[AES_BLOCK_SIZE], x); /* X_3 = E(K, X_2 XOR B_2) | |
180 | */ | |
181 | ||
182 | ppos = plain; | |
183 | for (i = 0; i < mlen / AES_BLOCK_SIZE; i++) { | |
184 | /* X_i+1 = E(K, X_i XOR B_i) */ | |
185 | xor_aes_block(x, ppos); | |
186 | ppos += AES_BLOCK_SIZE; | |
187 | aes_encrypt(aes, x, x); | |
188 | } | |
189 | if (last) { | |
190 | /* XOR zero-padded last block */ | |
191 | for (i = 0; i < last; i++) | |
192 | x[i] ^= *ppos++; | |
193 | aes_encrypt(aes, x, x); | |
194 | } | |
195 | ||
196 | aes_encrypt_deinit(aes); | |
197 | ||
198 | if (os_memcmp(x, t, 8) != 0) { | |
3558c41e JM |
199 | u16 seq_ctrl = le_to_host16(hdr->seq_ctrl); |
200 | wpa_printf(MSG_INFO, "Invalid CCMP MIC in frame: A1=" MACSTR | |
201 | " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u", | |
202 | MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), | |
203 | MAC2STR(hdr->addr3), | |
204 | WLAN_GET_SEQ_SEQ(seq_ctrl), | |
205 | WLAN_GET_SEQ_FRAG(seq_ctrl)); | |
206 | wpa_hexdump(MSG_DEBUG, "CCMP decrypted", plain, mlen); | |
d318c534 JM |
207 | os_free(plain); |
208 | return NULL; | |
209 | } | |
210 | ||
211 | *decrypted_len = mlen; | |
212 | return plain; | |
213 | } | |
2edd5c23 JM |
214 | |
215 | ||
216 | void ccmp_get_pn(u8 *pn, const u8 *data) | |
217 | { | |
218 | pn[0] = data[7]; /* PN5 */ | |
219 | pn[1] = data[6]; /* PN4 */ | |
220 | pn[2] = data[5]; /* PN3 */ | |
221 | pn[3] = data[4]; /* PN2 */ | |
222 | pn[4] = data[1]; /* PN1 */ | |
223 | pn[5] = data[0]; /* PN0 */ | |
224 | } | |
571ab37b JM |
225 | |
226 | ||
227 | u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos, | |
228 | u8 *pn, int keyid, size_t *encrypted_len) | |
229 | { | |
230 | u8 aad[2 + 30], nonce[13]; | |
231 | size_t aad_len; | |
232 | u8 b[AES_BLOCK_SIZE], x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; | |
233 | void *aes; | |
234 | u8 *crypt, *pos, *ppos, *mpos; | |
235 | size_t plen, last; | |
236 | struct ieee80211_hdr *hdr; | |
237 | int i; | |
238 | ||
239 | if (len < hdrlen || hdrlen < 24) | |
240 | return NULL; | |
241 | plen = len - hdrlen; | |
242 | last = plen % AES_BLOCK_SIZE; | |
243 | ||
71a7e936 | 244 | crypt = os_malloc(hdrlen + 8 + plen + 8 + AES_BLOCK_SIZE); |
571ab37b JM |
245 | if (crypt == NULL) |
246 | return NULL; | |
247 | ||
248 | os_memcpy(crypt, frame, hdrlen); | |
249 | hdr = (struct ieee80211_hdr *) crypt; | |
250 | hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); | |
251 | pos = crypt + hdrlen; | |
252 | *pos++ = pn[5]; /* PN0 */ | |
253 | *pos++ = pn[4]; /* PN1 */ | |
81d59891 | 254 | *pos++ = 0x00; /* Rsvd */ |
571ab37b JM |
255 | *pos++ = 0x20 | (keyid << 6); |
256 | *pos++ = pn[3]; /* PN2 */ | |
257 | *pos++ = pn[2]; /* PN3 */ | |
258 | *pos++ = pn[1]; /* PN4 */ | |
259 | *pos++ = pn[0]; /* PN5 */ | |
260 | ||
261 | aes = aes_encrypt_init(tk, 16); | |
262 | if (aes == NULL) { | |
263 | os_free(crypt); | |
264 | return NULL; | |
265 | } | |
266 | ||
267 | os_memset(aad, 0, sizeof(aad)); | |
268 | ccmp_aad_nonce(hdr, crypt + hdrlen, &aad[2], &aad_len, nonce); | |
269 | WPA_PUT_BE16(aad, aad_len); | |
270 | wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", &aad[2], aad_len); | |
271 | wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13); | |
272 | ||
273 | /* Authentication */ | |
274 | /* B_0: Flags | Nonce N | l(m) */ | |
275 | b[0] = 0x40 /* Adata */ | (3 /* M' */ << 3) | 1 /* L' */; | |
276 | os_memcpy(&b[1], nonce, 13); | |
277 | WPA_PUT_BE16(&b[14], plen); | |
278 | ||
279 | wpa_hexdump(MSG_EXCESSIVE, "CCMP B_0", b, AES_BLOCK_SIZE); | |
280 | aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ | |
281 | ||
282 | wpa_hexdump(MSG_EXCESSIVE, "CCMP B_1", aad, AES_BLOCK_SIZE); | |
283 | xor_aes_block(aad, x); | |
284 | aes_encrypt(aes, aad, x); /* X_2 = E(K, X_1 XOR B_1) */ | |
285 | ||
286 | wpa_hexdump(MSG_EXCESSIVE, "CCMP B_2", &aad[AES_BLOCK_SIZE], | |
287 | AES_BLOCK_SIZE); | |
288 | xor_aes_block(&aad[AES_BLOCK_SIZE], x); | |
289 | aes_encrypt(aes, &aad[AES_BLOCK_SIZE], x); /* X_3 = E(K, X_2 XOR B_2) | |
290 | */ | |
291 | ||
292 | ppos = frame + hdrlen; | |
293 | for (i = 0; i < plen / AES_BLOCK_SIZE; i++) { | |
294 | /* X_i+1 = E(K, X_i XOR B_i) */ | |
295 | xor_aes_block(x, ppos); | |
296 | ppos += AES_BLOCK_SIZE; | |
297 | aes_encrypt(aes, x, x); | |
298 | } | |
299 | if (last) { | |
300 | /* XOR zero-padded last block */ | |
301 | for (i = 0; i < last; i++) | |
302 | x[i] ^= *ppos++; | |
303 | aes_encrypt(aes, x, x); | |
304 | } | |
305 | ||
306 | /* Encryption */ | |
307 | ||
308 | /* CCM: M=8 L=2, Adata=1, M' = (M-2)/2 = 3, L' = L-1 = 1 */ | |
309 | ||
310 | /* A_i = Flags | Nonce N | Counter i */ | |
311 | a[0] = 0x01; /* Flags = L' */ | |
312 | os_memcpy(&a[1], nonce, 13); | |
313 | ||
314 | ppos = crypt + hdrlen + 8; | |
315 | ||
316 | /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ | |
317 | mpos = frame + hdrlen; | |
318 | for (i = 1; i <= plen / AES_BLOCK_SIZE; i++) { | |
319 | WPA_PUT_BE16(&a[14], i); | |
320 | /* S_i = E(K, A_i) */ | |
321 | aes_encrypt(aes, a, ppos); | |
322 | xor_aes_block(ppos, mpos); | |
323 | ppos += AES_BLOCK_SIZE; | |
324 | mpos += AES_BLOCK_SIZE; | |
325 | } | |
326 | if (last) { | |
327 | WPA_PUT_BE16(&a[14], i); | |
328 | aes_encrypt(aes, a, ppos); | |
329 | /* XOR zero-padded last block */ | |
330 | for (i = 0; i < last; i++) | |
331 | *ppos++ ^= *mpos++; | |
332 | } | |
333 | ||
334 | wpa_hexdump(MSG_EXCESSIVE, "CCMP T", x, 8); | |
335 | /* U = T XOR S_0; S_0 = E(K, A_0) */ | |
336 | WPA_PUT_BE16(&a[14], 0); | |
337 | aes_encrypt(aes, a, b); | |
338 | for (i = 0; i < 8; i++) | |
339 | ppos[i] = x[i] ^ b[i]; | |
340 | wpa_hexdump(MSG_EXCESSIVE, "CCMP U", ppos, 8); | |
341 | ||
342 | wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen + 8, plen); | |
343 | ||
344 | aes_encrypt_deinit(aes); | |
345 | ||
346 | *encrypted_len = hdrlen + 8 + plen + 8; | |
347 | ||
348 | return crypt; | |
349 | } |