1189251Ssam/* 2189251Ssam * EAP-FAST common helper functions (RFC 4851) 3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "crypto/sha1.h" 13214734Srpaulo#include "crypto/tls.h" 14189251Ssam#include "eap_defs.h" 15189251Ssam#include "eap_tlv_common.h" 16189251Ssam#include "eap_fast_common.h" 17189251Ssam 18189251Ssam 19189251Ssamvoid eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len) 20189251Ssam{ 21189251Ssam struct pac_tlv_hdr hdr; 22189251Ssam hdr.type = host_to_be16(type); 23189251Ssam hdr.len = host_to_be16(len); 24189251Ssam wpabuf_put_data(buf, &hdr, sizeof(hdr)); 25189251Ssam} 26189251Ssam 27189251Ssam 28189251Ssamvoid eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, 29189251Ssam u16 len) 30189251Ssam{ 31189251Ssam eap_fast_put_tlv_hdr(buf, type, len); 32189251Ssam wpabuf_put_data(buf, data, len); 33189251Ssam} 34189251Ssam 35189251Ssam 36189251Ssamvoid eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, 37189251Ssam const struct wpabuf *data) 38189251Ssam{ 39189251Ssam eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data)); 40189251Ssam wpabuf_put_buf(buf, data); 41189251Ssam} 42189251Ssam 43189251Ssam 44189251Ssamstruct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf) 45189251Ssam{ 46189251Ssam struct wpabuf *e; 47189251Ssam 48189251Ssam if (buf == NULL) 49189251Ssam return NULL; 50189251Ssam 51189251Ssam /* Encapsulate EAP packet in EAP-Payload TLV */ 52189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV"); 53189251Ssam e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf)); 54189251Ssam if (e == NULL) { 55189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " 56189251Ssam "for TLV encapsulation"); 57189251Ssam wpabuf_free(buf); 58189251Ssam return NULL; 59189251Ssam } 60189251Ssam eap_fast_put_tlv_buf(e, 61189251Ssam EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV, 62189251Ssam buf); 63189251Ssam wpabuf_free(buf); 64189251Ssam return e; 65189251Ssam} 66189251Ssam 67189251Ssam 68189251Ssamvoid eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, 69189251Ssam const u8 *client_random, u8 *master_secret) 70189251Ssam{ 71189251Ssam#define TLS_RANDOM_LEN 32 72189251Ssam#define TLS_MASTER_SECRET_LEN 48 73189251Ssam u8 seed[2 * TLS_RANDOM_LEN]; 74189251Ssam 75189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random", 76189251Ssam client_random, TLS_RANDOM_LEN); 77189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random", 78189251Ssam server_random, TLS_RANDOM_LEN); 79189251Ssam 80189251Ssam /* 81189251Ssam * RFC 4851, Section 5.1: 82189251Ssam * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", 83189251Ssam * server_random + client_random, 48) 84189251Ssam */ 85189251Ssam os_memcpy(seed, server_random, TLS_RANDOM_LEN); 86189251Ssam os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN); 87189251Ssam sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN, 88189251Ssam "PAC to master secret label hash", 89189251Ssam seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN); 90189251Ssam 91189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret", 92189251Ssam master_secret, TLS_MASTER_SECRET_LEN); 93189251Ssam} 94189251Ssam 95189251Ssam 96189251Ssamu8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, 97189251Ssam const char *label, size_t len) 98189251Ssam{ 99189251Ssam struct tls_keys keys; 100189251Ssam u8 *rnd = NULL, *out; 101189251Ssam int block_size; 102189251Ssam 103189251Ssam block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); 104189251Ssam if (block_size < 0) 105189251Ssam return NULL; 106189251Ssam 107189251Ssam out = os_malloc(block_size + len); 108189251Ssam if (out == NULL) 109189251Ssam return NULL; 110189251Ssam 111189251Ssam if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) 112189251Ssam == 0) { 113189251Ssam os_memmove(out, out + block_size, len); 114189251Ssam return out; 115189251Ssam } 116189251Ssam 117189251Ssam if (tls_connection_get_keys(ssl_ctx, conn, &keys)) 118189251Ssam goto fail; 119189251Ssam 120189251Ssam rnd = os_malloc(keys.client_random_len + keys.server_random_len); 121189251Ssam if (rnd == NULL) 122189251Ssam goto fail; 123189251Ssam 124189251Ssam os_memcpy(rnd, keys.server_random, keys.server_random_len); 125189251Ssam os_memcpy(rnd + keys.server_random_len, keys.client_random, 126189251Ssam keys.client_random_len); 127189251Ssam 128189251Ssam wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " 129189251Ssam "expansion", keys.master_key, keys.master_key_len); 130252726Srpaulo if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, 131252726Srpaulo label, rnd, keys.client_random_len + 132252726Srpaulo keys.server_random_len, out, block_size + len)) 133189251Ssam goto fail; 134189251Ssam os_free(rnd); 135189251Ssam os_memmove(out, out + block_size, len); 136189251Ssam return out; 137189251Ssam 138189251Ssamfail: 139189251Ssam os_free(rnd); 140189251Ssam os_free(out); 141189251Ssam return NULL; 142189251Ssam} 143189251Ssam 144189251Ssam 145189251Ssamvoid eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) 146189251Ssam{ 147189251Ssam /* 148189251Ssam * RFC 4851, Section 5.4: EAP Master Session Key Generation 149189251Ssam * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64) 150189251Ssam */ 151189251Ssam 152189251Ssam sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, 153189251Ssam "Session Key Generating Function", (u8 *) "", 0, 154189251Ssam msk, EAP_FAST_KEY_LEN); 155189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", 156189251Ssam msk, EAP_FAST_KEY_LEN); 157189251Ssam} 158189251Ssam 159189251Ssam 160189251Ssamvoid eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) 161189251Ssam{ 162189251Ssam /* 163189251Ssam * RFC 4851, Section 5.4: EAP Master Session Key Genreration 164189251Ssam * EMSK = T-PRF(S-IMCK[j], 165189251Ssam * "Extended Session Key Generating Function", 64) 166189251Ssam */ 167189251Ssam 168189251Ssam sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, 169189251Ssam "Extended Session Key Generating Function", (u8 *) "", 0, 170189251Ssam emsk, EAP_EMSK_LEN); 171189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)", 172189251Ssam emsk, EAP_EMSK_LEN); 173189251Ssam} 174189251Ssam 175189251Ssam 176189251Ssamint eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, 177189251Ssam int tlv_type, u8 *pos, int len) 178189251Ssam{ 179189251Ssam switch (tlv_type) { 180189251Ssam case EAP_TLV_EAP_PAYLOAD_TLV: 181189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV", 182189251Ssam pos, len); 183189251Ssam if (tlv->eap_payload_tlv) { 184189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " 185189251Ssam "EAP-Payload TLV in the message"); 186189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 187189251Ssam return -2; 188189251Ssam } 189189251Ssam tlv->eap_payload_tlv = pos; 190189251Ssam tlv->eap_payload_tlv_len = len; 191189251Ssam break; 192189251Ssam case EAP_TLV_RESULT_TLV: 193189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len); 194189251Ssam if (tlv->result) { 195189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " 196189251Ssam "Result TLV in the message"); 197189251Ssam tlv->result = EAP_TLV_RESULT_FAILURE; 198189251Ssam return -2; 199189251Ssam } 200189251Ssam if (len < 2) { 201189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " 202189251Ssam "Result TLV"); 203189251Ssam tlv->result = EAP_TLV_RESULT_FAILURE; 204189251Ssam break; 205189251Ssam } 206189251Ssam tlv->result = WPA_GET_BE16(pos); 207189251Ssam if (tlv->result != EAP_TLV_RESULT_SUCCESS && 208189251Ssam tlv->result != EAP_TLV_RESULT_FAILURE) { 209189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d", 210189251Ssam tlv->result); 211189251Ssam tlv->result = EAP_TLV_RESULT_FAILURE; 212189251Ssam } 213189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s", 214189251Ssam tlv->result == EAP_TLV_RESULT_SUCCESS ? 215189251Ssam "Success" : "Failure"); 216189251Ssam break; 217189251Ssam case EAP_TLV_INTERMEDIATE_RESULT_TLV: 218189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV", 219189251Ssam pos, len); 220189251Ssam if (len < 2) { 221189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " 222189251Ssam "Intermediate-Result TLV"); 223189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 224189251Ssam break; 225189251Ssam } 226189251Ssam if (tlv->iresult) { 227189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " 228189251Ssam "Intermediate-Result TLV in the message"); 229189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 230189251Ssam return -2; 231189251Ssam } 232189251Ssam tlv->iresult = WPA_GET_BE16(pos); 233189251Ssam if (tlv->iresult != EAP_TLV_RESULT_SUCCESS && 234189251Ssam tlv->iresult != EAP_TLV_RESULT_FAILURE) { 235189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate " 236189251Ssam "Result %d", tlv->iresult); 237189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 238189251Ssam } 239189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s", 240189251Ssam tlv->iresult == EAP_TLV_RESULT_SUCCESS ? 241189251Ssam "Success" : "Failure"); 242189251Ssam break; 243189251Ssam case EAP_TLV_CRYPTO_BINDING_TLV: 244189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV", 245189251Ssam pos, len); 246189251Ssam if (tlv->crypto_binding) { 247189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " 248189251Ssam "Crypto-Binding TLV in the message"); 249189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 250189251Ssam return -2; 251189251Ssam } 252189251Ssam tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len; 253189251Ssam if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) { 254189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " 255189251Ssam "Crypto-Binding TLV"); 256189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 257189251Ssam return -2; 258189251Ssam } 259189251Ssam tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *) 260189251Ssam (pos - sizeof(struct eap_tlv_hdr)); 261189251Ssam break; 262189251Ssam case EAP_TLV_REQUEST_ACTION_TLV: 263189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV", 264189251Ssam pos, len); 265189251Ssam if (tlv->request_action) { 266189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " 267189251Ssam "Request-Action TLV in the message"); 268189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 269189251Ssam return -2; 270189251Ssam } 271189251Ssam if (len < 2) { 272189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " 273189251Ssam "Request-Action TLV"); 274189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 275189251Ssam break; 276189251Ssam } 277189251Ssam tlv->request_action = WPA_GET_BE16(pos); 278189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d", 279189251Ssam tlv->request_action); 280189251Ssam break; 281189251Ssam case EAP_TLV_PAC_TLV: 282189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len); 283189251Ssam if (tlv->pac) { 284189251Ssam wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " 285189251Ssam "PAC TLV in the message"); 286189251Ssam tlv->iresult = EAP_TLV_RESULT_FAILURE; 287189251Ssam return -2; 288189251Ssam } 289189251Ssam tlv->pac = pos; 290189251Ssam tlv->pac_len = len; 291189251Ssam break; 292189251Ssam default: 293189251Ssam /* Unknown TLV */ 294189251Ssam return -1; 295189251Ssam } 296189251Ssam 297189251Ssam return 0; 298189251Ssam} 299