1 /*
2 * EAP server/peer: EAP-GPSK shared routines
3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eap_defs.h"
19 #include "aes_wrap.h"
20 #include "crypto.h"
21 #ifdef EAP_GPSK_SHA256
22 #include "sha256.h"
23 #endif /* EAP_GPSK_SHA256 */
24 #include "eap_gpsk_common.h"
25
26
27 /**
28 * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
29 * @vendor: CSuite/Vendor
30 * @specifier: CSuite/Specifier
31 * Returns: 1 if ciphersuite is support, or 0 if not
32 */
eap_gpsk_supported_ciphersuite(int vendor,int specifier)33 int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
34 {
35 if (vendor == EAP_GPSK_VENDOR_IETF &&
36 specifier == EAP_GPSK_CIPHER_AES)
37 return 1;
38 #ifdef EAP_GPSK_SHA256
39 if (vendor == EAP_GPSK_VENDOR_IETF &&
40 specifier == EAP_GPSK_CIPHER_SHA256)
41 return 1;
42 #endif /* EAP_GPSK_SHA256 */
43 return 0;
44 }
45
46
eap_gpsk_gkdf_cmac(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)47 static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
48 const u8 *data /* Z */, size_t data_len,
49 u8 *buf, size_t len /* X */)
50 {
51 u8 *opos;
52 size_t i, n, hashlen, left, clen;
53 u8 ibuf[2], hash[16];
54 const u8 *addr[2];
55 size_t vlen[2];
56
57 hashlen = sizeof(hash);
58 /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
59 addr[0] = ibuf;
60 vlen[0] = sizeof(ibuf);
61 addr[1] = data;
62 vlen[1] = data_len;
63
64 opos = buf;
65 left = len;
66 n = (len + hashlen - 1) / hashlen;
67 for (i = 1; i <= n; i++) {
68 WPA_PUT_BE16(ibuf, i);
69 omac1_aes_128_vector(psk, 2, addr, vlen, hash);
70 clen = left > hashlen ? hashlen : left;
71 os_memcpy(opos, hash, clen);
72 opos += clen;
73 left -= clen;
74 }
75
76 return 0;
77 }
78
79
80 #ifdef EAP_GPSK_SHA256
eap_gpsk_gkdf_sha256(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)81 static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
82 const u8 *data /* Z */, size_t data_len,
83 u8 *buf, size_t len /* X */)
84 {
85 u8 *opos;
86 size_t i, n, hashlen, left, clen;
87 u8 ibuf[2], hash[SHA256_MAC_LEN];
88 const u8 *addr[2];
89 size_t vlen[2];
90
91 hashlen = SHA256_MAC_LEN;
92 /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
93 addr[0] = ibuf;
94 vlen[0] = sizeof(ibuf);
95 addr[1] = data;
96 vlen[1] = data_len;
97
98 opos = buf;
99 left = len;
100 n = (len + hashlen - 1) / hashlen;
101 for (i = 1; i <= n; i++) {
102 WPA_PUT_BE16(ibuf, i);
103 hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
104 clen = left > hashlen ? hashlen : left;
105 os_memcpy(opos, hash, clen);
106 opos += clen;
107 left -= clen;
108 }
109
110 return 0;
111 }
112 #endif /* EAP_GPSK_SHA256 */
113
114
eap_gpsk_derive_keys_helper(u32 csuite_specifier,u8 * kdf_out,size_t kdf_out_len,const u8 * psk,size_t psk_len,const u8 * seed,size_t seed_len,u8 * msk,u8 * emsk,u8 * sk,size_t sk_len,u8 * pk,size_t pk_len)115 static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
116 u8 *kdf_out, size_t kdf_out_len,
117 const u8 *psk, size_t psk_len,
118 const u8 *seed, size_t seed_len,
119 u8 *msk, u8 *emsk,
120 u8 *sk, size_t sk_len,
121 u8 *pk, size_t pk_len)
122 {
123 u8 mk[32], *pos, *data;
124 size_t data_len, mk_len;
125 int (*gkdf)(const u8 *psk, const u8 *data, size_t data_len,
126 u8 *buf, size_t len);
127
128 gkdf = NULL;
129 switch (csuite_specifier) {
130 case EAP_GPSK_CIPHER_AES:
131 gkdf = eap_gpsk_gkdf_cmac;
132 mk_len = 16;
133 break;
134 #ifdef EAP_GPSK_SHA256
135 case EAP_GPSK_CIPHER_SHA256:
136 gkdf = eap_gpsk_gkdf_sha256;
137 mk_len = SHA256_MAC_LEN;
138 break;
139 #endif /* EAP_GPSK_SHA256 */
140 default:
141 return -1;
142 }
143
144 if (psk_len < mk_len)
145 return -1;
146
147 data_len = 2 + psk_len + 6 + seed_len;
148 data = os_malloc(data_len);
149 if (data == NULL)
150 return -1;
151 pos = data;
152 WPA_PUT_BE16(pos, psk_len);
153 pos += 2;
154 os_memcpy(pos, psk, psk_len);
155 pos += psk_len;
156 WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
157 pos += 4;
158 WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
159 pos += 2;
160 os_memcpy(pos, seed, seed_len); /* inputString */
161 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
162 data, data_len);
163
164 if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
165 os_free(data);
166 return -1;
167 }
168 os_free(data);
169 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
170
171 if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
172 return -1;
173
174 pos = kdf_out;
175 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
176 os_memcpy(msk, pos, EAP_MSK_LEN);
177 pos += EAP_MSK_LEN;
178
179 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
180 os_memcpy(emsk, pos, EAP_EMSK_LEN);
181 pos += EAP_EMSK_LEN;
182
183 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
184 os_memcpy(sk, pos, sk_len);
185 pos += sk_len;
186
187 if (pk) {
188 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
189 os_memcpy(pk, pos, pk_len);
190 }
191
192 return 0;
193 }
194
195
eap_gpsk_derive_keys_aes(const u8 * psk,size_t psk_len,const u8 * seed,size_t seed_len,u8 * msk,u8 * emsk,u8 * sk,size_t * sk_len,u8 * pk,size_t * pk_len)196 static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
197 const u8 *seed, size_t seed_len,
198 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
199 u8 *pk, size_t *pk_len)
200 {
201 #define EAP_GPSK_SK_LEN_AES 16
202 #define EAP_GPSK_PK_LEN_AES 16
203 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
204 EAP_GPSK_PK_LEN_AES];
205
206 /*
207 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
208 * (= seed)
209 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
210 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
211 * MSK = GKDF-160 (MK, inputString)[0..63]
212 * EMSK = GKDF-160 (MK, inputString)[64..127]
213 * SK = GKDF-160 (MK, inputString)[128..143]
214 * PK = GKDF-160 (MK, inputString)[144..159]
215 * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
216 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
217 * CSuite_Sel || inputString)
218 */
219
220 *sk_len = EAP_GPSK_SK_LEN_AES;
221 *pk_len = EAP_GPSK_PK_LEN_AES;
222
223 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
224 kdf_out, sizeof(kdf_out),
225 psk, psk_len, seed, seed_len,
226 msk, emsk, sk, *sk_len,
227 pk, *pk_len);
228 }
229
230
231 #ifdef EAP_GPSK_SHA256
eap_gpsk_derive_keys_sha256(const u8 * psk,size_t psk_len,const u8 * seed,size_t seed_len,u8 * msk,u8 * emsk,u8 * sk,size_t * sk_len)232 static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
233 const u8 *seed, size_t seed_len,
234 u8 *msk, u8 *emsk,
235 u8 *sk, size_t *sk_len)
236 {
237 #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
238 #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
239 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
240 EAP_GPSK_PK_LEN_SHA256];
241
242 /*
243 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
244 * (= seed)
245 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
246 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
247 * MSK = GKDF-160 (MK, inputString)[0..63]
248 * EMSK = GKDF-160 (MK, inputString)[64..127]
249 * SK = GKDF-160 (MK, inputString)[128..159]
250 * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
251 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
252 * CSuite_Sel || inputString)
253 */
254
255 *sk_len = EAP_GPSK_SK_LEN_SHA256;
256
257 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
258 kdf_out, sizeof(kdf_out),
259 psk, psk_len, seed, seed_len,
260 msk, emsk, sk, *sk_len,
261 NULL, 0);
262 }
263 #endif /* EAP_GPSK_SHA256 */
264
265
266 /**
267 * eap_gpsk_derive_keys - Derive EAP-GPSK keys
268 * @psk: Pre-shared key
269 * @psk_len: Length of psk in bytes
270 * @vendor: CSuite/Vendor
271 * @specifier: CSuite/Specifier
272 * @rand_peer: 32-byte RAND_Peer
273 * @rand_server: 32-byte RAND_Server
274 * @id_peer: ID_Peer
275 * @id_peer_len: Length of ID_Peer
276 * @id_server: ID_Server
277 * @id_server_len: Length of ID_Server
278 * @msk: Buffer for 64-byte MSK
279 * @emsk: Buffer for 64-byte EMSK
280 * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
281 * @sk_len: Buffer for returning length of SK
282 * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
283 * @pk_len: Buffer for returning length of PK
284 * Returns: 0 on success, -1 on failure
285 */
eap_gpsk_derive_keys(const u8 * psk,size_t psk_len,int vendor,int specifier,const u8 * rand_peer,const u8 * rand_server,const u8 * id_peer,size_t id_peer_len,const u8 * id_server,size_t id_server_len,u8 * msk,u8 * emsk,u8 * sk,size_t * sk_len,u8 * pk,size_t * pk_len)286 int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
287 int specifier,
288 const u8 *rand_peer, const u8 *rand_server,
289 const u8 *id_peer, size_t id_peer_len,
290 const u8 *id_server, size_t id_server_len,
291 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
292 u8 *pk, size_t *pk_len)
293 {
294 u8 *seed, *pos;
295 size_t seed_len;
296 int ret;
297
298 wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
299 vendor, specifier);
300
301 if (vendor != EAP_GPSK_VENDOR_IETF)
302 return -1;
303
304 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
305
306 /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
307 seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
308 seed = os_malloc(seed_len);
309 if (seed == NULL) {
310 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
311 "for key derivation");
312 return -1;
313 }
314
315 pos = seed;
316 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
317 pos += EAP_GPSK_RAND_LEN;
318 os_memcpy(pos, id_peer, id_peer_len);
319 pos += id_peer_len;
320 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
321 pos += EAP_GPSK_RAND_LEN;
322 os_memcpy(pos, id_server, id_server_len);
323 pos += id_server_len;
324 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
325
326 switch (specifier) {
327 case EAP_GPSK_CIPHER_AES:
328 ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
329 msk, emsk, sk, sk_len,
330 pk, pk_len);
331 break;
332 #ifdef EAP_GPSK_SHA256
333 case EAP_GPSK_CIPHER_SHA256:
334 ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
335 msk, emsk, sk, sk_len);
336 break;
337 #endif /* EAP_GPSK_SHA256 */
338 default:
339 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
340 "key derivation", vendor, specifier);
341 ret = -1;
342 break;
343 }
344
345 os_free(seed);
346
347 return ret;
348 }
349
350
351 /**
352 * eap_gpsk_mic_len - Get the length of the MIC
353 * @vendor: CSuite/Vendor
354 * @specifier: CSuite/Specifier
355 * Returns: MIC length in bytes
356 */
eap_gpsk_mic_len(int vendor,int specifier)357 size_t eap_gpsk_mic_len(int vendor, int specifier)
358 {
359 if (vendor != EAP_GPSK_VENDOR_IETF)
360 return 0;
361
362 switch (specifier) {
363 case EAP_GPSK_CIPHER_AES:
364 return 16;
365 #ifdef EAP_GPSK_SHA256
366 case EAP_GPSK_CIPHER_SHA256:
367 return 32;
368 #endif /* EAP_GPSK_SHA256 */
369 default:
370 return 0;
371 }
372 }
373
374
eap_gpsk_compute_mic_aes(const u8 * sk,size_t sk_len,const u8 * data,size_t len,u8 * mic)375 static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
376 const u8 *data, size_t len, u8 *mic)
377 {
378 if (sk_len != 16) {
379 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
380 "AES-CMAC MIC", (unsigned long) sk_len);
381 return -1;
382 }
383
384 return omac1_aes_128(sk, data, len, mic);
385 }
386
387
388 /**
389 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
390 * @sk: Session key SK from eap_gpsk_derive_keys()
391 * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
392 * @vendor: CSuite/Vendor
393 * @specifier: CSuite/Specifier
394 * @data: Input data to MIC
395 * @len: Input data length in bytes
396 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
397 * Returns: 0 on success, -1 on failure
398 */
eap_gpsk_compute_mic(const u8 * sk,size_t sk_len,int vendor,int specifier,const u8 * data,size_t len,u8 * mic)399 int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
400 int specifier, const u8 *data, size_t len, u8 *mic)
401 {
402 int ret;
403
404 if (vendor != EAP_GPSK_VENDOR_IETF)
405 return -1;
406
407 switch (specifier) {
408 case EAP_GPSK_CIPHER_AES:
409 ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
410 break;
411 #ifdef EAP_GPSK_SHA256
412 case EAP_GPSK_CIPHER_SHA256:
413 hmac_sha256(sk, sk_len, data, len, mic);
414 ret = 0;
415 break;
416 #endif /* EAP_GPSK_SHA256 */
417 default:
418 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
419 "MIC computation", vendor, specifier);
420 ret = -1;
421 break;
422 }
423
424 return ret;
425 }
426