aboutsummaryrefslogtreecommitdiffstats
path: root/src/eap_common
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2008-02-28 01:34:43 (GMT)
committerJouni Malinen <jm@jm.kir.nu>2008-02-28 01:34:43 (GMT)
commit6fc6879bd55a394f807cbbe927df736c190cb8ab (patch)
treecdf50da0c58f21510a808d53502a060d911ff243 /src/eap_common
downloadhostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.zip
hostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.gz
hostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.bz2
Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
Diffstat (limited to 'src/eap_common')
-rw-r--r--src/eap_common/.gitignore1
-rw-r--r--src/eap_common/Makefile6
-rw-r--r--src/eap_common/chap.c35
-rw-r--r--src/eap_common/chap.h23
-rw-r--r--src/eap_common/eap_common.c184
-rw-r--r--src/eap_common/eap_common.h28
-rw-r--r--src/eap_common/eap_defs.h84
-rw-r--r--src/eap_common/eap_fast_common.h85
-rw-r--r--src/eap_common/eap_gpsk_common.c426
-rw-r--r--src/eap_common/eap_gpsk_common.h66
-rw-r--r--src/eap_common/eap_ikev2_common.c132
-rw-r--r--src/eap_common/eap_ikev2_common.h42
-rw-r--r--src/eap_common/eap_pax_common.c150
-rw-r--r--src/eap_common/eap_pax_common.h97
-rw-r--r--src/eap_common/eap_psk_common.c74
-rw-r--r--src/eap_common/eap_psk_common.h78
-rw-r--r--src/eap_common/eap_sake_common.c393
-rw-r--r--src/eap_common/eap_sake_common.h102
-rw-r--r--src/eap_common/eap_sim_common.c867
-rw-r--r--src/eap_common/eap_sim_common.h172
-rw-r--r--src/eap_common/eap_tlv_common.h119
-rw-r--r--src/eap_common/eap_ttls.h71
-rw-r--r--src/eap_common/ikev2_common.c796
-rw-r--r--src/eap_common/ikev2_common.h344
24 files changed, 4375 insertions, 0 deletions
diff --git a/src/eap_common/.gitignore b/src/eap_common/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/eap_common/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
new file mode 100644
index 0000000..37d649c
--- /dev/null
+++ b/src/eap_common/Makefile
@@ -0,0 +1,6 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
diff --git a/src/eap_common/chap.c b/src/eap_common/chap.c
new file mode 100644
index 0000000..a088aff
--- /dev/null
+++ b/src/eap_common/chap.c
@@ -0,0 +1,35 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * 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 "md5.h"
+#include "crypto.h"
+#include "chap.h"
+
+void 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;
+ 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..209dc8a
--- /dev/null
+++ b/src/eap_common/chap.h
@@ -0,0 +1,23 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * 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 CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+void 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..6a75139
--- /dev/null
+++ b/src/eap_common/eap_defs.h
@@ -0,0 +1,84 @@
+/*
+ * 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 /* draft-ietf-pppext-eap-ttls-02.txt */,
+ 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_EXPANDED = 254 /* RFC 3748 */,
+ EAP_TYPE_GPSK = 255 /* EXPERIMENTAL - type not yet allocated
+ * draft-ietf-emu-eap-gpsk-01.txt */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+ EAP_VENDOR_IETF = 0,
+ 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.h b/src/eap_common/eap_fast_common.h
new file mode 100644
index 0000000..5d821da
--- /dev/null
+++ b/src/eap_common/eap_fast_common.h
@@ -0,0 +1,85 @@
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * 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_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 TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * draft-cam-winget-eap-fast-provisioning-04.txt:
+ * 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
+
+/* draft-cam-winget-eap-fast-provisioning-04.txt: 4.2.6 PAC-Type TLV
+ * Note: Machine Authentication PAC and User Authorization PAC were removed in
+ * draft-cam-winget-eap-fast-provisioning-03.txt
+ */
+#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
+
+
+/*
+ * draft-cam-winget-eap-fast-provisioning-04.txt:
+ * Section 3.4 - 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 */
+};
+
+#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..08bd950
--- /dev/null
+++ b/src/eap_common/eap_gpsk_common.c
@@ -0,0 +1,426 @@
+/*
+ * 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 "eap_defs.h"
+#include "aes_wrap.h"
+#include "crypto.h"
+#ifdef EAP_GPSK_SHA256
+#include "sha256.h"
+#endif /* EAP_GPSK_SHA256 */
+#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 %d for "
+ "AES-CMAC MIC", 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..8011046
--- /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 "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_psk_common.c b/src/eap_common/eap_psk_common.c
new file mode 100644
index 0000000..0def3e8
--- /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 "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_sake_common.c b/src/eap_common/eap_sake_common.c
new file mode 100644
index 0000000..eafad1d
--- /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 "sha1.h"
+#include "wpabuf.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..02d20ca
--- /dev/null
+++ b/src/eap_common/eap_sim_common.c
@@ -0,0 +1,867 @@
+/*
+ * EAP peer/server: EAP-SIM/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 "eap_common/eap_defs.h"
+#include "sha1.h"
+#include "crypto.h"
+#include "aes_wrap.h"
+#include "wpabuf.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);
+}
+
+
+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");
+ attr->identity = apos + 2;
+ attr->identity_len = alen - 2;
+ 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");
+ 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) {
+ 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;
+ 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 */
+};
+
+
+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->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 (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 (os_get_random(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..49d15cf
--- /dev/null
+++ b/src/eap_common/eap_sim_common.h
@@ -0,0 +1,172 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA 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_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
+
+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);
+
+
+/* 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_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
+
+/* 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
+
+
+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;
+ 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 checkcode_len;
+ enum eap_sim_id_req id_req;
+ int notification, counter, selected_version, client_error_code;
+ int counter_too_small;
+ int result_ind;
+};
+
+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..75a3b49
--- /dev/null
+++ b/src/eap_common/eap_tlv_common.h
@@ -0,0 +1,119 @@
+/*
+ * 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 /* draft-cam-winget-eap-fast-provisioning-04.txt,
+ * 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;
+
+/* draft-cam-winget-eap-fast-provisiong-04.txt, 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..f69af14
--- /dev/null
+++ b/src/eap_common/eap_ttls.h
@@ -0,0 +1,71 @@
+/*
+ * EAP server/peer: EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt)
+ * 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/ikev2_common.c b/src/eap_common/ikev2_common.c
new file mode 100644
index 0000000..818b5bd
--- /dev/null
+++ b/src/eap_common/ikev2_common.c
@@ -0,0 +1,796 @@
+/*
+ * 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 "sha1.h"
+#include "md5.h"
+#include "crypto.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 (os_get_random(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 */