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.
23 #include "eap_gpsk_common.h"
27 * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
28 * @vendor: CSuite/Vendor
29 * @specifier: CSuite/Specifier
30 * Returns: 1 if ciphersuite is support, or 0 if not
32 int eap_gpsk_supported_ciphersuite(int vendor
, int specifier
)
34 if (vendor
== EAP_GPSK_VENDOR_IETF
&&
35 specifier
== EAP_GPSK_CIPHER_AES
)
37 #ifdef EAP_GPSK_SHA256
38 if (vendor
== EAP_GPSK_VENDOR_IETF
&&
39 specifier
== EAP_GPSK_CIPHER_SHA256
)
41 #endif /* EAP_GPSK_SHA256 */
46 static int eap_gpsk_gkdf(const u8
*psk
/* Y */, size_t psk_len
,
47 const u8
*data
/* Z */, size_t data_len
,
48 u8
*buf
, size_t len
/* X */)
51 size_t i
, n
, hashlen
, left
, clen
;
52 u8 ibuf
[2], hash
[SHA1_MAC_LEN
];
56 hashlen
= SHA1_MAC_LEN
;
57 /* M_i = Hash-Function (i || Y || Z); */
59 vlen
[0] = sizeof(ibuf
);
67 n
= (len
+ hashlen
- 1) / hashlen
;
68 for (i
= 1; i
<= n
; i
++) {
69 WPA_PUT_BE16(ibuf
, i
);
70 sha1_vector(3, addr
, vlen
, hash
);
71 clen
= left
> hashlen
? hashlen
: left
;
72 os_memcpy(opos
, hash
, clen
);
81 static int eap_gpsk_derive_keys_aes(const u8
*psk
, size_t psk_len
,
82 const u8
*seed
, size_t seed_len
,
83 u8
*msk
, u8
*emsk
, u8
*sk
, size_t *sk_len
,
84 u8
*pk
, size_t *pk_len
)
86 #define EAP_GPSK_SK_LEN_AES 16
87 #define EAP_GPSK_PK_LEN_AES 16
88 u8 zero_string
[1], mk
[32], *pos
, *data
;
89 u8 kdf_out
[EAP_MSK_LEN
+ EAP_EMSK_LEN
+ EAP_GPSK_SK_LEN_AES
+
94 * inputString = RAND_Client || ID_Client || RAND_Server || ID_Server
96 * KS = 16, PL = psk_len, CSuite_Sel = 0x000000 0x000001
97 * MK = GKDF-32 (0x00, PL || PSK || CSuite_Sel || inputString)
98 * MSK = GKDF-160 (MK, inputString)[0..63]
99 * EMSK = GKDF-160 (MK, inputString)[64..127]
100 * SK = GKDF-160 (MK, inputString)[128..143]
101 * PK = GKDF-160 (MK, inputString)[144..159]
102 * MID = GKDF-16(0x00, "Method ID" || EAP_Method_Type || CSuite_Sel ||
104 * Hash-Function = SHA-1 (see [RFC3174])
105 * hashlen = 20 octets (160 bits)
108 os_memset(zero_string
, 0, sizeof(zero_string
));
110 data_len
= 2 + psk_len
+ 6 + seed_len
;
111 data
= os_malloc(data_len
);
115 WPA_PUT_BE16(pos
, psk_len
);
117 os_memcpy(pos
, psk
, psk_len
);
119 WPA_PUT_BE24(pos
, 0); /* CSuite/Vendor = IETF */
121 WPA_PUT_BE24(pos
, EAP_GPSK_CIPHER_AES
); /* CSuite/Specifier */
123 os_memcpy(pos
, seed
, seed_len
); /* inputString */
124 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: Data to MK derivation (AES)",
127 if (eap_gpsk_gkdf(zero_string
, sizeof(zero_string
), data
, data_len
,
128 mk
, sizeof(mk
)) < 0) {
133 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: MK", mk
, sizeof(mk
));
135 if (eap_gpsk_gkdf(mk
, sizeof(mk
), seed
, seed_len
,
136 kdf_out
, sizeof(kdf_out
)) < 0)
140 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: MSK", pos
, EAP_MSK_LEN
);
141 os_memcpy(msk
, pos
, EAP_MSK_LEN
);
144 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: EMSK", pos
, EAP_EMSK_LEN
);
145 os_memcpy(emsk
, pos
, EAP_EMSK_LEN
);
148 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: SK", pos
, EAP_GPSK_SK_LEN_AES
);
149 os_memcpy(sk
, pos
, EAP_GPSK_SK_LEN_AES
);
150 *sk_len
= EAP_GPSK_SK_LEN_AES
;
151 pos
+= EAP_GPSK_SK_LEN_AES
;
153 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: PK", pos
, EAP_GPSK_PK_LEN_AES
);
154 os_memcpy(pk
, pos
, EAP_GPSK_PK_LEN_AES
);
155 *pk_len
= EAP_GPSK_PK_LEN_AES
;
161 #ifdef EAP_GPSK_SHA256
162 static int eap_gpsk_gkdf_sha256(const u8
*psk
/* Y */, size_t psk_len
,
163 const u8
*data
/* Z */, size_t data_len
,
164 u8
*buf
, size_t len
/* X */)
167 size_t i
, n
, hashlen
, left
, clen
;
168 u8 ibuf
[2], hash
[SHA256_MAC_LEN
];
172 hashlen
= SHA256_MAC_LEN
;
173 /* M_i = Hash-Function (i || Y || Z); */
175 vlen
[0] = sizeof(ibuf
);
183 n
= (len
+ hashlen
- 1) / hashlen
;
184 for (i
= 1; i
<= n
; i
++) {
185 WPA_PUT_BE16(ibuf
, i
);
186 sha256_vector(3, addr
, vlen
, hash
);
187 clen
= left
> hashlen
? hashlen
: left
;
188 os_memcpy(opos
, hash
, clen
);
197 static int eap_gpsk_derive_keys_sha256(const u8
*psk
, size_t psk_len
,
198 const u8
*seed
, size_t seed_len
,
200 u8
*sk
, size_t *sk_len
,
201 u8
*pk
, size_t *pk_len
)
203 #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
204 #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
205 u8 mk
[SHA256_MAC_LEN
], zero_string
[1], *pos
, *data
;
206 u8 kdf_out
[EAP_MSK_LEN
+ EAP_EMSK_LEN
+ EAP_GPSK_SK_LEN_SHA256
+
207 EAP_GPSK_PK_LEN_SHA256
];
211 * inputString = RAND_Client || ID_Client || RAND_Server || ID_Server
213 * KS = 32, PL = psk_len, CSuite_Sel = 0x000000 0x000002
214 * MK = GKDF-32 (0x00, PL || PSK || CSuite_Sel || inputString)
215 * MSK = GKDF-192 (MK, inputString)[0..63]
216 * EMSK = GKDF-192 (MK, inputString)[64..127]
217 * SK = GKDF-192 (MK, inputString)[128..159]
218 * PK = GKDF-192 (MK, inputString)[160..191]
219 * MID = GKDF-16(0x00, "Method ID" || EAP_Method_Type || CSuite_Sel ||
221 * Hash-Function = SHA256 (see [RFC4634])
222 * hashlen = 32 octets (256 bits)
225 os_memset(zero_string
, 0, sizeof(zero_string
));
227 data_len
= 2 + psk_len
+ 6 + seed_len
;
228 data
= os_malloc(data_len
);
232 WPA_PUT_BE16(pos
, psk_len
);
234 os_memcpy(pos
, psk
, psk_len
);
236 WPA_PUT_BE24(pos
, 0); /* CSuite/Vendor = IETF */
238 WPA_PUT_BE24(pos
, EAP_GPSK_CIPHER_SHA256
); /* CSuite/Specifier */
240 os_memcpy(pos
, seed
, seed_len
); /* inputString */
241 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: Data to MK derivation (SHA256)",
244 if (eap_gpsk_gkdf_sha256(zero_string
, sizeof(zero_string
),
245 data
, data_len
, mk
, sizeof(mk
)) < 0) {
250 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: MK", mk
, sizeof(mk
));
252 if (eap_gpsk_gkdf_sha256(mk
, sizeof(mk
), seed
, seed_len
,
253 kdf_out
, sizeof(kdf_out
)) < 0)
257 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: MSK", pos
, EAP_MSK_LEN
);
258 os_memcpy(msk
, pos
, EAP_MSK_LEN
);
261 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: EMSK", pos
, EAP_EMSK_LEN
);
262 os_memcpy(emsk
, pos
, EAP_EMSK_LEN
);
265 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: SK",
266 pos
, EAP_GPSK_SK_LEN_SHA256
);
267 os_memcpy(sk
, pos
, EAP_GPSK_SK_LEN_SHA256
);
268 *sk_len
= EAP_GPSK_SK_LEN_AES
;
269 pos
+= EAP_GPSK_SK_LEN_AES
;
271 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: PK",
272 pos
, EAP_GPSK_PK_LEN_SHA256
);
273 os_memcpy(pk
, pos
, EAP_GPSK_PK_LEN_SHA256
);
274 *pk_len
= EAP_GPSK_PK_LEN_SHA256
;
278 #endif /* EAP_GPSK_SHA256 */
282 * eap_gpsk_derive_keys - Derive EAP-GPSK keys
283 * @psk: Pre-shared key (at least 16 bytes if AES is used)
284 * @psk_len: Length of psk in bytes
285 * @vendor: CSuite/Vendor
286 * @specifier: CSuite/Specifier
287 * @rand_client: 32-byte RAND_Client
288 * @rand_server: 32-byte RAND_Server
289 * @id_client: ID_Client
290 * @id_client_len: Length of ID_Client
291 * @id_server: ID_Server
292 * @id_server_len: Length of ID_Server
293 * @msk: Buffer for 64-byte MSK
294 * @emsk: Buffer for 64-byte EMSK
295 * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
296 * @sk_len: Buffer for returning length of SK
297 * @pk: Buffer for SK (at least EAP_GPSK_MAX_PK_LEN bytes)
298 * @pk_len: Buffer for returning length of PK
299 * Returns: 0 on success, -1 on failure
301 int eap_gpsk_derive_keys(const u8
*psk
, size_t psk_len
, int vendor
,
303 const u8
*rand_client
, const u8
*rand_server
,
304 const u8
*id_client
, size_t id_client_len
,
305 const u8
*id_server
, size_t id_server_len
,
306 u8
*msk
, u8
*emsk
, u8
*sk
, size_t *sk_len
,
307 u8
*pk
, size_t *pk_len
)
313 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Deriving keys (%d:%d)",
316 if (vendor
!= EAP_GPSK_VENDOR_IETF
)
319 wpa_hexdump_key(MSG_DEBUG
, "EAP-GPSK: PSK", psk
, psk_len
);
321 /* Seed = RAND_Client || ID_Client || RAND_Server || ID_Server */
322 seed_len
= 2 * EAP_GPSK_RAND_LEN
+ id_server_len
+ id_client_len
;
323 seed
= os_malloc(seed_len
);
325 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Failed to allocate memory "
326 "for key derivation");
331 os_memcpy(pos
, rand_client
, EAP_GPSK_RAND_LEN
);
332 pos
+= EAP_GPSK_RAND_LEN
;
333 os_memcpy(pos
, id_client
, id_client_len
);
334 pos
+= id_client_len
;
335 os_memcpy(pos
, rand_server
, EAP_GPSK_RAND_LEN
);
336 pos
+= EAP_GPSK_RAND_LEN
;
337 os_memcpy(pos
, id_server
, id_server_len
);
338 pos
+= id_server_len
;
339 wpa_hexdump(MSG_DEBUG
, "EAP-GPSK: Seed", seed
, seed_len
);
342 case EAP_GPSK_CIPHER_AES
:
343 ret
= eap_gpsk_derive_keys_aes(psk
, psk_len
, seed
, seed_len
,
344 msk
, emsk
, sk
, sk_len
,
347 #ifdef EAP_GPSK_SHA256
348 case EAP_GPSK_CIPHER_SHA256
:
349 ret
= eap_gpsk_derive_keys_sha256(psk
, psk_len
, seed
, seed_len
,
350 msk
, emsk
, sk
, sk_len
,
353 #endif /* EAP_GPSK_SHA256 */
355 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Unknown cipher %d:%d used in "
356 "key derivation", vendor
, specifier
);
368 * eap_gpsk_mic_len - Get the length of the MIC
369 * @vendor: CSuite/Vendor
370 * @specifier: CSuite/Specifier
371 * Returns: MIC length in bytes
373 size_t eap_gpsk_mic_len(int vendor
, int specifier
)
375 if (vendor
!= EAP_GPSK_VENDOR_IETF
)
379 case EAP_GPSK_CIPHER_AES
:
381 #ifdef EAP_GPSK_SHA256
382 case EAP_GPSK_CIPHER_SHA256
:
384 #endif /* EAP_GPSK_SHA256 */
391 static int eap_gpsk_compute_mic_aes(const u8
*sk
, size_t sk_len
,
392 const u8
*data
, size_t len
, u8
*mic
)
395 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Invalid SK length %d for "
396 "AES-CMAC MIC", sk_len
);
400 return omac1_aes_128(sk
, data
, len
, mic
);
405 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
406 * @sk: Session key SK from eap_gpsk_derive_keys()
407 * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
408 * @vendor: CSuite/Vendor
409 * @specifier: CSuite/Specifier
410 * @data: Input data to MIC
411 * @len: Input data length in bytes
412 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
413 * Returns: 0 on success, -1 on failure
415 int eap_gpsk_compute_mic(const u8
*sk
, size_t sk_len
, int vendor
,
416 int specifier
, const u8
*data
, size_t len
, u8
*mic
)
420 if (vendor
!= EAP_GPSK_VENDOR_IETF
)
424 case EAP_GPSK_CIPHER_AES
:
425 ret
= eap_gpsk_compute_mic_aes(sk
, sk_len
, data
, len
, mic
);
427 #ifdef EAP_GPSK_SHA256
428 case EAP_GPSK_CIPHER_SHA256
:
429 hmac_sha256(sk
, sk_len
, data
, len
, mic
);
432 #endif /* EAP_GPSK_SHA256 */
434 wpa_printf(MSG_DEBUG
, "EAP-GPSK: Unknown cipher %d:%d used in "
435 "MIC computation", vendor
, specifier
);