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