2 * EAP server/peer: EAP-GPSK shared routines
3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
21 #ifdef EAP_GPSK_SHA256
23 #endif /* EAP_GPSK_SHA256 */
24 #include "eap_gpsk_common.h"
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
33 int eap_gpsk_supported_ciphersuite(int vendor
, int specifier
)
35 if (vendor
== EAP_GPSK_VENDOR_IETF
&&
36 specifier
== EAP_GPSK_CIPHER_AES
)
38 #ifdef EAP_GPSK_SHA256
39 if (vendor
== EAP_GPSK_VENDOR_IETF
&&
40 specifier
== EAP_GPSK_CIPHER_SHA256
)
42 #endif /* EAP_GPSK_SHA256 */
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 */)
52 size_t i
, n
, hashlen
, left
, clen
;
57 hashlen
= sizeof(hash
);
58 /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
60 vlen
[0] = sizeof(ibuf
);
66 n
= (len
+ hashlen
- 1) / hashlen
;
67 for (i
= 1; i
<= n
; i
++) {
68 WPA_PUT_BE16(ibuf
, i
);
69 if (omac1_aes_128_vector(psk
, 2, addr
, vlen
, hash
))
71 clen
= left
> hashlen
? hashlen
: left
;
72 os_memcpy(opos
, hash
, clen
);
81 #ifdef EAP_GPSK_SHA256
82 static int eap_gpsk_gkdf_sha256(const u8
*psk
/* Y */,
83 const u8
*data
/* Z */, size_t data_len
,
84 u8
*buf
, size_t len
/* X */)
87 size_t i
, n
, hashlen
, left
, clen
;
88 u8 ibuf
[2], hash
[SHA256_MAC_LEN
];
92 hashlen
= SHA256_MAC_LEN
;
93 /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
95 vlen
[0] = sizeof(ibuf
);
101 n
= (len
+ hashlen
- 1) / hashlen
;
102 for (i
= 1; i
<= n
; i
++) {
103 WPA_PUT_BE16(ibuf
, i
);
104 hmac_sha256_vector(psk
, 32, 2, addr
, vlen
, hash
);
105 clen
= left
> hashlen
? hashlen
: left
;
106 os_memcpy(opos
, hash
, clen
);
113 #endif /* EAP_GPSK_SHA256 */
116 static int eap_gpsk_derive_keys_helper(u32 csuite_specifier
,
117 u8
*kdf_out
, size_t kdf_out_len
,
118 const u8
*psk
, size_t psk_len
,
119 const u8
*seed
, size_t seed_len
,
121 u8
*sk
, size_t sk_len
,
122 u8
*pk
, size_t pk_len
)
124 u8 mk
[32], *pos
, *data
;
125 size_t data_len
, mk_len
;
126 int (*gkdf
)(const u8
*psk
, const u8
*data
, size_t data_len
,
127 u8
*buf
, size_t len
);
130 switch (csuite_specifier
) {
131 case EAP_GPSK_CIPHER_AES
:
132 gkdf
= eap_gpsk_gkdf_cmac
;
135 #ifdef EAP_GPSK_SHA256
136 case EAP_GPSK_CIPHER_SHA256
:
137 gkdf
= eap_gpsk_gkdf_sha256
;
138 mk_len
= SHA256_MAC_LEN
;
140 #endif /* EAP_GPSK_SHA256 */
145 if (psk_len
< mk_len
)
148 data_len
= 2 + psk_len
+ 6 + seed_len
;
149 data
= os_malloc(data_len
);
153 WPA_PUT_BE16(pos
, psk_len
);
155 os_memcpy(pos
, psk
, psk_len
);
157 WPA_PUT_BE32(pos
, EAP_GPSK_VENDOR_IETF
); /* CSuite/Vendor = IETF */
159 WPA_PUT_BE16(pos
, csuite_specifier
); /* CSuite/Specifier */
161 os_memcpy(pos
, seed
, seed_len
); /* inputString */
162 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: Data to MK derivation",
165 if (gkdf(psk
, data
, data_len
, mk
, mk_len
) < 0) {
170 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: MK", mk
, mk_len
);
172 if (gkdf(mk
, seed
, seed_len
, kdf_out
, kdf_out_len
) < 0)
176 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: MSK", pos
, EAP_MSK_LEN
);
177 os_memcpy(msk
, pos
, EAP_MSK_LEN
);
180 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: EMSK", pos
, EAP_EMSK_LEN
);
181 os_memcpy(emsk
, pos
, EAP_EMSK_LEN
);
184 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: SK", pos
, sk_len
);
185 os_memcpy(sk
, pos
, sk_len
);
189 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: PK", pos
, pk_len
);
190 os_memcpy(pk
, pos
, pk_len
);
197 static int eap_gpsk_derive_keys_aes(const u8
*psk
, size_t psk_len
,
198 const u8
*seed
, size_t seed_len
,
199 u8
*msk
, u8
*emsk
, u8
*sk
, size_t *sk_len
,
200 u8
*pk
, size_t *pk_len
)
202 #define EAP_GPSK_SK_LEN_AES 16
203 #define EAP_GPSK_PK_LEN_AES 16
204 u8 kdf_out
[EAP_MSK_LEN
+ EAP_EMSK_LEN
+ EAP_GPSK_SK_LEN_AES
+
205 EAP_GPSK_PK_LEN_AES
];
208 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
210 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
211 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
212 * MSK = GKDF-160 (MK, inputString)[0..63]
213 * EMSK = GKDF-160 (MK, inputString)[64..127]
214 * SK = GKDF-160 (MK, inputString)[128..143]
215 * PK = GKDF-160 (MK, inputString)[144..159]
216 * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
217 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
218 * CSuite_Sel || inputString)
221 *sk_len
= EAP_GPSK_SK_LEN_AES
;
222 *pk_len
= EAP_GPSK_PK_LEN_AES
;
224 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES
,
225 kdf_out
, sizeof(kdf_out
),
226 psk
, psk_len
, seed
, seed_len
,
227 msk
, emsk
, sk
, *sk_len
,
232 #ifdef EAP_GPSK_SHA256
233 static int eap_gpsk_derive_keys_sha256(const u8
*psk
, size_t psk_len
,
234 const u8
*seed
, size_t seed_len
,
236 u8
*sk
, size_t *sk_len
)
238 #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
239 #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
240 u8 kdf_out
[EAP_MSK_LEN
+ EAP_EMSK_LEN
+ EAP_GPSK_SK_LEN_SHA256
+
241 EAP_GPSK_PK_LEN_SHA256
];
244 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
246 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
247 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
248 * MSK = GKDF-160 (MK, inputString)[0..63]
249 * EMSK = GKDF-160 (MK, inputString)[64..127]
250 * SK = GKDF-160 (MK, inputString)[128..159]
251 * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
252 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
253 * CSuite_Sel || inputString)
256 *sk_len
= EAP_GPSK_SK_LEN_SHA256
;
258 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256
,
259 kdf_out
, sizeof(kdf_out
),
260 psk
, psk_len
, seed
, seed_len
,
261 msk
, emsk
, sk
, *sk_len
,
264 #endif /* EAP_GPSK_SHA256 */
268 * eap_gpsk_derive_keys - Derive EAP-GPSK keys
269 * @psk: Pre-shared key
270 * @psk_len: Length of psk in bytes
271 * @vendor: CSuite/Vendor
272 * @specifier: CSuite/Specifier
273 * @rand_peer: 32-byte RAND_Peer
274 * @rand_server: 32-byte RAND_Server
276 * @id_peer_len: Length of ID_Peer
277 * @id_server: ID_Server
278 * @id_server_len: Length of ID_Server
279 * @msk: Buffer for 64-byte MSK
280 * @emsk: Buffer for 64-byte EMSK
281 * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
282 * @sk_len: Buffer for returning length of SK
283 * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
284 * @pk_len: Buffer for returning length of PK
285 * Returns: 0 on success, -1 on failure
287 int eap_gpsk_derive_keys(const u8
*psk
, size_t psk_len
, int vendor
,
289 const u8
*rand_peer
, const u8
*rand_server
,
290 const u8
*id_peer
, size_t id_peer_len
,
291 const u8
*id_server
, size_t id_server_len
,
292 u8
*msk
, u8
*emsk
, u8
*sk
, size_t *sk_len
,
293 u8
*pk
, size_t *pk_len
)
299 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Deriving keys (%d:%d)",
302 if (vendor
!= EAP_GPSK_VENDOR_IETF
)
305 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: PSK", psk
, psk_len
);
307 /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
308 seed_len
= 2 * EAP_GPSK_RAND_LEN
+ id_server_len
+ id_peer_len
;
309 seed
= os_malloc(seed_len
);
311 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Failed to allocate memory "
312 "for key derivation");
317 os_memcpy(pos
, rand_peer
, EAP_GPSK_RAND_LEN
);
318 pos
+= EAP_GPSK_RAND_LEN
;
319 os_memcpy(pos
, id_peer
, id_peer_len
);
321 os_memcpy(pos
, rand_server
, EAP_GPSK_RAND_LEN
);
322 pos
+= EAP_GPSK_RAND_LEN
;
323 os_memcpy(pos
, id_server
, id_server_len
);
324 pos
+= id_server_len
;
325 wpa_hexdump(MSG_DEBUG
, "EAP-GPSK: Seed", seed
, seed_len
);
328 case EAP_GPSK_CIPHER_AES
:
329 ret
= eap_gpsk_derive_keys_aes(psk
, psk_len
, seed
, seed_len
,
330 msk
, emsk
, sk
, sk_len
,
333 #ifdef EAP_GPSK_SHA256
334 case EAP_GPSK_CIPHER_SHA256
:
335 ret
= eap_gpsk_derive_keys_sha256(psk
, psk_len
, seed
, seed_len
,
336 msk
, emsk
, sk
, sk_len
);
338 #endif /* EAP_GPSK_SHA256 */
340 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Unknown cipher %d:%d used in "
341 "key derivation", vendor
, specifier
);
353 * eap_gpsk_mic_len - Get the length of the MIC
354 * @vendor: CSuite/Vendor
355 * @specifier: CSuite/Specifier
356 * Returns: MIC length in bytes
358 size_t eap_gpsk_mic_len(int vendor
, int specifier
)
360 if (vendor
!= EAP_GPSK_VENDOR_IETF
)
364 case EAP_GPSK_CIPHER_AES
:
366 #ifdef EAP_GPSK_SHA256
367 case EAP_GPSK_CIPHER_SHA256
:
369 #endif /* EAP_GPSK_SHA256 */
376 static int eap_gpsk_compute_mic_aes(const u8
*sk
, size_t sk_len
,
377 const u8
*data
, size_t len
, u8
*mic
)
380 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Invalid SK length %d for "
381 "AES-CMAC MIC", sk_len
);
385 return omac1_aes_128(sk
, data
, len
, mic
);
390 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
391 * @sk: Session key SK from eap_gpsk_derive_keys()
392 * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
393 * @vendor: CSuite/Vendor
394 * @specifier: CSuite/Specifier
395 * @data: Input data to MIC
396 * @len: Input data length in bytes
397 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
398 * Returns: 0 on success, -1 on failure
400 int eap_gpsk_compute_mic(const u8
*sk
, size_t sk_len
, int vendor
,
401 int specifier
, const u8
*data
, size_t len
, u8
*mic
)
405 if (vendor
!= EAP_GPSK_VENDOR_IETF
)
409 case EAP_GPSK_CIPHER_AES
:
410 ret
= eap_gpsk_compute_mic_aes(sk
, sk_len
, data
, len
, mic
);
412 #ifdef EAP_GPSK_SHA256
413 case EAP_GPSK_CIPHER_SHA256
:
414 hmac_sha256(sk
, sk_len
, data
, len
, mic
);
417 #endif /* EAP_GPSK_SHA256 */
419 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Unknown cipher %d:%d used in "
420 "MIC computation", vendor
, specifier
);