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 */ 33int 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 47static 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 81static 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 115static 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 196static 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 232static 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 */ 286int 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 */ 357size_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 375static 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 */ 399int 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