xref: /freebsd/contrib/wpa/src/pae/ieee802_1x_key.c (revision 7648bc9fee8dec6cb3c4941e0165a930fbe8dcb0)
15b9c547cSRui Paulo /*
25b9c547cSRui Paulo  * IEEE 802.1X-2010 Key Hierarchy
35b9c547cSRui Paulo  * Copyright (c) 2013, Qualcomm Atheros, Inc.
45b9c547cSRui Paulo  *
55b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
65b9c547cSRui Paulo  * See README for more details.
75b9c547cSRui Paulo  *
85b9c547cSRui Paulo  * SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2
95b9c547cSRui Paulo */
105b9c547cSRui Paulo 
115b9c547cSRui Paulo #include "utils/includes.h"
125b9c547cSRui Paulo 
135b9c547cSRui Paulo #include "utils/common.h"
145b9c547cSRui Paulo #include "crypto/md5.h"
155b9c547cSRui Paulo #include "crypto/sha1.h"
165b9c547cSRui Paulo #include "crypto/aes_wrap.h"
175b9c547cSRui Paulo #include "crypto/crypto.h"
185b9c547cSRui Paulo #include "ieee802_1x_key.h"
195b9c547cSRui Paulo 
205b9c547cSRui Paulo 
joint_two_mac(const u8 * mac1,const u8 * mac2,u8 * out)215b9c547cSRui Paulo static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
225b9c547cSRui Paulo {
235b9c547cSRui Paulo 	if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) {
245b9c547cSRui Paulo 		os_memcpy(out, mac1, ETH_ALEN);
255b9c547cSRui Paulo 		os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN);
265b9c547cSRui Paulo 	} else {
275b9c547cSRui Paulo 		os_memcpy(out, mac2, ETH_ALEN);
285b9c547cSRui Paulo 		os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN);
295b9c547cSRui Paulo 	}
305b9c547cSRui Paulo }
315b9c547cSRui Paulo 
325b9c547cSRui Paulo 
335b9c547cSRui Paulo /* IEEE Std 802.1X-2010, 6.2.1 KDF */
aes_kdf(const u8 * kdk,size_t kdk_bits,const char * label,const u8 * context,int ctx_bits,int ret_bits,u8 * ret)34*4bc52338SCy Schubert static int aes_kdf(const u8 *kdk, size_t kdk_bits,
35*4bc52338SCy Schubert 		   const char *label, const u8 *context,
365b9c547cSRui Paulo 		   int ctx_bits, int ret_bits, u8 *ret)
375b9c547cSRui Paulo {
385b9c547cSRui Paulo 	const int h = 128;
395b9c547cSRui Paulo 	const int r = 8;
405b9c547cSRui Paulo 	int i, n;
415b9c547cSRui Paulo 	int lab_len, ctx_len, ret_len, buf_len;
425b9c547cSRui Paulo 	u8 *buf;
435b9c547cSRui Paulo 
44*4bc52338SCy Schubert 	if (kdk_bits != 128 && kdk_bits != 256)
45*4bc52338SCy Schubert 		return -1;
46*4bc52338SCy Schubert 
475b9c547cSRui Paulo 	lab_len = os_strlen(label);
485b9c547cSRui Paulo 	ctx_len = (ctx_bits + 7) / 8;
495b9c547cSRui Paulo 	ret_len = ((ret_bits & 0xffff) + 7) / 8;
505b9c547cSRui Paulo 	buf_len = lab_len + ctx_len + 4;
515b9c547cSRui Paulo 
525b9c547cSRui Paulo 	os_memset(ret, 0, ret_len);
535b9c547cSRui Paulo 
545b9c547cSRui Paulo 	n = (ret_bits + h - 1) / h;
555b9c547cSRui Paulo 	if (n > ((0x1 << r) - 1))
565b9c547cSRui Paulo 		return -1;
575b9c547cSRui Paulo 
585b9c547cSRui Paulo 	buf = os_zalloc(buf_len);
595b9c547cSRui Paulo 	if (buf == NULL)
605b9c547cSRui Paulo 		return -1;
615b9c547cSRui Paulo 
625b9c547cSRui Paulo 	os_memcpy(buf + 1, label, lab_len);
635b9c547cSRui Paulo 	os_memcpy(buf + lab_len + 2, context, ctx_len);
645b9c547cSRui Paulo 	WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
655b9c547cSRui Paulo 
665b9c547cSRui Paulo 	for (i = 0; i < n; i++) {
67*4bc52338SCy Schubert 		int res;
68*4bc52338SCy Schubert 
695b9c547cSRui Paulo 		buf[0] = (u8) (i + 1);
70*4bc52338SCy Schubert 		if (kdk_bits == 128)
71*4bc52338SCy Schubert 			res = omac1_aes_128(kdk, buf, buf_len, ret);
72*4bc52338SCy Schubert 		else
73*4bc52338SCy Schubert 			res = omac1_aes_256(kdk, buf, buf_len, ret);
74*4bc52338SCy Schubert 		if (res) {
755b9c547cSRui Paulo 			os_free(buf);
765b9c547cSRui Paulo 			return -1;
775b9c547cSRui Paulo 		}
785b9c547cSRui Paulo 		ret = ret + h / 8;
795b9c547cSRui Paulo 	}
805b9c547cSRui Paulo 	os_free(buf);
815b9c547cSRui Paulo 	return 0;
825b9c547cSRui Paulo }
835b9c547cSRui Paulo 
845b9c547cSRui Paulo 
855b9c547cSRui Paulo /**
86*4bc52338SCy Schubert  * ieee802_1x_cak_aes_cmac
875b9c547cSRui Paulo  *
885b9c547cSRui Paulo  * IEEE Std 802.1X-2010, 6.2.2
895b9c547cSRui Paulo  * CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
905b9c547cSRui Paulo  */
ieee802_1x_cak_aes_cmac(const u8 * msk,size_t msk_bytes,const u8 * mac1,const u8 * mac2,u8 * cak,size_t cak_bytes)91*4bc52338SCy Schubert int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
92*4bc52338SCy Schubert 			    const u8 *mac2, u8 *cak, size_t cak_bytes)
935b9c547cSRui Paulo {
945b9c547cSRui Paulo 	u8 context[2 * ETH_ALEN];
955b9c547cSRui Paulo 
965b9c547cSRui Paulo 	joint_two_mac(mac1, mac2, context);
97*4bc52338SCy Schubert 	return aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CAK",
98*4bc52338SCy Schubert 		       context, sizeof(context) * 8, 8 * cak_bytes, cak);
995b9c547cSRui Paulo }
1005b9c547cSRui Paulo 
1015b9c547cSRui Paulo 
1025b9c547cSRui Paulo /**
103*4bc52338SCy Schubert  * ieee802_1x_ckn_aes_cmac
1045b9c547cSRui Paulo  *
1055b9c547cSRui Paulo  * IEEE Std 802.1X-2010, 6.2.2
1065b9c547cSRui Paulo  * CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
1075b9c547cSRui Paulo  */
ieee802_1x_ckn_aes_cmac(const u8 * msk,size_t msk_bytes,const u8 * mac1,const u8 * mac2,const u8 * sid,size_t sid_bytes,u8 * ckn)108*4bc52338SCy Schubert int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
1095b9c547cSRui Paulo 			    const u8 *mac2, const u8 *sid,
1105b9c547cSRui Paulo 			    size_t sid_bytes, u8 *ckn)
1115b9c547cSRui Paulo {
1125b9c547cSRui Paulo 	int res;
1135b9c547cSRui Paulo 	u8 *context;
1145b9c547cSRui Paulo 	size_t ctx_len = sid_bytes + ETH_ALEN * 2;
1155b9c547cSRui Paulo 
1165b9c547cSRui Paulo 	context = os_zalloc(ctx_len);
1175b9c547cSRui Paulo 	if (!context) {
1185b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__);
1195b9c547cSRui Paulo 		return -1;
1205b9c547cSRui Paulo 	}
1215b9c547cSRui Paulo 	os_memcpy(context, sid, sid_bytes);
1225b9c547cSRui Paulo 	joint_two_mac(mac1, mac2, context + sid_bytes);
1235b9c547cSRui Paulo 
124*4bc52338SCy Schubert 	res = aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CKN",
125*4bc52338SCy Schubert 		      context, ctx_len * 8, 128, ckn);
1265b9c547cSRui Paulo 	os_free(context);
1275b9c547cSRui Paulo 	return res;
1285b9c547cSRui Paulo }
1295b9c547cSRui Paulo 
1305b9c547cSRui Paulo 
1315b9c547cSRui Paulo /**
132*4bc52338SCy Schubert  * ieee802_1x_kek_aes_cmac
1335b9c547cSRui Paulo  *
1345b9c547cSRui Paulo  * IEEE Std 802.1X-2010, 9.3.3
1355b9c547cSRui Paulo  * KEK = KDF(Key, Label, Keyid, KEKLength)
1365b9c547cSRui Paulo  */
ieee802_1x_kek_aes_cmac(const u8 * cak,size_t cak_bytes,const u8 * ckn,size_t ckn_bytes,u8 * kek,size_t kek_bytes)137*4bc52338SCy Schubert int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
138*4bc52338SCy Schubert 			    size_t ckn_bytes, u8 *kek, size_t kek_bytes)
1395b9c547cSRui Paulo {
1405b9c547cSRui Paulo 	u8 context[16];
1415b9c547cSRui Paulo 
1425b9c547cSRui Paulo 	/* First 16 octets of CKN, with null octets appended to pad if needed */
1435b9c547cSRui Paulo 	os_memset(context, 0, sizeof(context));
1445b9c547cSRui Paulo 	os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
1455b9c547cSRui Paulo 
146*4bc52338SCy Schubert 	return aes_kdf(cak, 8 * cak_bytes, "IEEE8021 KEK",
147*4bc52338SCy Schubert 		       context, sizeof(context) * 8,
148*4bc52338SCy Schubert 		       8 * kek_bytes, kek);
1495b9c547cSRui Paulo }
1505b9c547cSRui Paulo 
1515b9c547cSRui Paulo 
1525b9c547cSRui Paulo /**
153*4bc52338SCy Schubert  * ieee802_1x_ick_aes_cmac
1545b9c547cSRui Paulo  *
1555b9c547cSRui Paulo  * IEEE Std 802.1X-2010, 9.3.3
1565b9c547cSRui Paulo  * ICK = KDF(Key, Label, Keyid, ICKLength)
1575b9c547cSRui Paulo  */
ieee802_1x_ick_aes_cmac(const u8 * cak,size_t cak_bytes,const u8 * ckn,size_t ckn_bytes,u8 * ick,size_t ick_bytes)158*4bc52338SCy Schubert int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
159*4bc52338SCy Schubert 			    size_t ckn_bytes, u8 *ick, size_t ick_bytes)
1605b9c547cSRui Paulo {
1615b9c547cSRui Paulo 	u8 context[16];
1625b9c547cSRui Paulo 
1635b9c547cSRui Paulo 	/* First 16 octets of CKN, with null octets appended to pad if needed */
1645b9c547cSRui Paulo 	os_memset(context, 0, sizeof(context));
1655b9c547cSRui Paulo 	os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
1665b9c547cSRui Paulo 
167*4bc52338SCy Schubert 	return aes_kdf(cak, 8 *cak_bytes, "IEEE8021 ICK",
168*4bc52338SCy Schubert 		       context, sizeof(context) * 8,
169*4bc52338SCy Schubert 		       8 * ick_bytes, ick);
1705b9c547cSRui Paulo }
1715b9c547cSRui Paulo 
1725b9c547cSRui Paulo 
1735b9c547cSRui Paulo /**
174*4bc52338SCy Schubert  * ieee802_1x_icv_aes_cmac
1755b9c547cSRui Paulo  *
1765b9c547cSRui Paulo  * IEEE Std 802.1X-2010, 9.4.1
1775b9c547cSRui Paulo  * ICV = AES-CMAC(ICK, M, 128)
1785b9c547cSRui Paulo  */
ieee802_1x_icv_aes_cmac(const u8 * ick,size_t ick_bytes,const u8 * msg,size_t msg_bytes,u8 * icv)179*4bc52338SCy Schubert int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg,
1805b9c547cSRui Paulo 			    size_t msg_bytes, u8 *icv)
1815b9c547cSRui Paulo {
182*4bc52338SCy Schubert 	int res;
183*4bc52338SCy Schubert 
184*4bc52338SCy Schubert 	if (ick_bytes == 16)
185*4bc52338SCy Schubert 		res = omac1_aes_128(ick, msg, msg_bytes, icv);
186*4bc52338SCy Schubert 	else if (ick_bytes == 32)
187*4bc52338SCy Schubert 		res = omac1_aes_256(ick, msg, msg_bytes, icv);
188*4bc52338SCy Schubert 	else
189*4bc52338SCy Schubert 		return -1;
190*4bc52338SCy Schubert 	if (res) {
191*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR,
192*4bc52338SCy Schubert 			   "MKA: AES-CMAC failed for ICV calculation");
1935b9c547cSRui Paulo 		return -1;
1945b9c547cSRui Paulo 	}
1955b9c547cSRui Paulo 	return 0;
1965b9c547cSRui Paulo }
1975b9c547cSRui Paulo 
1985b9c547cSRui Paulo 
1995b9c547cSRui Paulo /**
200*4bc52338SCy Schubert  * ieee802_1x_sak_aes_cmac
2015b9c547cSRui Paulo  *
2025b9c547cSRui Paulo  * IEEE Std 802.1X-2010, 9.8.1
2035b9c547cSRui Paulo  * SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
2045b9c547cSRui Paulo  */
ieee802_1x_sak_aes_cmac(const u8 * cak,size_t cak_bytes,const u8 * ctx,size_t ctx_bytes,u8 * sak,size_t sak_bytes)205*4bc52338SCy Schubert int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx,
206*4bc52338SCy Schubert 			    size_t ctx_bytes, u8 *sak, size_t sak_bytes)
2075b9c547cSRui Paulo {
208*4bc52338SCy Schubert 	return aes_kdf(cak, cak_bytes * 8, "IEEE8021 SAK", ctx, ctx_bytes * 8,
209*4bc52338SCy Schubert 		       sak_bytes * 8, sak);
2105b9c547cSRui Paulo }
211