]>
Commit | Line | Data |
---|---|---|
455bcc0f JM |
1 | /* |
2 | * GCM with GMAC Protocol (GCMP) | |
3 | * Copyright (c) 2012, Jouni Malinen <j@w1.fi> | |
4 | * | |
5 | * This software may be distributed under the terms of the BSD license. | |
6 | * See README for more details. | |
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" | |
1cd7a503 | 14 | #include "crypto/aes_wrap.h" |
455bcc0f JM |
15 | #include "wlantest.h" |
16 | ||
17 | ||
455bcc0f JM |
18 | static void gcmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data, |
19 | u8 *aad, size_t *aad_len, u8 *nonce) | |
20 | { | |
21 | u16 fc, stype, seq; | |
22 | int qos = 0, addr4 = 0; | |
23 | u8 *pos; | |
24 | ||
25 | fc = le_to_host16(hdr->frame_control); | |
26 | stype = WLAN_FC_GET_STYPE(fc); | |
27 | if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == | |
28 | (WLAN_FC_TODS | WLAN_FC_FROMDS)) | |
29 | addr4 = 1; | |
30 | ||
31 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { | |
32 | fc &= ~0x0070; /* Mask subtype bits */ | |
33 | if (stype & 0x08) { | |
34 | const u8 *qc; | |
35 | qos = 1; | |
36 | fc &= ~WLAN_FC_ORDER; | |
37 | qc = (const u8 *) (hdr + 1); | |
38 | if (addr4) | |
39 | qc += ETH_ALEN; | |
40 | } | |
41 | } | |
42 | ||
43 | fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA); | |
455bcc0f JM |
44 | WPA_PUT_LE16(aad, fc); |
45 | pos = aad + 2; | |
46 | os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN); | |
47 | pos += 3 * ETH_ALEN; | |
48 | seq = le_to_host16(hdr->seq_ctrl); | |
49 | seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */ | |
50 | WPA_PUT_LE16(pos, seq); | |
51 | pos += 2; | |
52 | ||
53 | os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2); | |
54 | pos += addr4 * ETH_ALEN; | |
55 | if (qos) { | |
56 | pos[0] &= ~0x70; | |
57 | if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */) | |
58 | pos[0] &= ~0x80; | |
59 | pos++; | |
60 | *pos++ = 0x00; | |
61 | } | |
62 | ||
63 | *aad_len = pos - aad; | |
64 | ||
65 | os_memcpy(nonce, hdr->addr2, ETH_ALEN); | |
66 | nonce[6] = data[7]; /* PN5 */ | |
67 | nonce[7] = data[6]; /* PN4 */ | |
68 | nonce[8] = data[5]; /* PN3 */ | |
69 | nonce[9] = data[4]; /* PN2 */ | |
70 | nonce[10] = data[1]; /* PN1 */ | |
71 | nonce[11] = data[0]; /* PN0 */ | |
72 | } | |
73 | ||
74 | ||
f1732956 | 75 | u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr, |
455bcc0f JM |
76 | const u8 *data, size_t data_len, size_t *decrypted_len) |
77 | { | |
b4a5fcb2 | 78 | u8 aad[30], nonce[12], *plain; |
37c8fe2e | 79 | size_t aad_len, mlen; |
455bcc0f JM |
80 | const u8 *m; |
81 | ||
82 | if (data_len < 8 + 16) | |
83 | return NULL; | |
84 | ||
85 | plain = os_malloc(data_len + AES_BLOCK_SIZE); | |
86 | if (plain == NULL) | |
87 | return NULL; | |
88 | ||
455bcc0f JM |
89 | m = data + 8; |
90 | mlen = data_len - 8 - 16; | |
91 | ||
92 | os_memset(aad, 0, sizeof(aad)); | |
b4a5fcb2 JM |
93 | gcmp_aad_nonce(hdr, data, aad, &aad_len, nonce); |
94 | wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len); | |
455bcc0f JM |
95 | wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce)); |
96 | ||
f1732956 | 97 | if (aes_gcm_ad(tk, tk_len, nonce, sizeof(nonce), m, mlen, aad, aad_len, |
d140db6a | 98 | m + mlen, plain) < 0) { |
455bcc0f | 99 | u16 seq_ctrl = le_to_host16(hdr->seq_ctrl); |
37c8fe2e | 100 | wpa_printf(MSG_INFO, "Invalid GCMP frame: A1=" MACSTR |
455bcc0f JM |
101 | " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u", |
102 | MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), | |
103 | MAC2STR(hdr->addr3), | |
104 | WLAN_GET_SEQ_SEQ(seq_ctrl), | |
105 | WLAN_GET_SEQ_FRAG(seq_ctrl)); | |
455bcc0f JM |
106 | os_free(plain); |
107 | return NULL; | |
108 | } | |
109 | ||
37c8fe2e | 110 | *decrypted_len = mlen; |
455bcc0f JM |
111 | return plain; |
112 | } | |
113 | ||
114 | ||
8b423edb MK |
115 | u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len, |
116 | size_t hdrlen, const u8 *qos, | |
117 | const u8 *pn, int keyid, size_t *encrypted_len) | |
455bcc0f | 118 | { |
b4a5fcb2 | 119 | u8 aad[30], nonce[12], *crypt, *pos; |
37c8fe2e | 120 | size_t aad_len, plen; |
455bcc0f | 121 | struct ieee80211_hdr *hdr; |
455bcc0f JM |
122 | |
123 | if (len < hdrlen || hdrlen < 24) | |
124 | return NULL; | |
125 | plen = len - hdrlen; | |
126 | ||
127 | crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE); | |
128 | if (crypt == NULL) | |
129 | return NULL; | |
130 | ||
131 | os_memcpy(crypt, frame, hdrlen); | |
132 | hdr = (struct ieee80211_hdr *) crypt; | |
455bcc0f JM |
133 | pos = crypt + hdrlen; |
134 | *pos++ = pn[5]; /* PN0 */ | |
135 | *pos++ = pn[4]; /* PN1 */ | |
136 | *pos++ = 0x00; /* Rsvd */ | |
137 | *pos++ = 0x20 | (keyid << 6); | |
138 | *pos++ = pn[3]; /* PN2 */ | |
139 | *pos++ = pn[2]; /* PN3 */ | |
140 | *pos++ = pn[1]; /* PN4 */ | |
141 | *pos++ = pn[0]; /* PN5 */ | |
142 | ||
455bcc0f | 143 | os_memset(aad, 0, sizeof(aad)); |
b4a5fcb2 JM |
144 | gcmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce); |
145 | wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len); | |
455bcc0f JM |
146 | wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce)); |
147 | ||
f1732956 JM |
148 | if (aes_gcm_ae(tk, tk_len, nonce, sizeof(nonce), frame + hdrlen, plen, |
149 | aad, aad_len, pos, pos + plen) < 0) { | |
455bcc0f JM |
150 | os_free(crypt); |
151 | return NULL; | |
152 | } | |
153 | ||
37c8fe2e JM |
154 | wpa_hexdump(MSG_EXCESSIVE, "GCMP MIC", pos + plen, 16); |
155 | wpa_hexdump(MSG_EXCESSIVE, "GCMP encrypted", pos, plen); | |
455bcc0f JM |
156 | |
157 | *encrypted_len = hdrlen + 8 + plen + 16; | |
158 | ||
159 | return crypt; | |
160 | } |