EAP-PEAP: Added preliminary code for PEAPv0 and PEAPv2 cryptobinding
This implementation is complete for PEAPv0 (Microsoft extension), but the use of cryptobinding is disabled in this version, i.e., this does not change protocol behavior at all. Interop tests between hostapd and wpa_supplicant work fine, but there are some problems in getting hostapd to interoperate with Windows XP SP3 RC2 (peer replies with Result Failure to the attempted cryptobinding). The implementation will remain disabled until this issue has been resolved.
This commit is contained in:
parent
0ac0e4df1c
commit
e1a69a0b10
2 changed files with 612 additions and 22 deletions
|
@ -15,6 +15,7 @@
|
|||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "eap_i.h"
|
||||
#include "eap_tls_common.h"
|
||||
#include "eap_config.h"
|
||||
|
@ -60,6 +61,10 @@ struct eap_peap_data {
|
|||
u8 *key_data;
|
||||
|
||||
struct wpabuf *pending_phase2_req;
|
||||
enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
|
||||
int crypto_binding_used;
|
||||
u8 ipmk[40];
|
||||
u8 cmk[20];
|
||||
};
|
||||
|
||||
|
||||
|
@ -112,6 +117,7 @@ static void * eap_peap_init(struct eap_sm *sm)
|
|||
data->peap_version = EAP_PEAP_VERSION;
|
||||
data->force_peap_version = -1;
|
||||
data->peap_outer_success = 2;
|
||||
data->crypto_binding = NO_BINDING;
|
||||
|
||||
if (config && config->phase1 &&
|
||||
eap_peap_parse_phase1(data, config->phase1) < 0) {
|
||||
|
@ -182,6 +188,192 @@ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
|
|||
}
|
||||
|
||||
|
||||
static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
|
||||
u8 *isk, size_t isk_len)
|
||||
{
|
||||
u8 *key;
|
||||
size_t key_len;
|
||||
|
||||
os_memset(isk, 0, isk_len);
|
||||
if (data->phase2_method == NULL || data->phase2_priv == NULL ||
|
||||
data->phase2_method->isKeyAvailable == NULL ||
|
||||
data->phase2_method->getKey == NULL)
|
||||
return 0;
|
||||
|
||||
if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
|
||||
(key = data->phase2_method->getKey(sm, data->phase2_priv,
|
||||
&key_len)) == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
|
||||
"from Phase 2");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key_len > isk_len)
|
||||
key_len = isk_len;
|
||||
os_memcpy(isk, key, key_len);
|
||||
os_free(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void peap_prfplus(int version, const u8 *key, size_t key_len,
|
||||
const char *label, const u8 *seed, size_t seed_len,
|
||||
u8 *buf, size_t buf_len)
|
||||
{
|
||||
unsigned char counter = 0;
|
||||
size_t pos, plen;
|
||||
u8 hash[SHA1_MAC_LEN];
|
||||
size_t label_len = os_strlen(label);
|
||||
u8 extra[2];
|
||||
const unsigned char *addr[5];
|
||||
size_t len[5];
|
||||
|
||||
addr[0] = hash;
|
||||
len[0] = 0;
|
||||
addr[1] = (unsigned char *) label;
|
||||
len[1] = label_len;
|
||||
addr[2] = seed;
|
||||
len[2] = seed_len;
|
||||
|
||||
if (version == 0) {
|
||||
/*
|
||||
* PRF+(K, S, LEN) = T1 | T2 | ... | Tn
|
||||
* T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
|
||||
* T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
|
||||
* ...
|
||||
* Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
|
||||
*/
|
||||
|
||||
extra[0] = 0;
|
||||
extra[1] = 0;
|
||||
|
||||
addr[3] = &counter;
|
||||
len[3] = 1;
|
||||
addr[4] = extra;
|
||||
len[4] = 2;
|
||||
} else {
|
||||
/*
|
||||
* PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
|
||||
* T1 = HMAC-SHA1(K, S | LEN | 0x01)
|
||||
* T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
|
||||
* T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
|
||||
* T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
|
||||
* ...
|
||||
*/
|
||||
|
||||
extra[0] = buf_len & 0xff;
|
||||
|
||||
addr[3] = extra;
|
||||
len[3] = 1;
|
||||
addr[4] = &counter;
|
||||
len[4] = 1;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < buf_len) {
|
||||
counter++;
|
||||
plen = buf_len - pos;
|
||||
hmac_sha1_vector(key, key_len, 5, addr, len, hash);
|
||||
if (plen >= SHA1_MAC_LEN) {
|
||||
os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
|
||||
pos += SHA1_MAC_LEN;
|
||||
} else {
|
||||
os_memcpy(&buf[pos], hash, plen);
|
||||
break;
|
||||
}
|
||||
len[0] = SHA1_MAC_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
|
||||
{
|
||||
u8 *tk;
|
||||
u8 isk[32], imck[60];
|
||||
|
||||
/*
|
||||
* Tunnel key (TK) is the first 60 octets of the key generated by
|
||||
* phase 1 of PEAP (based on TLS).
|
||||
*/
|
||||
tk = data->key_data;
|
||||
if (tk == NULL)
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
|
||||
|
||||
if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
|
||||
|
||||
/*
|
||||
* IPMK Seed = "Inner Methods Compound Keys" | ISK
|
||||
* TempKey = First 40 octets of TK
|
||||
* IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
|
||||
* (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
|
||||
* in the end of the label just before ISK; is that just a typo?)
|
||||
*/
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
|
||||
peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
|
||||
isk, sizeof(isk), imck, sizeof(imck));
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
|
||||
imck, sizeof(imck));
|
||||
|
||||
/* TODO: fast-connect: IPMK|CMK = TK */
|
||||
os_memcpy(data->ipmk, imck, 40);
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
|
||||
os_memcpy(data->cmk, imck + 40, 20);
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
|
||||
struct eap_peap_data *data,
|
||||
struct wpabuf *buf)
|
||||
{
|
||||
u8 *mac;
|
||||
u8 eap_type = EAP_TYPE_PEAP;
|
||||
const u8 *addr[2];
|
||||
size_t len[2];
|
||||
u16 tlv_type;
|
||||
u8 binding_nonce[32];
|
||||
|
||||
/* FIX: should binding_nonce be copied from request? */
|
||||
if (os_get_random(binding_nonce, 32))
|
||||
return -1;
|
||||
|
||||
/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
|
||||
addr[0] = wpabuf_put(buf, 0);
|
||||
len[0] = 60;
|
||||
addr[1] = &eap_type;
|
||||
len[1] = 1;
|
||||
|
||||
tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
|
||||
if (data->peap_version >= 2)
|
||||
tlv_type |= EAP_TLV_TYPE_MANDATORY;
|
||||
wpabuf_put_be16(buf, tlv_type);
|
||||
wpabuf_put_be16(buf, 56);
|
||||
|
||||
wpabuf_put_u8(buf, 0); /* Reserved */
|
||||
wpabuf_put_u8(buf, data->peap_version); /* Version */
|
||||
wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
|
||||
wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
|
||||
wpabuf_put_data(buf, binding_nonce, 32); /* Nonce */
|
||||
mac = wpabuf_put(buf, 20); /* Compound_MAC */
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
|
||||
addr[0], len[0]);
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
|
||||
addr[1], len[1]);
|
||||
hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
|
||||
data->crypto_binding_used = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_tlv_build_result - Build EAP-TLV Result message
|
||||
* @id: EAP identifier for the header
|
||||
|
@ -191,11 +383,21 @@ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
|
|||
* This funtion builds an EAP-TLV Result message. The caller is responsible for
|
||||
* freeing the returned buffer.
|
||||
*/
|
||||
static struct wpabuf * eap_tlv_build_result(int id, u16 status)
|
||||
static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
|
||||
struct eap_peap_data *data,
|
||||
int crypto_tlv_used,
|
||||
int id, u16 status)
|
||||
{
|
||||
struct wpabuf *msg;
|
||||
size_t len;
|
||||
|
||||
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 6,
|
||||
if (data->crypto_binding == NO_BINDING)
|
||||
crypto_tlv_used = 0;
|
||||
|
||||
len = 6;
|
||||
if (crypto_tlv_used)
|
||||
len += 60; /* Cryptobinding TLV */
|
||||
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
@ -205,10 +407,69 @@ static struct wpabuf * eap_tlv_build_result(int id, u16 status)
|
|||
wpabuf_put_be16(msg, 2); /* Length */
|
||||
wpabuf_put_be16(msg, status); /* Status */
|
||||
|
||||
if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
|
||||
wpabuf_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
|
||||
struct eap_peap_data *data,
|
||||
const u8 *crypto_tlv,
|
||||
size_t crypto_tlv_len)
|
||||
{
|
||||
u8 buf[61], mac[SHA1_MAC_LEN];
|
||||
const u8 *pos;
|
||||
|
||||
if (eap_peap_derive_cmk(sm, data) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (crypto_tlv_len != 4 + 56) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
|
||||
"length %d", (int) crypto_tlv_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = crypto_tlv;
|
||||
pos += 4; /* TLV header */
|
||||
if (pos[1] != data->peap_version) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
|
||||
"mismatch (was %d; expected %d)",
|
||||
pos[1], data->peap_version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos[3] != 0) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
|
||||
"SubType %d", pos[3]);
|
||||
return -1;
|
||||
}
|
||||
pos += 4;
|
||||
pos += 32; /* Nonce */
|
||||
|
||||
/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
|
||||
os_memcpy(buf, crypto_tlv, 60);
|
||||
os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
|
||||
buf[60] = EAP_TYPE_PEAP;
|
||||
hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
|
||||
|
||||
if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
|
||||
"cryptobinding TLV");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_tlv_process - Process a received EAP-TLV message and generate a response
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
|
@ -223,14 +484,15 @@ static struct wpabuf * eap_tlv_build_result(int id, u16 status)
|
|||
* @force_failure: Force negotiation to fail
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
|
||||
static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
|
||||
struct eap_method_ret *ret,
|
||||
const struct wpabuf *req, struct wpabuf **resp,
|
||||
int force_failure)
|
||||
{
|
||||
size_t left, tlv_len;
|
||||
const u8 *pos;
|
||||
const u8 *result_tlv = NULL;
|
||||
size_t result_tlv_len = 0;
|
||||
const u8 *result_tlv = NULL, *crypto_tlv = NULL;
|
||||
size_t result_tlv_len = 0, crypto_tlv_len = 0;
|
||||
int tlv_type, mandatory;
|
||||
|
||||
/* Parse TLVs */
|
||||
|
@ -257,6 +519,10 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
|
|||
result_tlv = pos;
|
||||
result_tlv_len = tlv_len;
|
||||
break;
|
||||
case EAP_TLV_CRYPTO_BINDING_TLV:
|
||||
crypto_tlv = pos;
|
||||
crypto_tlv_len = tlv_len;
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
|
||||
"%d%s", tlv_type,
|
||||
|
@ -282,6 +548,20 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
|
|||
}
|
||||
|
||||
/* Process supported TLVs */
|
||||
if (crypto_tlv && data->crypto_binding != NO_BINDING) {
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
|
||||
crypto_tlv, crypto_tlv_len);
|
||||
if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
|
||||
crypto_tlv_len + 4) < 0) {
|
||||
if (result_tlv == NULL)
|
||||
return -1;
|
||||
force_failure = 1;
|
||||
}
|
||||
} else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (result_tlv) {
|
||||
int status, resp_status;
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
|
||||
|
@ -317,7 +597,8 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
|
|||
}
|
||||
ret->methodState = METHOD_DONE;
|
||||
|
||||
*resp = eap_tlv_build_result(eap_get_id(req), resp_status);
|
||||
*resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
|
||||
eap_get_id(req), resp_status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -376,7 +657,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm,
|
|||
break;
|
||||
case EAP_TYPE_TLV:
|
||||
os_memset(&iret, 0, sizeof(iret));
|
||||
if (eap_tlv_process(sm, &iret, req, resp,
|
||||
if (eap_tlv_process(sm, data, &iret, req, resp,
|
||||
data->phase2_eap_started &&
|
||||
!data->phase2_eap_success)) {
|
||||
ret->methodState = METHOD_DONE;
|
||||
|
@ -426,9 +707,11 @@ static int eap_peap_phase2_request(struct eap_sm *sm,
|
|||
data->phase2_type.method);
|
||||
if (data->phase2_method) {
|
||||
sm->init_phase2 = 1;
|
||||
sm->mschapv2_full_key = 1;
|
||||
data->phase2_priv =
|
||||
data->phase2_method->init(sm);
|
||||
sm->init_phase2 = 0;
|
||||
sm->mschapv2_full_key = 0;
|
||||
}
|
||||
}
|
||||
if (data->phase2_priv == NULL || data->phase2_method == NULL) {
|
||||
|
@ -883,6 +1166,7 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
|
|||
struct eap_peap_data *data = priv;
|
||||
wpabuf_free(data->pending_phase2_req);
|
||||
data->pending_phase2_req = NULL;
|
||||
data->crypto_binding_used = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -947,6 +1231,17 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
|||
return NULL;
|
||||
|
||||
*len = EAP_TLS_KEY_LEN;
|
||||
|
||||
if (data->crypto_binding_used) {
|
||||
u8 csk[128];
|
||||
peap_prfplus(data->peap_version, data->ipmk, 40,
|
||||
"Session Key Generating Function",
|
||||
(u8 *) "", 0, csk, sizeof(csk));
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
|
||||
os_memcpy(key, csk, EAP_TLS_KEY_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
|
||||
key, EAP_TLS_KEY_LEN);
|
||||
} else
|
||||
os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
|
||||
|
||||
return key;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "sha1.h"
|
||||
#include "eap_i.h"
|
||||
#include "eap_tls_common.h"
|
||||
#include "eap_common/eap_tlv_common.h"
|
||||
|
@ -41,11 +42,20 @@ struct eap_peap_data {
|
|||
} state;
|
||||
|
||||
int peap_version;
|
||||
int recv_version;
|
||||
const struct eap_method *phase2_method;
|
||||
void *phase2_priv;
|
||||
int force_version;
|
||||
struct wpabuf *pending_phase2_resp;
|
||||
enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request;
|
||||
int crypto_binding_sent;
|
||||
int crypto_binding_used;
|
||||
enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
|
||||
u8 binding_nonce[32];
|
||||
u8 ipmk[40];
|
||||
u8 cmk[20];
|
||||
u8 *phase2_key;
|
||||
size_t phase2_key_len;
|
||||
};
|
||||
|
||||
|
||||
|
@ -167,6 +177,7 @@ static void * eap_peap_init(struct eap_sm *sm)
|
|||
data->peap_version = data->force_version;
|
||||
}
|
||||
data->state = START;
|
||||
data->crypto_binding = NO_BINDING;
|
||||
|
||||
if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
|
||||
wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
|
||||
|
@ -187,6 +198,7 @@ static void eap_peap_reset(struct eap_sm *sm, void *priv)
|
|||
data->phase2_method->reset(sm, data->phase2_priv);
|
||||
eap_server_tls_ssl_deinit(sm, &data->ssl);
|
||||
wpabuf_free(data->pending_phase2_resp);
|
||||
os_free(data->phase2_key);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
@ -303,13 +315,147 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
|
|||
}
|
||||
|
||||
|
||||
static void eap_peap_get_isk(struct eap_peap_data *data,
|
||||
u8 *isk, size_t isk_len)
|
||||
{
|
||||
size_t key_len;
|
||||
|
||||
os_memset(isk, 0, isk_len);
|
||||
if (data->phase2_key == NULL)
|
||||
return;
|
||||
|
||||
key_len = data->phase2_key_len;
|
||||
if (key_len > isk_len)
|
||||
key_len = isk_len;
|
||||
os_memcpy(isk, data->phase2_key, key_len);
|
||||
}
|
||||
|
||||
|
||||
void peap_prfplus(int version, const u8 *key, size_t key_len,
|
||||
const char *label, const u8 *seed, size_t seed_len,
|
||||
u8 *buf, size_t buf_len)
|
||||
{
|
||||
unsigned char counter = 0;
|
||||
size_t pos, plen;
|
||||
u8 hash[SHA1_MAC_LEN];
|
||||
size_t label_len = os_strlen(label);
|
||||
u8 extra[2];
|
||||
const unsigned char *addr[5];
|
||||
size_t len[5];
|
||||
|
||||
addr[0] = hash;
|
||||
len[0] = 0;
|
||||
addr[1] = (unsigned char *) label;
|
||||
len[1] = label_len;
|
||||
addr[2] = seed;
|
||||
len[2] = seed_len;
|
||||
|
||||
if (version == 0) {
|
||||
/*
|
||||
* PRF+(K, S, LEN) = T1 | T2 | ... | Tn
|
||||
* T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
|
||||
* T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
|
||||
* ...
|
||||
* Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
|
||||
*/
|
||||
|
||||
extra[0] = 0;
|
||||
extra[1] = 0;
|
||||
|
||||
addr[3] = &counter;
|
||||
len[3] = 1;
|
||||
addr[4] = extra;
|
||||
len[4] = 2;
|
||||
} else {
|
||||
/*
|
||||
* PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
|
||||
* T1 = HMAC-SHA1(K, S | LEN | 0x01)
|
||||
* T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
|
||||
* T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
|
||||
* T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
|
||||
* ...
|
||||
*/
|
||||
|
||||
extra[0] = buf_len & 0xff;
|
||||
|
||||
addr[3] = extra;
|
||||
len[3] = 1;
|
||||
addr[4] = &counter;
|
||||
len[4] = 1;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < buf_len) {
|
||||
counter++;
|
||||
plen = buf_len - pos;
|
||||
hmac_sha1_vector(key, key_len, 5, addr, len, hash);
|
||||
if (plen >= SHA1_MAC_LEN) {
|
||||
os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
|
||||
pos += SHA1_MAC_LEN;
|
||||
} else {
|
||||
os_memcpy(&buf[pos], hash, plen);
|
||||
break;
|
||||
}
|
||||
len[0] = SHA1_MAC_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
|
||||
{
|
||||
u8 *tk;
|
||||
u8 isk[32], imck[60];
|
||||
|
||||
/*
|
||||
* Tunnel key (TK) is the first 60 octets of the key generated by
|
||||
* phase 1 of PEAP (based on TLS).
|
||||
*/
|
||||
tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",
|
||||
EAP_TLS_KEY_LEN);
|
||||
if (tk == NULL)
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
|
||||
|
||||
eap_peap_get_isk(data, isk, sizeof(isk));
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
|
||||
|
||||
/*
|
||||
* IPMK Seed = "Inner Methods Compound Keys" | ISK
|
||||
* TempKey = First 40 octets of TK
|
||||
* IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
|
||||
* (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
|
||||
* in the end of the label just before ISK; is that just a typo?)
|
||||
*/
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
|
||||
peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
|
||||
isk, sizeof(isk), imck, sizeof(imck));
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
|
||||
imck, sizeof(imck));
|
||||
|
||||
os_free(tk);
|
||||
|
||||
/* TODO: fast-connect: IPMK|CMK = TK */
|
||||
os_memcpy(data->ipmk, imck, 40);
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
|
||||
os_memcpy(data->cmk, imck + 40, 20);
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
|
||||
struct eap_peap_data *data,
|
||||
u8 id)
|
||||
{
|
||||
struct wpabuf *buf, *encr_req;
|
||||
size_t len;
|
||||
|
||||
buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 6,
|
||||
len = 6; /* Result TLV */
|
||||
if (data->crypto_binding != NO_BINDING)
|
||||
len += 60; /* Cryptobinding TLV */
|
||||
|
||||
buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
|
||||
EAP_CODE_REQUEST, id);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
@ -322,6 +468,50 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
|
|||
wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ?
|
||||
EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE);
|
||||
|
||||
if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
|
||||
data->crypto_binding != NO_BINDING) {
|
||||
u8 *mac;
|
||||
u8 eap_type = EAP_TYPE_PEAP;
|
||||
const u8 *addr[2];
|
||||
size_t len[2];
|
||||
u16 tlv_type;
|
||||
|
||||
if (eap_peap_derive_cmk(sm, data) < 0 ||
|
||||
os_get_random(data->binding_nonce, 32)) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
|
||||
addr[0] = wpabuf_put(buf, 0);
|
||||
len[0] = 60;
|
||||
addr[1] = &eap_type;
|
||||
len[1] = 1;
|
||||
|
||||
tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
|
||||
if (data->peap_version >= 2)
|
||||
tlv_type |= EAP_TLV_TYPE_MANDATORY;
|
||||
wpabuf_put_be16(buf, tlv_type);
|
||||
wpabuf_put_be16(buf, 56);
|
||||
|
||||
wpabuf_put_u8(buf, 0); /* Reserved */
|
||||
wpabuf_put_u8(buf, data->peap_version); /* Version */
|
||||
wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */
|
||||
wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */
|
||||
wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
|
||||
mac = wpabuf_put(buf, 20); /* Compound_MAC */
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK",
|
||||
data->cmk, 20);
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
|
||||
addr[0], len[0]);
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
|
||||
addr[1], len[1]);
|
||||
hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC",
|
||||
mac, SHA1_MAC_LEN);
|
||||
data->crypto_binding_sent = 1;
|
||||
}
|
||||
|
||||
wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data",
|
||||
buf);
|
||||
|
||||
|
@ -423,14 +613,66 @@ static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
|
|||
}
|
||||
|
||||
|
||||
static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
|
||||
struct eap_peap_data *data,
|
||||
const u8 *crypto_tlv,
|
||||
size_t crypto_tlv_len)
|
||||
{
|
||||
u8 buf[61], mac[SHA1_MAC_LEN];
|
||||
const u8 *pos;
|
||||
|
||||
if (crypto_tlv_len != 4 + 56) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
|
||||
"length %d", (int) crypto_tlv_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = crypto_tlv;
|
||||
pos += 4; /* TLV header */
|
||||
if (pos[1] != data->peap_version) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
|
||||
"mismatch (was %d; expected %d)",
|
||||
pos[1], data->peap_version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos[3] != 1) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
|
||||
"SubType %d", pos[3]);
|
||||
return -1;
|
||||
}
|
||||
pos += 4;
|
||||
pos += 32; /* Nonce */
|
||||
|
||||
/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
|
||||
os_memcpy(buf, crypto_tlv, 60);
|
||||
os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
|
||||
buf[60] = EAP_TYPE_PEAP;
|
||||
hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
|
||||
|
||||
if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
|
||||
"cryptobinding TLV");
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20);
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data",
|
||||
buf, 61);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
||||
struct eap_peap_data *data,
|
||||
struct wpabuf *in_data)
|
||||
{
|
||||
const u8 *pos;
|
||||
size_t left;
|
||||
const u8 *result_tlv = NULL;
|
||||
size_t result_tlv_len = 0;
|
||||
const u8 *result_tlv = NULL, *crypto_tlv = NULL;
|
||||
size_t result_tlv_len = 0, crypto_tlv_len = 0;
|
||||
int tlv_type, mandatory, tlv_len;
|
||||
|
||||
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left);
|
||||
|
@ -440,7 +682,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
|||
}
|
||||
|
||||
/* Parse TLVs */
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left);
|
||||
while (left >= 4) {
|
||||
mandatory = !!(pos[0] & 0x80);
|
||||
tlv_type = pos[0] & 0x3f;
|
||||
|
@ -449,7 +691,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
|||
pos += 4;
|
||||
left -= 4;
|
||||
if ((size_t) tlv_len > left) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
|
||||
"(tlv_len=%d left=%lu)", tlv_len,
|
||||
(unsigned long) left);
|
||||
eap_peap_state(data, FAILURE);
|
||||
|
@ -460,8 +702,12 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
|||
result_tlv = pos;
|
||||
result_tlv_len = tlv_len;
|
||||
break;
|
||||
case EAP_TLV_CRYPTO_BINDING_TLV:
|
||||
crypto_tlv = pos;
|
||||
crypto_tlv_len = tlv_len;
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
|
||||
"%d%s", tlv_type,
|
||||
mandatory ? " (mandatory)" : "");
|
||||
if (mandatory) {
|
||||
|
@ -476,21 +722,37 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
|||
left -= tlv_len;
|
||||
}
|
||||
if (left) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
|
||||
"Request (left=%lu)", (unsigned long) left);
|
||||
eap_peap_state(data, FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Process supported TLVs */
|
||||
if (crypto_tlv && data->crypto_binding_sent) {
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
|
||||
crypto_tlv, crypto_tlv_len);
|
||||
if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
|
||||
crypto_tlv_len + 4) < 0) {
|
||||
eap_peap_state(data, FAILURE);
|
||||
return;
|
||||
}
|
||||
data->crypto_binding_used = 1;
|
||||
} else if (!crypto_tlv && data->crypto_binding_sent &&
|
||||
data->crypto_binding == REQUIRE_BINDING) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
|
||||
eap_peap_state(data, FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result_tlv) {
|
||||
int status;
|
||||
const char *requested;
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV",
|
||||
result_tlv, result_tlv_len);
|
||||
if (result_tlv_len < 2) {
|
||||
wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
|
||||
wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV "
|
||||
"(len=%lu)",
|
||||
(unsigned long) result_tlv_len);
|
||||
eap_peap_state(data, FAILURE);
|
||||
|
@ -500,7 +762,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
|||
"Failure";
|
||||
status = WPA_GET_BE16(result_tlv);
|
||||
if (status == EAP_TLV_RESULT_SUCCESS) {
|
||||
wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
|
||||
wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success "
|
||||
"- requested %s", requested);
|
||||
if (data->tlv_request == TLV_REQ_SUCCESS)
|
||||
eap_peap_state(data, SUCCESS);
|
||||
|
@ -508,11 +770,11 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
|
|||
eap_peap_state(data, FAILURE);
|
||||
|
||||
} else if (status == EAP_TLV_RESULT_FAILURE) {
|
||||
wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - "
|
||||
"requested %s", requested);
|
||||
wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
|
||||
"- requested %s", requested);
|
||||
eap_peap_state(data, FAILURE);
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
|
||||
wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result "
|
||||
"Status %d", status);
|
||||
eap_peap_state(data, FAILURE);
|
||||
}
|
||||
|
@ -589,6 +851,19 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm,
|
|||
return;
|
||||
}
|
||||
|
||||
os_free(data->phase2_key);
|
||||
if (data->phase2_method->getKey) {
|
||||
data->phase2_key = data->phase2_method->getKey(
|
||||
sm, data->phase2_priv, &data->phase2_key_len);
|
||||
if (data->phase2_key == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey "
|
||||
"failed");
|
||||
eap_peap_req_failure(sm, data);
|
||||
eap_peap_phase2_init(sm, data, EAP_TYPE_NONE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (data->state) {
|
||||
case PHASE1_ID2:
|
||||
case PHASE2_ID:
|
||||
|
@ -881,7 +1156,7 @@ static void eap_peap_process(struct eap_sm *sm, void *priv,
|
|||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - "
|
||||
"Flags 0x%02x", (unsigned long) wpabuf_len(respData),
|
||||
flags);
|
||||
peer_version = flags & EAP_PEAP_VERSION_MASK;
|
||||
data->recv_version = peer_version = flags & EAP_PEAP_VERSION_MASK;
|
||||
if (data->force_version >= 0 && peer_version != data->force_version) {
|
||||
wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced"
|
||||
" version (forced=%d peer=%d) - reject",
|
||||
|
@ -979,6 +1254,26 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
|||
if (data->state != SUCCESS)
|
||||
return NULL;
|
||||
|
||||
if (data->crypto_binding_used) {
|
||||
u8 csk[128];
|
||||
peap_prfplus(data->peap_version, data->ipmk, 40,
|
||||
"Session Key Generating Function",
|
||||
(u8 *) "", 0, csk, sizeof(csk));
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
|
||||
eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
|
||||
if (eapKeyData) {
|
||||
os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN);
|
||||
*len = EAP_TLS_KEY_LEN;
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
|
||||
eapKeyData, EAP_TLS_KEY_LEN);
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive "
|
||||
"key");
|
||||
}
|
||||
|
||||
return eapKeyData;
|
||||
}
|
||||
|
||||
/* TODO: PEAPv1 - different label in some cases */
|
||||
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
|
||||
"client EAP encryption",
|
||||
|
|
Loading…
Reference in a new issue