139beb93cSSam Leffler /*
239beb93cSSam Leffler * EAP server/peer: EAP-GPSK shared routines
339beb93cSSam Leffler * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
739beb93cSSam Leffler */
839beb93cSSam Leffler
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler
1139beb93cSSam Leffler #include "common.h"
12e28a4053SRui Paulo #include "crypto/aes_wrap.h"
13e28a4053SRui Paulo #include "crypto/sha256.h"
1439beb93cSSam Leffler #include "eap_defs.h"
1539beb93cSSam Leffler #include "eap_gpsk_common.h"
1639beb93cSSam Leffler
1739beb93cSSam Leffler
1839beb93cSSam Leffler /**
1939beb93cSSam Leffler * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
2039beb93cSSam Leffler * @vendor: CSuite/Vendor
2139beb93cSSam Leffler * @specifier: CSuite/Specifier
2239beb93cSSam Leffler * Returns: 1 if ciphersuite is support, or 0 if not
2339beb93cSSam Leffler */
eap_gpsk_supported_ciphersuite(int vendor,int specifier)2439beb93cSSam Leffler int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
2539beb93cSSam Leffler {
2639beb93cSSam Leffler if (vendor == EAP_GPSK_VENDOR_IETF &&
2739beb93cSSam Leffler specifier == EAP_GPSK_CIPHER_AES)
2839beb93cSSam Leffler return 1;
2939beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
3039beb93cSSam Leffler if (vendor == EAP_GPSK_VENDOR_IETF &&
3139beb93cSSam Leffler specifier == EAP_GPSK_CIPHER_SHA256)
3239beb93cSSam Leffler return 1;
3339beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
3439beb93cSSam Leffler return 0;
3539beb93cSSam Leffler }
3639beb93cSSam Leffler
3739beb93cSSam Leffler
eap_gpsk_gkdf_cmac(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)3839beb93cSSam Leffler static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
3939beb93cSSam Leffler const u8 *data /* Z */, size_t data_len,
4039beb93cSSam Leffler u8 *buf, size_t len /* X */)
4139beb93cSSam Leffler {
4239beb93cSSam Leffler u8 *opos;
4339beb93cSSam Leffler size_t i, n, hashlen, left, clen;
4439beb93cSSam Leffler u8 ibuf[2], hash[16];
4539beb93cSSam Leffler const u8 *addr[2];
4639beb93cSSam Leffler size_t vlen[2];
4739beb93cSSam Leffler
4839beb93cSSam Leffler hashlen = sizeof(hash);
4939beb93cSSam Leffler /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
5039beb93cSSam Leffler addr[0] = ibuf;
5139beb93cSSam Leffler vlen[0] = sizeof(ibuf);
5239beb93cSSam Leffler addr[1] = data;
5339beb93cSSam Leffler vlen[1] = data_len;
5439beb93cSSam Leffler
5539beb93cSSam Leffler opos = buf;
5639beb93cSSam Leffler left = len;
5739beb93cSSam Leffler n = (len + hashlen - 1) / hashlen;
5839beb93cSSam Leffler for (i = 1; i <= n; i++) {
5939beb93cSSam Leffler WPA_PUT_BE16(ibuf, i);
6039beb93cSSam Leffler if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
6139beb93cSSam Leffler return -1;
6239beb93cSSam Leffler clen = left > hashlen ? hashlen : left;
6339beb93cSSam Leffler os_memcpy(opos, hash, clen);
6439beb93cSSam Leffler opos += clen;
6539beb93cSSam Leffler left -= clen;
6639beb93cSSam Leffler }
6739beb93cSSam Leffler
6839beb93cSSam Leffler return 0;
6939beb93cSSam Leffler }
7039beb93cSSam Leffler
7139beb93cSSam Leffler
7239beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
eap_gpsk_gkdf_sha256(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)7339beb93cSSam Leffler static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
7439beb93cSSam Leffler const u8 *data /* Z */, size_t data_len,
7539beb93cSSam Leffler u8 *buf, size_t len /* X */)
7639beb93cSSam Leffler {
7739beb93cSSam Leffler u8 *opos;
7839beb93cSSam Leffler size_t i, n, hashlen, left, clen;
7939beb93cSSam Leffler u8 ibuf[2], hash[SHA256_MAC_LEN];
8039beb93cSSam Leffler const u8 *addr[2];
8139beb93cSSam Leffler size_t vlen[2];
8239beb93cSSam Leffler
8339beb93cSSam Leffler hashlen = SHA256_MAC_LEN;
8439beb93cSSam Leffler /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
8539beb93cSSam Leffler addr[0] = ibuf;
8639beb93cSSam Leffler vlen[0] = sizeof(ibuf);
8739beb93cSSam Leffler addr[1] = data;
8839beb93cSSam Leffler vlen[1] = data_len;
8939beb93cSSam Leffler
9039beb93cSSam Leffler opos = buf;
9139beb93cSSam Leffler left = len;
9239beb93cSSam Leffler n = (len + hashlen - 1) / hashlen;
9339beb93cSSam Leffler for (i = 1; i <= n; i++) {
9439beb93cSSam Leffler WPA_PUT_BE16(ibuf, i);
95*780fb4a2SCy Schubert if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash))
96*780fb4a2SCy Schubert return -1;
9739beb93cSSam Leffler clen = left > hashlen ? hashlen : left;
9839beb93cSSam Leffler os_memcpy(opos, hash, clen);
9939beb93cSSam Leffler opos += clen;
10039beb93cSSam Leffler left -= clen;
10139beb93cSSam Leffler }
10239beb93cSSam Leffler
10339beb93cSSam Leffler return 0;
10439beb93cSSam Leffler }
10539beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
10639beb93cSSam Leffler
10739beb93cSSam Leffler
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)10839beb93cSSam Leffler static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
10939beb93cSSam Leffler u8 *kdf_out, size_t kdf_out_len,
11039beb93cSSam Leffler const u8 *psk, size_t psk_len,
11139beb93cSSam Leffler const u8 *seed, size_t seed_len,
11239beb93cSSam Leffler u8 *msk, u8 *emsk,
11339beb93cSSam Leffler u8 *sk, size_t sk_len,
11439beb93cSSam Leffler u8 *pk, size_t pk_len)
11539beb93cSSam Leffler {
11639beb93cSSam Leffler u8 mk[32], *pos, *data;
11739beb93cSSam Leffler size_t data_len, mk_len;
11839beb93cSSam Leffler int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
11939beb93cSSam Leffler u8 *buf, size_t len);
12039beb93cSSam Leffler
12139beb93cSSam Leffler gkdf = NULL;
12239beb93cSSam Leffler switch (csuite_specifier) {
12339beb93cSSam Leffler case EAP_GPSK_CIPHER_AES:
12439beb93cSSam Leffler gkdf = eap_gpsk_gkdf_cmac;
12539beb93cSSam Leffler mk_len = 16;
12639beb93cSSam Leffler break;
12739beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
12839beb93cSSam Leffler case EAP_GPSK_CIPHER_SHA256:
12939beb93cSSam Leffler gkdf = eap_gpsk_gkdf_sha256;
13039beb93cSSam Leffler mk_len = SHA256_MAC_LEN;
13139beb93cSSam Leffler break;
13239beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
13339beb93cSSam Leffler default:
13439beb93cSSam Leffler return -1;
13539beb93cSSam Leffler }
13639beb93cSSam Leffler
13739beb93cSSam Leffler if (psk_len < mk_len)
13839beb93cSSam Leffler return -1;
13939beb93cSSam Leffler
14039beb93cSSam Leffler data_len = 2 + psk_len + 6 + seed_len;
14139beb93cSSam Leffler data = os_malloc(data_len);
14239beb93cSSam Leffler if (data == NULL)
14339beb93cSSam Leffler return -1;
14439beb93cSSam Leffler pos = data;
14539beb93cSSam Leffler WPA_PUT_BE16(pos, psk_len);
14639beb93cSSam Leffler pos += 2;
14739beb93cSSam Leffler os_memcpy(pos, psk, psk_len);
14839beb93cSSam Leffler pos += psk_len;
14939beb93cSSam Leffler WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
15039beb93cSSam Leffler pos += 4;
15139beb93cSSam Leffler WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
15239beb93cSSam Leffler pos += 2;
15339beb93cSSam Leffler os_memcpy(pos, seed, seed_len); /* inputString */
15439beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
15539beb93cSSam Leffler data, data_len);
15639beb93cSSam Leffler
15739beb93cSSam Leffler if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
15839beb93cSSam Leffler os_free(data);
15939beb93cSSam Leffler return -1;
16039beb93cSSam Leffler }
16139beb93cSSam Leffler os_free(data);
16239beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
16339beb93cSSam Leffler
16439beb93cSSam Leffler if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
16539beb93cSSam Leffler return -1;
16639beb93cSSam Leffler
16739beb93cSSam Leffler pos = kdf_out;
16839beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
16939beb93cSSam Leffler os_memcpy(msk, pos, EAP_MSK_LEN);
17039beb93cSSam Leffler pos += EAP_MSK_LEN;
17139beb93cSSam Leffler
17239beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
17339beb93cSSam Leffler os_memcpy(emsk, pos, EAP_EMSK_LEN);
17439beb93cSSam Leffler pos += EAP_EMSK_LEN;
17539beb93cSSam Leffler
17639beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
17739beb93cSSam Leffler os_memcpy(sk, pos, sk_len);
17839beb93cSSam Leffler pos += sk_len;
17939beb93cSSam Leffler
18039beb93cSSam Leffler if (pk) {
18139beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
18239beb93cSSam Leffler os_memcpy(pk, pos, pk_len);
18339beb93cSSam Leffler }
18439beb93cSSam Leffler
18539beb93cSSam Leffler return 0;
18639beb93cSSam Leffler }
18739beb93cSSam Leffler
18839beb93cSSam Leffler
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)18939beb93cSSam Leffler static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
19039beb93cSSam Leffler const u8 *seed, size_t seed_len,
19139beb93cSSam Leffler u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
19239beb93cSSam Leffler u8 *pk, size_t *pk_len)
19339beb93cSSam Leffler {
19439beb93cSSam Leffler #define EAP_GPSK_SK_LEN_AES 16
19539beb93cSSam Leffler #define EAP_GPSK_PK_LEN_AES 16
19639beb93cSSam Leffler u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
19739beb93cSSam Leffler EAP_GPSK_PK_LEN_AES];
19839beb93cSSam Leffler
19939beb93cSSam Leffler /*
20039beb93cSSam Leffler * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
20139beb93cSSam Leffler * (= seed)
20239beb93cSSam Leffler * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
20339beb93cSSam Leffler * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
20439beb93cSSam Leffler * MSK = GKDF-160 (MK, inputString)[0..63]
20539beb93cSSam Leffler * EMSK = GKDF-160 (MK, inputString)[64..127]
20639beb93cSSam Leffler * SK = GKDF-160 (MK, inputString)[128..143]
20739beb93cSSam Leffler * PK = GKDF-160 (MK, inputString)[144..159]
20839beb93cSSam Leffler * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
20939beb93cSSam Leffler * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
21039beb93cSSam Leffler * CSuite_Sel || inputString)
21139beb93cSSam Leffler */
21239beb93cSSam Leffler
21339beb93cSSam Leffler *sk_len = EAP_GPSK_SK_LEN_AES;
21439beb93cSSam Leffler *pk_len = EAP_GPSK_PK_LEN_AES;
21539beb93cSSam Leffler
21639beb93cSSam Leffler return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
21739beb93cSSam Leffler kdf_out, sizeof(kdf_out),
21839beb93cSSam Leffler psk, psk_len, seed, seed_len,
21939beb93cSSam Leffler msk, emsk, sk, *sk_len,
22039beb93cSSam Leffler pk, *pk_len);
22139beb93cSSam Leffler }
22239beb93cSSam Leffler
22339beb93cSSam Leffler
22439beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
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)22539beb93cSSam Leffler static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
22639beb93cSSam Leffler const u8 *seed, size_t seed_len,
22739beb93cSSam Leffler u8 *msk, u8 *emsk,
22839beb93cSSam Leffler u8 *sk, size_t *sk_len)
22939beb93cSSam Leffler {
23039beb93cSSam Leffler #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
23139beb93cSSam Leffler #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
23239beb93cSSam Leffler u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
23339beb93cSSam Leffler EAP_GPSK_PK_LEN_SHA256];
23439beb93cSSam Leffler
23539beb93cSSam Leffler /*
23639beb93cSSam Leffler * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
23739beb93cSSam Leffler * (= seed)
23839beb93cSSam Leffler * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
23939beb93cSSam Leffler * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
24039beb93cSSam Leffler * MSK = GKDF-160 (MK, inputString)[0..63]
24139beb93cSSam Leffler * EMSK = GKDF-160 (MK, inputString)[64..127]
24239beb93cSSam Leffler * SK = GKDF-160 (MK, inputString)[128..159]
24339beb93cSSam Leffler * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
24439beb93cSSam Leffler * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
24539beb93cSSam Leffler * CSuite_Sel || inputString)
24639beb93cSSam Leffler */
24739beb93cSSam Leffler
24839beb93cSSam Leffler *sk_len = EAP_GPSK_SK_LEN_SHA256;
24939beb93cSSam Leffler
25039beb93cSSam Leffler return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
25139beb93cSSam Leffler kdf_out, sizeof(kdf_out),
25239beb93cSSam Leffler psk, psk_len, seed, seed_len,
25339beb93cSSam Leffler msk, emsk, sk, *sk_len,
25439beb93cSSam Leffler NULL, 0);
25539beb93cSSam Leffler }
25639beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
25739beb93cSSam Leffler
25839beb93cSSam Leffler
25939beb93cSSam Leffler /**
26039beb93cSSam Leffler * eap_gpsk_derive_keys - Derive EAP-GPSK keys
26139beb93cSSam Leffler * @psk: Pre-shared key
26239beb93cSSam Leffler * @psk_len: Length of psk in bytes
26339beb93cSSam Leffler * @vendor: CSuite/Vendor
26439beb93cSSam Leffler * @specifier: CSuite/Specifier
26539beb93cSSam Leffler * @rand_peer: 32-byte RAND_Peer
26639beb93cSSam Leffler * @rand_server: 32-byte RAND_Server
26739beb93cSSam Leffler * @id_peer: ID_Peer
26839beb93cSSam Leffler * @id_peer_len: Length of ID_Peer
26939beb93cSSam Leffler * @id_server: ID_Server
27039beb93cSSam Leffler * @id_server_len: Length of ID_Server
27139beb93cSSam Leffler * @msk: Buffer for 64-byte MSK
27239beb93cSSam Leffler * @emsk: Buffer for 64-byte EMSK
27339beb93cSSam Leffler * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
27439beb93cSSam Leffler * @sk_len: Buffer for returning length of SK
27539beb93cSSam Leffler * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
27639beb93cSSam Leffler * @pk_len: Buffer for returning length of PK
27739beb93cSSam Leffler * Returns: 0 on success, -1 on failure
27839beb93cSSam Leffler */
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)27939beb93cSSam Leffler int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
28039beb93cSSam Leffler int specifier,
28139beb93cSSam Leffler const u8 *rand_peer, const u8 *rand_server,
28239beb93cSSam Leffler const u8 *id_peer, size_t id_peer_len,
28339beb93cSSam Leffler const u8 *id_server, size_t id_server_len,
28439beb93cSSam Leffler u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
28539beb93cSSam Leffler u8 *pk, size_t *pk_len)
28639beb93cSSam Leffler {
28739beb93cSSam Leffler u8 *seed, *pos;
28839beb93cSSam Leffler int ret;
28939beb93cSSam Leffler
29039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
29139beb93cSSam Leffler vendor, specifier);
29239beb93cSSam Leffler
29339beb93cSSam Leffler if (vendor != EAP_GPSK_VENDOR_IETF)
29439beb93cSSam Leffler return -1;
29539beb93cSSam Leffler
29639beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
29739beb93cSSam Leffler
29839beb93cSSam Leffler /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
2995b9c547cSRui Paulo seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
30039beb93cSSam Leffler if (seed == NULL) {
30139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
30239beb93cSSam Leffler "for key derivation");
30339beb93cSSam Leffler return -1;
30439beb93cSSam Leffler }
30539beb93cSSam Leffler
30639beb93cSSam Leffler pos = seed;
30739beb93cSSam Leffler os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
30839beb93cSSam Leffler pos += EAP_GPSK_RAND_LEN;
30939beb93cSSam Leffler os_memcpy(pos, id_peer, id_peer_len);
31039beb93cSSam Leffler pos += id_peer_len;
31139beb93cSSam Leffler os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
31239beb93cSSam Leffler pos += EAP_GPSK_RAND_LEN;
31339beb93cSSam Leffler os_memcpy(pos, id_server, id_server_len);
31439beb93cSSam Leffler pos += id_server_len;
3155b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
31639beb93cSSam Leffler
31739beb93cSSam Leffler switch (specifier) {
31839beb93cSSam Leffler case EAP_GPSK_CIPHER_AES:
3195b9c547cSRui Paulo ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
32039beb93cSSam Leffler msk, emsk, sk, sk_len,
32139beb93cSSam Leffler pk, pk_len);
32239beb93cSSam Leffler break;
32339beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
32439beb93cSSam Leffler case EAP_GPSK_CIPHER_SHA256:
3255b9c547cSRui Paulo ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
3265b9c547cSRui Paulo pos - seed,
32739beb93cSSam Leffler msk, emsk, sk, sk_len);
32839beb93cSSam Leffler break;
32939beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
33039beb93cSSam Leffler default:
33139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
33239beb93cSSam Leffler "key derivation", vendor, specifier);
33339beb93cSSam Leffler ret = -1;
33439beb93cSSam Leffler break;
33539beb93cSSam Leffler }
33639beb93cSSam Leffler
33739beb93cSSam Leffler os_free(seed);
33839beb93cSSam Leffler
33939beb93cSSam Leffler return ret;
34039beb93cSSam Leffler }
34139beb93cSSam Leffler
34239beb93cSSam Leffler
eap_gpsk_derive_mid_helper(u32 csuite_specifier,u8 * kdf_out,size_t kdf_out_len,const u8 * psk,const u8 * seed,size_t seed_len,u8 method_type)3435b9c547cSRui Paulo static int eap_gpsk_derive_mid_helper(u32 csuite_specifier,
3445b9c547cSRui Paulo u8 *kdf_out, size_t kdf_out_len,
3455b9c547cSRui Paulo const u8 *psk, const u8 *seed,
3465b9c547cSRui Paulo size_t seed_len, u8 method_type)
3475b9c547cSRui Paulo {
3485b9c547cSRui Paulo u8 *pos, *data;
3495b9c547cSRui Paulo size_t data_len;
3505b9c547cSRui Paulo int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
3515b9c547cSRui Paulo u8 *buf, size_t len);
3525b9c547cSRui Paulo
3535b9c547cSRui Paulo gkdf = NULL;
3545b9c547cSRui Paulo switch (csuite_specifier) {
3555b9c547cSRui Paulo case EAP_GPSK_CIPHER_AES:
3565b9c547cSRui Paulo gkdf = eap_gpsk_gkdf_cmac;
3575b9c547cSRui Paulo break;
3585b9c547cSRui Paulo #ifdef EAP_GPSK_SHA256
3595b9c547cSRui Paulo case EAP_GPSK_CIPHER_SHA256:
3605b9c547cSRui Paulo gkdf = eap_gpsk_gkdf_sha256;
3615b9c547cSRui Paulo break;
3625b9c547cSRui Paulo #endif /* EAP_GPSK_SHA256 */
3635b9c547cSRui Paulo default:
3645b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in "
3655b9c547cSRui Paulo "Session-Id derivation", csuite_specifier);
3665b9c547cSRui Paulo return -1;
3675b9c547cSRui Paulo }
3685b9c547cSRui Paulo
3695b9c547cSRui Paulo #define SID_LABEL "Method ID"
3705b9c547cSRui Paulo /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */
3715b9c547cSRui Paulo data_len = strlen(SID_LABEL) + 1 + 6 + seed_len;
3725b9c547cSRui Paulo data = os_malloc(data_len);
3735b9c547cSRui Paulo if (data == NULL)
3745b9c547cSRui Paulo return -1;
3755b9c547cSRui Paulo pos = data;
3765b9c547cSRui Paulo os_memcpy(pos, SID_LABEL, strlen(SID_LABEL));
3775b9c547cSRui Paulo pos += strlen(SID_LABEL);
3785b9c547cSRui Paulo #undef SID_LABEL
3795b9c547cSRui Paulo os_memcpy(pos, &method_type, 1);
3805b9c547cSRui Paulo pos += 1;
3815b9c547cSRui Paulo WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
3825b9c547cSRui Paulo pos += 4;
3835b9c547cSRui Paulo WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
3845b9c547cSRui Paulo pos += 2;
3855b9c547cSRui Paulo os_memcpy(pos, seed, seed_len); /* inputString */
3865b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation",
3875b9c547cSRui Paulo data, data_len);
3885b9c547cSRui Paulo
3895b9c547cSRui Paulo if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) {
3905b9c547cSRui Paulo os_free(data);
3915b9c547cSRui Paulo return -1;
3925b9c547cSRui Paulo }
3935b9c547cSRui Paulo os_free(data);
3945b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len);
3955b9c547cSRui Paulo
3965b9c547cSRui Paulo return 0;
3975b9c547cSRui Paulo }
3985b9c547cSRui Paulo
3995b9c547cSRui Paulo
4005b9c547cSRui Paulo /**
4015b9c547cSRui Paulo * eap_gpsk_session_id - Derive EAP-GPSK Session ID
4025b9c547cSRui Paulo * @psk: Pre-shared key
4035b9c547cSRui Paulo * @psk_len: Length of psk in bytes
4045b9c547cSRui Paulo * @vendor: CSuite/Vendor
4055b9c547cSRui Paulo * @specifier: CSuite/Specifier
4065b9c547cSRui Paulo * @rand_peer: 32-byte RAND_Peer
4075b9c547cSRui Paulo * @rand_server: 32-byte RAND_Server
4085b9c547cSRui Paulo * @id_peer: ID_Peer
4095b9c547cSRui Paulo * @id_peer_len: Length of ID_Peer
4105b9c547cSRui Paulo * @id_server: ID_Server
4115b9c547cSRui Paulo * @id_server_len: Length of ID_Server
4125b9c547cSRui Paulo * @method_type: EAP Authentication Method Type
4135b9c547cSRui Paulo * @sid: Buffer for 17-byte Session ID
4145b9c547cSRui Paulo * @sid_len: Buffer for returning length of Session ID
4155b9c547cSRui Paulo * Returns: 0 on success, -1 on failure
4165b9c547cSRui Paulo */
eap_gpsk_derive_session_id(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 method_type,u8 * sid,size_t * sid_len)4175b9c547cSRui Paulo int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
4185b9c547cSRui Paulo int specifier,
4195b9c547cSRui Paulo const u8 *rand_peer, const u8 *rand_server,
4205b9c547cSRui Paulo const u8 *id_peer, size_t id_peer_len,
4215b9c547cSRui Paulo const u8 *id_server, size_t id_server_len,
4225b9c547cSRui Paulo u8 method_type, u8 *sid, size_t *sid_len)
4235b9c547cSRui Paulo {
4245b9c547cSRui Paulo u8 *seed, *pos;
4255b9c547cSRui Paulo u8 kdf_out[16];
4265b9c547cSRui Paulo int ret;
4275b9c547cSRui Paulo
4285b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
4295b9c547cSRui Paulo vendor, specifier);
4305b9c547cSRui Paulo
4315b9c547cSRui Paulo if (vendor != EAP_GPSK_VENDOR_IETF)
4325b9c547cSRui Paulo return -1;
4335b9c547cSRui Paulo
4345b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
4355b9c547cSRui Paulo
4365b9c547cSRui Paulo /*
4375b9c547cSRui Paulo * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
4385b9c547cSRui Paulo * (= seed)
4395b9c547cSRui Paulo * KS = 16, CSuite_Sel = 0x00000000 0x0001
4405b9c547cSRui Paulo * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
4415b9c547cSRui Paulo * CSuite_Sel || inputString)
4425b9c547cSRui Paulo */
4435b9c547cSRui Paulo seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
4445b9c547cSRui Paulo if (seed == NULL) {
4455b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
4465b9c547cSRui Paulo "for Session-Id derivation");
4475b9c547cSRui Paulo return -1;
4485b9c547cSRui Paulo }
4495b9c547cSRui Paulo
4505b9c547cSRui Paulo pos = seed;
4515b9c547cSRui Paulo os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
4525b9c547cSRui Paulo pos += EAP_GPSK_RAND_LEN;
4535b9c547cSRui Paulo os_memcpy(pos, id_peer, id_peer_len);
4545b9c547cSRui Paulo pos += id_peer_len;
4555b9c547cSRui Paulo os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
4565b9c547cSRui Paulo pos += EAP_GPSK_RAND_LEN;
4575b9c547cSRui Paulo os_memcpy(pos, id_server, id_server_len);
4585b9c547cSRui Paulo pos += id_server_len;
4595b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
4605b9c547cSRui Paulo
4615b9c547cSRui Paulo ret = eap_gpsk_derive_mid_helper(specifier,
4625b9c547cSRui Paulo kdf_out, sizeof(kdf_out),
4635b9c547cSRui Paulo psk, seed, pos - seed,
4645b9c547cSRui Paulo method_type);
4655b9c547cSRui Paulo
4665b9c547cSRui Paulo sid[0] = method_type;
4675b9c547cSRui Paulo os_memcpy(sid + 1, kdf_out, sizeof(kdf_out));
4685b9c547cSRui Paulo *sid_len = 1 + sizeof(kdf_out);
4695b9c547cSRui Paulo
4705b9c547cSRui Paulo os_free(seed);
4715b9c547cSRui Paulo
4725b9c547cSRui Paulo return ret;
4735b9c547cSRui Paulo }
4745b9c547cSRui Paulo
4755b9c547cSRui Paulo
47639beb93cSSam Leffler /**
47739beb93cSSam Leffler * eap_gpsk_mic_len - Get the length of the MIC
47839beb93cSSam Leffler * @vendor: CSuite/Vendor
47939beb93cSSam Leffler * @specifier: CSuite/Specifier
48039beb93cSSam Leffler * Returns: MIC length in bytes
48139beb93cSSam Leffler */
eap_gpsk_mic_len(int vendor,int specifier)48239beb93cSSam Leffler size_t eap_gpsk_mic_len(int vendor, int specifier)
48339beb93cSSam Leffler {
48439beb93cSSam Leffler if (vendor != EAP_GPSK_VENDOR_IETF)
48539beb93cSSam Leffler return 0;
48639beb93cSSam Leffler
48739beb93cSSam Leffler switch (specifier) {
48839beb93cSSam Leffler case EAP_GPSK_CIPHER_AES:
48939beb93cSSam Leffler return 16;
49039beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
49139beb93cSSam Leffler case EAP_GPSK_CIPHER_SHA256:
49239beb93cSSam Leffler return 32;
49339beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
49439beb93cSSam Leffler default:
49539beb93cSSam Leffler return 0;
49639beb93cSSam Leffler }
49739beb93cSSam Leffler }
49839beb93cSSam Leffler
49939beb93cSSam Leffler
eap_gpsk_compute_mic_aes(const u8 * sk,size_t sk_len,const u8 * data,size_t len,u8 * mic)50039beb93cSSam Leffler static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
50139beb93cSSam Leffler const u8 *data, size_t len, u8 *mic)
50239beb93cSSam Leffler {
50339beb93cSSam Leffler if (sk_len != 16) {
50439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
50539beb93cSSam Leffler "AES-CMAC MIC", (unsigned long) sk_len);
50639beb93cSSam Leffler return -1;
50739beb93cSSam Leffler }
50839beb93cSSam Leffler
50939beb93cSSam Leffler return omac1_aes_128(sk, data, len, mic);
51039beb93cSSam Leffler }
51139beb93cSSam Leffler
51239beb93cSSam Leffler
51339beb93cSSam Leffler /**
51439beb93cSSam Leffler * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
51539beb93cSSam Leffler * @sk: Session key SK from eap_gpsk_derive_keys()
51639beb93cSSam Leffler * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
51739beb93cSSam Leffler * @vendor: CSuite/Vendor
51839beb93cSSam Leffler * @specifier: CSuite/Specifier
51939beb93cSSam Leffler * @data: Input data to MIC
52039beb93cSSam Leffler * @len: Input data length in bytes
52139beb93cSSam Leffler * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
52239beb93cSSam Leffler * Returns: 0 on success, -1 on failure
52339beb93cSSam Leffler */
eap_gpsk_compute_mic(const u8 * sk,size_t sk_len,int vendor,int specifier,const u8 * data,size_t len,u8 * mic)52439beb93cSSam Leffler int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
52539beb93cSSam Leffler int specifier, const u8 *data, size_t len, u8 *mic)
52639beb93cSSam Leffler {
52739beb93cSSam Leffler int ret;
52839beb93cSSam Leffler
52939beb93cSSam Leffler if (vendor != EAP_GPSK_VENDOR_IETF)
53039beb93cSSam Leffler return -1;
53139beb93cSSam Leffler
53239beb93cSSam Leffler switch (specifier) {
53339beb93cSSam Leffler case EAP_GPSK_CIPHER_AES:
53439beb93cSSam Leffler ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
53539beb93cSSam Leffler break;
53639beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
53739beb93cSSam Leffler case EAP_GPSK_CIPHER_SHA256:
538*780fb4a2SCy Schubert ret = hmac_sha256(sk, sk_len, data, len, mic);
53939beb93cSSam Leffler break;
54039beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
54139beb93cSSam Leffler default:
54239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
54339beb93cSSam Leffler "MIC computation", vendor, specifier);
54439beb93cSSam Leffler ret = -1;
54539beb93cSSam Leffler break;
54639beb93cSSam Leffler }
54739beb93cSSam Leffler
548*780fb4a2SCy Schubert if (ret)
549*780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC");
550*780fb4a2SCy Schubert
55139beb93cSSam Leffler return ret;
55239beb93cSSam Leffler }
553