wpa_supplicant: Initial Revision 0.8.X
Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date: Thu Apr 21 20:41:01 2011 +0300
Enable CONFIG_DRIVER_NL80211=y in the default configuration
nl80211 should be preferred over WEXT with any recent Linux
kernel version.
Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/eap_common/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/eap_common/chap.c b/src/eap_common/chap.c
new file mode 100644
index 0000000..60bfc1c
--- /dev/null
+++ b/src/eap_common/chap.c
@@ -0,0 +1,34 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "chap.h"
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+ size_t challenge_len, u8 *response)
+{
+ const u8 *addr[3];
+ size_t len[3];
+
+ addr[0] = &id;
+ len[0] = 1;
+ addr[1] = secret;
+ len[1] = secret_len;
+ addr[2] = challenge;
+ len[2] = challenge_len;
+ return md5_vector(3, addr, len, response);
+}
diff --git a/src/eap_common/chap.h b/src/eap_common/chap.h
new file mode 100644
index 0000000..b9c400c
--- /dev/null
+++ b/src/eap_common/chap.h
@@ -0,0 +1,23 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+ size_t challenge_len, u8 *response);
+
+#endif /* CHAP_H */
diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c
new file mode 100644
index 0000000..4afa1dd
--- /dev/null
+++ b/src/eap_common/eap_common.c
@@ -0,0 +1,184 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen)
+{
+ const struct eap_hdr *hdr;
+ const u8 *pos;
+ size_t len;
+
+ hdr = wpabuf_head(msg);
+
+ if (wpabuf_len(msg) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+ return NULL;
+ }
+
+ len = be_to_host16(hdr->length);
+ if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) {
+ wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+ return NULL;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+
+ if (*pos == EAP_TYPE_EXPANDED) {
+ int exp_vendor;
+ u32 exp_type;
+ if (len < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+ "length");
+ return NULL;
+ }
+ pos++;
+ exp_vendor = WPA_GET_BE24(pos);
+ pos += 3;
+ exp_type = WPA_GET_BE32(pos);
+ pos += 4;
+ if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+ "type");
+ return NULL;
+ }
+
+ *plen = len - sizeof(*hdr) - 8;
+ return pos;
+ } else {
+ if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+ return NULL;
+ }
+ *plen = len - sizeof(*hdr) - 1;
+ return pos + 1;
+ }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ struct wpabuf *buf;
+ struct eap_hdr *hdr;
+ size_t len;
+
+ len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+ payload_len;
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ hdr = wpabuf_put(buf, sizeof(*hdr));
+ hdr->code = code;
+ hdr->identifier = identifier;
+ hdr->length = host_to_be16(len);
+
+ if (vendor == EAP_VENDOR_IETF) {
+ wpabuf_put_u8(buf, type);
+ } else {
+ wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(buf, vendor);
+ wpabuf_put_be32(buf, type);
+ }
+
+ return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+ struct eap_hdr *hdr;
+ hdr = wpabuf_mhead(msg);
+ if (wpabuf_len(msg) < sizeof(*hdr))
+ return;
+ hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+ const struct eap_hdr *eap;
+
+ if (wpabuf_len(msg) < sizeof(*eap))
+ return 0;
+
+ eap = wpabuf_head(msg);
+ return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+ if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+ return EAP_TYPE_NONE;
+
+ return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h
new file mode 100644
index 0000000..b95e76b
--- /dev/null
+++ b/src/eap_common/eap_common.h
@@ -0,0 +1,28 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_COMMON_H
+#define EAP_COMMON_H
+
+#include "wpabuf.h"
+
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen);
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier);
+void eap_update_len(struct wpabuf *msg);
+u8 eap_get_id(const struct wpabuf *msg);
+EapType eap_get_type(const struct wpabuf *msg);
+
+#endif /* EAP_COMMON_H */
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
new file mode 100644
index 0000000..3035301
--- /dev/null
+++ b/src/eap_common/eap_defs.h
@@ -0,0 +1,86 @@
+/*
+ * EAP server/peer: Shared EAP definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_DEFS_H
+#define EAP_DEFS_H
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_hdr {
+ u8 code;
+ u8 identifier;
+ be16 length; /* including code and identifier; network byte order */
+ /* followed by length-4 octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
+ EAP_CODE_FAILURE = 4 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
+/*
+ * EAP Method Types as allocated by IANA:
+ * http://www.iana.org/assignments/eap-numbers
+ */
+typedef enum {
+ EAP_TYPE_NONE = 0,
+ EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+ EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
+ EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
+ EAP_TYPE_MD5 = 4, /* RFC 3748 */
+ EAP_TYPE_OTP = 5 /* RFC 3748 */,
+ EAP_TYPE_GTC = 6, /* RFC 3748 */
+ EAP_TYPE_TLS = 13 /* RFC 2716 */,
+ EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+ EAP_TYPE_SIM = 18 /* RFC 4186 */,
+ EAP_TYPE_TTLS = 21 /* RFC 5281 */,
+ EAP_TYPE_AKA = 23 /* RFC 4187 */,
+ EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+ EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+ EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+ EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment;
+ * type 38 has previously been allocated for
+ * EAP-HTTP Digest, (funk.com) */,
+ EAP_TYPE_FAST = 43 /* RFC 4851 */,
+ EAP_TYPE_PAX = 46 /* RFC 4746 */,
+ EAP_TYPE_PSK = 47 /* RFC 4764 */,
+ EAP_TYPE_SAKE = 48 /* RFC 4763 */,
+ EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
+ EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
+ EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+ EAP_TYPE_PWD = 52 /* RFC 5931 */,
+ EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+ EAP_VENDOR_IETF = 0,
+ EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
+ EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
+};
+
+#define EAP_MSK_LEN 64
+#define EAP_EMSK_LEN 64
+
+#endif /* EAP_DEFS_H */
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
new file mode 100644
index 0000000..4de34a8
--- /dev/null
+++ b/src/eap_common/eap_fast_common.c
@@ -0,0 +1,304 @@
+/*
+ * EAP-FAST common helper functions (RFC 4851)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_defs.h"
+#include "eap_tlv_common.h"
+#include "eap_fast_common.h"
+
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+ struct pac_tlv_hdr hdr;
+ hdr.type = host_to_be16(type);
+ hdr.len = host_to_be16(len);
+ wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+ u16 len)
+{
+ eap_fast_put_tlv_hdr(buf, type, len);
+ wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data)
+{
+ eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
+ wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
+ if (e == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ eap_fast_put_tlv_buf(e,
+ EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
+ buf);
+ wpabuf_free(buf);
+ return e;
+}
+
+
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+ const u8 *client_random, u8 *master_secret)
+{
+#define TLS_RANDOM_LEN 32
+#define TLS_MASTER_SECRET_LEN 48
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+ client_random, TLS_RANDOM_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+ server_random, TLS_RANDOM_LEN);
+
+ /*
+ * RFC 4851, Section 5.1:
+ * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+ * server_random + client_random, 48)
+ */
+ os_memcpy(seed, server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
+ sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
+ "PAC to master secret label hash",
+ seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
+ master_secret, TLS_MASTER_SECRET_LEN);
+}
+
+
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+ const char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+ int block_size;
+
+ block_size = tls_connection_get_keyblock_size(ssl_ctx, conn);
+ if (block_size < 0)
+ return NULL;
+
+ out = os_malloc(block_size + len);
+ if (out == NULL)
+ return NULL;
+
+ if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len)
+ == 0) {
+ os_memmove(out, out + block_size, len);
+ return out;
+ }
+
+ if (tls_connection_get_keys(ssl_ctx, conn, &keys))
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
+ "expansion", keys.master_key, keys.master_key_len);
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, block_size + len))
+ goto fail;
+ os_free(rnd);
+ os_memmove(out, out + block_size, len);
+ return out;
+
+fail:
+ os_free(rnd);
+ os_free(out);
+ return NULL;
+}
+
+
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
+{
+ /*
+ * RFC 4851, Section 5.4: EAP Master Session Key Generation
+ * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
+ */
+
+ sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+ "Session Key Generating Function", (u8 *) "", 0,
+ msk, EAP_FAST_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+ msk, EAP_FAST_KEY_LEN);
+}
+
+
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+{
+ /*
+ * RFC 4851, Section 5.4: EAP Master Session Key Genreration
+ * EMSK = T-PRF(S-IMCK[j],
+ * "Extended Session Key Generating Function", 64)
+ */
+
+ sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+ "Extended Session Key Generating Function", (u8 *) "", 0,
+ emsk, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
+ emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+ int tlv_type, u8 *pos, int len)
+{
+ switch (tlv_type) {
+ case EAP_TLV_EAP_PAYLOAD_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
+ pos, len);
+ if (tlv->eap_payload_tlv) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "EAP-Payload TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->eap_payload_tlv = pos;
+ tlv->eap_payload_tlv_len = len;
+ break;
+ case EAP_TLV_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
+ if (tlv->result) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Result TLV in the message");
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Result TLV");
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ tlv->result = WPA_GET_BE16(pos);
+ if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
+ tlv->result != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
+ tlv->result);
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+ tlv->result == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
+ pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Intermediate-Result TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ if (tlv->iresult) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Intermediate-Result TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->iresult = WPA_GET_BE16(pos);
+ if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
+ tlv->iresult != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
+ "Result %d", tlv->iresult);
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
+ tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
+ pos, len);
+ if (tlv->crypto_binding) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Crypto-Binding TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+ if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Crypto-Binding TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
+ (pos - sizeof(struct eap_tlv_hdr));
+ break;
+ case EAP_TLV_REQUEST_ACTION_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
+ pos, len);
+ if (tlv->request_action) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Request-Action TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Request-Action TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ tlv->request_action = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
+ tlv->request_action);
+ break;
+ case EAP_TLV_PAC_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
+ if (tlv->pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "PAC TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->pac = pos;
+ tlv->pac_len = len;
+ break;
+ default:
+ /* Unknown TLV */
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
new file mode 100644
index 0000000..c85fd37
--- /dev/null
+++ b/src/eap_common/eap_fast_common.h
@@ -0,0 +1,113 @@
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_FAST_H
+#define EAP_FAST_H
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+#define EAP_FAST_CMK_LEN 20
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general PAC TLV format (Section 4.2).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/*
+ * 6 was previous assigned for SERVER_PROTECTED_DATA, but
+ * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
+ */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_tlv_hdr {
+ be16 type;
+ be16 len;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+#define EAP_FAST_PAC_KEY_LEN 32
+
+/* RFC 5422: 4.2.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+/* Application Specific Short Lived PACs (only in volatile storage) */
+/* User Authorization PAC */
+#define PAC_TYPE_USER_AUTHORIZATION 3
+/* Application Specific Long Lived PACs */
+/* Machine Authentication PAC */
+#define PAC_TYPE_MACHINE_AUTHENTICATION 2
+
+
+/*
+ * RFC 5422:
+ * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
+ */
+struct eap_fast_key_block_provisioning {
+ /* Extra key material after TLS key_block */
+ u8 session_key_seed[EAP_FAST_SKS_LEN];
+ u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
+ u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
+};
+
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_fast_tlv_parse {
+ u8 *eap_payload_tlv;
+ size_t eap_payload_tlv_len;
+ struct eap_tlv_crypto_binding_tlv *crypto_binding;
+ size_t crypto_binding_len;
+ int iresult;
+ int result;
+ int request_action;
+ u8 *pac;
+ size_t pac_len;
+};
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+ u16 len);
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data);
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+ const u8 *client_random, u8 *master_secret);
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+ const char *label, size_t len);
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+ int tlv_type, u8 *pos, int len);
+
+#endif /* EAP_FAST_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
new file mode 100644
index 0000000..4076262
--- /dev/null
+++ b/src/eap_common/eap_gpsk_common.c
@@ -0,0 +1,423 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha256.h"
+#include "eap_defs.h"
+#include "eap_gpsk_common.h"
+
+
+/**
+ * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: 1 if ciphersuite is support, or 0 if not
+ */
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
+{
+ if (vendor == EAP_GPSK_VENDOR_IETF &&
+ specifier == EAP_GPSK_CIPHER_AES)
+ return 1;
+#ifdef EAP_GPSK_SHA256
+ if (vendor == EAP_GPSK_VENDOR_IETF &&
+ specifier == EAP_GPSK_CIPHER_SHA256)
+ return 1;
+#endif /* EAP_GPSK_SHA256 */
+ return 0;
+}
+
+
+static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
+ const u8 *data /* Z */, size_t data_len,
+ u8 *buf, size_t len /* X */)
+{
+ u8 *opos;
+ size_t i, n, hashlen, left, clen;
+ u8 ibuf[2], hash[16];
+ const u8 *addr[2];
+ size_t vlen[2];
+
+ hashlen = sizeof(hash);
+ /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
+ addr[0] = ibuf;
+ vlen[0] = sizeof(ibuf);
+ addr[1] = data;
+ vlen[1] = data_len;
+
+ opos = buf;
+ left = len;
+ n = (len + hashlen - 1) / hashlen;
+ for (i = 1; i <= n; i++) {
+ WPA_PUT_BE16(ibuf, i);
+ if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
+ return -1;
+ clen = left > hashlen ? hashlen : left;
+ os_memcpy(opos, hash, clen);
+ opos += clen;
+ left -= clen;
+ }
+
+ return 0;
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
+ const u8 *data /* Z */, size_t data_len,
+ u8 *buf, size_t len /* X */)
+{
+ u8 *opos;
+ size_t i, n, hashlen, left, clen;
+ u8 ibuf[2], hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t vlen[2];
+
+ hashlen = SHA256_MAC_LEN;
+ /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
+ addr[0] = ibuf;
+ vlen[0] = sizeof(ibuf);
+ addr[1] = data;
+ vlen[1] = data_len;
+
+ opos = buf;
+ left = len;
+ n = (len + hashlen - 1) / hashlen;
+ for (i = 1; i <= n; i++) {
+ WPA_PUT_BE16(ibuf, i);
+ hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
+ clen = left > hashlen ? hashlen : left;
+ os_memcpy(opos, hash, clen);
+ opos += clen;
+ left -= clen;
+ }
+
+ return 0;
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
+ u8 *kdf_out, size_t kdf_out_len,
+ const u8 *psk, size_t psk_len,
+ const u8 *seed, size_t seed_len,
+ u8 *msk, u8 *emsk,
+ u8 *sk, size_t sk_len,
+ u8 *pk, size_t pk_len)
+{
+ u8 mk[32], *pos, *data;
+ size_t data_len, mk_len;
+ int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
+ u8 *buf, size_t len);
+
+ gkdf = NULL;
+ switch (csuite_specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ gkdf = eap_gpsk_gkdf_cmac;
+ mk_len = 16;
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ gkdf = eap_gpsk_gkdf_sha256;
+ mk_len = SHA256_MAC_LEN;
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ return -1;
+ }
+
+ if (psk_len < mk_len)
+ return -1;
+
+ data_len = 2 + psk_len + 6 + seed_len;
+ data = os_malloc(data_len);
+ if (data == NULL)
+ return -1;
+ pos = data;
+ WPA_PUT_BE16(pos, psk_len);
+ pos += 2;
+ os_memcpy(pos, psk, psk_len);
+ pos += psk_len;
+ WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
+ pos += 4;
+ WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
+ pos += 2;
+ os_memcpy(pos, seed, seed_len); /* inputString */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
+ data, data_len);
+
+ if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
+ os_free(data);
+ return -1;
+ }
+ os_free(data);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
+
+ if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
+ return -1;
+
+ pos = kdf_out;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
+ os_memcpy(msk, pos, EAP_MSK_LEN);
+ pos += EAP_MSK_LEN;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
+ os_memcpy(emsk, pos, EAP_EMSK_LEN);
+ pos += EAP_EMSK_LEN;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
+ os_memcpy(sk, pos, sk_len);
+ pos += sk_len;
+
+ if (pk) {
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
+ os_memcpy(pk, pos, pk_len);
+ }
+
+ return 0;
+}
+
+
+static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
+ const u8 *seed, size_t seed_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len)
+{
+#define EAP_GPSK_SK_LEN_AES 16
+#define EAP_GPSK_PK_LEN_AES 16
+ u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
+ EAP_GPSK_PK_LEN_AES];
+
+ /*
+ * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+ * (= seed)
+ * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
+ * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
+ * MSK = GKDF-160 (MK, inputString)[0..63]
+ * EMSK = GKDF-160 (MK, inputString)[64..127]
+ * SK = GKDF-160 (MK, inputString)[128..143]
+ * PK = GKDF-160 (MK, inputString)[144..159]
+ * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
+ * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+ * CSuite_Sel || inputString)
+ */
+
+ *sk_len = EAP_GPSK_SK_LEN_AES;
+ *pk_len = EAP_GPSK_PK_LEN_AES;
+
+ return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
+ kdf_out, sizeof(kdf_out),
+ psk, psk_len, seed, seed_len,
+ msk, emsk, sk, *sk_len,
+ pk, *pk_len);
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
+ const u8 *seed, size_t seed_len,
+ u8 *msk, u8 *emsk,
+ u8 *sk, size_t *sk_len)
+{
+#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
+#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
+ u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
+ EAP_GPSK_PK_LEN_SHA256];
+
+ /*
+ * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+ * (= seed)
+ * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
+ * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
+ * MSK = GKDF-160 (MK, inputString)[0..63]
+ * EMSK = GKDF-160 (MK, inputString)[64..127]
+ * SK = GKDF-160 (MK, inputString)[128..159]
+ * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
+ * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+ * CSuite_Sel || inputString)
+ */
+
+ *sk_len = EAP_GPSK_SK_LEN_SHA256;
+
+ return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
+ kdf_out, sizeof(kdf_out),
+ psk, psk_len, seed, seed_len,
+ msk, emsk, sk, *sk_len,
+ NULL, 0);
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+/**
+ * eap_gpsk_derive_keys - Derive EAP-GPSK keys
+ * @psk: Pre-shared key
+ * @psk_len: Length of psk in bytes
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @rand_peer: 32-byte RAND_Peer
+ * @rand_server: 32-byte RAND_Server
+ * @id_peer: ID_Peer
+ * @id_peer_len: Length of ID_Peer
+ * @id_server: ID_Server
+ * @id_server_len: Length of ID_Server
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
+ * @sk_len: Buffer for returning length of SK
+ * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
+ * @pk_len: Buffer for returning length of PK
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_peer, const u8 *rand_server,
+ const u8 *id_peer, size_t id_peer_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len)
+{
+ u8 *seed, *pos;
+ size_t seed_len;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
+ vendor, specifier);
+
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
+
+ /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
+ seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
+ seed = os_malloc(seed_len);
+ if (seed == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
+ "for key derivation");
+ return -1;
+ }
+
+ pos = seed;
+ os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+ os_memcpy(pos, id_peer, id_peer_len);
+ pos += id_peer_len;
+ os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+ os_memcpy(pos, id_server, id_server_len);
+ pos += id_server_len;
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+
+ switch (specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
+ msk, emsk, sk, sk_len,
+ pk, pk_len);
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
+ msk, emsk, sk, sk_len);
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+ "key derivation", vendor, specifier);
+ ret = -1;
+ break;
+ }
+
+ os_free(seed);
+
+ return ret;
+}
+
+
+/**
+ * eap_gpsk_mic_len - Get the length of the MIC
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: MIC length in bytes
+ */
+size_t eap_gpsk_mic_len(int vendor, int specifier)
+{
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return 0;
+
+ switch (specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ return 16;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ return 32;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ return 0;
+ }
+}
+
+
+static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
+ const u8 *data, size_t len, u8 *mic)
+{
+ if (sk_len != 16) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
+ "AES-CMAC MIC", (unsigned long) sk_len);
+ return -1;
+ }
+
+ return omac1_aes_128(sk, data, len, mic);
+}
+
+
+/**
+ * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
+ * @sk: Session key SK from eap_gpsk_derive_keys()
+ * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @data: Input data to MIC
+ * @len: Input data length in bytes
+ * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+ int specifier, const u8 *data, size_t len, u8 *mic)
+{
+ int ret;
+
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return -1;
+
+ switch (specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ hmac_sha256(sk, sk_len, data, len, mic);
+ ret = 0;
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+ "MIC computation", vendor, specifier);
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
diff --git a/src/eap_common/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h
new file mode 100644
index 0000000..a30ab97
--- /dev/null
+++ b/src/eap_common/eap_gpsk_common.h
@@ -0,0 +1,66 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_GPSK_COMMON_H
+#define EAP_GPSK_COMMON_H
+
+#define EAP_GPSK_OPCODE_GPSK_1 1
+#define EAP_GPSK_OPCODE_GPSK_2 2
+#define EAP_GPSK_OPCODE_GPSK_3 3
+#define EAP_GPSK_OPCODE_GPSK_4 4
+#define EAP_GPSK_OPCODE_FAIL 5
+#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6
+
+/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */
+#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001
+#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002
+#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003
+
+#define EAP_GPSK_RAND_LEN 32
+#define EAP_GPSK_MAX_SK_LEN 32
+#define EAP_GPSK_MAX_PK_LEN 32
+#define EAP_GPSK_MAX_MIC_LEN 32
+
+#define EAP_GPSK_VENDOR_IETF 0x00000000
+#define EAP_GPSK_CIPHER_RESERVED 0x000000
+#define EAP_GPSK_CIPHER_AES 0x000001
+#define EAP_GPSK_CIPHER_SHA256 0x000002
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_gpsk_csuite {
+ u8 vendor[4];
+ u8 specifier[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier);
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_client, const u8 *rand_server,
+ const u8 *id_client, size_t id_client_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len);
+size_t eap_gpsk_mic_len(int vendor, int specifier);
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+ int specifier, const u8 *data, size_t len, u8 *mic);
+
+#endif /* EAP_GPSK_COMMON_H */
diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c
new file mode 100644
index 0000000..e9a9c55
--- /dev/null
+++ b/src/eap_common/eap_ikev2_common.c
@@ -0,0 +1,132 @@
+/*
+ * EAP-IKEv2 common routines
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "ikev2_common.h"
+#include "eap_ikev2_common.h"
+
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+ const u8 *i_nonce, size_t i_nonce_len,
+ const u8 *r_nonce, size_t r_nonce_len,
+ u8 *keymat)
+{
+ u8 *nonces;
+ size_t nlen;
+
+ /* KEYMAT = prf+(SK_d, Ni | Nr) */
+ if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL)
+ return -1;
+
+ nlen = i_nonce_len + r_nonce_len;
+ nonces = os_malloc(nlen);
+ if (nonces == NULL)
+ return -1;
+ os_memcpy(nonces, i_nonce, i_nonce_len);
+ os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len);
+
+ if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen,
+ keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) {
+ os_free(nonces);
+ return -1;
+ }
+ os_free(nonces);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT",
+ keymat, EAP_MSK_LEN + EAP_EMSK_LEN);
+
+ return 0;
+}
+
+
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
+{
+ struct wpabuf *msg;
+
+#ifdef CCNS_PL
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
+ "for fragment ack");
+ return NULL;
+ }
+ wpabuf_put_u8(msg, 0); /* Flags */
+#else /* CCNS_PL */
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
+ "for fragment ack");
+ return NULL;
+ }
+#endif /* CCNS_PL */
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
+
+ return msg;
+}
+
+
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+ int initiator, const struct wpabuf *msg,
+ const u8 *pos, const u8 *end)
+{
+ const struct ikev2_integ_alg *integ;
+ size_t icv_len;
+ u8 icv[IKEV2_MAX_HASH_LEN];
+ const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+ integ = ikev2_get_integ(integ_alg);
+ if (integ == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+ "transform / cannot validate ICV");
+ return -1;
+ }
+ icv_len = integ->hash_len;
+
+ if (end - pos < (int) icv_len) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the "
+ "message for Integrity Checksum Data");
+ return -1;
+ }
+
+ if (SK_a == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation");
+ return -1;
+ }
+
+ if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len,
+ wpabuf_head(msg),
+ wpabuf_len(msg) - icv_len, icv) < 0) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV");
+ return -1;
+ }
+
+ if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
+ wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
+ icv, icv_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV",
+ end - icv_len, icv_len);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in "
+ "the received message");
+
+ return icv_len;
+}
diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h
new file mode 100644
index 0000000..a9fc2ca
--- /dev/null
+++ b/src/eap_common/eap_ikev2_common.h
@@ -0,0 +1,42 @@
+/*
+ * EAP-IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_IKEV2_COMMON_H
+#define EAP_IKEV2_COMMON_H
+
+#ifdef CCNS_PL
+/* incorrect bit order */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02
+#define IKEV2_FLAGS_ICV_INCLUDED 0x04
+#else /* CCNS_PL */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
+#define IKEV2_FLAGS_ICV_INCLUDED 0x20
+#endif /* CCNS_PL */
+
+#define IKEV2_FRAGMENT_SIZE 1400
+
+struct ikev2_keys;
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+ const u8 *i_nonce, size_t i_nonce_len,
+ const u8 *r_nonce, size_t r_nonce_len,
+ u8 *keymat);
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code);
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+ int initiator, const struct wpabuf *msg,
+ const u8 *pos, const u8 *end);
+
+#endif /* EAP_IKEV2_COMMON_H */
diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c
new file mode 100644
index 0000000..32dc80c
--- /dev/null
+++ b/src/eap_common/eap_pax_common.c
@@ -0,0 +1,150 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_pax_common.h"
+
+
+/**
+ * eap_pax_kdf - PAX Key Derivation Function
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key (X)
+ * @key_len: Length of the secret key in bytes
+ * @identifier: Public identifier for the key (Y)
+ * @entropy: Exchanged entropy to seed the KDF (Z)
+ * @entropy_len: Length of the entropy in bytes
+ * @output_len: Output len in bytes (W)
+ * @output: Buffer for the derived key
+ * Returns: 0 on success, -1 failed
+ *
+ * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z)
+ */
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+ const char *identifier,
+ const u8 *entropy, size_t entropy_len,
+ size_t output_len, u8 *output)
+{
+ u8 mac[SHA1_MAC_LEN];
+ u8 counter, *pos;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_blocks, left;
+
+ num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN;
+ if (identifier == NULL || num_blocks >= 255)
+ return -1;
+
+ /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+ return -1;
+
+ addr[0] = (const u8 *) identifier;
+ len[0] = os_strlen(identifier);
+ addr[1] = entropy;
+ len[1] = entropy_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = output;
+ left = output_len;
+ for (counter = 1; counter <= (u8) num_blocks; counter++) {
+ size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left;
+ hmac_sha1_vector(key, key_len, 3, addr, len, mac);
+ os_memcpy(pos, mac, clen);
+ pos += clen;
+ left -= clen;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_pax_mac - EAP-PAX MAC
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key
+ * @key_len: Length of the secret key in bytes
+ * @data1: Optional data, first block; %NULL if not used
+ * @data1_len: Length of data1 in bytes
+ * @data2: Optional data, second block; %NULL if not used
+ * @data2_len: Length of data2 in bytes
+ * @data3: Optional data, third block; %NULL if not used
+ * @data3_len: Length of data3 in bytes
+ * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Wrapper function to calculate EAP-PAX MAC.
+ */
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+ const u8 *data1, size_t data1_len,
+ const u8 *data2, size_t data2_len,
+ const u8 *data3, size_t data3_len,
+ u8 *mac)
+{
+ u8 hash[SHA1_MAC_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t count;
+
+ /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+ return -1;
+
+ addr[0] = data1;
+ len[0] = data1_len;
+ addr[1] = data2;
+ len[1] = data2_len;
+ addr[2] = data3;
+ len[2] = data3_len;
+
+ count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0);
+ hmac_sha1_vector(key, key_len, count, addr, len, hash);
+ os_memcpy(mac, hash, EAP_PAX_MAC_LEN);
+
+ return 0;
+}
+
+
+/**
+ * eap_pax_initial_key_derivation - EAP-PAX initial key derivation
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @ak: Authentication Key
+ * @e: Entropy
+ * @mk: Buffer for the derived Master Key
+ * @ck: Buffer for the derived Confirmation Key
+ * @ick: Buffer for the derived Integrity Check Key
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+ u8 *mk, u8 *ck, u8 *ick)
+{
+ wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation");
+ if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key",
+ e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) ||
+ eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key",
+ e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) ||
+ eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key",
+ e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick))
+ return -1;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN);
+
+ return 0;
+}
diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h
new file mode 100644
index 0000000..dcc171e
--- /dev/null
+++ b/src/eap_common/eap_pax_common.h
@@ -0,0 +1,97 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PAX_COMMON_H
+#define EAP_PAX_COMMON_H
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_pax_hdr {
+ u8 op_code;
+ u8 flags;
+ u8 mac_id;
+ u8 dh_group_id;
+ u8 public_key_id;
+ /* Followed by variable length payload and ICV */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* op_code: */
+enum {
+ EAP_PAX_OP_STD_1 = 0x01,
+ EAP_PAX_OP_STD_2 = 0x02,
+ EAP_PAX_OP_STD_3 = 0x03,
+ EAP_PAX_OP_SEC_1 = 0x11,
+ EAP_PAX_OP_SEC_2 = 0x12,
+ EAP_PAX_OP_SEC_3 = 0x13,
+ EAP_PAX_OP_SEC_4 = 0x14,
+ EAP_PAX_OP_SEC_5 = 0x15,
+ EAP_PAX_OP_ACK = 0x21
+};
+
+/* flags: */
+#define EAP_PAX_FLAGS_MF 0x01
+#define EAP_PAX_FLAGS_CE 0x02
+#define EAP_PAX_FLAGS_AI 0x04
+
+/* mac_id: */
+#define EAP_PAX_MAC_HMAC_SHA1_128 0x01
+#define EAP_PAX_HMAC_SHA256_128 0x02
+
+/* dh_group_id: */
+#define EAP_PAX_DH_GROUP_NONE 0x00
+#define EAP_PAX_DH_GROUP_2048_MODP 0x01
+#define EAP_PAX_DH_GROUP_3072_MODP 0x02
+#define EAP_PAX_DH_GROUP_NIST_ECC_P_256 0x03
+
+/* public_key_id: */
+#define EAP_PAX_PUBLIC_KEY_NONE 0x00
+#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP 0x01
+#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 0x02
+#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC 0x03
+
+/* ADE type: */
+#define EAP_PAX_ADE_VENDOR_SPECIFIC 0x01
+#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING 0x02
+#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING 0x03
+
+
+#define EAP_PAX_RAND_LEN 32
+#define EAP_PAX_MAC_LEN 16
+#define EAP_PAX_ICV_LEN 16
+#define EAP_PAX_AK_LEN 16
+#define EAP_PAX_MK_LEN 16
+#define EAP_PAX_CK_LEN 16
+#define EAP_PAX_ICK_LEN 16
+
+
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+ const char *identifier,
+ const u8 *entropy, size_t entropy_len,
+ size_t output_len, u8 *output);
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+ const u8 *data1, size_t data1_len,
+ const u8 *data2, size_t data2_len,
+ const u8 *data3, size_t data3_len,
+ u8 *mac);
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+ u8 *mk, u8 *ck, u8 *ick);
+
+#endif /* EAP_PAX_COMMON_H */
diff --git a/src/eap_common/eap_peap_common.c b/src/eap_common/eap_peap_common.c
new file mode 100644
index 0000000..3a64b8e
--- /dev/null
+++ b/src/eap_common/eap_peap_common.c
@@ -0,0 +1,88 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_peap_common.h"
+
+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;
+ }
+}
diff --git a/src/eap_common/eap_peap_common.h b/src/eap_common/eap_peap_common.h
new file mode 100644
index 0000000..f59afb0
--- /dev/null
+++ b/src/eap_common/eap_peap_common.h
@@ -0,0 +1,22 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PEAP_COMMON_H
+#define EAP_PEAP_COMMON_H
+
+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);
+
+#endif /* EAP_PEAP_COMMON_H */
diff --git a/src/eap_common/eap_psk_common.c b/src/eap_common/eap_psk_common.c
new file mode 100644
index 0000000..7417d5c
--- /dev/null
+++ b/src/eap_common/eap_psk_common.c
@@ -0,0 +1,74 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_defs.h"
+#include "eap_psk_common.h"
+
+#define aes_block_size 16
+
+
+int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk)
+{
+ os_memset(ak, 0, aes_block_size);
+ if (aes_128_encrypt_block(psk, ak, ak))
+ return -1;
+ os_memcpy(kdk, ak, aes_block_size);
+ ak[aes_block_size - 1] ^= 0x01;
+ kdk[aes_block_size - 1] ^= 0x02;
+ if (aes_128_encrypt_block(psk, ak, ak) ||
+ aes_128_encrypt_block(psk, kdk, kdk))
+ return -1;
+ return 0;
+}
+
+
+int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk,
+ u8 *emsk)
+{
+ u8 hash[aes_block_size];
+ u8 counter = 1;
+ int i;
+
+ if (aes_128_encrypt_block(kdk, rand_p, hash))
+ return -1;
+
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash, tek))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+
+ for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+
+ for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash,
+ &emsk[i * aes_block_size]))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/src/eap_common/eap_psk_common.h b/src/eap_common/eap_psk_common.h
new file mode 100644
index 0000000..8adc054
--- /dev/null
+++ b/src/eap_common/eap_psk_common.h
@@ -0,0 +1,78 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PSK_COMMON_H
+#define EAP_PSK_COMMON_H
+
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_PSK_LEN 16
+#define EAP_PSK_AK_LEN 16
+#define EAP_PSK_KDK_LEN 16
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+#define EAP_PSK_E_FLAG 0x20
+
+#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6)
+#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length ID_S */
+} STRUCT_PACKED;
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 mac_p[EAP_PSK_MAC_LEN];
+ /* Followed by variable length ID_P */
+} STRUCT_PACKED;
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 mac_s[EAP_PSK_MAC_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk);
+int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek,
+ u8 *msk, u8 *emsk);
+
+#endif /* EAP_PSK_COMMON_H */
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
new file mode 100644
index 0000000..c24b146
--- /dev/null
+++ b/src/eap_common/eap_pwd_common.c
@@ -0,0 +1,312 @@
+/*
+ * EAP server/peer: EAP-pwd shared routines
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_pwd_common.h"
+
+/* The random function H(x) = HMAC-SHA256(0^32, x) */
+void H_Init(HMAC_CTX *ctx)
+{
+ u8 allzero[SHA256_DIGEST_LENGTH];
+
+ os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
+ HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
+}
+
+
+void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
+{
+ HMAC_Update(ctx, data, len);
+}
+
+
+void H_Final(HMAC_CTX *ctx, u8 *digest)
+{
+ unsigned int mdlen = SHA256_DIGEST_LENGTH;
+
+ HMAC_Final(ctx, digest, &mdlen);
+ HMAC_CTX_cleanup(ctx);
+}
+
+
+/* a counter-based KDF based on NIST SP800-108 */
+void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
+ u8 *result, int resultbitlen)
+{
+ HMAC_CTX hctx;
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ u16 i, ctr, L;
+ int resultbytelen, len = 0;
+ unsigned int mdlen = SHA256_DIGEST_LENGTH;
+ unsigned char mask = 0xff;
+
+ resultbytelen = (resultbitlen + 7)/8;
+ ctr = 0;
+ L = htons(resultbitlen);
+ while (len < resultbytelen) {
+ ctr++; i = htons(ctr);
+ HMAC_Init(&hctx, key, keylen, EVP_sha256());
+ if (ctr > 1)
+ HMAC_Update(&hctx, digest, mdlen);
+ HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
+ HMAC_Update(&hctx, label, labellen);
+ HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
+ HMAC_Final(&hctx, digest, &mdlen);
+ if ((len + (int) mdlen) > resultbytelen)
+ os_memcpy(result + len, digest, resultbytelen - len);
+ else
+ os_memcpy(result + len, digest, mdlen);
+ len += mdlen;
+ HMAC_CTX_cleanup(&hctx);
+ }
+
+ /* since we're expanding to a bit length, mask off the excess */
+ if (resultbitlen % 8) {
+ mask >>= ((resultbytelen * 8) - resultbitlen);
+ result[0] &= mask;
+ }
+}
+
+
+/*
+ * compute a "random" secret point on an elliptic curve based
+ * on the password and identities.
+ */
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+ u8 *password, int password_len,
+ u8 *id_server, int id_server_len,
+ u8 *id_peer, int id_peer_len, u8 *token)
+{
+ BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ HMAC_CTX ctx;
+ unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
+ int nid, is_odd, primebitlen, primebytelen, ret = 0;
+
+ switch (num) { /* from IANA registry for IKE D-H groups */
+ case 19:
+ nid = NID_X9_62_prime256v1;
+ break;
+ case 20:
+ nid = NID_secp384r1;
+ break;
+ case 21:
+ nid = NID_secp521r1;
+ break;
+ case 25:
+ nid = NID_X9_62_prime192v1;
+ break;
+ case 26:
+ nid = NID_secp224r1;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
+ return -1;
+ }
+
+ grp->pwe = NULL;
+ grp->order = NULL;
+ grp->prime = NULL;
+
+ if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
+ goto fail;
+ }
+
+ if (((rnd = BN_new()) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
+ ((grp->order = BN_new()) == NULL) ||
+ ((grp->prime = BN_new()) == NULL) ||
+ ((x_candidate = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+ goto fail;
+ }
+
+ if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
+ {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
+ "curve");
+ goto fail;
+ }
+ if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
+ goto fail;
+ }
+ if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
+ "curve");
+ goto fail;
+ }
+ primebitlen = BN_num_bits(grp->prime);
+ primebytelen = BN_num_bytes(grp->prime);
+ if ((prfbuf = os_malloc(primebytelen)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
+ "buffer");
+ goto fail;
+ }
+ os_memset(prfbuf, 0, primebytelen);
+ ctr = 0;
+ while (1) {
+ if (ctr > 10) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
+ "point on curve for group %d, something's "
+ "fishy", num);
+ goto fail;
+ }
+ ctr++;
+
+ /*
+ * compute counter-mode password value and stretch to prime
+ * pwd-seed = H(token | peer-id | server-id | password |
+ * counter)
+ */
+ H_Init(&ctx);
+ H_Update(&ctx, token, sizeof(u32));
+ H_Update(&ctx, id_peer, id_peer_len);
+ H_Update(&ctx, id_server, id_server_len);
+ H_Update(&ctx, password, password_len);
+ H_Update(&ctx, &ctr, sizeof(ctr));
+ H_Final(&ctx, pwe_digest);
+
+ BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
+
+ eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
+ (unsigned char *) "EAP-pwd Hunting And Pecking",
+ os_strlen("EAP-pwd Hunting And Pecking"),
+ prfbuf, primebitlen);
+
+ BN_bin2bn(prfbuf, primebytelen, x_candidate);
+ if (BN_ucmp(x_candidate, grp->prime) >= 0)
+ continue;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+ prfbuf, primebytelen);
+
+ /*
+ * need to unambiguously identify the solution, if there is
+ * one...
+ */
+ if (BN_is_odd(rnd))
+ is_odd = 1;
+ else
+ is_odd = 0;
+
+ /*
+ * solve the quadratic equation, if it's not solvable then we
+ * don't have a point
+ */
+ if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
+ grp->pwe,
+ x_candidate,
+ is_odd, NULL))
+ continue;
+ /*
+ * If there's a solution to the equation then the point must be
+ * on the curve so why check again explicitly? OpenSSL code
+ * says this is required by X9.62. We're not X9.62 but it can't
+ * hurt just to be sure.
+ */
+ if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+ continue;
+ }
+
+ if (BN_cmp(cofactor, BN_value_one())) {
+ /* make sure the point is not in a small sub-group */
+ if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
+ cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: cannot "
+ "multiply generator by order");
+ continue;
+ }
+ if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is at "
+ "infinity");
+ continue;
+ }
+ }
+ /* if we got here then we have a new generator. */
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
+ grp->group_num = num;
+ if (0) {
+ fail:
+ EC_GROUP_free(grp->group);
+ EC_POINT_free(grp->pwe);
+ BN_free(grp->order);
+ BN_free(grp->prime);
+ os_free(grp);
+ grp = NULL;
+ ret = 1;
+ }
+ /* cleanliness and order.... */
+ BN_free(cofactor);
+ BN_free(x_candidate);
+ BN_free(rnd);
+ os_free(prfbuf);
+
+ return ret;
+}
+
+
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
+ BIGNUM *peer_scalar, BIGNUM *server_scalar,
+ u8 *commit_peer, u8 *commit_server,
+ u32 *ciphersuite, u8 *msk, u8 *emsk)
+{
+ HMAC_CTX ctx;
+ u8 mk[SHA256_DIGEST_LENGTH], *cruft;
+ u8 session_id[SHA256_DIGEST_LENGTH + 1];
+ u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+ if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
+ return -1;
+
+ /*
+ * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
+ * scal_s)
+ */
+ session_id[0] = EAP_TYPE_PWD;
+ H_Init(&ctx);
+ H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32));
+ BN_bn2bin(peer_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+ BN_bn2bin(server_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+ H_Final(&ctx, &session_id[1]);
+
+ /* then compute MK = H(k | commit-peer | commit-server) */
+ H_Init(&ctx);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(k, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
+ H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH);
+ H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH);
+ H_Final(&ctx, mk);
+
+ /* stretch the mk with the session-id to get MSK | EMSK */
+ eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
+ session_id, SHA256_DIGEST_LENGTH+1,
+ msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
+
+ os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+ os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+ os_free(cruft);
+
+ return 1;
+}
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
new file mode 100644
index 0000000..971386d
--- /dev/null
+++ b/src/eap_common/eap_pwd_common.h
@@ -0,0 +1,79 @@
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
+#include <openssl/sha.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+/*
+ * definition of a finite cyclic group
+ * TODO: support one based on a prime field
+ */
+typedef struct group_definition_ {
+ u16 group_num;
+ EC_GROUP *group;
+ EC_POINT *pwe;
+ BIGNUM *order;
+ BIGNUM *prime;
+} EAP_PWD_group;
+
+/*
+ * EAP-pwd header, included on all payloads
+ */
+struct eap_pwd_hdr {
+ u8 l_bit:1;
+ u8 m_bit:1;
+ u8 exch:6;
+ u8 total_length[0]; /* included when l_bit is set */
+} STRUCT_PACKED;
+
+#define EAP_PWD_OPCODE_ID_EXCH 1
+#define EAP_PWD_OPCODE_COMMIT_EXCH 2
+#define EAP_PWD_OPCODE_CONFIRM_EXCH 3
+#define EAP_PWD_GET_LENGTH_BIT(x) ((x)->lm_exch & 0x80)
+#define EAP_PWD_SET_LENGTH_BIT(x) ((x)->lm_exch |= 0x80)
+#define EAP_PWD_GET_MORE_BIT(x) ((x)->lm_exch & 0x40)
+#define EAP_PWD_SET_MORE_BIT(x) ((x)->lm_exch |= 0x40)
+#define EAP_PWD_GET_EXCHANGE(x) ((x)->lm_exch & 0x3f)
+#define EAP_PWD_SET_EXCHANGE(x,y) ((x)->lm_exch |= (y))
+
+/* EAP-pwd-ID payload */
+struct eap_pwd_id {
+ be16 group_num;
+ u8 random_function;
+#define EAP_PWD_DEFAULT_RAND_FUNC 1
+ u8 prf;
+#define EAP_PWD_DEFAULT_PRF 1
+ u8 token[4];
+ u8 prep;
+#define EAP_PWD_PREP_NONE 0
+#define EAP_PWD_PREP_MS 1
+ u8 identity[0]; /* length inferred from payload */
+} STRUCT_PACKED;
+
+/* common routines */
+int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
+ int, u8 *);
+int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
+ u8 *, u8 *, u32 *, u8 *, u8 *);
+void H_Init(HMAC_CTX *);
+void H_Update(HMAC_CTX *, const u8 *, int);
+void H_Final(HMAC_CTX *, u8 *);
+
+#endif /* EAP_PWD_COMMON_H */
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
new file mode 100644
index 0000000..9002b0c
--- /dev/null
+++ b/src/eap_common/eap_sake_common.c
@@ -0,0 +1,393 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpabuf.h"
+#include "crypto/sha1.h"
+#include "eap_defs.h"
+#include "eap_sake_common.h"
+
+
+static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr,
+ const u8 *pos)
+{
+ size_t i;
+
+ switch (pos[0]) {
+ case EAP_SAKE_AT_RAND_S:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S");
+ if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->rand_s = pos + 2;
+ break;
+ case EAP_SAKE_AT_RAND_P:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P");
+ if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->rand_p = pos + 2;
+ break;
+ case EAP_SAKE_AT_MIC_S:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S");
+ if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->mic_s = pos + 2;
+ break;
+ case EAP_SAKE_AT_MIC_P:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P");
+ if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->mic_p = pos + 2;
+ break;
+ case EAP_SAKE_AT_SERVERID:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID");
+ attr->serverid = pos + 2;
+ attr->serverid_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_PEERID:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID");
+ attr->peerid = pos + 2;
+ attr->peerid_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_SPI_S:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S");
+ attr->spi_s = pos + 2;
+ attr->spi_s_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_SPI_P:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P");
+ attr->spi_p = pos + 2;
+ attr->spi_p_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_ANY_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ");
+ if (pos[1] != 4) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ"
+ " length %d", pos[1]);
+ return -1;
+ }
+ attr->any_id_req = pos + 2;
+ break;
+ case EAP_SAKE_AT_PERM_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ");
+ if (pos[1] != 4) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+ "AT_PERM_ID_REQ length %d", pos[1]);
+ return -1;
+ }
+ attr->perm_id_req = pos + 2;
+ break;
+ case EAP_SAKE_AT_ENCR_DATA:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA");
+ attr->encr_data = pos + 2;
+ attr->encr_data_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_IV:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+ attr->iv = pos + 2;
+ attr->iv_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_PADDING:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING");
+ for (i = 2; i < pos[1]; i++) {
+ if (pos[i]) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING "
+ "with non-zero pad byte");
+ return -1;
+ }
+ }
+ break;
+ case EAP_SAKE_AT_NEXT_TMPID:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID");
+ attr->next_tmpid = pos + 2;
+ attr->next_tmpid_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_MSK_LIFE:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+ if (pos[1] != 6) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+ "AT_MSK_LIFE length %d", pos[1]);
+ return -1;
+ }
+ attr->msk_life = pos + 2;
+ break;
+ default:
+ if (pos[0] < 128) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable"
+ " attribute %d", pos[0]);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable "
+ "attribute %d", pos[0]);
+ break;
+ }
+
+ if (attr->iv && !attr->encr_data) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without "
+ "AT_ENCR_DATA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_sake_parse_attributes - Parse EAP-SAKE attributes
+ * @buf: Packet payload (starting with the first attribute)
+ * @len: Payload length
+ * @attr: Structure to be filled with found attributes
+ * Returns: 0 on success or -1 on failure
+ */
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+ struct eap_sake_parse_attr *attr)
+{
+ const u8 *pos = buf, *end = buf + len;
+
+ os_memset(attr, 0, sizeof(*attr));
+ while (pos < end) {
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute");
+ return -1;
+ }
+
+ if (pos[1] < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute "
+ "length (%d)", pos[1]);
+ return -1;
+ }
+
+ if (pos + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow");
+ return -1;
+ }
+
+ if (eap_sake_parse_add_attr(attr, pos))
+ return -1;
+
+ pos += pos[1];
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @data: Extra data (start) to bind into the key
+ * @data_len: Length of the data
+ * @data2: Extra data (end) to bind into the key
+ * @data2_len: Length of the data2
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
+ */
+static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len,
+ u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[4];
+ size_t len[4];
+
+ addr[0] = (u8 *) label; /* Label | Y */
+ len[0] = label_len;
+ addr[1] = data; /* Msg[start] */
+ len[1] = data_len;
+ addr[2] = data2; /* Msg[end] */
+ len[2] = data2_len;
+ addr[3] = &counter; /* Length */
+ len[3] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ hmac_sha1_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA1_MAC_LEN;
+ } else {
+ hmac_sha1_vector(key, key_len, 4, addr, len,
+ hash);
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
+
+
+/**
+ * eap_sake_derive_keys - Derive EAP-SAKE keys
+ * @root_secret_a: 16-byte Root-Secret-A
+ * @root_secret_b: 16-byte Root-Secret-B
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ *
+ * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
+ */
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
+ u8 *emsk)
+{
+ u8 sms_a[EAP_SAKE_SMS_LEN];
+ u8 sms_b[EAP_SAKE_SMS_LEN];
+ u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys");
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
+ root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
+ eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
+ "SAKE Master Secret A",
+ rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+ sms_a, EAP_SAKE_SMS_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
+ eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
+ rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+ tek, EAP_SAKE_TEK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
+ tek, EAP_SAKE_TEK_AUTH_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
+ tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
+ root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
+ eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
+ "SAKE Master Secret B",
+ rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+ sms_b, EAP_SAKE_SMS_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
+ eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
+ rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+ key_buf, sizeof(key_buf));
+ os_memcpy(msk, key_buf, EAP_MSK_LEN);
+ os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+/**
+ * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet
+ * @tek_auth: 16-byte TEK-Auth
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @serverid: SERVERID
+ * @serverid_len: SERVERID length
+ * @peerid: PEERID
+ * @peerid_len: PEERID length
+ * @peer: MIC calculation for 0 = Server, 1 = Peer message
+ * @eap: EAP packet
+ * @eap_len: EAP packet length
+ * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
+ * @mic: Buffer for the computed 16-byte MIC
+ */
+int eap_sake_compute_mic(const u8 *tek_auth,
+ const u8 *rand_s, const u8 *rand_p,
+ const u8 *serverid, size_t serverid_len,
+ const u8 *peerid, size_t peerid_len,
+ int peer, const u8 *eap, size_t eap_len,
+ const u8 *mic_pos, u8 *mic)
+{
+ u8 _rand[2 * EAP_SAKE_RAND_LEN];
+ u8 *tmp, *pos;
+ size_t tmplen;
+
+ tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
+ tmp = os_malloc(tmplen);
+ if (tmp == NULL)
+ return -1;
+ pos = tmp;
+ if (peer) {
+ if (peerid) {
+ os_memcpy(pos, peerid, peerid_len);
+ pos += peerid_len;
+ }
+ *pos++ = 0x00;
+ if (serverid) {
+ os_memcpy(pos, serverid, serverid_len);
+ pos += serverid_len;
+ }
+ *pos++ = 0x00;
+
+ os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN);
+ os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p,
+ EAP_SAKE_RAND_LEN);
+ } else {
+ if (serverid) {
+ os_memcpy(pos, serverid, serverid_len);
+ pos += serverid_len;
+ }
+ *pos++ = 0x00;
+ if (peerid) {
+ os_memcpy(pos, peerid, peerid_len);
+ pos += peerid_len;
+ }
+ *pos++ = 0x00;
+
+ os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN);
+ os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s,
+ EAP_SAKE_RAND_LEN);
+ }
+
+ os_memcpy(pos, eap, eap_len);
+ os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
+
+ eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
+ peer ? "Peer MIC" : "Server MIC",
+ _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
+ mic, EAP_SAKE_MIC_LEN);
+
+ os_free(tmp);
+
+ return 0;
+}
+
+
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+ size_t len)
+{
+ wpabuf_put_u8(buf, type);
+ wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */
+ if (data)
+ wpabuf_put_data(buf, data, len);
+ else
+ os_memset(wpabuf_put(buf, len), 0, len);
+}
diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h
new file mode 100644
index 0000000..201e207
--- /dev/null
+++ b/src/eap_common/eap_sake_common.h
@@ -0,0 +1,102 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_SAKE_COMMON_H
+#define EAP_SAKE_COMMON_H
+
+#define EAP_SAKE_VERSION 2
+
+#define EAP_SAKE_SUBTYPE_CHALLENGE 1
+#define EAP_SAKE_SUBTYPE_CONFIRM 2
+#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3
+#define EAP_SAKE_SUBTYPE_IDENTITY 4
+
+#define EAP_SAKE_AT_RAND_S 1
+#define EAP_SAKE_AT_RAND_P 2
+#define EAP_SAKE_AT_MIC_S 3
+#define EAP_SAKE_AT_MIC_P 4
+#define EAP_SAKE_AT_SERVERID 5
+#define EAP_SAKE_AT_PEERID 6
+#define EAP_SAKE_AT_SPI_S 7
+#define EAP_SAKE_AT_SPI_P 8
+#define EAP_SAKE_AT_ANY_ID_REQ 9
+#define EAP_SAKE_AT_PERM_ID_REQ 10
+#define EAP_SAKE_AT_ENCR_DATA 128
+#define EAP_SAKE_AT_IV 129
+#define EAP_SAKE_AT_PADDING 130
+#define EAP_SAKE_AT_NEXT_TMPID 131
+#define EAP_SAKE_AT_MSK_LIFE 132
+
+#define EAP_SAKE_RAND_LEN 16
+#define EAP_SAKE_MIC_LEN 16
+#define EAP_SAKE_ROOT_SECRET_LEN 16
+#define EAP_SAKE_SMS_LEN 16
+#define EAP_SAKE_TEK_AUTH_LEN 16
+#define EAP_SAKE_TEK_CIPHER_LEN 16
+#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_sake_hdr {
+ u8 version; /* EAP_SAKE_VERSION */
+ u8 session_id;
+ u8 subtype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+struct eap_sake_parse_attr {
+ const u8 *rand_s;
+ const u8 *rand_p;
+ const u8 *mic_s;
+ const u8 *mic_p;
+ const u8 *serverid;
+ size_t serverid_len;
+ const u8 *peerid;
+ size_t peerid_len;
+ const u8 *spi_s;
+ size_t spi_s_len;
+ const u8 *spi_p;
+ size_t spi_p_len;
+ const u8 *any_id_req;
+ const u8 *perm_id_req;
+ const u8 *encr_data;
+ size_t encr_data_len;
+ const u8 *iv;
+ size_t iv_len;
+ const u8 *next_tmpid;
+ size_t next_tmpid_len;
+ const u8 *msk_life;
+};
+
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+ struct eap_sake_parse_attr *attr);
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p,
+ u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_compute_mic(const u8 *tek_auth,
+ const u8 *rand_s, const u8 *rand_p,
+ const u8 *serverid, size_t serverid_len,
+ const u8 *peerid, size_t peerid_len,
+ int peer, const u8 *eap, size_t eap_len,
+ const u8 *mic_pos, u8 *mic);
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+ size_t len);
+
+#endif /* EAP_SAKE_COMMON_H */
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
new file mode 100644
index 0000000..0b37b0b
--- /dev/null
+++ b/src/eap_common/eap_sim_common.c
@@ -0,0 +1,1215 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpabuf.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "eap_common/eap_defs.h"
+#include "eap_common/eap_sim_common.h"
+
+
+static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
+{
+ return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
+}
+
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *nonce_mt, u16 selected_version,
+ const u8 *ver_list, size_t ver_list_len,
+ int num_chal, const u8 *kc, u8 *mk)
+{
+ u8 sel_ver[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = identity;
+ len[0] = identity_len;
+ addr[1] = kc;
+ len[1] = num_chal * EAP_SIM_KC_LEN;
+ addr[2] = nonce_mt;
+ len[2] = EAP_SIM_NONCE_MT_LEN;
+ addr[3] = ver_list;
+ len[3] = ver_list_len;
+ addr[4] = sel_ver;
+ len[4] = 2;
+
+ WPA_PUT_BE16(sel_ver, selected_version);
+
+ /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+ sha1_vector(5, addr, len, mk);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+}
+
+
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *mk)
+{
+ const u8 *addr[3];
+ size_t len[3];
+
+ addr[0] = identity;
+ len[0] = identity_len;
+ addr[1] = ik;
+ len[1] = EAP_AKA_IK_LEN;
+ addr[2] = ck;
+ len[2] = EAP_AKA_CK_LEN;
+
+ /* MK = SHA1(Identity|IK|CK) */
+ sha1_vector(3, addr, len, mk);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
+}
+
+
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
+{
+ u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
+ EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
+ if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
+ return -1;
+ }
+ pos = buf;
+ os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+ pos += EAP_SIM_K_ENCR_LEN;
+ os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
+ pos += EAP_SIM_K_AUT_LEN;
+ os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
+ pos += EAP_SIM_KEYING_DATA_LEN;
+ os_memcpy(emsk, pos, EAP_EMSK_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
+ k_encr, EAP_SIM_K_ENCR_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
+ k_aut, EAP_SIM_K_AUT_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
+ msk, EAP_SIM_KEYING_DATA_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
+ os_memset(buf, 0, sizeof(buf));
+
+ return 0;
+}
+
+
+int eap_sim_derive_keys_reauth(u16 _counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, const u8 *mk, u8 *msk,
+ u8 *emsk)
+{
+ u8 xkey[SHA1_MAC_LEN];
+ u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
+ u8 counter[2];
+ const u8 *addr[4];
+ size_t len[4];
+
+ while (identity_len > 0 && identity[identity_len - 1] == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
+ "character from the end of identity");
+ identity_len--;
+ }
+ addr[0] = identity;
+ len[0] = identity_len;
+ addr[1] = counter;
+ len[1] = 2;
+ addr[2] = nonce_s;
+ len[2] = EAP_SIM_NONCE_S_LEN;
+ addr[3] = mk;
+ len[3] = EAP_SIM_MK_LEN;
+
+ WPA_PUT_BE16(counter, _counter);
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+ identity, identity_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+
+ /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
+ sha1_vector(4, addr, len, xkey);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
+
+ if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
+ return -1;
+ }
+ if (msk) {
+ os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
+ msk, EAP_SIM_KEYING_DATA_LEN);
+ }
+ if (emsk) {
+ os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
+ }
+ os_memset(buf, 0, sizeof(buf));
+
+ return 0;
+}
+
+
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra, size_t extra_len)
+{
+ unsigned char hmac[SHA1_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *tmp;
+
+ if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
+ mac < wpabuf_head_u8(req) ||
+ mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
+ return -1;
+
+ tmp = os_malloc(wpabuf_len(req));
+ if (tmp == NULL)
+ return -1;
+
+ addr[0] = tmp;
+ len[0] = wpabuf_len(req);
+ addr[1] = extra;
+ len[1] = extra_len;
+
+ /* HMAC-SHA1-128 */
+ os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
+ os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
+ tmp, wpabuf_len(req));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
+ extra, extra_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
+ k_aut, EAP_SIM_K_AUT_LEN);
+ hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
+ hmac, EAP_SIM_MAC_LEN);
+ os_free(tmp);
+
+ return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+ const u8 *extra, size_t extra_len)
+{
+ unsigned char hmac[SHA1_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = msg;
+ len[0] = msg_len;
+ addr[1] = extra;
+ len[1] = extra_len;
+
+ /* HMAC-SHA1-128 */
+ os_memset(mac, 0, EAP_SIM_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
+ extra, extra_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
+ k_aut, EAP_SIM_K_AUT_LEN);
+ hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+ os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
+ mac, EAP_SIM_MAC_LEN);
+}
+
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+static void prf_prime(const u8 *k, const char *seed1,
+ const u8 *seed2, size_t seed2_len,
+ const u8 *seed3, size_t seed3_len,
+ u8 *res, size_t res_len)
+{
+ const u8 *addr[5];
+ size_t len[5];
+ u8 hash[SHA256_MAC_LEN];
+ u8 iter;
+
+ /*
+ * PRF'(K,S) = T1 | T2 | T3 | T4 | ...
+ * T1 = HMAC-SHA-256 (K, S | 0x01)
+ * T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
+ * T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
+ * T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
+ * ...
+ */
+
+ addr[0] = hash;
+ len[0] = 0;
+ addr[1] = (const u8 *) seed1;
+ len[1] = os_strlen(seed1);
+ addr[2] = seed2;
+ len[2] = seed2_len;
+ addr[3] = seed3;
+ len[3] = seed3_len;
+ addr[4] = &iter;
+ len[4] = 1;
+
+ iter = 0;
+ while (res_len) {
+ size_t hlen;
+ iter++;
+ hmac_sha256_vector(k, 32, 5, addr, len, hash);
+ len[0] = SHA256_MAC_LEN;
+ hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
+ os_memcpy(res, hash, hlen);
+ res += hlen;
+ res_len -= hlen;
+ }
+}
+
+
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *k_encr,
+ u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
+{
+ u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
+ u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
+ EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
+ u8 *pos;
+
+ /*
+ * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
+ * K_encr = MK[0..127]
+ * K_aut = MK[128..383]
+ * K_re = MK[384..639]
+ * MSK = MK[640..1151]
+ * EMSK = MK[1152..1663]
+ */
+
+ os_memcpy(key, ik, EAP_AKA_IK_LEN);
+ os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
+
+ prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
+ keys, sizeof(keys));
+
+ pos = keys;
+ os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
+ k_encr, EAP_SIM_K_ENCR_LEN);
+ pos += EAP_SIM_K_ENCR_LEN;
+
+ os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
+ k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+ pos += EAP_AKA_PRIME_K_AUT_LEN;
+
+ os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
+ k_re, EAP_AKA_PRIME_K_RE_LEN);
+ pos += EAP_AKA_PRIME_K_RE_LEN;
+
+ os_memcpy(msk, pos, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
+ pos += EAP_MSK_LEN;
+
+ os_memcpy(emsk, pos, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, u8 *msk, u8 *emsk)
+{
+ u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
+ u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
+ u8 *pos;
+
+ /*
+ * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
+ * MSK = MK[0..511]
+ * EMSK = MK[512..1023]
+ */
+
+ WPA_PUT_BE16(seed3, counter);
+ os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
+ seed3, sizeof(seed3),
+ keys, sizeof(keys));
+
+ pos = keys;
+ os_memcpy(msk, pos, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
+ pos += EAP_MSK_LEN;
+
+ os_memcpy(emsk, pos, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
+
+ os_memset(keys, 0, sizeof(keys));
+
+ return 0;
+}
+
+
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra, size_t extra_len)
+{
+ unsigned char hmac[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *tmp;
+
+ if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
+ mac < wpabuf_head_u8(req) ||
+ mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
+ return -1;
+
+ tmp = os_malloc(wpabuf_len(req));
+ if (tmp == NULL)
+ return -1;
+
+ addr[0] = tmp;
+ len[0] = wpabuf_len(req);
+ addr[1] = extra;
+ len[1] = extra_len;
+
+ /* HMAC-SHA-256-128 */
+ os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
+ os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
+ tmp, wpabuf_len(req));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data",
+ extra, extra_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut",
+ k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+ hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC",
+ hmac, EAP_SIM_MAC_LEN);
+ os_free(tmp);
+
+ return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+ u8 *mac, const u8 *extra, size_t extra_len)
+{
+ unsigned char hmac[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = msg;
+ len[0] = msg_len;
+ addr[1] = extra;
+ len[1] = extra_len;
+
+ /* HMAC-SHA-256-128 */
+ os_memset(mac, 0, EAP_SIM_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data",
+ extra, extra_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut",
+ k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+ hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
+ os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC",
+ mac, EAP_SIM_MAC_LEN);
+}
+
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+ const u8 *network_name,
+ size_t network_name_len)
+{
+ u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN];
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[5];
+ size_t len[5];
+ u8 fc;
+ u8 l0[2], l1[2];
+
+ /* 3GPP TS 33.402 V8.0.0
+ * (CK', IK') = F(CK, IK, <access network identity>)
+ */
+ /* TODO: CK', IK' generation should really be moved into the actual
+ * AKA procedure with network name passed in there and option to use
+ * AMF separation bit = 1 (3GPP TS 33.401). */
+
+ /* Change Request 33.402 CR 0033 to version 8.1.1 from
+ * 3GPP TSG-SA WG3 Meeting #53 in September 2008:
+ *
+ * CK' || IK' = HMAC-SHA-256(Key, S)
+ * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
+ * Key = CK || IK
+ * FC = 0x20
+ * P0 = access network identity (3GPP TS 24.302)
+ * L0 = length of acceess network identity (2 octets, big endian)
+ * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
+ * L1 = 0x00 0x06
+ */
+
+ fc = 0x20;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
+ wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
+ network_name, network_name_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
+
+ os_memcpy(key, ck, EAP_AKA_CK_LEN);
+ os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
+ key, sizeof(key));
+
+ addr[0] = &fc;
+ len[0] = 1;
+ addr[1] = network_name;
+ len[1] = network_name_len;
+ WPA_PUT_BE16(l0, network_name_len);
+ addr[2] = l0;
+ len[2] = 2;
+ addr[3] = sqn_ak;
+ len[3] = 6;
+ WPA_PUT_BE16(l1, 6);
+ addr[4] = l1;
+ len[4] = 2;
+
+ hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
+ hash, sizeof(hash));
+
+ os_memcpy(ck, hash, EAP_AKA_CK_LEN);
+ os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+ struct eap_sim_attrs *attr, int aka, int encr)
+{
+ const u8 *pos = start, *apos;
+ size_t alen, plen, i, list_len;
+
+ os_memset(attr, 0, sizeof(*attr));
+ attr->id_req = NO_ID_REQ;
+ attr->notification = -1;
+ attr->counter = -1;
+ attr->selected_version = -1;
+ attr->client_error_code = -1;
+
+ while (pos < end) {
+ if (pos + 2 > end) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
+ return -1;
+ }
+ wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
+ pos[0], pos[1] * 4);
+ if (pos + pos[1] * 4 > end) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
+ "(pos=%p len=%d end=%p)",
+ pos, pos[1] * 4, end);
+ return -1;
+ }
+ if (pos[1] == 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
+ return -1;
+ }
+ apos = pos + 2;
+ alen = pos[1] * 4 - 2;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
+ apos, alen);
+
+ switch (pos[0]) {
+ case EAP_SIM_AT_RAND:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
+ apos += 2;
+ alen -= 2;
+ if ((!aka && (alen % GSM_RAND_LEN)) ||
+ (aka && alen != EAP_AKA_RAND_LEN)) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->rand = apos;
+ attr->num_chal = alen / GSM_RAND_LEN;
+ break;
+ case EAP_SIM_AT_AUTN:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_AUTN");
+ return -1;
+ }
+ apos += 2;
+ alen -= 2;
+ if (alen != EAP_AKA_AUTN_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->autn = apos;
+ break;
+ case EAP_SIM_AT_PADDING:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_PADDING");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
+ for (i = 2; i < alen; i++) {
+ if (apos[i] != 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
+ "AT_PADDING used a non-zero"
+ " padding byte");
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
+ "(encr) padding bytes",
+ apos + 2, alen - 2);
+ return -1;
+ }
+ }
+ break;
+ case EAP_SIM_AT_NONCE_MT:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
+ if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_NONCE_MT length");
+ return -1;
+ }
+ attr->nonce_mt = apos + 2;
+ break;
+ case EAP_SIM_AT_PERMANENT_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
+ attr->id_req = PERMANENT_ID;
+ break;
+ case EAP_SIM_AT_MAC:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
+ if (alen != 2 + EAP_SIM_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
+ "length");
+ return -1;
+ }
+ attr->mac = apos + 2;
+ break;
+ case EAP_SIM_AT_NOTIFICATION:
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_NOTIFICATION length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->notification = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
+ attr->notification);
+ break;
+ case EAP_SIM_AT_ANY_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
+ attr->id_req = ANY_ID;
+ break;
+ case EAP_SIM_AT_IDENTITY:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
+ plen = WPA_GET_BE16(apos);
+ apos += 2;
+ alen -= 2;
+ if (plen > alen) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_IDENTITY (Actual Length %lu, "
+ "remaining length %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+
+ attr->identity = apos;
+ attr->identity_len = plen;
+ break;
+ case EAP_SIM_AT_VERSION_LIST:
+ if (aka) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: "
+ "Unexpected AT_VERSION_LIST");
+ return -1;
+ }
+ list_len = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
+ if (list_len < 2 || list_len > alen - 2) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+ "AT_VERSION_LIST (list_len=%lu "
+ "attr_len=%lu)",
+ (unsigned long) list_len,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->version_list = apos + 2;
+ attr->version_list_len = list_len;
+ break;
+ case EAP_SIM_AT_SELECTED_VERSION:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_SELECTED_VERSION length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->selected_version = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
+ "%d", attr->selected_version);
+ break;
+ case EAP_SIM_AT_FULLAUTH_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
+ attr->id_req = FULLAUTH_ID;
+ break;
+ case EAP_SIM_AT_COUNTER:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_COUNTER");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_COUNTER (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->counter = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
+ attr->counter);
+ break;
+ case EAP_SIM_AT_COUNTER_TOO_SMALL:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_COUNTER_TOO_SMALL");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_COUNTER_TOO_SMALL (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_COUNTER_TOO_SMALL");
+ attr->counter_too_small = 1;
+ break;
+ case EAP_SIM_AT_NONCE_S:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NONCE_S");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NONCE_S");
+ if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_NONCE_S (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->nonce_s = apos + 2;
+ break;
+ case EAP_SIM_AT_CLIENT_ERROR_CODE:
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_CLIENT_ERROR_CODE length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->client_error_code = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
+ "%d", attr->client_error_code);
+ break;
+ case EAP_SIM_AT_IV:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
+ if (alen != 2 + EAP_SIM_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
+ "length %lu", (unsigned long) alen);
+ return -1;
+ }
+ attr->iv = apos + 2;
+ break;
+ case EAP_SIM_AT_ENCR_DATA:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
+ attr->encr_data = apos + 2;
+ attr->encr_data_len = alen - 2;
+ if (attr->encr_data_len % 16) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_ENCR_DATA length %lu",
+ (unsigned long)
+ attr->encr_data_len);
+ return -1;
+ }
+ break;
+ case EAP_SIM_AT_NEXT_PSEUDONYM:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NEXT_PSEUDONYM");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NEXT_PSEUDONYM");
+ plen = apos[0] * 256 + apos[1];
+ if (plen > alen - 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+ " AT_NEXT_PSEUDONYM (actual"
+ " len %lu, attr len %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->next_pseudonym = pos + 4;
+ attr->next_pseudonym_len = plen;
+ break;
+ case EAP_SIM_AT_NEXT_REAUTH_ID:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NEXT_REAUTH_ID");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NEXT_REAUTH_ID");
+ plen = apos[0] * 256 + apos[1];
+ if (plen > alen - 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+ " AT_NEXT_REAUTH_ID (actual"
+ " len %lu, attr len %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->next_reauth_id = pos + 4;
+ attr->next_reauth_id_len = plen;
+ break;
+ case EAP_SIM_AT_RES:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
+ attr->res_len_bits = WPA_GET_BE16(apos);
+ apos += 2;
+ alen -= 2;
+ if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
+ alen > EAP_AKA_MAX_RES_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
+ "(len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->res = apos;
+ attr->res_len = alen;
+ break;
+ case EAP_SIM_AT_AUTS:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_AUTS");
+ return -1;
+ }
+ if (alen != EAP_AKA_AUTS_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->auts = apos;
+ break;
+ case EAP_SIM_AT_CHECKCODE:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_CHECKCODE");
+ return -1;
+ }
+ apos += 2;
+ alen -= 2;
+ if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
+ alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+ "AT_CHECKCODE (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->checkcode = apos;
+ attr->checkcode_len = alen;
+ break;
+ case EAP_SIM_AT_RESULT_IND:
+ if (encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
+ "AT_RESULT_IND");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_RESULT_IND (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
+ attr->result_ind = 1;
+ break;
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+ case EAP_SIM_AT_KDF_INPUT:
+ if (aka != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+ "AT_KDF_INPUT");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
+ plen = WPA_GET_BE16(apos);
+ apos += 2;
+ alen -= 2;
+ if (plen > alen) {
+ wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+ "AT_KDF_INPUT (Actual Length %lu, "
+ "remaining length %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->kdf_input = apos;
+ attr->kdf_input_len = plen;
+ break;
+ case EAP_SIM_AT_KDF:
+ if (aka != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+ "AT_KDF");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+ "AT_KDF (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
+ "AT_KDF attributes - ignore this");
+ continue;
+ }
+ attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
+ attr->kdf_count++;
+ break;
+ case EAP_SIM_AT_BIDDING:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+ "AT_BIDDING (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->bidding = apos;
+ break;
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+ default:
+ if (pos[0] < 128) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
+ "non-skippable attribute %d",
+ pos[0]);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
+ " attribute %d ignored", pos[0]);
+ break;
+ }
+
+ pos += pos[1] * 4;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
+ "(aka=%d encr=%d)", aka, encr);
+
+ return 0;
+}
+
+
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+ size_t encr_data_len, const u8 *iv,
+ struct eap_sim_attrs *attr, int aka)
+{
+ u8 *decrypted;
+
+ if (!iv) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
+ return NULL;
+ }
+
+ decrypted = os_malloc(encr_data_len);
+ if (decrypted == NULL)
+ return NULL;
+ os_memcpy(decrypted, encr_data, encr_data_len);
+
+ if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
+ os_free(decrypted);
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
+ decrypted, encr_data_len);
+
+ if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
+ aka, 1)) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
+ "decrypted AT_ENCR_DATA");
+ os_free(decrypted);
+ return NULL;
+ }
+
+ return decrypted;
+}
+
+
+#define EAP_SIM_INIT_LEN 128
+
+struct eap_sim_msg {
+ struct wpabuf *buf;
+ size_t mac, iv, encr; /* index from buf */
+ int type;
+};
+
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
+{
+ struct eap_sim_msg *msg;
+ struct eap_hdr *eap;
+ u8 *pos;
+
+ msg = os_zalloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ msg->type = type;
+ msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
+ if (msg->buf == NULL) {
+ os_free(msg);
+ return NULL;
+ }
+ eap = wpabuf_put(msg->buf, sizeof(*eap));
+ eap->code = code;
+ eap->identifier = id;
+
+ pos = wpabuf_put(msg->buf, 4);
+ *pos++ = type;
+ *pos++ = subtype;
+ *pos++ = 0; /* Reserved */
+ *pos++ = 0; /* Reserved */
+
+ return msg;
+}
+
+
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+ const u8 *extra, size_t extra_len)
+{
+ struct eap_hdr *eap;
+ struct wpabuf *buf;
+
+ if (msg == NULL)
+ return NULL;
+
+ eap = wpabuf_mhead(msg->buf);
+ eap->length = host_to_be16(wpabuf_len(msg->buf));
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+ if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) {
+ eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf),
+ (u8 *) wpabuf_mhead(msg->buf) +
+ msg->mac, extra, extra_len);
+ } else
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+ if (k_aut && msg->mac) {
+ eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf),
+ (u8 *) wpabuf_mhead(msg->buf) + msg->mac,
+ extra, extra_len);
+ }
+
+ buf = msg->buf;
+ os_free(msg);
+ return buf;
+}
+
+
+void eap_sim_msg_free(struct eap_sim_msg *msg)
+{
+ if (msg) {
+ wpabuf_free(msg->buf);
+ os_free(msg);
+ }
+}
+
+
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len)
+{
+ int attr_len = 2 + len;
+ int pad_len;
+ u8 *start;
+
+ if (msg == NULL)
+ return NULL;
+
+ pad_len = (4 - attr_len % 4) % 4;
+ attr_len += pad_len;
+ if (wpabuf_resize(&msg->buf, attr_len))
+ return NULL;
+ start = wpabuf_put(msg->buf, 0);
+ wpabuf_put_u8(msg->buf, attr);
+ wpabuf_put_u8(msg->buf, attr_len / 4);
+ wpabuf_put_data(msg->buf, data, len);
+ if (pad_len)
+ os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+ return start;
+}
+
+
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
+ const u8 *data, size_t len)
+{
+ int attr_len = 4 + len;
+ int pad_len;
+ u8 *start;
+
+ if (msg == NULL)
+ return NULL;
+
+ pad_len = (4 - attr_len % 4) % 4;
+ attr_len += pad_len;
+ if (wpabuf_resize(&msg->buf, attr_len))
+ return NULL;
+ start = wpabuf_put(msg->buf, 0);
+ wpabuf_put_u8(msg->buf, attr);
+ wpabuf_put_u8(msg->buf, attr_len / 4);
+ wpabuf_put_be16(msg->buf, value);
+ if (data)
+ wpabuf_put_data(msg->buf, data, len);
+ else
+ wpabuf_put(msg->buf, len);
+ if (pad_len)
+ os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+ return start;
+}
+
+
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
+{
+ u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
+ if (pos)
+ msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
+ return pos;
+}
+
+
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr)
+{
+ u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
+ if (pos == NULL)
+ return -1;
+ msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
+ if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
+ EAP_SIM_IV_LEN)) {
+ msg->iv = 0;
+ return -1;
+ }
+
+ pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
+ if (pos == NULL) {
+ msg->iv = 0;
+ return -1;
+ }
+ msg->encr = pos - wpabuf_head_u8(msg->buf);
+
+ return 0;
+}
+
+
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
+{
+ size_t encr_len;
+
+ if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
+ return -1;
+
+ encr_len = wpabuf_len(msg->buf) - msg->encr - 4;
+ if (encr_len % 16) {
+ u8 *pos;
+ int pad_len = 16 - (encr_len % 16);
+ if (pad_len < 4) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: "
+ "eap_sim_msg_add_encr_end - invalid pad_len"
+ " %d", pad_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, " *AT_PADDING");
+ pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
+ if (pos == NULL)
+ return -1;
+ os_memset(pos + 4, 0, pad_len - 4);
+ encr_len += pad_len;
+ }
+ wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)",
+ (unsigned long) encr_len);
+ wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1;
+ return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv,
+ wpabuf_mhead_u8(msg->buf) + msg->encr + 4,
+ encr_len);
+}
+
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ const char *type = aka ? "AKA" : "SIM";
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+ switch (notification) {
+ case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
+ wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+ "notification (after authentication)", type);
+ break;
+ case EAP_SIM_TEMPORARILY_DENIED:
+ wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+ "User has been temporarily denied access to the "
+ "requested service", type);
+ break;
+ case EAP_SIM_NOT_SUBSCRIBED:
+ wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+ "User has not subscribed to the requested service",
+ type);
+ break;
+ case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
+ wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+ "notification (before authentication)", type);
+ break;
+ case EAP_SIM_SUCCESS:
+ wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
+ "notification", type);
+ break;
+ default:
+ if (notification >= 32768) {
+ wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
+ "non-failure notification %d",
+ type, notification);
+ } else {
+ wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
+ "failure notification %d",
+ type, notification);
+ }
+ }
+}
diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h
new file mode 100644
index 0000000..48c8eaa
--- /dev/null
+++ b/src/eap_common/eap_sim_common.h
@@ -0,0 +1,235 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_SIM_COMMON_H
+#define EAP_SIM_COMMON_H
+
+#define EAP_SIM_NONCE_S_LEN 16
+#define EAP_SIM_NONCE_MT_LEN 16
+#define EAP_SIM_MAC_LEN 16
+#define EAP_SIM_MK_LEN 20
+#define EAP_SIM_K_AUT_LEN 16
+#define EAP_SIM_K_ENCR_LEN 16
+#define EAP_SIM_KEYING_DATA_LEN 64
+#define EAP_SIM_IV_LEN 16
+#define EAP_SIM_KC_LEN 8
+#define EAP_SIM_SRES_LEN 4
+
+#define GSM_RAND_LEN 16
+
+#define EAP_SIM_VERSION 1
+
+/* EAP-SIM Subtypes */
+#define EAP_SIM_SUBTYPE_START 10
+#define EAP_SIM_SUBTYPE_CHALLENGE 11
+#define EAP_SIM_SUBTYPE_NOTIFICATION 12
+#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
+#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
+#define EAP_SIM_UNSUPPORTED_VERSION 1
+#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
+#define EAP_SIM_RAND_NOT_FRESH 3
+
+#define EAP_SIM_MAX_FAST_REAUTHS 1000
+
+#define EAP_SIM_MAX_CHAL 3
+
+
+/* EAP-AKA Subtypes */
+#define EAP_AKA_SUBTYPE_CHALLENGE 1
+#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2
+#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4
+#define EAP_AKA_SUBTYPE_IDENTITY 5
+#define EAP_AKA_SUBTYPE_NOTIFICATION 12
+#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13
+#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0
+
+#define EAP_AKA_RAND_LEN 16
+#define EAP_AKA_AUTN_LEN 16
+#define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MAX_LEN 16
+#define EAP_AKA_IK_LEN 16
+#define EAP_AKA_CK_LEN 16
+#define EAP_AKA_MAX_FAST_REAUTHS 1000
+#define EAP_AKA_MIN_RES_LEN 4
+#define EAP_AKA_MAX_RES_LEN 16
+#define EAP_AKA_CHECKCODE_LEN 20
+
+#define EAP_AKA_PRIME_K_AUT_LEN 32
+#define EAP_AKA_PRIME_CHECKCODE_LEN 32
+#define EAP_AKA_PRIME_K_RE_LEN 32
+
+struct wpabuf;
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *nonce_mt, u16 selected_version,
+ const u8 *ver_list, size_t ver_list_len,
+ int num_chal, const u8 *kc, u8 *mk);
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *mk);
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk,
+ u8 *emsk);
+int eap_sim_derive_keys_reauth(u16 _counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, const u8 *mk, u8 *msk,
+ u8 *emsk);
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra, size_t extra_len);
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+ const u8 *extra, size_t extra_len);
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *k_encr,
+ u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk);
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, u8 *msk, u8 *emsk);
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len);
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+ u8 *mac, const u8 *extra, size_t extra_len);
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+ const u8 *network_name,
+ size_t network_name_len);
+#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+static inline void eap_aka_prime_derive_keys(const u8 *identity,
+ size_t identity_len,
+ const u8 *ik, const u8 *ck,
+ u8 *k_encr, u8 *k_aut, u8 *k_re,
+ u8 *msk, u8 *emsk)
+{
+}
+
+static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity,
+ size_t identity_len,
+ const u8 *nonce_s, u8 *msk,
+ u8 *emsk)
+{
+ return -1;
+}
+
+static inline int eap_sim_verify_mac_sha256(const u8 *k_aut,
+ const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len)
+{
+ return -1;
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+/* EAP-SIM/AKA Attributes (0..127 non-skippable) */
+#define EAP_SIM_AT_RAND 1
+#define EAP_SIM_AT_AUTN 2 /* only AKA */
+#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */
+#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */
+#define EAP_SIM_AT_PADDING 6 /* only encrypted */
+#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */
+#define EAP_SIM_AT_PERMANENT_ID_REQ 10
+#define EAP_SIM_AT_MAC 11
+#define EAP_SIM_AT_NOTIFICATION 12
+#define EAP_SIM_AT_ANY_ID_REQ 13
+#define EAP_SIM_AT_IDENTITY 14 /* only send */
+#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */
+#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */
+#define EAP_SIM_AT_FULLAUTH_ID_REQ 17
+#define EAP_SIM_AT_COUNTER 19 /* only encrypted */
+#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
+#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
+#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
+#define EAP_SIM_AT_KDF 24 /* only AKA' */
+#define EAP_SIM_AT_IV 129
+#define EAP_SIM_AT_ENCR_DATA 130
+#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
+#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */
+#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */
+#define EAP_SIM_AT_RESULT_IND 135
+#define EAP_SIM_AT_BIDDING 136
+
+/* AT_NOTIFICATION notification code values */
+#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0
+#define EAP_SIM_TEMPORARILY_DENIED 1026
+#define EAP_SIM_NOT_SUBSCRIBED 1031
+#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
+#define EAP_SIM_SUCCESS 32768
+
+/* EAP-AKA' AT_KDF Key Derivation Function values */
+#define EAP_AKA_PRIME_KDF 1
+
+/* AT_BIDDING flags */
+#define EAP_AKA_BIDDING_FLAG_D 0x8000
+
+
+enum eap_sim_id_req {
+ NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
+};
+
+
+struct eap_sim_attrs {
+ const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s;
+ const u8 *next_pseudonym, *next_reauth_id;
+ const u8 *nonce_mt, *identity, *res, *auts;
+ const u8 *checkcode;
+ const u8 *kdf_input;
+ const u8 *bidding;
+ size_t num_chal, version_list_len, encr_data_len;
+ size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
+ size_t res_len_bits;
+ size_t checkcode_len;
+ size_t kdf_input_len;
+ enum eap_sim_id_req id_req;
+ int notification, counter, selected_version, client_error_code;
+ int counter_too_small;
+ int result_ind;
+#define EAP_AKA_PRIME_KDF_MAX 10
+ u16 kdf[EAP_AKA_PRIME_KDF_MAX];
+ size_t kdf_count;
+};
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+ struct eap_sim_attrs *attr, int aka, int encr);
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+ size_t encr_data_len, const u8 *iv,
+ struct eap_sim_attrs *attr, int aka);
+
+
+struct eap_sim_msg;
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+ const u8 *extra, size_t extra_len);
+void eap_sim_msg_free(struct eap_sim_msg *msg);
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len);
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr,
+ u16 value, const u8 *data, size_t len);
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr);
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr);
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr,
+ int attr_pad);
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka);
+
+#endif /* EAP_SIM_COMMON_H */
diff --git a/src/eap_common/eap_tlv_common.h b/src/eap_common/eap_tlv_common.h
new file mode 100644
index 0000000..f86015d
--- /dev/null
+++ b/src/eap_common/eap_tlv_common.h
@@ -0,0 +1,118 @@
+/*
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_TLV_COMMON_H
+#define EAP_TLV_COMMON_H
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_ERROR_CODE_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
+#define EAP_TLV_CRYPTO_BINDING_TLV 12
+#define EAP_TLV_CALLING_STATION_ID_TLV 13
+#define EAP_TLV_CALLED_STATION_ID_TLV 14
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
+#define EAP_TLV_IDENTITY_TYPE_TLV 17
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
+#define EAP_TLV_REQUEST_ACTION_TLV 19
+#define EAP_TLV_PKCS7_TLV 20
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+#define EAP_TLV_TYPE_MASK 0x3fff
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_tlv_hdr {
+ be16 tlv_type;
+ be16 length;
+} STRUCT_PACKED;
+
+struct eap_tlv_nak_tlv {
+ be16 tlv_type;
+ be16 length;
+ be32 vendor_id;
+ be16 nak_type;
+} STRUCT_PACKED;
+
+struct eap_tlv_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
+struct eap_tlv_intermediate_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+ /* Followed by optional TLVs */
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
+struct eap_tlv_crypto_binding_tlv {
+ be16 tlv_type;
+ be16 length;
+ u8 reserved;
+ u8 version;
+ u8 received_version;
+ u8 subtype;
+ u8 nonce[32];
+ u8 compound_mac[20];
+} STRUCT_PACKED;
+
+struct eap_tlv_pac_ack_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 pac_type;
+ be16 pac_len;
+ be16 result;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */
+struct eap_tlv_request_action_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 action;
+} STRUCT_PACKED;
+
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+struct eap_tlv_pac_type_tlv {
+ be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
+ be16 length;
+ be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define EAP_TLV_ACTION_PROCESS_TLV 1
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
+
+#endif /* EAP_TLV_COMMON_H */
diff --git a/src/eap_common/eap_ttls.h b/src/eap_common/eap_ttls.h
new file mode 100644
index 0000000..797d084
--- /dev/null
+++ b/src/eap_common/eap_ttls.h
@@ -0,0 +1,71 @@
+/*
+ * EAP server/peer: EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_TTLS_H
+#define EAP_TTLS_H
+
+struct ttls_avp {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ /* optional 32-bit Vendor-ID */
+ /* Data */
+};
+
+struct ttls_avp_vendor {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ be32 vendor_id;
+ /* Data */
+};
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+ int __pad; \
+ __pad = (4 - (((pos) - (start)) & 3)) & 3; \
+ os_memset((pos), 0, __pad); \
+ pos += __pad; \
+} while (0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#endif /* EAP_TTLS_H */
diff --git a/src/eap_common/eap_wsc_common.c b/src/eap_common/eap_wsc_common.c
new file mode 100644
index 0000000..5d4e8cc
--- /dev/null
+++ b/src/eap_common/eap_wsc_common.c
@@ -0,0 +1,39 @@
+/*
+ * EAP-WSC common routines for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "wps/wps.h"
+#include "eap_wsc_common.h"
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+ "FRAG_ACK");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK");
+ wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */
+ wpabuf_put_u8(msg, 0); /* Flags */
+
+ return msg;
+}
diff --git a/src/eap_common/eap_wsc_common.h b/src/eap_common/eap_wsc_common.h
new file mode 100644
index 0000000..fdf61d3
--- /dev/null
+++ b/src/eap_common/eap_wsc_common.h
@@ -0,0 +1,33 @@
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#endif /* EAP_WSC_COMMON_H */
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
new file mode 100644
index 0000000..003c288
--- /dev/null
+++ b/src/eap_common/ikev2_common.c
@@ -0,0 +1,797 @@
+/*
+ * IKEv2 common routines for initiator and responder
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/random.h"
+#include "ikev2_common.h"
+
+
+static struct ikev2_integ_alg ikev2_integ_algs[] = {
+ { AUTH_HMAC_SHA1_96, 20, 12 },
+ { AUTH_HMAC_MD5_96, 16, 12 }
+};
+
+#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0]))
+
+
+static struct ikev2_prf_alg ikev2_prf_algs[] = {
+ { PRF_HMAC_SHA1, 20, 20 },
+ { PRF_HMAC_MD5, 16, 16 }
+};
+
+#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0]))
+
+
+static struct ikev2_encr_alg ikev2_encr_algs[] = {
+ { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */
+ { ENCR_3DES, 24, 8 }
+};
+
+#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0]))
+
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_INTEG_ALGS; i++) {
+ if (ikev2_integ_algs[i].id == id)
+ return &ikev2_integ_algs[i];
+ }
+
+ return NULL;
+}
+
+
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *hash)
+{
+ u8 tmphash[IKEV2_MAX_HASH_LEN];
+
+ switch (alg) {
+ case AUTH_HMAC_SHA1_96:
+ if (key_len != 20)
+ return -1;
+ hmac_sha1(key, key_len, data, data_len, tmphash);
+ os_memcpy(hash, tmphash, 12);
+ break;
+ case AUTH_HMAC_MD5_96:
+ if (key_len != 16)
+ return -1;
+ hmac_md5(key, key_len, data, data_len, tmphash);
+ os_memcpy(hash, tmphash, 12);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+const struct ikev2_prf_alg * ikev2_get_prf(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_PRF_ALGS; i++) {
+ if (ikev2_prf_algs[i].id == id)
+ return &ikev2_prf_algs[i];
+ }
+
+ return NULL;
+}
+
+
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *hash)
+{
+ switch (alg) {
+ case PRF_HMAC_SHA1:
+ hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
+ break;
+ case PRF_HMAC_MD5:
+ hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len,
+ u8 *out, size_t out_len)
+{
+ u8 hash[IKEV2_MAX_HASH_LEN];
+ size_t hash_len;
+ u8 iter, *pos, *end;
+ const u8 *addr[3];
+ size_t len[3];
+ const struct ikev2_prf_alg *prf;
+ int res;
+
+ prf = ikev2_get_prf(alg);
+ if (prf == NULL)
+ return -1;
+ hash_len = prf->hash_len;
+
+ addr[0] = hash;
+ len[0] = hash_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &iter;
+ len[2] = 1;
+
+ pos = out;
+ end = out + out_len;
+ iter = 1;
+ while (pos < end) {
+ size_t clen;
+ if (iter == 1)
+ res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1],
+ &len[1], hash);
+ else
+ res = ikev2_prf_hash(alg, key, key_len, 3, addr, len,
+ hash);
+ if (res < 0)
+ return -1;
+ clen = hash_len;
+ if ((int) clen > end - pos)
+ clen = end - pos;
+ os_memcpy(pos, hash, clen);
+ pos += clen;
+ iter++;
+ }
+
+ return 0;
+}
+
+
+const struct ikev2_encr_alg * ikev2_get_encr(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_ENCR_ALGS; i++) {
+ if (ikev2_encr_algs[i].id == id)
+ return &ikev2_encr_algs[i];
+ }
+
+ return NULL;
+}
+
+
+#ifdef CCNS_PL
+/* from des.c */
+struct des3_key_s {
+ u32 ek[3][32];
+ u32 dk[3][32];
+};
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
+#endif /* CCNS_PL */
+
+
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *plain, u8 *crypt, size_t len)
+{
+ struct crypto_cipher *cipher;
+ int encr_alg;
+
+#ifdef CCNS_PL
+ if (alg == ENCR_3DES) {
+ struct des3_key_s des3key;
+ size_t i, blocks;
+ u8 *pos;
+
+ /* ECB mode is used incorrectly for 3DES!? */
+ if (key_len != 24) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
+ return -1;
+ }
+ des3_key_setup(key, &des3key);
+
+ blocks = len / 8;
+ pos = crypt;
+ for (i = 0; i < blocks; i++) {
+ des3_encrypt(pos, &des3key, pos);
+ pos += 8;
+ }
+ } else {
+#endif /* CCNS_PL */
+ switch (alg) {
+ case ENCR_3DES:
+ encr_alg = CRYPTO_CIPHER_ALG_3DES;
+ break;
+ case ENCR_AES_CBC:
+ encr_alg = CRYPTO_CIPHER_ALG_AES;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+ return -1;
+ }
+
+ cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+ if (cipher == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+ return -1;
+ }
+
+ if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Encryption failed");
+ crypto_cipher_deinit(cipher);
+ return -1;
+ }
+ crypto_cipher_deinit(cipher);
+#ifdef CCNS_PL
+ }
+#endif /* CCNS_PL */
+
+ return 0;
+}
+
+
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *crypt, u8 *plain, size_t len)
+{
+ struct crypto_cipher *cipher;
+ int encr_alg;
+
+#ifdef CCNS_PL
+ if (alg == ENCR_3DES) {
+ struct des3_key_s des3key;
+ size_t i, blocks;
+
+ /* ECB mode is used incorrectly for 3DES!? */
+ if (key_len != 24) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
+ return -1;
+ }
+ des3_key_setup(key, &des3key);
+
+ if (len % 8) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted "
+ "length");
+ return -1;
+ }
+ blocks = len / 8;
+ for (i = 0; i < blocks; i++) {
+ des3_decrypt(crypt, &des3key, plain);
+ plain += 8;
+ crypt += 8;
+ }
+ } else {
+#endif /* CCNS_PL */
+ switch (alg) {
+ case ENCR_3DES:
+ encr_alg = CRYPTO_CIPHER_ALG_3DES;
+ break;
+ case ENCR_AES_CBC:
+ encr_alg = CRYPTO_CIPHER_ALG_AES;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+ return -1;
+ }
+
+ cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+ if (cipher == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+ return -1;
+ }
+
+ if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Decryption failed");
+ crypto_cipher_deinit(cipher);
+ return -1;
+ }
+ crypto_cipher_deinit(cipher);
+#ifdef CCNS_PL
+ }
+#endif /* CCNS_PL */
+
+ return 0;
+}
+
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+ u8 next_payload, const u8 *pos, const u8 *end)
+{
+ const struct ikev2_payload_hdr *phdr;
+
+ os_memset(payloads, 0, sizeof(*payloads));
+
+ while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
+ int plen, pdatalen;
+ const u8 *pdata;
+ wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
+ next_payload);
+ if (end - pos < (int) sizeof(*phdr)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short message for "
+ "payload header (left=%ld)",
+ (long) (end - pos));
+ }
+ phdr = (const struct ikev2_payload_hdr *) pos;
+ plen = WPA_GET_BE16(phdr->payload_length);
+ if (plen < (int) sizeof(*phdr) || pos + plen > end) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid payload header "
+ "length %d", plen);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x"
+ " Payload Length: %d",
+ phdr->next_payload, phdr->flags, plen);
+
+ pdata = (const u8 *) (phdr + 1);
+ pdatalen = plen - sizeof(*phdr);
+
+ switch (next_payload) {
+ case IKEV2_PAYLOAD_SA:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Security "
+ "Association");
+ payloads->sa = pdata;
+ payloads->sa_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_KEY_EXCHANGE:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Key "
+ "Exchange");
+ payloads->ke = pdata;
+ payloads->ke_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_IDi:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDi");
+ payloads->idi = pdata;
+ payloads->idi_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_IDr:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDr");
+ payloads->idr = pdata;
+ payloads->idr_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_CERTIFICATE:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Certificate");
+ payloads->cert = pdata;
+ payloads->cert_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_AUTHENTICATION:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: "
+ "Authentication");
+ payloads->auth = pdata;
+ payloads->auth_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_NONCE:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Nonce");
+ payloads->nonce = pdata;
+ payloads->nonce_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_ENCRYPTED:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Encrypted");
+ payloads->encrypted = pdata;
+ payloads->encrypted_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_NOTIFICATION:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: "
+ "Notification");
+ payloads->notification = pdata;
+ payloads->notification_len = pdatalen;
+ break;
+ default:
+ if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported "
+ "critical payload %u - reject the "
+ "entire message", next_payload);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "IKEV2: Skipped "
+ "unsupported payload %u",
+ next_payload);
+ }
+ }
+
+ if (next_payload == IKEV2_PAYLOAD_ENCRYPTED &&
+ pos + plen == end) {
+ /*
+ * Next Payload in the case of Encrypted Payload is
+ * actually the payload type for the first embedded
+ * payload.
+ */
+ payloads->encr_next_payload = phdr->next_payload;
+ next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD;
+ } else
+ next_payload = phdr->next_payload;
+
+ pos += plen;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after "
+ "payloads");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+ const u8 *ID, size_t ID_len, u8 ID_type,
+ struct ikev2_keys *keys, int initiator,
+ const u8 *shared_secret, size_t shared_secret_len,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *key_pad, size_t key_pad_len,
+ u8 *auth_data)
+{
+ size_t sign_len, buf_len;
+ u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN];
+ const struct ikev2_prf_alg *prf;
+ const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr;
+
+ prf = ikev2_get_prf(prf_alg);
+ if (sign_msg == NULL || ID == NULL || SK_p == NULL ||
+ shared_secret == NULL || nonce == NULL || prf == NULL)
+ return -1;
+
+ /* prf(SK_pi/r,IDi/r') */
+ buf_len = 4 + ID_len;
+ buf = os_zalloc(buf_len);
+ if (buf == NULL)
+ return -1;
+ buf[0] = ID_type;
+ os_memcpy(buf + 4, ID, ID_len);
+ if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len,
+ 1, (const u8 **) &buf, &buf_len, hash) < 0) {
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+
+ /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */
+ sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len;
+ sign_data = os_malloc(sign_len);
+ if (sign_data == NULL)
+ return -1;
+ pos = sign_data;
+ os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg));
+ pos += wpabuf_len(sign_msg);
+ os_memcpy(pos, nonce, nonce_len);
+ pos += nonce_len;
+ os_memcpy(pos, hash, prf->hash_len);
+
+ /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */
+ if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1,
+ &key_pad, &key_pad_len, hash) < 0 ||
+ ikev2_prf_hash(prf->id, hash, prf->hash_len, 1,
+ (const u8 **) &sign_data, &sign_len, auth_data) < 0)
+ {
+ os_free(sign_data);
+ return -1;
+ }
+ os_free(sign_data);
+
+ return 0;
+}
+
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id,
+ struct ikev2_keys *keys, int initiator,
+ const struct ikev2_hdr *hdr,
+ const u8 *encrypted, size_t encrypted_len,
+ size_t *res_len)
+{
+ size_t iv_len;
+ const u8 *pos, *end, *iv, *integ;
+ u8 hash[IKEV2_MAX_HASH_LEN], *decrypted;
+ size_t decrypted_len, pad_len;
+ const struct ikev2_integ_alg *integ_alg;
+ const struct ikev2_encr_alg *encr_alg;
+ const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+ const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+ if (encrypted == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH");
+ return NULL;
+ }
+
+ encr_alg = ikev2_get_encr(encr_id);
+ if (encr_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+ return NULL;
+ }
+ iv_len = encr_alg->block_size;
+
+ integ_alg = ikev2_get_integ(integ_id);
+ if (integ_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+ return NULL;
+ }
+
+ if (encrypted_len < iv_len + 1 + integ_alg->hash_len) {
+ wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity "
+ "Checksum");
+ return NULL;
+ }
+
+ iv = encrypted;
+ pos = iv + iv_len;
+ end = encrypted + encrypted_len;
+ integ = end - integ_alg->hash_len;
+
+ if (SK_a == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+ return NULL;
+ }
+ if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+ (const u8 *) hdr,
+ integ - (const u8 *) hdr, hash) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity "
+ "hash");
+ return NULL;
+ }
+ if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
+ "Data");
+ return NULL;
+ }
+
+ if (SK_e == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+ return NULL;
+ }
+
+ decrypted_len = integ - pos;
+ decrypted = os_malloc(decrypted_len);
+ if (decrypted == NULL)
+ return NULL;
+
+ if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos,
+ decrypted, decrypted_len) < 0) {
+ os_free(decrypted);
+ return NULL;
+ }
+
+ pad_len = decrypted[decrypted_len - 1];
+ if (decrypted_len < pad_len + 1) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted "
+ "payload");
+ os_free(decrypted);
+ return NULL;
+ }
+
+ decrypted_len -= pad_len + 1;
+
+ *res_len = decrypted_len;
+ return decrypted;
+}
+
+
+void ikev2_update_hdr(struct wpabuf *msg)
+{
+ struct ikev2_hdr *hdr;
+
+ /* Update lenth field in HDR */
+ hdr = wpabuf_mhead(msg);
+ WPA_PUT_BE32(hdr->length, wpabuf_len(msg));
+}
+
+
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+ int initiator, struct wpabuf *msg,
+ struct wpabuf *plain, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ size_t iv_len, pad_len;
+ u8 *icv, *iv;
+ const struct ikev2_integ_alg *integ_alg;
+ const struct ikev2_encr_alg *encr_alg;
+ const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+ const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload");
+
+ /* Encr - RFC 4306, Sect. 3.14 */
+
+ encr_alg = ikev2_get_encr(encr_id);
+ if (encr_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+ return -1;
+ }
+ iv_len = encr_alg->block_size;
+
+ integ_alg = ikev2_get_integ(integ_id);
+ if (integ_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+ return -1;
+ }
+
+ if (SK_e == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+ return -1;
+ }
+
+ if (SK_a == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+ return -1;
+ }
+
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ iv = wpabuf_put(msg, iv_len);
+ if (random_get_bytes(iv, iv_len)) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
+ return -1;
+ }
+
+ pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len;
+ if (pad_len == iv_len)
+ pad_len = 0;
+ wpabuf_put(plain, pad_len);
+ wpabuf_put_u8(plain, pad_len);
+
+ if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv,
+ wpabuf_head(plain), wpabuf_mhead(plain),
+ wpabuf_len(plain)) < 0)
+ return -1;
+
+ wpabuf_put_buf(msg, plain);
+
+ /* Need to update all headers (Length fields) prior to hash func */
+ icv = wpabuf_put(msg, integ_alg->hash_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+
+ ikev2_update_hdr(msg);
+
+ return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+ wpabuf_head(msg),
+ wpabuf_len(msg) - integ_alg->hash_len, icv);
+
+ return 0;
+}
+
+
+int ikev2_keys_set(struct ikev2_keys *keys)
+{
+ return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei &&
+ keys->SK_er && keys->SK_pi && keys->SK_pr;
+}
+
+
+void ikev2_free_keys(struct ikev2_keys *keys)
+{
+ os_free(keys->SK_d);
+ os_free(keys->SK_ai);
+ os_free(keys->SK_ar);
+ os_free(keys->SK_ei);
+ os_free(keys->SK_er);
+ os_free(keys->SK_pi);
+ os_free(keys->SK_pr);
+ keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er =
+ keys->SK_pi = keys->SK_pr = NULL;
+}
+
+
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+ const struct ikev2_integ_alg *integ,
+ const struct ikev2_encr_alg *encr,
+ const u8 *skeyseed, const u8 *data, size_t data_len,
+ struct ikev2_keys *keys)
+{
+ u8 *keybuf, *pos;
+ size_t keybuf_len;
+
+ /*
+ * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } =
+ * prf+(SKEYSEED, Ni | Nr | SPIi | SPIr )
+ */
+ ikev2_free_keys(keys);
+ keys->SK_d_len = prf->key_len;
+ keys->SK_integ_len = integ->key_len;
+ keys->SK_encr_len = encr->key_len;
+ keys->SK_prf_len = prf->key_len;
+#ifdef CCNS_PL
+ /* Uses encryption key length for SK_d; should be PRF length */
+ keys->SK_d_len = keys->SK_encr_len;
+#endif /* CCNS_PL */
+
+ keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len +
+ 2 * keys->SK_encr_len + 2 * keys->SK_prf_len;
+ keybuf = os_malloc(keybuf_len);
+ if (keybuf == NULL)
+ return -1;
+
+ if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len,
+ data, data_len, keybuf, keybuf_len)) {
+ os_free(keybuf);
+ return -1;
+ }
+
+ pos = keybuf;
+
+ keys->SK_d = os_malloc(keys->SK_d_len);
+ if (keys->SK_d) {
+ os_memcpy(keys->SK_d, pos, keys->SK_d_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d",
+ keys->SK_d, keys->SK_d_len);
+ }
+ pos += keys->SK_d_len;
+
+ keys->SK_ai = os_malloc(keys->SK_integ_len);
+ if (keys->SK_ai) {
+ os_memcpy(keys->SK_ai, pos, keys->SK_integ_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai",
+ keys->SK_ai, keys->SK_integ_len);
+ }
+ pos += keys->SK_integ_len;
+
+ keys->SK_ar = os_malloc(keys->SK_integ_len);
+ if (keys->SK_ar) {
+ os_memcpy(keys->SK_ar, pos, keys->SK_integ_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar",
+ keys->SK_ar, keys->SK_integ_len);
+ }
+ pos += keys->SK_integ_len;
+
+ keys->SK_ei = os_malloc(keys->SK_encr_len);
+ if (keys->SK_ei) {
+ os_memcpy(keys->SK_ei, pos, keys->SK_encr_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei",
+ keys->SK_ei, keys->SK_encr_len);
+ }
+ pos += keys->SK_encr_len;
+
+ keys->SK_er = os_malloc(keys->SK_encr_len);
+ if (keys->SK_er) {
+ os_memcpy(keys->SK_er, pos, keys->SK_encr_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er",
+ keys->SK_er, keys->SK_encr_len);
+ }
+ pos += keys->SK_encr_len;
+
+ keys->SK_pi = os_malloc(keys->SK_prf_len);
+ if (keys->SK_pi) {
+ os_memcpy(keys->SK_pi, pos, keys->SK_prf_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi",
+ keys->SK_pi, keys->SK_prf_len);
+ }
+ pos += keys->SK_prf_len;
+
+ keys->SK_pr = os_malloc(keys->SK_prf_len);
+ if (keys->SK_pr) {
+ os_memcpy(keys->SK_pr, pos, keys->SK_prf_len);
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr",
+ keys->SK_pr, keys->SK_prf_len);
+ }
+
+ os_free(keybuf);
+
+ if (!ikev2_keys_set(keys)) {
+ ikev2_free_keys(keys);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h
new file mode 100644
index 0000000..c96a070
--- /dev/null
+++ b/src/eap_common/ikev2_common.h
@@ -0,0 +1,344 @@
+/*
+ * IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IKEV2_COMMON_H
+#define IKEV2_COMMON_H
+
+/*
+ * Nonce length must be at least 16 octets. It must also be at least half the
+ * key size of the negotiated PRF.
+ */
+#define IKEV2_NONCE_MIN_LEN 16
+#define IKEV2_NONCE_MAX_LEN 256
+
+/* IKE Header - RFC 4306, Sect. 3.1 */
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#define IKEV2_SPI_LEN 8
+
+struct ikev2_hdr {
+ u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */
+ u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */
+ u8 next_payload;
+ u8 version; /* MjVer | MnVer */
+ u8 exchange_type;
+ u8 flags;
+ u8 message_id[4];
+ u8 length[4]; /* total length of HDR + payloads */
+} STRUCT_PACKED;
+
+struct ikev2_payload_hdr {
+ u8 next_payload;
+ u8 flags;
+ u8 payload_length[2]; /* this payload, including the payload header */
+} STRUCT_PACKED;
+
+struct ikev2_proposal {
+ u8 type; /* 0 (last) or 2 (more) */
+ u8 reserved;
+ u8 proposal_length[2]; /* including all transform and attributes */
+ u8 proposal_num;
+ u8 protocol_id; /* IKEV2_PROTOCOL_* */
+ u8 spi_size;
+ u8 num_transforms;
+ /* SPI of spi_size octets */
+ /* Transforms */
+} STRUCT_PACKED;
+
+struct ikev2_transform {
+ u8 type; /* 0 (last) or 3 (more) */
+ u8 reserved;
+ u8 transform_length[2]; /* including Header and Attributes */
+ u8 transform_type;
+ u8 reserved2;
+ u8 transform_id[2];
+ /* Transform Attributes */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* Current IKEv2 version from RFC 4306 */
+#define IKEV2_MjVer 2
+#define IKEV2_MnVer 0
+#ifdef CCNS_PL
+#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4))
+#else /* CCNS_PL */
+#define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer))
+#endif /* CCNS_PL */
+
+/* IKEv2 Exchange Types */
+enum {
+ /* 0-33 RESERVED */
+ IKE_SA_INIT = 34,
+ IKE_SA_AUTH = 35,
+ CREATE_CHILD_SA = 36,
+ INFORMATION = 37
+ /* 38-239 RESERVED TO IANA */
+ /* 240-255 Reserved for private use */
+};
+
+/* IKEv2 Flags */
+#define IKEV2_HDR_INITIATOR 0x08
+#define IKEV2_HDR_VERSION 0x10
+#define IKEV2_HDR_RESPONSE 0x20
+
+/* Payload Header Flags */
+#define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01
+
+
+/* EAP-IKEv2 Payload Types (in Next Payload Type field)
+ * http://www.iana.org/assignments/eap-ikev2-payloads */
+enum {
+ IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0,
+ IKEV2_PAYLOAD_SA = 33,
+ IKEV2_PAYLOAD_KEY_EXCHANGE = 34,
+ IKEV2_PAYLOAD_IDi = 35,
+ IKEV2_PAYLOAD_IDr = 36,
+ IKEV2_PAYLOAD_CERTIFICATE = 37,
+ IKEV2_PAYLOAD_CERT_REQ = 38,
+ IKEV2_PAYLOAD_AUTHENTICATION = 39,
+ IKEV2_PAYLOAD_NONCE = 40,
+ IKEV2_PAYLOAD_NOTIFICATION = 41,
+ IKEV2_PAYLOAD_VENDOD_ID = 43,
+ IKEV2_PAYLOAD_ENCRYPTED = 46,
+ IKEV2_PAYLOAD_NEXT_FAST_ID = 121
+};
+
+
+/* IKEv2 Proposal - Protocol ID */
+enum {
+ IKEV2_PROTOCOL_RESERVED = 0,
+ IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */
+ IKEV2_PROTOCOL_AH = 2,
+ IKEV2_PROTOCOL_ESP = 3
+};
+
+
+/* IKEv2 Transform Types */
+enum {
+ IKEV2_TRANSFORM_ENCR = 1,
+ IKEV2_TRANSFORM_PRF = 2,
+ IKEV2_TRANSFORM_INTEG = 3,
+ IKEV2_TRANSFORM_DH = 4,
+ IKEV2_TRANSFORM_ESN = 5
+};
+
+/* IKEv2 Tranform Type 1 (Encryption Algorithm) */
+enum {
+ ENCR_DES_IV64 = 1,
+ ENCR_DES = 2,
+ ENCR_3DES = 3,
+ ENCR_RC5 = 4,
+ ENCR_IDEA = 5,
+ ENCR_CAST = 6,
+ ENCR_BLOWFISH = 7,
+ ENCR_3IDEA = 8,
+ ENCR_DES_IV32 = 9,
+ ENCR_NULL = 11,
+ ENCR_AES_CBC = 12,
+ ENCR_AES_CTR = 13
+};
+
+/* IKEv2 Transform Type 2 (Pseudo-random Function) */
+enum {
+ PRF_HMAC_MD5 = 1,
+ PRF_HMAC_SHA1 = 2,
+ PRF_HMAC_TIGER = 3,
+ PRF_AES128_XCBC = 4
+};
+
+/* IKEv2 Transform Type 3 (Integrity Algorithm) */
+enum {
+ AUTH_HMAC_MD5_96 = 1,
+ AUTH_HMAC_SHA1_96 = 2,
+ AUTH_DES_MAC = 3,
+ AUTH_KPDK_MD5 = 4,
+ AUTH_AES_XCBC_96 = 5
+};
+
+/* IKEv2 Transform Type 4 (Diffie-Hellman Group) */
+enum {
+ DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */
+ DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */
+ DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */
+ DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */
+ DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */
+ DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */
+ DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */
+ DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */
+};
+
+
+/* Identification Data Types (RFC 4306, Sect. 3.5) */
+enum {
+ ID_IPV4_ADDR = 1,
+ ID_FQDN = 2,
+ ID_RFC822_ADDR = 3,
+ ID_IPV6_ADDR = 5,
+ ID_DER_ASN1_DN = 9,
+ ID_DER_ASN1_GN= 10,
+ ID_KEY_ID = 11
+};
+
+
+/* Certificate Encoding (RFC 4306, Sect. 3.6) */
+enum {
+ CERT_ENCODING_PKCS7_X509 = 1,
+ CERT_ENCODING_PGP_CERT = 2,
+ CERT_ENCODING_DNS_SIGNED_KEY = 3,
+ /* X.509 Certificate - Signature: DER encoded X.509 certificate whose
+ * public key is used to validate the sender's AUTH payload */
+ CERT_ENCODING_X509_CERT_SIGN = 4,
+ CERT_ENCODING_KERBEROS_TOKEN = 6,
+ /* DER encoded X.509 certificate revocation list */
+ CERT_ENCODING_CRL = 7,
+ CERT_ENCODING_ARL = 8,
+ CERT_ENCODING_SPKI_CERT = 9,
+ CERT_ENCODING_X509_CERT_ATTR = 10,
+ /* PKCS #1 encoded RSA key */
+ CERT_ENCODING_RAW_RSA_KEY = 11,
+ CERT_ENCODING_HASH_AND_URL_X509_CERT = 12,
+ CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13
+};
+
+
+/* Authentication Method (RFC 4306, Sect. 3.8) */
+enum {
+ AUTH_RSA_SIGN = 1,
+ AUTH_SHARED_KEY_MIC = 2,
+ AUTH_DSS_SIGN = 3
+};
+
+
+/* Notify Message Types (RFC 4306, Sect. 3.10.1) */
+enum {
+ UNSUPPORTED_CRITICAL_PAYLOAD = 1,
+ INVALID_IKE_SPI = 4,
+ INVALID_MAJOR_VERSION = 5,
+ INVALID_SYNTAX = 7,
+ INVALID_MESSAGE_ID = 9,
+ INVALID_SPI = 11,
+ NO_PROPOSAL_CHOSEN = 14,
+ INVALID_KE_PAYLOAD = 17,
+ AUTHENTICATION_FAILED = 24,
+ SINGLE_PAIR_REQUIRED = 34,
+ NO_ADDITIONAL_SAS = 35,
+ INTERNAL_ADDRESS_FAILURE = 36,
+ FAILED_CP_REQUIRED = 37,
+ TS_UNACCEPTABLE = 38,
+ INVALID_SELECTORS = 39
+};
+
+
+struct ikev2_keys {
+ u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr;
+ size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len;
+};
+
+
+int ikev2_keys_set(struct ikev2_keys *keys);
+void ikev2_free_keys(struct ikev2_keys *keys);
+
+
+/* Maximum hash length for supported hash algorithms */
+#define IKEV2_MAX_HASH_LEN 20
+
+struct ikev2_integ_alg {
+ int id;
+ size_t key_len;
+ size_t hash_len;
+};
+
+struct ikev2_prf_alg {
+ int id;
+ size_t key_len;
+ size_t hash_len;
+};
+
+struct ikev2_encr_alg {
+ int id;
+ size_t key_len;
+ size_t block_size;
+};
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id);
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *hash);
+const struct ikev2_prf_alg * ikev2_get_prf(int id);
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *hash);
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len,
+ u8 *out, size_t out_len);
+const struct ikev2_encr_alg * ikev2_get_encr(int id);
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *plain, u8 *crypt, size_t len);
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *crypt, u8 *plain, size_t len);
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+ const u8 *ID, size_t ID_len, u8 ID_type,
+ struct ikev2_keys *keys, int initiator,
+ const u8 *shared_secret, size_t shared_secret_len,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *key_pad, size_t key_pad_len,
+ u8 *auth_data);
+
+
+struct ikev2_payloads {
+ const u8 *sa;
+ size_t sa_len;
+ const u8 *ke;
+ size_t ke_len;
+ const u8 *idi;
+ size_t idi_len;
+ const u8 *idr;
+ size_t idr_len;
+ const u8 *cert;
+ size_t cert_len;
+ const u8 *auth;
+ size_t auth_len;
+ const u8 *nonce;
+ size_t nonce_len;
+ const u8 *encrypted;
+ size_t encrypted_len;
+ u8 encr_next_payload;
+ const u8 *notification;
+ size_t notification_len;
+};
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+ u8 next_payload, const u8 *pos, const u8 *end);
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys,
+ int initiator, const struct ikev2_hdr *hdr,
+ const u8 *encrypted, size_t encrypted_len,
+ size_t *res_len);
+void ikev2_update_hdr(struct wpabuf *msg);
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+ int initiator, struct wpabuf *msg,
+ struct wpabuf *plain, u8 next_payload);
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+ const struct ikev2_integ_alg *integ,
+ const struct ikev2_encr_alg *encr,
+ const u8 *skeyseed, const u8 *data, size_t data_len,
+ struct ikev2_keys *keys);
+
+#endif /* IKEV2_COMMON_H */