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