xref: /freebsd/contrib/wpa/src/pae/ieee802_1x_kay.c (revision 206b73d0429edb7c49b612537544e677fa568e83)
15b9c547cSRui Paulo /*
24bc52338SCy Schubert  * IEEE 802.1X-2010 Key Agreement Protocol of PAE state machine
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 
95b9c547cSRui Paulo #include <time.h>
105b9c547cSRui Paulo #include "includes.h"
115b9c547cSRui Paulo #include "common.h"
125b9c547cSRui Paulo #include "list.h"
135b9c547cSRui Paulo #include "eloop.h"
145b9c547cSRui Paulo #include "wpabuf.h"
155b9c547cSRui Paulo #include "state_machine.h"
165b9c547cSRui Paulo #include "l2_packet/l2_packet.h"
175b9c547cSRui Paulo #include "common/eapol_common.h"
185b9c547cSRui Paulo #include "crypto/aes_wrap.h"
195b9c547cSRui Paulo #include "ieee802_1x_cp.h"
205b9c547cSRui Paulo #include "ieee802_1x_key.h"
215b9c547cSRui Paulo #include "ieee802_1x_kay.h"
225b9c547cSRui Paulo #include "ieee802_1x_kay_i.h"
235b9c547cSRui Paulo #include "ieee802_1x_secy_ops.h"
245b9c547cSRui Paulo 
255b9c547cSRui Paulo 
265b9c547cSRui Paulo #define DEFAULT_SA_KEY_LEN	16
275b9c547cSRui Paulo #define DEFAULT_ICV_LEN		16
285b9c547cSRui Paulo #define MAX_ICV_LEN		32  /* 32 bytes, 256 bits */
295b9c547cSRui Paulo 
304bc52338SCy Schubert #define MAX_MISSING_SAK_USE 10  /* Accept up to 10 inbound MKPDUs without
314bc52338SCy Schubert 				 * SAK-USE before dropping */
324bc52338SCy Schubert 
335b9c547cSRui Paulo #define PENDING_PN_EXHAUSTION 0xC0000000
345b9c547cSRui Paulo 
35780fb4a2SCy Schubert #define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3)
36780fb4a2SCy Schubert 
375b9c547cSRui Paulo /* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
385b9c547cSRui Paulo #define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
395b9c547cSRui Paulo static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009;
405b9c547cSRui Paulo 
415b9c547cSRui Paulo /* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */
425b9c547cSRui Paulo static struct macsec_ciphersuite cipher_suite_tbl[] = {
435b9c547cSRui Paulo 	/* GCM-AES-128 */
445b9c547cSRui Paulo 	{
45780fb4a2SCy Schubert 		.id = CS_ID_GCM_AES_128,
46780fb4a2SCy Schubert 		.name = CS_NAME_GCM_AES_128,
47780fb4a2SCy Schubert 		.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
48780fb4a2SCy Schubert 		.sak_len = DEFAULT_SA_KEY_LEN,
495b9c547cSRui Paulo 	},
5085732ac8SCy Schubert 	/* GCM-AES-256 */
5185732ac8SCy Schubert 	{
5285732ac8SCy Schubert 		.id = CS_ID_GCM_AES_256,
5385732ac8SCy Schubert 		.name = CS_NAME_GCM_AES_256,
5485732ac8SCy Schubert 		.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
5585732ac8SCy Schubert 		.sak_len = 32,
5685732ac8SCy Schubert 	},
575b9c547cSRui Paulo };
585b9c547cSRui Paulo #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
595b9c547cSRui Paulo #define DEFAULT_CS_INDEX  0
605b9c547cSRui Paulo 
615b9c547cSRui Paulo static struct mka_alg mka_alg_tbl[] = {
625b9c547cSRui Paulo 	{
63780fb4a2SCy Schubert 		.parameter = MKA_ALGO_AGILITY_2009,
645b9c547cSRui Paulo 
65780fb4a2SCy Schubert 		.icv_len = DEFAULT_ICV_LEN,
66780fb4a2SCy Schubert 
674bc52338SCy Schubert 		.cak_trfm = ieee802_1x_cak_aes_cmac,
684bc52338SCy Schubert 		.ckn_trfm = ieee802_1x_ckn_aes_cmac,
694bc52338SCy Schubert 		.kek_trfm = ieee802_1x_kek_aes_cmac,
704bc52338SCy Schubert 		.ick_trfm = ieee802_1x_ick_aes_cmac,
714bc52338SCy Schubert 		.icv_hash = ieee802_1x_icv_aes_cmac,
725b9c547cSRui Paulo 	},
735b9c547cSRui Paulo };
745b9c547cSRui Paulo #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
755b9c547cSRui Paulo 
765b9c547cSRui Paulo 
775b9c547cSRui Paulo static int is_ki_equal(struct ieee802_1x_mka_ki *ki1,
785b9c547cSRui Paulo 		       struct ieee802_1x_mka_ki *ki2)
795b9c547cSRui Paulo {
805b9c547cSRui Paulo 	return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 &&
815b9c547cSRui Paulo 		ki1->kn == ki2->kn;
825b9c547cSRui Paulo }
835b9c547cSRui Paulo 
845b9c547cSRui Paulo 
855b9c547cSRui Paulo static void set_mka_param_body_len(void *body, unsigned int len)
865b9c547cSRui Paulo {
875b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr = body;
885b9c547cSRui Paulo 	hdr->length = (len >> 8) & 0x0f;
895b9c547cSRui Paulo 	hdr->length1 = len & 0xff;
905b9c547cSRui Paulo }
915b9c547cSRui Paulo 
925b9c547cSRui Paulo 
935b9c547cSRui Paulo static unsigned int get_mka_param_body_len(const void *body)
945b9c547cSRui Paulo {
955b9c547cSRui Paulo 	const struct ieee802_1x_mka_hdr *hdr = body;
965b9c547cSRui Paulo 	return (hdr->length << 8) | hdr->length1;
975b9c547cSRui Paulo }
985b9c547cSRui Paulo 
995b9c547cSRui Paulo 
100780fb4a2SCy Schubert static u8 get_mka_param_body_type(const void *body)
1015b9c547cSRui Paulo {
1025b9c547cSRui Paulo 	const struct ieee802_1x_mka_hdr *hdr = body;
1035b9c547cSRui Paulo 	return hdr->type;
1045b9c547cSRui Paulo }
1055b9c547cSRui Paulo 
1065b9c547cSRui Paulo 
1074bc52338SCy Schubert static const char * mi_txt(const u8 *mi)
1084bc52338SCy Schubert {
1094bc52338SCy Schubert 	static char txt[MI_LEN * 2 + 1];
1104bc52338SCy Schubert 
1114bc52338SCy Schubert 	wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN);
1124bc52338SCy Schubert 	return txt;
1134bc52338SCy Schubert }
1144bc52338SCy Schubert 
1154bc52338SCy Schubert 
1164bc52338SCy Schubert static const char * sci_txt(const struct ieee802_1x_mka_sci *sci)
1174bc52338SCy Schubert {
1184bc52338SCy Schubert 	static char txt[ETH_ALEN * 3 + 1 + 5 + 1];
1194bc52338SCy Schubert 
1204bc52338SCy Schubert 	os_snprintf(txt, sizeof(txt), MACSTR "@%u",
1214bc52338SCy Schubert 		    MAC2STR(sci->addr), be_to_host16(sci->port));
1224bc52338SCy Schubert 	return txt;
1234bc52338SCy Schubert }
1244bc52338SCy Schubert 
1254bc52338SCy Schubert 
1264bc52338SCy Schubert static const char * algo_agility_txt(const u8 *algo_agility)
1274bc52338SCy Schubert {
1284bc52338SCy Schubert 	static char txt[4 * 2 + 1];
1294bc52338SCy Schubert 
1304bc52338SCy Schubert 	wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4);
1314bc52338SCy Schubert 	return txt;
1324bc52338SCy Schubert }
1334bc52338SCy Schubert 
1344bc52338SCy Schubert 
1355b9c547cSRui Paulo /**
1365b9c547cSRui Paulo  * ieee802_1x_mka_dump_basic_body -
1375b9c547cSRui Paulo  */
1385b9c547cSRui Paulo static void
1395b9c547cSRui Paulo ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body)
1405b9c547cSRui Paulo {
1415b9c547cSRui Paulo 	size_t body_len;
1425b9c547cSRui Paulo 
1435b9c547cSRui Paulo 	if (!body)
1445b9c547cSRui Paulo 		return;
1455b9c547cSRui Paulo 
1464bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-8 */
1475b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
1484bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set");
1494bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version);
1504bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority);
1514bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server);
1524bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired);
1534bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d",
1544bc52338SCy Schubert 		   body->macsec_capability);
1554bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len);
1564bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci));
1574bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s",
1584bc52338SCy Schubert 		   mi_txt(body->actor_mi));
1594bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d",
1605b9c547cSRui Paulo 		   be_to_host32(body->actor_mn));
1614bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s",
1624bc52338SCy Schubert 		   algo_agility_txt(body->algo_agility));
1634bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "\tCAK Name", body->ckn,
1645b9c547cSRui Paulo 		    body_len + MKA_HDR_LEN - sizeof(*body));
1655b9c547cSRui Paulo }
1665b9c547cSRui Paulo 
1675b9c547cSRui Paulo 
1685b9c547cSRui Paulo /**
1695b9c547cSRui Paulo  * ieee802_1x_mka_dump_peer_body -
1705b9c547cSRui Paulo  */
1715b9c547cSRui Paulo static void
1725b9c547cSRui Paulo ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body)
1735b9c547cSRui Paulo {
1745b9c547cSRui Paulo 	size_t body_len;
1755b9c547cSRui Paulo 	size_t i;
1765b9c547cSRui Paulo 	u8 *mi;
177780fb4a2SCy Schubert 	be32 mn;
1785b9c547cSRui Paulo 
1795b9c547cSRui Paulo 	if (body == NULL)
1805b9c547cSRui Paulo 		return;
1815b9c547cSRui Paulo 
1824bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-9 */
1835b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
1845b9c547cSRui Paulo 	if (body->type == MKA_LIVE_PEER_LIST) {
1854bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "Live Peer List parameter set");
1864bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
1875b9c547cSRui Paulo 	} else if (body->type == MKA_POTENTIAL_PEER_LIST) {
1884bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "Potential Peer List parameter set");
1894bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
1905b9c547cSRui Paulo 	}
1915b9c547cSRui Paulo 
1925b9c547cSRui Paulo 	for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
1935b9c547cSRui Paulo 		mi = body->peer + i;
1945b9c547cSRui Paulo 		os_memcpy(&mn, mi + MI_LEN, sizeof(mn));
1954bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "\tMember Id: %s  Message Number: %d",
1964bc52338SCy Schubert 			   mi_txt(mi), be_to_host32(mn));
1975b9c547cSRui Paulo 	}
1985b9c547cSRui Paulo }
1995b9c547cSRui Paulo 
2005b9c547cSRui Paulo 
2015b9c547cSRui Paulo /**
2025b9c547cSRui Paulo  * ieee802_1x_mka_dump_dist_sak_body -
2035b9c547cSRui Paulo  */
2045b9c547cSRui Paulo static void
2055b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body)
2065b9c547cSRui Paulo {
2075b9c547cSRui Paulo 	size_t body_len;
2085b9c547cSRui Paulo 
2095b9c547cSRui Paulo 	if (body == NULL)
2105b9c547cSRui Paulo 		return;
2115b9c547cSRui Paulo 
2124bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */
2135b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
2144bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "Distributed SAK parameter set");
2154bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan);
2164bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d",
2175b9c547cSRui Paulo 		   body->confid_offset);
2184bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len);
2195b9c547cSRui Paulo 	if (!body_len)
2205b9c547cSRui Paulo 		return;
2215b9c547cSRui Paulo 
2224bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Number............: %d",
2235b9c547cSRui Paulo 		   be_to_host32(body->kn));
2244bc52338SCy Schubert 	/* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */
2254bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24);
2265b9c547cSRui Paulo }
2275b9c547cSRui Paulo 
2285b9c547cSRui Paulo 
2295b9c547cSRui Paulo static const char * yes_no(int val)
2305b9c547cSRui Paulo {
2315b9c547cSRui Paulo 	return val ? "Yes" : "No";
2325b9c547cSRui Paulo }
2335b9c547cSRui Paulo 
2345b9c547cSRui Paulo 
2355b9c547cSRui Paulo /**
2365b9c547cSRui Paulo  * ieee802_1x_mka_dump_sak_use_body -
2375b9c547cSRui Paulo  */
2385b9c547cSRui Paulo static void
2395b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
2405b9c547cSRui Paulo {
2415b9c547cSRui Paulo 	int body_len;
2425b9c547cSRui Paulo 
2435b9c547cSRui Paulo 	if (body == NULL)
2445b9c547cSRui Paulo 		return;
2455b9c547cSRui Paulo 
2464bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-10 */
2475b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
2484bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set");
2495b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
2505b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
2515b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
2524bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan);
2534bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx));
2544bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx));
2554bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx));
2564bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tPlain Rx.........: %s", yes_no(body->prx));
2575b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
2585b9c547cSRui Paulo 		   yes_no(body->delay_protect));
2595b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
2605b9c547cSRui Paulo 	if (!body_len)
2615b9c547cSRui Paulo 		return;
2625b9c547cSRui Paulo 
2634bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Server MI....: %s", mi_txt(body->lsrv_mi));
2645b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
2655b9c547cSRui Paulo 		   be_to_host32(body->lkn));
2665b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
2675b9c547cSRui Paulo 		   be_to_host32(body->llpn));
2684bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi));
2694bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u",
2705b9c547cSRui Paulo 		   be_to_host32(body->okn));
2714bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u",
2725b9c547cSRui Paulo 		   be_to_host32(body->olpn));
2735b9c547cSRui Paulo }
2745b9c547cSRui Paulo 
2755b9c547cSRui Paulo 
2765b9c547cSRui Paulo /**
2775b9c547cSRui Paulo  * ieee802_1x_kay_get_participant -
2785b9c547cSRui Paulo  */
2795b9c547cSRui Paulo static struct ieee802_1x_mka_participant *
28085732ac8SCy Schubert ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn,
28185732ac8SCy Schubert 			       size_t len)
2825b9c547cSRui Paulo {
2835b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
2845b9c547cSRui Paulo 
2855b9c547cSRui Paulo 	dl_list_for_each(participant, &kay->participant_list,
2865b9c547cSRui Paulo 			 struct ieee802_1x_mka_participant, list) {
28785732ac8SCy Schubert 		if (participant->ckn.len == len &&
28885732ac8SCy Schubert 		    os_memcmp(participant->ckn.name, ckn,
2895b9c547cSRui Paulo 			      participant->ckn.len) == 0)
2905b9c547cSRui Paulo 			return participant;
2915b9c547cSRui Paulo 	}
2925b9c547cSRui Paulo 
2935b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: participant is not found");
2945b9c547cSRui Paulo 
2955b9c547cSRui Paulo 	return NULL;
2965b9c547cSRui Paulo }
2975b9c547cSRui Paulo 
2985b9c547cSRui Paulo 
2995b9c547cSRui Paulo /**
3005b9c547cSRui Paulo  * ieee802_1x_kay_get_principal_participant -
3015b9c547cSRui Paulo  */
3025b9c547cSRui Paulo static struct ieee802_1x_mka_participant *
3035b9c547cSRui Paulo ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay)
3045b9c547cSRui Paulo {
3055b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
3065b9c547cSRui Paulo 
3075b9c547cSRui Paulo 	dl_list_for_each(participant, &kay->participant_list,
3085b9c547cSRui Paulo 			 struct ieee802_1x_mka_participant, list) {
3095b9c547cSRui Paulo 		if (participant->principal)
3105b9c547cSRui Paulo 			return participant;
3115b9c547cSRui Paulo 	}
3125b9c547cSRui Paulo 
313780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: principal participant is not found");
3145b9c547cSRui Paulo 	return NULL;
3155b9c547cSRui Paulo }
3165b9c547cSRui Paulo 
3175b9c547cSRui Paulo 
3185b9c547cSRui Paulo static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers,
3195b9c547cSRui Paulo 						const u8 *mi)
3205b9c547cSRui Paulo {
3215b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
3225b9c547cSRui Paulo 
3235b9c547cSRui Paulo 	dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) {
3245b9c547cSRui Paulo 		if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
3255b9c547cSRui Paulo 			return peer;
3265b9c547cSRui Paulo 	}
3275b9c547cSRui Paulo 
3285b9c547cSRui Paulo 	return NULL;
3295b9c547cSRui Paulo }
3305b9c547cSRui Paulo 
3315b9c547cSRui Paulo 
3325b9c547cSRui Paulo /**
333780fb4a2SCy Schubert  * ieee802_1x_kay_get_potential_peer
3345b9c547cSRui Paulo  */
3355b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
336780fb4a2SCy Schubert ieee802_1x_kay_get_potential_peer(
337780fb4a2SCy Schubert 	struct ieee802_1x_mka_participant *participant, const u8 *mi)
3385b9c547cSRui Paulo {
3395b9c547cSRui Paulo 	return get_peer_mi(&participant->potential_peers, mi);
3405b9c547cSRui Paulo }
3415b9c547cSRui Paulo 
3425b9c547cSRui Paulo 
3435b9c547cSRui Paulo /**
3445b9c547cSRui Paulo  * ieee802_1x_kay_get_live_peer
3455b9c547cSRui Paulo  */
3465b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
3475b9c547cSRui Paulo ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant,
3485b9c547cSRui Paulo 			     const u8 *mi)
3495b9c547cSRui Paulo {
3505b9c547cSRui Paulo 	return get_peer_mi(&participant->live_peers, mi);
3515b9c547cSRui Paulo }
3525b9c547cSRui Paulo 
3535b9c547cSRui Paulo 
3545b9c547cSRui Paulo /**
355780fb4a2SCy Schubert  * ieee802_1x_kay_is_in_potential_peer
356780fb4a2SCy Schubert  */
357780fb4a2SCy Schubert static Boolean
358780fb4a2SCy Schubert ieee802_1x_kay_is_in_potential_peer(
359780fb4a2SCy Schubert 	struct ieee802_1x_mka_participant *participant, const u8 *mi)
360780fb4a2SCy Schubert {
361780fb4a2SCy Schubert 	return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL;
362780fb4a2SCy Schubert }
363780fb4a2SCy Schubert 
364780fb4a2SCy Schubert 
365780fb4a2SCy Schubert /**
366780fb4a2SCy Schubert  * ieee802_1x_kay_is_in_live_peer
367780fb4a2SCy Schubert  */
368780fb4a2SCy Schubert static Boolean
369780fb4a2SCy Schubert ieee802_1x_kay_is_in_live_peer(
370780fb4a2SCy Schubert 	struct ieee802_1x_mka_participant *participant, const u8 *mi)
371780fb4a2SCy Schubert {
372780fb4a2SCy Schubert 	return ieee802_1x_kay_get_live_peer(participant, mi) != NULL;
373780fb4a2SCy Schubert }
374780fb4a2SCy Schubert 
375780fb4a2SCy Schubert 
376780fb4a2SCy Schubert /**
377780fb4a2SCy Schubert  * ieee802_1x_kay_get_peer
378780fb4a2SCy Schubert  */
379780fb4a2SCy Schubert static struct ieee802_1x_kay_peer *
380780fb4a2SCy Schubert ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
381780fb4a2SCy Schubert 			const u8 *mi)
382780fb4a2SCy Schubert {
383780fb4a2SCy Schubert 	struct ieee802_1x_kay_peer *peer;
384780fb4a2SCy Schubert 
385780fb4a2SCy Schubert 	peer = ieee802_1x_kay_get_live_peer(participant, mi);
386780fb4a2SCy Schubert 	if (peer)
387780fb4a2SCy Schubert 		return peer;
388780fb4a2SCy Schubert 
389780fb4a2SCy Schubert 	return ieee802_1x_kay_get_potential_peer(participant, mi);
390780fb4a2SCy Schubert }
391780fb4a2SCy Schubert 
392780fb4a2SCy Schubert 
393780fb4a2SCy Schubert /**
3945b9c547cSRui Paulo  * ieee802_1x_kay_get_cipher_suite
3955b9c547cSRui Paulo  */
3965b9c547cSRui Paulo static struct macsec_ciphersuite *
3975b9c547cSRui Paulo ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
3984bc52338SCy Schubert 				const u8 *cs_id, unsigned int *idx)
3995b9c547cSRui Paulo {
4005b9c547cSRui Paulo 	unsigned int i;
401780fb4a2SCy Schubert 	u64 cs;
402780fb4a2SCy Schubert 	be64 _cs;
403780fb4a2SCy Schubert 
404780fb4a2SCy Schubert 	os_memcpy(&_cs, cs_id, CS_ID_LEN);
405780fb4a2SCy Schubert 	cs = be_to_host64(_cs);
4065b9c547cSRui Paulo 
4075b9c547cSRui Paulo 	for (i = 0; i < CS_TABLE_SIZE; i++) {
4084bc52338SCy Schubert 		if (cipher_suite_tbl[i].id == cs) {
4094bc52338SCy Schubert 			*idx = i;
4105b9c547cSRui Paulo 			return &cipher_suite_tbl[i];
4115b9c547cSRui Paulo 		}
4124bc52338SCy Schubert 	}
4135b9c547cSRui Paulo 
414780fb4a2SCy Schubert 	return NULL;
415780fb4a2SCy Schubert }
416780fb4a2SCy Schubert 
417780fb4a2SCy Schubert 
41885732ac8SCy Schubert u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci)
41985732ac8SCy Schubert {
42085732ac8SCy Schubert 	struct ieee802_1x_mka_sci tmp;
42185732ac8SCy Schubert 
42285732ac8SCy Schubert 	os_memcpy(tmp.addr, sci->addr, ETH_ALEN);
42385732ac8SCy Schubert 	tmp.port = sci->port;
42485732ac8SCy Schubert 
42585732ac8SCy Schubert 	return *((u64 *) &tmp);
42685732ac8SCy Schubert }
42785732ac8SCy Schubert 
42885732ac8SCy Schubert 
429780fb4a2SCy Schubert static Boolean sci_equal(const struct ieee802_1x_mka_sci *a,
430780fb4a2SCy Schubert 			 const struct ieee802_1x_mka_sci *b)
431780fb4a2SCy Schubert {
432780fb4a2SCy Schubert 	return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0;
433780fb4a2SCy Schubert }
434780fb4a2SCy Schubert 
4355b9c547cSRui Paulo 
4365b9c547cSRui Paulo /**
4375b9c547cSRui Paulo  * ieee802_1x_kay_get_peer_sci
4385b9c547cSRui Paulo  */
4395b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
4405b9c547cSRui Paulo ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
4415b9c547cSRui Paulo 			    const struct ieee802_1x_mka_sci *sci)
4425b9c547cSRui Paulo {
4435b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
4445b9c547cSRui Paulo 
4455b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
4465b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
447780fb4a2SCy Schubert 		if (sci_equal(&peer->sci, sci))
4485b9c547cSRui Paulo 			return peer;
4495b9c547cSRui Paulo 	}
4505b9c547cSRui Paulo 
4515b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->potential_peers,
4525b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
453780fb4a2SCy Schubert 		if (sci_equal(&peer->sci, sci))
4545b9c547cSRui Paulo 			return peer;
4555b9c547cSRui Paulo 	}
4565b9c547cSRui Paulo 
4575b9c547cSRui Paulo 	return NULL;
4585b9c547cSRui Paulo }
4595b9c547cSRui Paulo 
4605b9c547cSRui Paulo 
46185732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey);
46285732ac8SCy Schubert 
4635b9c547cSRui Paulo /**
4645b9c547cSRui Paulo  * ieee802_1x_kay_init_receive_sa -
4655b9c547cSRui Paulo  */
4665b9c547cSRui Paulo static struct receive_sa *
4675b9c547cSRui Paulo ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
4685b9c547cSRui Paulo 			       struct data_key *key)
4695b9c547cSRui Paulo {
4705b9c547cSRui Paulo 	struct receive_sa *psa;
4715b9c547cSRui Paulo 
4725b9c547cSRui Paulo 	if (!psc || !key)
4735b9c547cSRui Paulo 		return NULL;
4745b9c547cSRui Paulo 
4755b9c547cSRui Paulo 	psa = os_zalloc(sizeof(*psa));
4765b9c547cSRui Paulo 	if (!psa) {
4775b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
4785b9c547cSRui Paulo 		return NULL;
4795b9c547cSRui Paulo 	}
4805b9c547cSRui Paulo 
48185732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(key);
4825b9c547cSRui Paulo 	psa->pkey = key;
4835b9c547cSRui Paulo 	psa->lowest_pn = lowest_pn;
4845b9c547cSRui Paulo 	psa->next_pn = lowest_pn;
4855b9c547cSRui Paulo 	psa->an = an;
4865b9c547cSRui Paulo 	psa->sc = psc;
4875b9c547cSRui Paulo 
4885b9c547cSRui Paulo 	os_get_time(&psa->created_time);
4895b9c547cSRui Paulo 	psa->in_use = FALSE;
4905b9c547cSRui Paulo 
4915b9c547cSRui Paulo 	dl_list_add(&psc->sa_list, &psa->list);
4925b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
4934bc52338SCy Schubert 		   "KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC",
49485732ac8SCy Schubert 		   an, lowest_pn);
4955b9c547cSRui Paulo 
4965b9c547cSRui Paulo 	return psa;
4975b9c547cSRui Paulo }
4985b9c547cSRui Paulo 
4995b9c547cSRui Paulo 
50085732ac8SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey);
50185732ac8SCy Schubert 
5025b9c547cSRui Paulo /**
5035b9c547cSRui Paulo  * ieee802_1x_kay_deinit_receive_sa -
5045b9c547cSRui Paulo  */
5055b9c547cSRui Paulo static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
5065b9c547cSRui Paulo {
50785732ac8SCy Schubert 	ieee802_1x_kay_deinit_data_key(psa->pkey);
5085b9c547cSRui Paulo 	psa->pkey = NULL;
5095b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
510780fb4a2SCy Schubert 		   "KaY: Delete receive SA(an: %hhu) of SC",
511780fb4a2SCy Schubert 		   psa->an);
5125b9c547cSRui Paulo 	dl_list_del(&psa->list);
5135b9c547cSRui Paulo 	os_free(psa);
5145b9c547cSRui Paulo }
5155b9c547cSRui Paulo 
5165b9c547cSRui Paulo 
5175b9c547cSRui Paulo /**
5185b9c547cSRui Paulo  * ieee802_1x_kay_init_receive_sc -
5195b9c547cSRui Paulo  */
5205b9c547cSRui Paulo static struct receive_sc *
52185732ac8SCy Schubert ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
5225b9c547cSRui Paulo {
5235b9c547cSRui Paulo 	struct receive_sc *psc;
5245b9c547cSRui Paulo 
5255b9c547cSRui Paulo 	if (!psci)
5265b9c547cSRui Paulo 		return NULL;
5275b9c547cSRui Paulo 
5285b9c547cSRui Paulo 	psc = os_zalloc(sizeof(*psc));
5295b9c547cSRui Paulo 	if (!psc) {
5305b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
5315b9c547cSRui Paulo 		return NULL;
5325b9c547cSRui Paulo 	}
5335b9c547cSRui Paulo 
5345b9c547cSRui Paulo 	os_memcpy(&psc->sci, psci, sizeof(psc->sci));
5355b9c547cSRui Paulo 
5365b9c547cSRui Paulo 	os_get_time(&psc->created_time);
5375b9c547cSRui Paulo 	psc->receiving = FALSE;
5385b9c547cSRui Paulo 
5395b9c547cSRui Paulo 	dl_list_init(&psc->sa_list);
5404bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s",
5414bc52338SCy Schubert 		   sci_txt(&psc->sci));
5425b9c547cSRui Paulo 
5435b9c547cSRui Paulo 	return psc;
5445b9c547cSRui Paulo }
5455b9c547cSRui Paulo 
5465b9c547cSRui Paulo 
54785732ac8SCy Schubert static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay,
54885732ac8SCy Schubert 					 struct receive_sa *sa)
54985732ac8SCy Schubert {
55085732ac8SCy Schubert 	secy_disable_receive_sa(kay, sa);
55185732ac8SCy Schubert 	secy_delete_receive_sa(kay, sa);
55285732ac8SCy Schubert 	ieee802_1x_kay_deinit_receive_sa(sa);
55385732ac8SCy Schubert }
55485732ac8SCy Schubert 
55585732ac8SCy Schubert 
5565b9c547cSRui Paulo /**
5575b9c547cSRui Paulo  * ieee802_1x_kay_deinit_receive_sc -
5585b9c547cSRui Paulo  **/
5595b9c547cSRui Paulo static void
5605b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc(
5615b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant, struct receive_sc *psc)
5625b9c547cSRui Paulo {
5635b9c547cSRui Paulo 	struct receive_sa *psa, *pre_sa;
5645b9c547cSRui Paulo 
56585732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Delete receive SC");
5665b9c547cSRui Paulo 	dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
56785732ac8SCy Schubert 			      list)
56885732ac8SCy Schubert 		ieee802_1x_delete_receive_sa(participant->kay, psa);
56985732ac8SCy Schubert 
5705b9c547cSRui Paulo 	dl_list_del(&psc->list);
57185732ac8SCy Schubert 	secy_delete_receive_sc(participant->kay, psc);
5725b9c547cSRui Paulo 	os_free(psc);
5735b9c547cSRui Paulo }
5745b9c547cSRui Paulo 
5755b9c547cSRui Paulo 
576780fb4a2SCy Schubert static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer)
577780fb4a2SCy Schubert {
5784bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMI: %s  MN: %d  SCI: %s",
5794bc52338SCy Schubert 		   mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci));
580780fb4a2SCy Schubert }
581780fb4a2SCy Schubert 
582780fb4a2SCy Schubert 
5835b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
584780fb4a2SCy Schubert ieee802_1x_kay_create_peer(const u8 *mi, u32 mn)
5855b9c547cSRui Paulo {
5865b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
5875b9c547cSRui Paulo 
5885b9c547cSRui Paulo 	peer = os_zalloc(sizeof(*peer));
589780fb4a2SCy Schubert 	if (!peer) {
5905b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
5915b9c547cSRui Paulo 		return NULL;
5925b9c547cSRui Paulo 	}
5935b9c547cSRui Paulo 
5945b9c547cSRui Paulo 	os_memcpy(peer->mi, mi, MI_LEN);
5955b9c547cSRui Paulo 	peer->mn = mn;
5965b9c547cSRui Paulo 	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
5975b9c547cSRui Paulo 	peer->sak_used = FALSE;
5984bc52338SCy Schubert 	peer->missing_sak_use_count = 0;
599780fb4a2SCy Schubert 
600780fb4a2SCy Schubert 	return peer;
601780fb4a2SCy Schubert }
602780fb4a2SCy Schubert 
603780fb4a2SCy Schubert 
604780fb4a2SCy Schubert /**
605780fb4a2SCy Schubert  * ieee802_1x_kay_create_live_peer
606780fb4a2SCy Schubert  */
607780fb4a2SCy Schubert static struct ieee802_1x_kay_peer *
608780fb4a2SCy Schubert ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
609780fb4a2SCy Schubert 				const u8 *mi, u32 mn)
610780fb4a2SCy Schubert {
611780fb4a2SCy Schubert 	struct ieee802_1x_kay_peer *peer;
612780fb4a2SCy Schubert 	struct receive_sc *rxsc;
613780fb4a2SCy Schubert 
614780fb4a2SCy Schubert 	peer = ieee802_1x_kay_create_peer(mi, mn);
615780fb4a2SCy Schubert 	if (!peer)
616780fb4a2SCy Schubert 		return NULL;
617780fb4a2SCy Schubert 
6185b9c547cSRui Paulo 	os_memcpy(&peer->sci, &participant->current_peer_sci,
6195b9c547cSRui Paulo 		  sizeof(peer->sci));
6205b9c547cSRui Paulo 
62185732ac8SCy Schubert 	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci);
622780fb4a2SCy Schubert 	if (!rxsc) {
623780fb4a2SCy Schubert 		os_free(peer);
6245b9c547cSRui Paulo 		return NULL;
625780fb4a2SCy Schubert 	}
6265b9c547cSRui Paulo 
6274bc52338SCy Schubert 	if (secy_create_receive_sc(participant->kay, rxsc)) {
6284bc52338SCy Schubert 		os_free(rxsc);
6294bc52338SCy Schubert 		os_free(peer);
6304bc52338SCy Schubert 		return NULL;
6314bc52338SCy Schubert 	}
632780fb4a2SCy Schubert 	dl_list_add(&participant->live_peers, &peer->list);
6335b9c547cSRui Paulo 	dl_list_add(&participant->rxsc_list, &rxsc->list);
6345b9c547cSRui Paulo 
6355b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: Live peer created");
636780fb4a2SCy Schubert 	ieee802_1x_kay_dump_peer(peer);
6375b9c547cSRui Paulo 
6385b9c547cSRui Paulo 	return peer;
6395b9c547cSRui Paulo }
6405b9c547cSRui Paulo 
6415b9c547cSRui Paulo 
6425b9c547cSRui Paulo /**
6435b9c547cSRui Paulo  * ieee802_1x_kay_create_potential_peer
6445b9c547cSRui Paulo  */
6455b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
6465b9c547cSRui Paulo ieee802_1x_kay_create_potential_peer(
6475b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn)
6485b9c547cSRui Paulo {
6495b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
6505b9c547cSRui Paulo 
651780fb4a2SCy Schubert 	peer = ieee802_1x_kay_create_peer(mi, mn);
652780fb4a2SCy Schubert 	if (!peer)
6535b9c547cSRui Paulo 		return NULL;
6545b9c547cSRui Paulo 
6555b9c547cSRui Paulo 	dl_list_add(&participant->potential_peers, &peer->list);
6565b9c547cSRui Paulo 
6574bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Potential peer created");
658780fb4a2SCy Schubert 	ieee802_1x_kay_dump_peer(peer);
6595b9c547cSRui Paulo 
6605b9c547cSRui Paulo 	return peer;
6615b9c547cSRui Paulo }
6625b9c547cSRui Paulo 
6635b9c547cSRui Paulo 
6645b9c547cSRui Paulo /**
6655b9c547cSRui Paulo  * ieee802_1x_kay_move_live_peer
6665b9c547cSRui Paulo  */
6675b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
6685b9c547cSRui Paulo ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
6695b9c547cSRui Paulo 			      u8 *mi, u32 mn)
6705b9c547cSRui Paulo {
6715b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
6725b9c547cSRui Paulo 	struct receive_sc *rxsc;
6735b9c547cSRui Paulo 
674780fb4a2SCy Schubert 	peer = ieee802_1x_kay_get_potential_peer(participant, mi);
67585732ac8SCy Schubert 	if (!peer)
67685732ac8SCy Schubert 		return NULL;
677780fb4a2SCy Schubert 
67885732ac8SCy Schubert 	rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
679780fb4a2SCy Schubert 	if (!rxsc)
680780fb4a2SCy Schubert 		return NULL;
6815b9c547cSRui Paulo 
6825b9c547cSRui Paulo 	os_memcpy(&peer->sci, &participant->current_peer_sci,
6835b9c547cSRui Paulo 		  sizeof(peer->sci));
6845b9c547cSRui Paulo 	peer->mn = mn;
6855b9c547cSRui Paulo 	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
6865b9c547cSRui Paulo 
6874bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer");
688780fb4a2SCy Schubert 	ieee802_1x_kay_dump_peer(peer);
6895b9c547cSRui Paulo 
6905b9c547cSRui Paulo 	dl_list_del(&peer->list);
6914bc52338SCy Schubert 	if (secy_create_receive_sc(participant->kay, rxsc)) {
6924bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer");
6934bc52338SCy Schubert 		os_free(rxsc);
6944bc52338SCy Schubert 		os_free(peer);
6954bc52338SCy Schubert 		return NULL;
6964bc52338SCy Schubert 	}
6975b9c547cSRui Paulo 	dl_list_add_tail(&participant->live_peers, &peer->list);
6985b9c547cSRui Paulo 
6995b9c547cSRui Paulo 	dl_list_add(&participant->rxsc_list, &rxsc->list);
7005b9c547cSRui Paulo 
7015b9c547cSRui Paulo 	return peer;
7025b9c547cSRui Paulo }
7035b9c547cSRui Paulo 
7045b9c547cSRui Paulo 
7055b9c547cSRui Paulo 
7065b9c547cSRui Paulo /**
7075b9c547cSRui Paulo  *  ieee802_1x_mka_basic_body_present -
7085b9c547cSRui Paulo  */
7095b9c547cSRui Paulo static Boolean
7105b9c547cSRui Paulo ieee802_1x_mka_basic_body_present(
7115b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
7125b9c547cSRui Paulo {
7135b9c547cSRui Paulo 	return TRUE;
7145b9c547cSRui Paulo }
7155b9c547cSRui Paulo 
7165b9c547cSRui Paulo 
7175b9c547cSRui Paulo /**
7185b9c547cSRui Paulo  * ieee802_1x_mka_basic_body_length -
7195b9c547cSRui Paulo  */
7205b9c547cSRui Paulo static int
7215b9c547cSRui Paulo ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant)
7225b9c547cSRui Paulo {
7235b9c547cSRui Paulo 	int length;
7245b9c547cSRui Paulo 
7255b9c547cSRui Paulo 	length = sizeof(struct ieee802_1x_mka_basic_body);
7265b9c547cSRui Paulo 	length += participant->ckn.len;
727780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
7285b9c547cSRui Paulo }
7295b9c547cSRui Paulo 
7305b9c547cSRui Paulo 
7315b9c547cSRui Paulo /**
7325b9c547cSRui Paulo  * ieee802_1x_mka_encode_basic_body
7335b9c547cSRui Paulo  */
7345b9c547cSRui Paulo static int
7355b9c547cSRui Paulo ieee802_1x_mka_encode_basic_body(
7365b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
7375b9c547cSRui Paulo 	struct wpabuf *buf)
7385b9c547cSRui Paulo {
7395b9c547cSRui Paulo 	struct ieee802_1x_mka_basic_body *body;
7405b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
7414bc52338SCy Schubert 	unsigned int length = sizeof(struct ieee802_1x_mka_basic_body);
7425b9c547cSRui Paulo 
7434bc52338SCy Schubert 	length += participant->ckn.len;
7444bc52338SCy Schubert 	body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length));
7455b9c547cSRui Paulo 
7465b9c547cSRui Paulo 	body->version = kay->mka_version;
7475b9c547cSRui Paulo 	body->priority = kay->actor_priority;
7484bc52338SCy Schubert 	/* The Key Server flag is set if and only if the participant has not
7494bc52338SCy Schubert 	 * decided that another participant is or will be the Key Server. */
7505b9c547cSRui Paulo 	if (participant->is_elected)
7515b9c547cSRui Paulo 		body->key_server = participant->is_key_server;
7525b9c547cSRui Paulo 	else
7535b9c547cSRui Paulo 		body->key_server = participant->can_be_key_server;
7545b9c547cSRui Paulo 
7555b9c547cSRui Paulo 	body->macsec_desired = kay->macsec_desired;
756780fb4a2SCy Schubert 	body->macsec_capability = kay->macsec_capable;
7575b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
7585b9c547cSRui Paulo 
7595b9c547cSRui Paulo 	os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
7605b9c547cSRui Paulo 		  sizeof(kay->actor_sci.addr));
761780fb4a2SCy Schubert 	body->actor_sci.port = kay->actor_sci.port;
7625b9c547cSRui Paulo 
7635b9c547cSRui Paulo 	os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
7645b9c547cSRui Paulo 	participant->mn = participant->mn + 1;
7655b9c547cSRui Paulo 	body->actor_mn = host_to_be32(participant->mn);
766780fb4a2SCy Schubert 	os_memcpy(body->algo_agility, kay->algo_agility,
7675b9c547cSRui Paulo 		  sizeof(body->algo_agility));
7685b9c547cSRui Paulo 
7695b9c547cSRui Paulo 	os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
7705b9c547cSRui Paulo 
7715b9c547cSRui Paulo 	ieee802_1x_mka_dump_basic_body(body);
7725b9c547cSRui Paulo 
7735b9c547cSRui Paulo 	return 0;
7745b9c547cSRui Paulo }
7755b9c547cSRui Paulo 
7765b9c547cSRui Paulo 
777780fb4a2SCy Schubert static Boolean
778780fb4a2SCy Schubert reset_participant_mi(struct ieee802_1x_mka_participant *participant)
779780fb4a2SCy Schubert {
780780fb4a2SCy Schubert 	if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
781780fb4a2SCy Schubert 		return FALSE;
782780fb4a2SCy Schubert 	participant->mn = 0;
783780fb4a2SCy Schubert 
784780fb4a2SCy Schubert 	return TRUE;
785780fb4a2SCy Schubert }
786780fb4a2SCy Schubert 
787780fb4a2SCy Schubert 
7885b9c547cSRui Paulo /**
7895b9c547cSRui Paulo  * ieee802_1x_mka_decode_basic_body -
7905b9c547cSRui Paulo  */
7915b9c547cSRui Paulo static struct ieee802_1x_mka_participant *
7925b9c547cSRui Paulo ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
7935b9c547cSRui Paulo 				 size_t msg_len)
7945b9c547cSRui Paulo {
7955b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
7965b9c547cSRui Paulo 	const struct ieee802_1x_mka_basic_body *body;
7975b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
79885732ac8SCy Schubert 	size_t ckn_len;
79985732ac8SCy Schubert 	size_t body_len;
8005b9c547cSRui Paulo 
8015b9c547cSRui Paulo 	body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
8025b9c547cSRui Paulo 
8035b9c547cSRui Paulo 	if (body->version > MKA_VERSION_ID) {
8045b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
8054bc52338SCy Schubert 			   "KaY: Peer's version(%d) greater than MKA current version(%d)",
8065b9c547cSRui Paulo 			   body->version, MKA_VERSION_ID);
8075b9c547cSRui Paulo 	}
8085b9c547cSRui Paulo 	if (kay->is_obliged_key_server && body->key_server) {
8094bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server");
8105b9c547cSRui Paulo 		return NULL;
8115b9c547cSRui Paulo 	}
8125b9c547cSRui Paulo 
81385732ac8SCy Schubert 	body_len = get_mka_param_body_len(body);
81485732ac8SCy Schubert 	if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
81585732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
81685732ac8SCy Schubert 			   body_len);
81785732ac8SCy Schubert 		return NULL;
81885732ac8SCy Schubert 	}
81985732ac8SCy Schubert 	ckn_len = body_len -
82085732ac8SCy Schubert 	    (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
82185732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
8225b9c547cSRui Paulo 	if (!participant) {
8234bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
8244bc52338SCy Schubert 			   "KaY: Peer is not included in my CA - ignore MKPDU");
8255b9c547cSRui Paulo 		return NULL;
8265b9c547cSRui Paulo 	}
8275b9c547cSRui Paulo 
8285b9c547cSRui Paulo 	/* If the peer's MI is my MI, I will choose new MI */
8295b9c547cSRui Paulo 	if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
830780fb4a2SCy Schubert 		if (!reset_participant_mi(participant))
8315b9c547cSRui Paulo 			return NULL;
8324bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
8334bc52338SCy Schubert 			   "KaY: Peer using my MI - selected a new random MI: %s",
8344bc52338SCy Schubert 			   mi_txt(participant->mi));
8355b9c547cSRui Paulo 	}
8365b9c547cSRui Paulo 
8375b9c547cSRui Paulo 	os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
838780fb4a2SCy Schubert 	participant->current_peer_id.mn = body->actor_mn;
8395b9c547cSRui Paulo 	os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
8405b9c547cSRui Paulo 		  sizeof(participant->current_peer_sci.addr));
841780fb4a2SCy Schubert 	participant->current_peer_sci.port = body->actor_sci.port;
8425b9c547cSRui Paulo 
8435b9c547cSRui Paulo 	/* handler peer */
8445b9c547cSRui Paulo 	peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
8455b9c547cSRui Paulo 	if (!peer) {
8464bc52338SCy Schubert 		/* Check duplicated SCI
8474bc52338SCy Schubert 		 *
8484bc52338SCy Schubert 		 * A duplicated SCI indicates either an active attacker or
8494bc52338SCy Schubert 		 * a valid peer whose MI is being changed. The latter scenario
8504bc52338SCy Schubert 		 * is more likely because to have gotten this far the received
8514bc52338SCy Schubert 		 * MKPDU must have had a valid ICV, indicating the peer holds
8524bc52338SCy Schubert 		 * the same CAK as our participant.
8534bc52338SCy Schubert 		 *
8544bc52338SCy Schubert 		 * Before creating a new peer object for the new MI we must
8554bc52338SCy Schubert 		 * clean up the resources (SCs and SAs) associated with the
8564bc52338SCy Schubert 		 * old peer. An easy way to do this is to ignore MKPDUs with
8574bc52338SCy Schubert 		 * the new MI's for now and just wait for the old peer to
8584bc52338SCy Schubert 		 * time out and clean itself up (within MKA_LIFE_TIME).
8594bc52338SCy Schubert 		 *
8604bc52338SCy Schubert 		 * This method is preferable to deleting the old peer here
8614bc52338SCy Schubert 		 * and now and continuing on with processing because if this
8624bc52338SCy Schubert 		 * MKPDU is from an attacker it's better to ignore the MKPDU
8634bc52338SCy Schubert 		 * than to process it (and delete a valid peer as well).
8645b9c547cSRui Paulo 		 */
8655b9c547cSRui Paulo 		peer = ieee802_1x_kay_get_peer_sci(participant,
8665b9c547cSRui Paulo 						   &body->actor_sci);
8675b9c547cSRui Paulo 		if (peer) {
8684bc52338SCy Schubert 			time_t new_expire;
8694bc52338SCy Schubert 
8705b9c547cSRui Paulo 			wpa_printf(MSG_WARNING,
8714bc52338SCy Schubert 				   "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU");
8724bc52338SCy Schubert 			/* Reduce timeout to speed up this process but left the
8734bc52338SCy Schubert 			 * chance for old one to prove aliveness. */
8744bc52338SCy Schubert 			new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000;
8754bc52338SCy Schubert 			if (peer->expire > new_expire)
8764bc52338SCy Schubert 				peer->expire = new_expire;
8774bc52338SCy Schubert 			return NULL;
8785b9c547cSRui Paulo 		}
8795b9c547cSRui Paulo 
8805b9c547cSRui Paulo 		peer = ieee802_1x_kay_create_potential_peer(
8815b9c547cSRui Paulo 			participant, body->actor_mi,
8825b9c547cSRui Paulo 			be_to_host32(body->actor_mn));
8834bc52338SCy Schubert 		if (!peer) {
8844bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
8854bc52338SCy Schubert 				   "KaY: No potential peer entry found - ignore MKPDU");
8865b9c547cSRui Paulo 			return NULL;
8874bc52338SCy Schubert 		}
8885b9c547cSRui Paulo 
8895b9c547cSRui Paulo 		peer->macsec_desired = body->macsec_desired;
890780fb4a2SCy Schubert 		peer->macsec_capability = body->macsec_capability;
8915b9c547cSRui Paulo 		peer->is_key_server = (Boolean) body->key_server;
8925b9c547cSRui Paulo 		peer->key_server_priority = body->priority;
8935b9c547cSRui Paulo 	} else if (peer->mn < be_to_host32(body->actor_mn)) {
8945b9c547cSRui Paulo 		peer->mn = be_to_host32(body->actor_mn);
8955b9c547cSRui Paulo 		peer->macsec_desired = body->macsec_desired;
896780fb4a2SCy Schubert 		peer->macsec_capability = body->macsec_capability;
8975b9c547cSRui Paulo 		peer->is_key_server = (Boolean) body->key_server;
8985b9c547cSRui Paulo 		peer->key_server_priority = body->priority;
8995b9c547cSRui Paulo 	} else {
9004bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
9014bc52338SCy Schubert 			   "KaY: The peer MN did not increase - ignore MKPDU");
9025b9c547cSRui Paulo 		return NULL;
9035b9c547cSRui Paulo 	}
9045b9c547cSRui Paulo 
9055b9c547cSRui Paulo 	return participant;
9065b9c547cSRui Paulo }
9075b9c547cSRui Paulo 
9085b9c547cSRui Paulo 
9095b9c547cSRui Paulo /**
9105b9c547cSRui Paulo  * ieee802_1x_mka_live_peer_body_present
9115b9c547cSRui Paulo  */
9125b9c547cSRui Paulo static Boolean
9135b9c547cSRui Paulo ieee802_1x_mka_live_peer_body_present(
9145b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
9155b9c547cSRui Paulo {
9165b9c547cSRui Paulo 	return !dl_list_empty(&participant->live_peers);
9175b9c547cSRui Paulo }
9185b9c547cSRui Paulo 
9195b9c547cSRui Paulo 
9205b9c547cSRui Paulo /**
9215b9c547cSRui Paulo  * ieee802_1x_kay_get_live_peer_length
9225b9c547cSRui Paulo  */
9235b9c547cSRui Paulo static int
9245b9c547cSRui Paulo ieee802_1x_mka_get_live_peer_length(
9255b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
9265b9c547cSRui Paulo {
9275b9c547cSRui Paulo 	int len = MKA_HDR_LEN;
9285b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
9295b9c547cSRui Paulo 
9305b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
9315b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list)
9325b9c547cSRui Paulo 		len += sizeof(struct ieee802_1x_mka_peer_id);
9335b9c547cSRui Paulo 
934780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(len);
9355b9c547cSRui Paulo }
9365b9c547cSRui Paulo 
9375b9c547cSRui Paulo 
9385b9c547cSRui Paulo /**
9395b9c547cSRui Paulo  * ieee802_1x_mka_encode_live_peer_body -
9405b9c547cSRui Paulo  */
9415b9c547cSRui Paulo static int
9425b9c547cSRui Paulo ieee802_1x_mka_encode_live_peer_body(
9435b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
9445b9c547cSRui Paulo 	struct wpabuf *buf)
9455b9c547cSRui Paulo {
9465b9c547cSRui Paulo 	struct ieee802_1x_mka_peer_body *body;
9475b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
9485b9c547cSRui Paulo 	unsigned int length;
9495b9c547cSRui Paulo 	struct ieee802_1x_mka_peer_id *body_peer;
9505b9c547cSRui Paulo 
9515b9c547cSRui Paulo 	length = ieee802_1x_mka_get_live_peer_length(participant);
9525b9c547cSRui Paulo 	body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
9535b9c547cSRui Paulo 
9545b9c547cSRui Paulo 	body->type = MKA_LIVE_PEER_LIST;
9555b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
9565b9c547cSRui Paulo 
9575b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
9585b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
9595b9c547cSRui Paulo 		body_peer = wpabuf_put(buf,
9605b9c547cSRui Paulo 				       sizeof(struct ieee802_1x_mka_peer_id));
9615b9c547cSRui Paulo 		os_memcpy(body_peer->mi, peer->mi, MI_LEN);
9625b9c547cSRui Paulo 		body_peer->mn = host_to_be32(peer->mn);
9635b9c547cSRui Paulo 	}
9645b9c547cSRui Paulo 
9655b9c547cSRui Paulo 	ieee802_1x_mka_dump_peer_body(body);
9665b9c547cSRui Paulo 	return 0;
9675b9c547cSRui Paulo }
9685b9c547cSRui Paulo 
9695b9c547cSRui Paulo /**
9705b9c547cSRui Paulo  * ieee802_1x_mka_potential_peer_body_present
9715b9c547cSRui Paulo  */
9725b9c547cSRui Paulo static Boolean
9735b9c547cSRui Paulo ieee802_1x_mka_potential_peer_body_present(
9745b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
9755b9c547cSRui Paulo {
9765b9c547cSRui Paulo 	return !dl_list_empty(&participant->potential_peers);
9775b9c547cSRui Paulo }
9785b9c547cSRui Paulo 
9795b9c547cSRui Paulo 
9805b9c547cSRui Paulo /**
9815b9c547cSRui Paulo  * ieee802_1x_kay_get_potential_peer_length
9825b9c547cSRui Paulo  */
9835b9c547cSRui Paulo static int
9845b9c547cSRui Paulo ieee802_1x_mka_get_potential_peer_length(
9855b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
9865b9c547cSRui Paulo {
9875b9c547cSRui Paulo 	int len = MKA_HDR_LEN;
9885b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
9895b9c547cSRui Paulo 
9905b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->potential_peers,
9915b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list)
9925b9c547cSRui Paulo 		len += sizeof(struct ieee802_1x_mka_peer_id);
9935b9c547cSRui Paulo 
994780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(len);
9955b9c547cSRui Paulo }
9965b9c547cSRui Paulo 
9975b9c547cSRui Paulo 
9985b9c547cSRui Paulo /**
9995b9c547cSRui Paulo  * ieee802_1x_mka_encode_potential_peer_body -
10005b9c547cSRui Paulo  */
10015b9c547cSRui Paulo static int
10025b9c547cSRui Paulo ieee802_1x_mka_encode_potential_peer_body(
10035b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
10045b9c547cSRui Paulo 	struct wpabuf *buf)
10055b9c547cSRui Paulo {
10065b9c547cSRui Paulo 	struct ieee802_1x_mka_peer_body *body;
10075b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
10085b9c547cSRui Paulo 	unsigned int length;
10095b9c547cSRui Paulo 	struct ieee802_1x_mka_peer_id *body_peer;
10105b9c547cSRui Paulo 
10115b9c547cSRui Paulo 	length = ieee802_1x_mka_get_potential_peer_length(participant);
10125b9c547cSRui Paulo 	body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
10135b9c547cSRui Paulo 
10145b9c547cSRui Paulo 	body->type = MKA_POTENTIAL_PEER_LIST;
10155b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
10165b9c547cSRui Paulo 
10175b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->potential_peers,
10185b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
10195b9c547cSRui Paulo 		body_peer = wpabuf_put(buf,
10205b9c547cSRui Paulo 				       sizeof(struct ieee802_1x_mka_peer_id));
10215b9c547cSRui Paulo 		os_memcpy(body_peer->mi, peer->mi, MI_LEN);
10225b9c547cSRui Paulo 		body_peer->mn = host_to_be32(peer->mn);
10235b9c547cSRui Paulo 	}
10245b9c547cSRui Paulo 
10255b9c547cSRui Paulo 	ieee802_1x_mka_dump_peer_body(body);
10265b9c547cSRui Paulo 	return 0;
10275b9c547cSRui Paulo }
10285b9c547cSRui Paulo 
10295b9c547cSRui Paulo 
10305b9c547cSRui Paulo /**
10315b9c547cSRui Paulo  * ieee802_1x_mka_i_in_peerlist -
10325b9c547cSRui Paulo  */
10335b9c547cSRui Paulo static Boolean
10345b9c547cSRui Paulo ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
10355b9c547cSRui Paulo 			     const u8 *mka_msg, size_t msg_len)
10365b9c547cSRui Paulo {
10375b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
10385b9c547cSRui Paulo 	size_t body_len;
10395b9c547cSRui Paulo 	size_t left_len;
1040780fb4a2SCy Schubert 	u8 body_type;
10415b9c547cSRui Paulo 	const u8 *pos;
10425b9c547cSRui Paulo 	size_t i;
10435b9c547cSRui Paulo 
1044780fb4a2SCy Schubert 	for (pos = mka_msg, left_len = msg_len;
1045780fb4a2SCy Schubert 	     left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
10464bc52338SCy Schubert 	     left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN,
10474bc52338SCy Schubert 		     pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) {
10485b9c547cSRui Paulo 		hdr = (struct ieee802_1x_mka_hdr *) pos;
10495b9c547cSRui Paulo 		body_len = get_mka_param_body_len(hdr);
10505b9c547cSRui Paulo 		body_type = get_mka_param_body_type(hdr);
10515b9c547cSRui Paulo 
105285732ac8SCy Schubert 		if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) {
10535b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
1054780fb4a2SCy Schubert 				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
1055780fb4a2SCy Schubert 				   left_len, MKA_HDR_LEN,
105685732ac8SCy Schubert 				   MKA_ALIGN_LENGTH(body_len),
105785732ac8SCy Schubert 				   DEFAULT_ICV_LEN);
105885732ac8SCy Schubert 			return FALSE;
10595b9c547cSRui Paulo 		}
10605b9c547cSRui Paulo 
106185732ac8SCy Schubert 		if (body_type != MKA_LIVE_PEER_LIST &&
106285732ac8SCy Schubert 		    body_type != MKA_POTENTIAL_PEER_LIST)
106385732ac8SCy Schubert 			continue;
106485732ac8SCy Schubert 
10655b9c547cSRui Paulo 		if ((body_len % 16) != 0) {
10665b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
1067780fb4a2SCy Schubert 				   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1068780fb4a2SCy Schubert 				   body_len);
1069780fb4a2SCy Schubert 			continue;
10705b9c547cSRui Paulo 		}
10715b9c547cSRui Paulo 
107285732ac8SCy Schubert 		ieee802_1x_mka_dump_peer_body(
107385732ac8SCy Schubert 			(struct ieee802_1x_mka_peer_body *)pos);
107485732ac8SCy Schubert 
1075780fb4a2SCy Schubert 		for (i = 0; i < body_len;
1076780fb4a2SCy Schubert 		     i += sizeof(struct ieee802_1x_mka_peer_id)) {
1077780fb4a2SCy Schubert 			const struct ieee802_1x_mka_peer_id *peer_mi;
10785b9c547cSRui Paulo 
1079780fb4a2SCy Schubert 			peer_mi = (const struct ieee802_1x_mka_peer_id *)
1080780fb4a2SCy Schubert 				(pos + MKA_HDR_LEN + i);
1081780fb4a2SCy Schubert 			if (os_memcmp(peer_mi->mi, participant->mi,
10824bc52338SCy Schubert 				      MI_LEN) == 0) {
10834bc52338SCy Schubert 				u32 mn = be_to_host32(peer_mi->mn);
10844bc52338SCy Schubert 
10854bc52338SCy Schubert 				wpa_printf(MSG_DEBUG,
10864bc52338SCy Schubert 					   "KaY: My MI - received MN %u, most recently transmitted MN %u",
10874bc52338SCy Schubert 					   mn, participant->mn);
1088*206b73d0SCy Schubert 				/* IEEE Std 802.1X-2010 is not exactly clear
1089*206b73d0SCy Schubert 				 * which values of MN should be accepted here.
1090*206b73d0SCy Schubert 				 * It uses "acceptably recent MN" language
1091*206b73d0SCy Schubert 				 * without defining what would be acceptable
1092*206b73d0SCy Schubert 				 * recent. For now, allow the last two used MN
1093*206b73d0SCy Schubert 				 * values (i.e., peer having copied my MI,MN
1094*206b73d0SCy Schubert 				 * from either of the last two MKPDUs that I
1095*206b73d0SCy Schubert 				 * have sent). */
1096*206b73d0SCy Schubert 				if (mn == participant->mn ||
1097*206b73d0SCy Schubert 				    (participant->mn > 1 &&
1098*206b73d0SCy Schubert 				     mn == participant->mn - 1))
10995b9c547cSRui Paulo 					return TRUE;
1100780fb4a2SCy Schubert 			}
11015b9c547cSRui Paulo 		}
11024bc52338SCy Schubert 	}
11035b9c547cSRui Paulo 
11045b9c547cSRui Paulo 	return FALSE;
11055b9c547cSRui Paulo }
11065b9c547cSRui Paulo 
11075b9c547cSRui Paulo 
11085b9c547cSRui Paulo /**
11095b9c547cSRui Paulo  * ieee802_1x_mka_decode_live_peer_body -
11105b9c547cSRui Paulo  */
11115b9c547cSRui Paulo static int ieee802_1x_mka_decode_live_peer_body(
11125b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
11135b9c547cSRui Paulo 	const u8 *peer_msg, size_t msg_len)
11145b9c547cSRui Paulo {
11155b9c547cSRui Paulo 	const struct ieee802_1x_mka_hdr *hdr;
11165b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
11175b9c547cSRui Paulo 	size_t body_len;
11185b9c547cSRui Paulo 	size_t i;
11195b9c547cSRui Paulo 	Boolean is_included;
11205b9c547cSRui Paulo 
11215b9c547cSRui Paulo 	is_included = ieee802_1x_kay_is_in_live_peer(
11225b9c547cSRui Paulo 		participant, participant->current_peer_id.mi);
11235b9c547cSRui Paulo 
11245b9c547cSRui Paulo 	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
11255b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
1126780fb4a2SCy Schubert 	if (body_len % 16 != 0) {
1127780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
1128780fb4a2SCy Schubert 			   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1129780fb4a2SCy Schubert 			   body_len);
1130780fb4a2SCy Schubert 		return -1;
1131780fb4a2SCy Schubert 	}
11325b9c547cSRui Paulo 
1133780fb4a2SCy Schubert 	for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
1134780fb4a2SCy Schubert 		const struct ieee802_1x_mka_peer_id *peer_mi;
1135780fb4a2SCy Schubert 		u32 peer_mn;
1136780fb4a2SCy Schubert 
1137780fb4a2SCy Schubert 		peer_mi = (const struct ieee802_1x_mka_peer_id *)
1138780fb4a2SCy Schubert 			(peer_msg + MKA_HDR_LEN + i);
1139780fb4a2SCy Schubert 		peer_mn = be_to_host32(peer_mi->mn);
11405b9c547cSRui Paulo 
11415b9c547cSRui Paulo 		/* it is myself */
11425b9c547cSRui Paulo 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
11435b9c547cSRui Paulo 			/* My message id is used by other participant */
1144780fb4a2SCy Schubert 			if (peer_mn > participant->mn &&
1145780fb4a2SCy Schubert 			    !reset_participant_mi(participant))
1146780fb4a2SCy Schubert 				wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
11475b9c547cSRui Paulo 			continue;
11485b9c547cSRui Paulo 		}
1149780fb4a2SCy Schubert 
11505b9c547cSRui Paulo 		if (!is_included)
11515b9c547cSRui Paulo 			continue;
11525b9c547cSRui Paulo 
1153780fb4a2SCy Schubert 		peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi);
1154780fb4a2SCy Schubert 		if (peer) {
11555b9c547cSRui Paulo 			peer->mn = peer_mn;
1156780fb4a2SCy Schubert 		} else if (!ieee802_1x_kay_create_potential_peer(
1157780fb4a2SCy Schubert 				participant, peer_mi->mi, peer_mn)) {
11585b9c547cSRui Paulo 			return -1;
11595b9c547cSRui Paulo 		}
11605b9c547cSRui Paulo 	}
11615b9c547cSRui Paulo 
11625b9c547cSRui Paulo 	return 0;
11635b9c547cSRui Paulo }
11645b9c547cSRui Paulo 
11655b9c547cSRui Paulo 
11665b9c547cSRui Paulo /**
11675b9c547cSRui Paulo  * ieee802_1x_mka_decode_potential_peer_body -
11685b9c547cSRui Paulo  */
11695b9c547cSRui Paulo static int
11705b9c547cSRui Paulo ieee802_1x_mka_decode_potential_peer_body(
11715b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
11725b9c547cSRui Paulo 	const u8 *peer_msg, size_t msg_len)
11735b9c547cSRui Paulo {
1174780fb4a2SCy Schubert 	const struct ieee802_1x_mka_hdr *hdr;
11755b9c547cSRui Paulo 	size_t body_len;
11765b9c547cSRui Paulo 	size_t i;
11775b9c547cSRui Paulo 
1178780fb4a2SCy Schubert 	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
11795b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
1180780fb4a2SCy Schubert 	if (body_len % 16 != 0) {
1181780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
1182780fb4a2SCy Schubert 			   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1183780fb4a2SCy Schubert 			   body_len);
1184780fb4a2SCy Schubert 		return -1;
1185780fb4a2SCy Schubert 	}
11865b9c547cSRui Paulo 
1187780fb4a2SCy Schubert 	for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
1188780fb4a2SCy Schubert 		const struct ieee802_1x_mka_peer_id *peer_mi;
1189780fb4a2SCy Schubert 		u32 peer_mn;
1190780fb4a2SCy Schubert 
1191780fb4a2SCy Schubert 		peer_mi = (struct ieee802_1x_mka_peer_id *)
1192780fb4a2SCy Schubert 			(peer_msg + MKA_HDR_LEN + i);
1193780fb4a2SCy Schubert 		peer_mn = be_to_host32(peer_mi->mn);
11945b9c547cSRui Paulo 
11955b9c547cSRui Paulo 		/* it is myself */
11965b9c547cSRui Paulo 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
11975b9c547cSRui Paulo 			/* My message id is used by other participant */
1198780fb4a2SCy Schubert 			if (peer_mn > participant->mn &&
1199780fb4a2SCy Schubert 			    !reset_participant_mi(participant))
1200780fb4a2SCy Schubert 				wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
12015b9c547cSRui Paulo 			continue;
12025b9c547cSRui Paulo 		}
12035b9c547cSRui Paulo 	}
12045b9c547cSRui Paulo 
12055b9c547cSRui Paulo 	return 0;
12065b9c547cSRui Paulo }
12075b9c547cSRui Paulo 
12085b9c547cSRui Paulo 
12095b9c547cSRui Paulo /**
12105b9c547cSRui Paulo  * ieee802_1x_mka_sak_use_body_present
12115b9c547cSRui Paulo  */
12125b9c547cSRui Paulo static Boolean
12135b9c547cSRui Paulo ieee802_1x_mka_sak_use_body_present(
12145b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
12155b9c547cSRui Paulo {
1216780fb4a2SCy Schubert 	return participant->to_use_sak;
12175b9c547cSRui Paulo }
12185b9c547cSRui Paulo 
12195b9c547cSRui Paulo 
12205b9c547cSRui Paulo /**
12215b9c547cSRui Paulo  * ieee802_1x_mka_get_sak_use_length
12225b9c547cSRui Paulo  */
12235b9c547cSRui Paulo static int
12245b9c547cSRui Paulo ieee802_1x_mka_get_sak_use_length(
12255b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
12265b9c547cSRui Paulo {
12275b9c547cSRui Paulo 	int length = MKA_HDR_LEN;
12285b9c547cSRui Paulo 
12295b9c547cSRui Paulo 	if (participant->kay->macsec_desired && participant->advised_desired)
12305b9c547cSRui Paulo 		length = sizeof(struct ieee802_1x_mka_sak_use_body);
12315b9c547cSRui Paulo 
1232780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
12335b9c547cSRui Paulo }
12345b9c547cSRui Paulo 
12355b9c547cSRui Paulo 
12365b9c547cSRui Paulo /**
12374bc52338SCy Schubert  * ieee802_1x_mka_get_lpn
12385b9c547cSRui Paulo  */
12395b9c547cSRui Paulo static u32
12405b9c547cSRui Paulo ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
12415b9c547cSRui Paulo 		       struct ieee802_1x_mka_ki *ki)
12425b9c547cSRui Paulo {
12434bc52338SCy Schubert 	struct transmit_sa *txsa;
12445b9c547cSRui Paulo 	u32 lpn = 0;
12455b9c547cSRui Paulo 
12464bc52338SCy Schubert 	dl_list_for_each(txsa, &principal->txsc->sa_list,
12474bc52338SCy Schubert 			 struct transmit_sa, list) {
12484bc52338SCy Schubert 		if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
12494bc52338SCy Schubert 			/* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
12504bc52338SCy Schubert 			 * MKA to communicate the lowest PN used for
12514bc52338SCy Schubert 			 * transmission with the SAK within the last two
12524bc52338SCy Schubert 			 * seconds".  Achieve this 2 second delay by setting the
12534bc52338SCy Schubert 			 * lpn using the transmit next PN (i.e., txsa->next_pn)
12544bc52338SCy Schubert 			 * that was read last time here (i.e., mka_hello_time
12554bc52338SCy Schubert 			 * 2 seconds ago).
12564bc52338SCy Schubert 			 *
12574bc52338SCy Schubert 			 * The lowest acceptable PN is the same as the last
12584bc52338SCy Schubert 			 * transmitted PN, which is one less than the next
12594bc52338SCy Schubert 			 * transmit PN.
12604bc52338SCy Schubert 			 *
12614bc52338SCy Schubert 			 * NOTE: This method only works if mka_hello_time is 2s.
12624bc52338SCy Schubert 			 */
12634bc52338SCy Schubert 			lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
12645b9c547cSRui Paulo 
12654bc52338SCy Schubert 			/* Now read the current transmit next PN for use next
12664bc52338SCy Schubert 			 * time through. */
12674bc52338SCy Schubert 			secy_get_transmit_next_pn(principal->kay, txsa);
12685b9c547cSRui Paulo 			break;
12695b9c547cSRui Paulo 		}
12705b9c547cSRui Paulo 	}
12715b9c547cSRui Paulo 
12725b9c547cSRui Paulo 	if (lpn == 0)
12735b9c547cSRui Paulo 		lpn = 1;
12745b9c547cSRui Paulo 
12755b9c547cSRui Paulo 	return lpn;
12765b9c547cSRui Paulo }
12775b9c547cSRui Paulo 
12785b9c547cSRui Paulo 
12795b9c547cSRui Paulo /**
12805b9c547cSRui Paulo  * ieee802_1x_mka_encode_sak_use_body -
12815b9c547cSRui Paulo  */
12825b9c547cSRui Paulo static int
12835b9c547cSRui Paulo ieee802_1x_mka_encode_sak_use_body(
12845b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
12855b9c547cSRui Paulo 	struct wpabuf *buf)
12865b9c547cSRui Paulo {
12875b9c547cSRui Paulo 	struct ieee802_1x_mka_sak_use_body *body;
1288780fb4a2SCy Schubert 	struct ieee802_1x_kay *kay = participant->kay;
12895b9c547cSRui Paulo 	unsigned int length;
12905b9c547cSRui Paulo 	u32 pn = 1;
12915b9c547cSRui Paulo 
12925b9c547cSRui Paulo 	length = ieee802_1x_mka_get_sak_use_length(participant);
1293780fb4a2SCy Schubert 	body = wpabuf_put(buf, length);
12945b9c547cSRui Paulo 
12955b9c547cSRui Paulo 	body->type = MKA_SAK_USE;
12965b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
12975b9c547cSRui Paulo 
12985b9c547cSRui Paulo 	if (length == MKA_HDR_LEN) {
12995b9c547cSRui Paulo 		body->ptx = TRUE;
13005b9c547cSRui Paulo 		body->prx = TRUE;
13015b9c547cSRui Paulo 		body->lan = 0;
13025b9c547cSRui Paulo 		body->lrx = FALSE;
13035b9c547cSRui Paulo 		body->ltx = FALSE;
13045b9c547cSRui Paulo 		body->delay_protect = FALSE;
13055b9c547cSRui Paulo 		return 0;
13065b9c547cSRui Paulo 	}
13075b9c547cSRui Paulo 
13084bc52338SCy Schubert 	/* data delay protect */
13094bc52338SCy Schubert 	body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME;
13104bc52338SCy Schubert 	/* lowest accept packet number */
13115b9c547cSRui Paulo 	pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
1312780fb4a2SCy Schubert 	if (pn > kay->pn_exhaustion) {
13135b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
13145b9c547cSRui Paulo 		if (participant->is_key_server)
13155b9c547cSRui Paulo 			participant->new_sak = TRUE;
13165b9c547cSRui Paulo 	}
13175b9c547cSRui Paulo 
13185b9c547cSRui Paulo 	body->llpn = host_to_be32(pn);
13195b9c547cSRui Paulo 	pn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
13205b9c547cSRui Paulo 	body->olpn = host_to_be32(pn);
13215b9c547cSRui Paulo 
13225b9c547cSRui Paulo 	/* plain tx, plain rx */
1323780fb4a2SCy Schubert 	body->ptx = !kay->macsec_protect;
1324780fb4a2SCy Schubert 	body->prx = kay->macsec_validate != Strict;
13255b9c547cSRui Paulo 
13265b9c547cSRui Paulo 	/* latest key: rx, tx, key server member identifier key number */
13275b9c547cSRui Paulo 	body->lan = participant->lan;
1328780fb4a2SCy Schubert 	os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi));
13295b9c547cSRui Paulo 	body->lkn = host_to_be32(participant->lki.kn);
13305b9c547cSRui Paulo 	body->lrx = participant->lrx;
13315b9c547cSRui Paulo 	body->ltx = participant->ltx;
13325b9c547cSRui Paulo 
13335b9c547cSRui Paulo 	/* old key: rx, tx, key server member identifier key number */
13345b9c547cSRui Paulo 	body->oan = participant->oan;
13355b9c547cSRui Paulo 	if (participant->oki.kn != participant->lki.kn &&
13365b9c547cSRui Paulo 	    participant->oki.kn != 0) {
13375b9c547cSRui Paulo 		body->otx = TRUE;
13385b9c547cSRui Paulo 		body->orx = TRUE;
13395b9c547cSRui Paulo 		os_memcpy(body->osrv_mi, participant->oki.mi,
13405b9c547cSRui Paulo 			  sizeof(body->osrv_mi));
13415b9c547cSRui Paulo 		body->okn = host_to_be32(participant->oki.kn);
13425b9c547cSRui Paulo 	} else {
13435b9c547cSRui Paulo 		body->otx = FALSE;
13445b9c547cSRui Paulo 		body->orx = FALSE;
13455b9c547cSRui Paulo 	}
13465b9c547cSRui Paulo 
13475b9c547cSRui Paulo 	/* set CP's variable */
13485b9c547cSRui Paulo 	if (body->ltx) {
1349780fb4a2SCy Schubert 		kay->tx_enable = TRUE;
1350780fb4a2SCy Schubert 		kay->port_enable = TRUE;
13515b9c547cSRui Paulo 	}
1352780fb4a2SCy Schubert 	if (body->lrx)
1353780fb4a2SCy Schubert 		kay->rx_enable = TRUE;
13545b9c547cSRui Paulo 
13555b9c547cSRui Paulo 	ieee802_1x_mka_dump_sak_use_body(body);
13565b9c547cSRui Paulo 	return 0;
13575b9c547cSRui Paulo }
13585b9c547cSRui Paulo 
13595b9c547cSRui Paulo 
13605b9c547cSRui Paulo /**
13615b9c547cSRui Paulo  * ieee802_1x_mka_decode_sak_use_body -
13625b9c547cSRui Paulo  */
13635b9c547cSRui Paulo static int
13645b9c547cSRui Paulo ieee802_1x_mka_decode_sak_use_body(
13655b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
13665b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
13675b9c547cSRui Paulo {
13685b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
13695b9c547cSRui Paulo 	struct ieee802_1x_mka_sak_use_body *body;
13705b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
13714bc52338SCy Schubert 	struct receive_sc *rxsc;
13724bc52338SCy Schubert 	struct receive_sa *rxsa;
13735b9c547cSRui Paulo 	struct data_key *sa_key = NULL;
13745b9c547cSRui Paulo 	size_t body_len;
13755b9c547cSRui Paulo 	struct ieee802_1x_mka_ki ki;
13765b9c547cSRui Paulo 	u32 lpn;
13775b9c547cSRui Paulo 	Boolean all_receiving;
1378780fb4a2SCy Schubert 	Boolean found;
1379780fb4a2SCy Schubert 	struct ieee802_1x_kay *kay = participant->kay;
13805b9c547cSRui Paulo 
13815b9c547cSRui Paulo 	if (!participant->principal) {
13825b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
13835b9c547cSRui Paulo 		return -1;
13845b9c547cSRui Paulo 	}
13855b9c547cSRui Paulo 	peer = ieee802_1x_kay_get_live_peer(participant,
13865b9c547cSRui Paulo 					    participant->current_peer_id.mi);
13875b9c547cSRui Paulo 	if (!peer) {
13884bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
13894bc52338SCy Schubert 			   "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set",
13904bc52338SCy Schubert 			   mi_txt(participant->current_peer_id.mi));
13915b9c547cSRui Paulo 		return -1;
13925b9c547cSRui Paulo 	}
13935b9c547cSRui Paulo 
13945b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
13955b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
13965b9c547cSRui Paulo 	body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
13975b9c547cSRui Paulo 	ieee802_1x_mka_dump_sak_use_body(body);
13985b9c547cSRui Paulo 
13995b9c547cSRui Paulo 	if ((body_len != 0) && (body_len < 40)) {
14005b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1401780fb4a2SCy Schubert 			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets",
1402780fb4a2SCy Schubert 			   body_len);
14035b9c547cSRui Paulo 		return -1;
14045b9c547cSRui Paulo 	}
14055b9c547cSRui Paulo 
14065b9c547cSRui Paulo 	/* TODO: what action should I take when peer does not support MACsec */
14075b9c547cSRui Paulo 	if (body_len == 0) {
14085b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
14095b9c547cSRui Paulo 		return 0;
14105b9c547cSRui Paulo 	}
14115b9c547cSRui Paulo 
14125b9c547cSRui Paulo 	/* TODO: when the plain tx or rx of peer is true, should I change
14135b9c547cSRui Paulo 	 * the attribute of controlled port
14145b9c547cSRui Paulo 	 */
14155b9c547cSRui Paulo 	if (body->prx)
14165b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
14175b9c547cSRui Paulo 
14185b9c547cSRui Paulo 	if (body->ptx)
14195b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
14205b9c547cSRui Paulo 
14215b9c547cSRui Paulo 	/* check latest key is valid */
14225b9c547cSRui Paulo 	if (body->ltx || body->lrx) {
1423780fb4a2SCy Schubert 		found = FALSE;
14245b9c547cSRui Paulo 		os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
1425780fb4a2SCy Schubert 		ki.kn = be_to_host32(body->lkn);
14265b9c547cSRui Paulo 		dl_list_for_each(sa_key, &participant->sak_list,
14275b9c547cSRui Paulo 				 struct data_key, list) {
14285b9c547cSRui Paulo 			if (is_ki_equal(&sa_key->key_identifier, &ki)) {
1429780fb4a2SCy Schubert 				found = TRUE;
14305b9c547cSRui Paulo 				break;
14315b9c547cSRui Paulo 			}
14325b9c547cSRui Paulo 		}
1433780fb4a2SCy Schubert 		if (!found) {
14344bc52338SCy Schubert 			wpa_printf(MSG_INFO, "KaY: Latest key is invalid");
14355b9c547cSRui Paulo 			return -1;
14365b9c547cSRui Paulo 		}
14375b9c547cSRui Paulo 		if (os_memcmp(participant->lki.mi, body->lsrv_mi,
14385b9c547cSRui Paulo 			      sizeof(participant->lki.mi)) == 0 &&
1439780fb4a2SCy Schubert 		    be_to_host32(body->lkn) == participant->lki.kn &&
14405b9c547cSRui Paulo 		    body->lan == participant->lan) {
14415b9c547cSRui Paulo 			peer->sak_used = TRUE;
14425b9c547cSRui Paulo 		}
14435b9c547cSRui Paulo 		if (body->ltx && peer->is_key_server) {
1444780fb4a2SCy Schubert 			ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE);
1445780fb4a2SCy Schubert 			ieee802_1x_cp_sm_step(kay->cp);
14465b9c547cSRui Paulo 		}
14475b9c547cSRui Paulo 	}
14485b9c547cSRui Paulo 
144985732ac8SCy Schubert 	/* check old key is valid (but only if we remember our old key) */
145085732ac8SCy Schubert 	if (participant->oki.kn != 0 && (body->otx || body->orx)) {
14515b9c547cSRui Paulo 		if (os_memcmp(participant->oki.mi, body->osrv_mi,
14525b9c547cSRui Paulo 			      sizeof(participant->oki.mi)) != 0 ||
1453780fb4a2SCy Schubert 		    be_to_host32(body->okn) != participant->oki.kn ||
14545b9c547cSRui Paulo 		    body->oan != participant->oan) {
14555b9c547cSRui Paulo 			wpa_printf(MSG_WARNING, "KaY: Old key is invalid");
14565b9c547cSRui Paulo 			return -1;
14575b9c547cSRui Paulo 		}
14585b9c547cSRui Paulo 	}
14595b9c547cSRui Paulo 
14605b9c547cSRui Paulo 	/* TODO: how to set the MACsec hardware when delay_protect is true */
1461780fb4a2SCy Schubert 	if (body->delay_protect &&
1462780fb4a2SCy Schubert 	    (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
14635b9c547cSRui Paulo 		wpa_printf(MSG_WARNING,
14644bc52338SCy Schubert 			   "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE");
14655b9c547cSRui Paulo 		return -1;
14665b9c547cSRui Paulo 	}
14675b9c547cSRui Paulo 
14685b9c547cSRui Paulo 	/* check all live peer have used the sak for receiving sa */
14695b9c547cSRui Paulo 	all_receiving = TRUE;
14705b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
14715b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
14725b9c547cSRui Paulo 		if (!peer->sak_used) {
14735b9c547cSRui Paulo 			all_receiving = FALSE;
14745b9c547cSRui Paulo 			break;
14755b9c547cSRui Paulo 		}
14765b9c547cSRui Paulo 	}
14775b9c547cSRui Paulo 	if (all_receiving) {
14785b9c547cSRui Paulo 		participant->to_dist_sak = FALSE;
1479780fb4a2SCy Schubert 		ieee802_1x_cp_set_allreceiving(kay->cp, TRUE);
1480780fb4a2SCy Schubert 		ieee802_1x_cp_sm_step(kay->cp);
14815b9c547cSRui Paulo 	}
14825b9c547cSRui Paulo 
14834bc52338SCy Schubert 	/* if I'm key server, and detects peer member pn exhaustion, rekey. */
1484780fb4a2SCy Schubert 	lpn = be_to_host32(body->llpn);
1485780fb4a2SCy Schubert 	if (lpn > kay->pn_exhaustion) {
14865b9c547cSRui Paulo 		if (participant->is_key_server) {
14875b9c547cSRui Paulo 			participant->new_sak = TRUE;
14885b9c547cSRui Paulo 			wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
14895b9c547cSRui Paulo 		}
14905b9c547cSRui Paulo 	}
14915b9c547cSRui Paulo 
14924bc52338SCy Schubert 	if (sa_key)
14934bc52338SCy Schubert 		sa_key->next_pn = lpn;
1494780fb4a2SCy Schubert 	found = FALSE;
14954bc52338SCy Schubert 	dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc,
14964bc52338SCy Schubert 			 list) {
14974bc52338SCy Schubert 		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa,
14984bc52338SCy Schubert 				 list) {
14994bc52338SCy Schubert 			if (sa_key && rxsa->pkey == sa_key) {
1500780fb4a2SCy Schubert 				found = TRUE;
15015b9c547cSRui Paulo 				break;
15025b9c547cSRui Paulo 			}
15035b9c547cSRui Paulo 		}
15044bc52338SCy Schubert 		if (found)
15054bc52338SCy Schubert 			break;
15064bc52338SCy Schubert 	}
1507780fb4a2SCy Schubert 	if (!found) {
15084bc52338SCy Schubert 		wpa_printf(MSG_WARNING, "KaY: Can't find rxsa");
15095b9c547cSRui Paulo 		return -1;
15105b9c547cSRui Paulo 	}
15115b9c547cSRui Paulo 
15124bc52338SCy Schubert 	if (body->delay_protect) {
15134bc52338SCy Schubert 		secy_get_receive_lowest_pn(participant->kay, rxsa);
15144bc52338SCy Schubert 		if (lpn > rxsa->lowest_pn) {
15154bc52338SCy Schubert 			/* Delay protect window (communicated via MKA) is
15164bc52338SCy Schubert 			 * tighter than SecY's current replay protect window,
15174bc52338SCy Schubert 			 * so tell SecY the new (and higher) lpn. */
15184bc52338SCy Schubert 			rxsa->lowest_pn = lpn;
15194bc52338SCy Schubert 			secy_set_receive_lowest_pn(participant->kay, rxsa);
15204bc52338SCy Schubert 			wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn);
15214bc52338SCy Schubert 		}
15224bc52338SCy Schubert 		/* FIX: Delay protection for olpn not implemented.
15234bc52338SCy Schubert 		 * Note that Old Key is only active for MKA_SAK_RETIRE_TIME
15244bc52338SCy Schubert 		 * (3 seconds) and delay protection does allow PN's within
15254bc52338SCy Schubert 		 * a 2 seconds window, so olpn would be a lot of work for
15264bc52338SCy Schubert 		 * just 1 second's worth of protection. */
15275b9c547cSRui Paulo 	}
15285b9c547cSRui Paulo 
15295b9c547cSRui Paulo 	return 0;
15305b9c547cSRui Paulo }
15315b9c547cSRui Paulo 
15325b9c547cSRui Paulo 
15335b9c547cSRui Paulo /**
15345b9c547cSRui Paulo  * ieee802_1x_mka_dist_sak_body_present
15355b9c547cSRui Paulo  */
15365b9c547cSRui Paulo static Boolean
15375b9c547cSRui Paulo ieee802_1x_mka_dist_sak_body_present(
15385b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
15395b9c547cSRui Paulo {
15404bc52338SCy Schubert 	return participant->is_key_server && participant->to_dist_sak &&
15414bc52338SCy Schubert 		participant->new_key;
15425b9c547cSRui Paulo }
15435b9c547cSRui Paulo 
15445b9c547cSRui Paulo 
15455b9c547cSRui Paulo /**
15465b9c547cSRui Paulo  * ieee802_1x_kay_get_dist_sak_length
15475b9c547cSRui Paulo  */
15485b9c547cSRui Paulo static int
15495b9c547cSRui Paulo ieee802_1x_mka_get_dist_sak_length(
15505b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
15515b9c547cSRui Paulo {
1552780fb4a2SCy Schubert 	int length = MKA_HDR_LEN;
1553780fb4a2SCy Schubert 	unsigned int cs_index = participant->kay->macsec_csindex;
15545b9c547cSRui Paulo 
1555780fb4a2SCy Schubert 	if (participant->advised_desired && cs_index < CS_TABLE_SIZE) {
15565b9c547cSRui Paulo 		length = sizeof(struct ieee802_1x_mka_dist_sak_body);
15575b9c547cSRui Paulo 		if (cs_index != DEFAULT_CS_INDEX)
15585b9c547cSRui Paulo 			length += CS_ID_LEN;
15595b9c547cSRui Paulo 
15605b9c547cSRui Paulo 		length += cipher_suite_tbl[cs_index].sak_len + 8;
15615b9c547cSRui Paulo 	}
15625b9c547cSRui Paulo 
1563780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
15645b9c547cSRui Paulo }
15655b9c547cSRui Paulo 
15665b9c547cSRui Paulo 
15675b9c547cSRui Paulo /**
15685b9c547cSRui Paulo  * ieee802_1x_mka_encode_dist_sak_body -
15695b9c547cSRui Paulo  */
15705b9c547cSRui Paulo static int
15715b9c547cSRui Paulo ieee802_1x_mka_encode_dist_sak_body(
15725b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
15735b9c547cSRui Paulo 	struct wpabuf *buf)
15745b9c547cSRui Paulo {
15755b9c547cSRui Paulo 	struct ieee802_1x_mka_dist_sak_body *body;
15765b9c547cSRui Paulo 	struct data_key *sak;
15775b9c547cSRui Paulo 	unsigned int length;
1578780fb4a2SCy Schubert 	unsigned int cs_index;
15795b9c547cSRui Paulo 	int sak_pos;
15805b9c547cSRui Paulo 
15815b9c547cSRui Paulo 	length = ieee802_1x_mka_get_dist_sak_length(participant);
15825b9c547cSRui Paulo 	body = wpabuf_put(buf, length);
15835b9c547cSRui Paulo 	body->type = MKA_DISTRIBUTED_SAK;
15845b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
15855b9c547cSRui Paulo 	if (length == MKA_HDR_LEN) {
15865b9c547cSRui Paulo 		body->confid_offset = 0;
15875b9c547cSRui Paulo 		body->dan = 0;
15885b9c547cSRui Paulo 		return 0;
15895b9c547cSRui Paulo 	}
15905b9c547cSRui Paulo 
15915b9c547cSRui Paulo 	sak = participant->new_key;
15924bc52338SCy Schubert 	if (!sak) {
15934bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
15944bc52338SCy Schubert 			   "KaY: No SAK available to build Distributed SAK parameter set");
15954bc52338SCy Schubert 		return -1;
15964bc52338SCy Schubert 	}
15975b9c547cSRui Paulo 	body->confid_offset = sak->confidentiality_offset;
15985b9c547cSRui Paulo 	body->dan = sak->an;
15995b9c547cSRui Paulo 	body->kn = host_to_be32(sak->key_identifier.kn);
16005b9c547cSRui Paulo 	cs_index = participant->kay->macsec_csindex;
16015b9c547cSRui Paulo 	sak_pos = 0;
1602780fb4a2SCy Schubert 	if (cs_index >= CS_TABLE_SIZE)
1603780fb4a2SCy Schubert 		return -1;
16045b9c547cSRui Paulo 	if (cs_index != DEFAULT_CS_INDEX) {
1605780fb4a2SCy Schubert 		be64 cs;
1606780fb4a2SCy Schubert 
1607780fb4a2SCy Schubert 		cs = host_to_be64(cipher_suite_tbl[cs_index].id);
1608780fb4a2SCy Schubert 		os_memcpy(body->sak, &cs, CS_ID_LEN);
16095b9c547cSRui Paulo 		sak_pos = CS_ID_LEN;
16105b9c547cSRui Paulo 	}
16114bc52338SCy Schubert 	if (aes_wrap(participant->kek.key, participant->kek.len,
16125b9c547cSRui Paulo 		     cipher_suite_tbl[cs_index].sak_len / 8,
16135b9c547cSRui Paulo 		     sak->key, body->sak + sak_pos)) {
16145b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
16155b9c547cSRui Paulo 		return -1;
16165b9c547cSRui Paulo 	}
16175b9c547cSRui Paulo 
16185b9c547cSRui Paulo 	ieee802_1x_mka_dump_dist_sak_body(body);
16195b9c547cSRui Paulo 
16205b9c547cSRui Paulo 	return 0;
16215b9c547cSRui Paulo }
16225b9c547cSRui Paulo 
16235b9c547cSRui Paulo 
16245b9c547cSRui Paulo /**
16255b9c547cSRui Paulo  * ieee802_1x_kay_init_data_key -
16265b9c547cSRui Paulo  */
1627780fb4a2SCy Schubert static void ieee802_1x_kay_init_data_key(struct data_key *pkey)
16285b9c547cSRui Paulo {
1629780fb4a2SCy Schubert 	pkey->transmits = TRUE;
1630780fb4a2SCy Schubert 	pkey->receives = TRUE;
16315b9c547cSRui Paulo 	os_get_time(&pkey->created_time);
16325b9c547cSRui Paulo 
16334bc52338SCy Schubert 	pkey->next_pn = 1;
16345b9c547cSRui Paulo 	pkey->user = 1;
16355b9c547cSRui Paulo }
16365b9c547cSRui Paulo 
16375b9c547cSRui Paulo 
16385b9c547cSRui Paulo /**
16395b9c547cSRui Paulo  * ieee802_1x_kay_decode_dist_sak_body -
16405b9c547cSRui Paulo  */
16415b9c547cSRui Paulo static int
16425b9c547cSRui Paulo ieee802_1x_mka_decode_dist_sak_body(
16435b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
16445b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
16455b9c547cSRui Paulo {
16465b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
16475b9c547cSRui Paulo 	struct ieee802_1x_mka_dist_sak_body *body;
16485b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
16495b9c547cSRui Paulo 	struct macsec_ciphersuite *cs;
16505b9c547cSRui Paulo 	size_t body_len;
16515b9c547cSRui Paulo 	struct data_key *sa_key = NULL;
16525b9c547cSRui Paulo 	int sak_len;
16535b9c547cSRui Paulo 	u8 *wrap_sak;
16545b9c547cSRui Paulo 	u8 *unwrap_sak;
1655780fb4a2SCy Schubert 	struct ieee802_1x_kay *kay = participant->kay;
16565b9c547cSRui Paulo 
16575b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
16585b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
16595b9c547cSRui Paulo 	if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
16605b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1661780fb4a2SCy Schubert 			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets",
1662780fb4a2SCy Schubert 			   body_len);
16635b9c547cSRui Paulo 		return -1;
16645b9c547cSRui Paulo 	}
16655b9c547cSRui Paulo 
16665b9c547cSRui Paulo 	if (!participant->principal) {
16675b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16685b9c547cSRui Paulo 			   "KaY: I can't accept the distributed SAK as I am not principal");
16695b9c547cSRui Paulo 		return -1;
16705b9c547cSRui Paulo 	}
16715b9c547cSRui Paulo 	if (participant->is_key_server) {
16725b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16734bc52338SCy Schubert 			   "KaY: Reject distributed SAK since I'm a key server");
16745b9c547cSRui Paulo 		return -1;
16755b9c547cSRui Paulo 	}
1676780fb4a2SCy Schubert 	if (!kay->macsec_desired ||
1677780fb4a2SCy Schubert 	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
16785b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16795b9c547cSRui Paulo 			   "KaY: I am not MACsec-desired or without MACsec capable");
16805b9c547cSRui Paulo 		return -1;
16815b9c547cSRui Paulo 	}
16825b9c547cSRui Paulo 
16835b9c547cSRui Paulo 	peer = ieee802_1x_kay_get_live_peer(participant,
16845b9c547cSRui Paulo 					    participant->current_peer_id.mi);
16855b9c547cSRui Paulo 	if (!peer) {
16865b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16875b9c547cSRui Paulo 			   "KaY: The key server is not in my live peers list");
16885b9c547cSRui Paulo 		return -1;
16895b9c547cSRui Paulo 	}
1690780fb4a2SCy Schubert 	if (!sci_equal(&kay->key_server_sci, &peer->sci)) {
16915b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
16925b9c547cSRui Paulo 		return -1;
16935b9c547cSRui Paulo 	}
1694780fb4a2SCy Schubert 
16955b9c547cSRui Paulo 	if (body_len == 0) {
1696780fb4a2SCy Schubert 		kay->authenticated = TRUE;
1697780fb4a2SCy Schubert 		kay->secured = FALSE;
1698780fb4a2SCy Schubert 		kay->failed = FALSE;
16995b9c547cSRui Paulo 		participant->advised_desired = FALSE;
1700780fb4a2SCy Schubert 		ieee802_1x_cp_connect_authenticated(kay->cp);
1701780fb4a2SCy Schubert 		ieee802_1x_cp_sm_step(kay->cp);
17025b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec");
170385732ac8SCy Schubert 		participant->to_use_sak = FALSE;
17045b9c547cSRui Paulo 		return 0;
17055b9c547cSRui Paulo 	}
1706780fb4a2SCy Schubert 
17075b9c547cSRui Paulo 	participant->advised_desired = TRUE;
1708780fb4a2SCy Schubert 	kay->authenticated = FALSE;
1709780fb4a2SCy Schubert 	kay->secured = TRUE;
1710780fb4a2SCy Schubert 	kay->failed = FALSE;
1711780fb4a2SCy Schubert 	ieee802_1x_cp_connect_secure(kay->cp);
1712780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
17135b9c547cSRui Paulo 
17145b9c547cSRui Paulo 	body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
17155b9c547cSRui Paulo 	ieee802_1x_mka_dump_dist_sak_body(body);
17165b9c547cSRui Paulo 	dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
17175b9c547cSRui Paulo 	{
17185b9c547cSRui Paulo 		if (os_memcmp(sa_key->key_identifier.mi,
17195b9c547cSRui Paulo 			      participant->current_peer_id.mi, MI_LEN) == 0 &&
17205b9c547cSRui Paulo 		    sa_key->key_identifier.kn == be_to_host32(body->kn)) {
17214bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
17224bc52338SCy Schubert 				   "KaY: SAK has already been installed - do not set it again");
17235b9c547cSRui Paulo 			return 0;
17245b9c547cSRui Paulo 		}
17255b9c547cSRui Paulo 	}
1726780fb4a2SCy Schubert 
17275b9c547cSRui Paulo 	if (body_len == 28) {
17285b9c547cSRui Paulo 		sak_len = DEFAULT_SA_KEY_LEN;
17295b9c547cSRui Paulo 		wrap_sak =  body->sak;
1730780fb4a2SCy Schubert 		kay->macsec_csindex = DEFAULT_CS_INDEX;
1731780fb4a2SCy Schubert 		cs = &cipher_suite_tbl[kay->macsec_csindex];
17325b9c547cSRui Paulo 	} else {
17334bc52338SCy Schubert 		unsigned int idx;
17344bc52338SCy Schubert 
17354bc52338SCy Schubert 		cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak,
17364bc52338SCy Schubert 						     &idx);
17375b9c547cSRui Paulo 		if (!cs) {
17385b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
17395b9c547cSRui Paulo 				   "KaY: I can't support the Cipher Suite advised by key server");
17405b9c547cSRui Paulo 			return -1;
17415b9c547cSRui Paulo 		}
17425b9c547cSRui Paulo 		sak_len = cs->sak_len;
17435b9c547cSRui Paulo 		wrap_sak = body->sak + CS_ID_LEN;
17444bc52338SCy Schubert 		kay->macsec_csindex = idx;
17455b9c547cSRui Paulo 	}
17465b9c547cSRui Paulo 
17475b9c547cSRui Paulo 	unwrap_sak = os_zalloc(sak_len);
17485b9c547cSRui Paulo 	if (!unwrap_sak) {
17495b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
17505b9c547cSRui Paulo 		return -1;
17515b9c547cSRui Paulo 	}
17524bc52338SCy Schubert 	if (aes_unwrap(participant->kek.key, participant->kek.len,
17534bc52338SCy Schubert 		       sak_len >> 3, wrap_sak, unwrap_sak)) {
17545b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
17555b9c547cSRui Paulo 		os_free(unwrap_sak);
17565b9c547cSRui Paulo 		return -1;
17575b9c547cSRui Paulo 	}
17584bc52338SCy Schubert 	wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:",
175985732ac8SCy Schubert 			unwrap_sak, sak_len);
17605b9c547cSRui Paulo 
1761780fb4a2SCy Schubert 	sa_key = os_zalloc(sizeof(*sa_key));
17625b9c547cSRui Paulo 	if (!sa_key) {
17635b9c547cSRui Paulo 		os_free(unwrap_sak);
17645b9c547cSRui Paulo 		return -1;
17655b9c547cSRui Paulo 	}
17665b9c547cSRui Paulo 
1767780fb4a2SCy Schubert 	os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi,
1768780fb4a2SCy Schubert 		  MI_LEN);
1769780fb4a2SCy Schubert 	sa_key->key_identifier.kn = be_to_host32(body->kn);
1770780fb4a2SCy Schubert 
1771780fb4a2SCy Schubert 	sa_key->key = unwrap_sak;
1772780fb4a2SCy Schubert 	sa_key->key_len = sak_len;
1773780fb4a2SCy Schubert 
1774780fb4a2SCy Schubert 	sa_key->confidentiality_offset = body->confid_offset;
1775780fb4a2SCy Schubert 	sa_key->an = body->dan;
1776780fb4a2SCy Schubert 	ieee802_1x_kay_init_data_key(sa_key);
1777780fb4a2SCy Schubert 
177885732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(sa_key);
17795b9c547cSRui Paulo 	dl_list_add(&participant->sak_list, &sa_key->list);
17805b9c547cSRui Paulo 
1781780fb4a2SCy Schubert 	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
1782780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
1783780fb4a2SCy Schubert 	ieee802_1x_cp_set_offset(kay->cp, body->confid_offset);
1784780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
1785780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
1786780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedan(kay->cp, body->dan);
1787780fb4a2SCy Schubert 	ieee802_1x_cp_signal_newsak(kay->cp);
1788780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
17895b9c547cSRui Paulo 
179085732ac8SCy Schubert 	kay->rcvd_keys++;
17915b9c547cSRui Paulo 	participant->to_use_sak = TRUE;
17925b9c547cSRui Paulo 
17935b9c547cSRui Paulo 	return 0;
17945b9c547cSRui Paulo }
17955b9c547cSRui Paulo 
17965b9c547cSRui Paulo 
17975b9c547cSRui Paulo /**
17985b9c547cSRui Paulo  * ieee802_1x_mka_icv_body_present
17995b9c547cSRui Paulo  */
18005b9c547cSRui Paulo static Boolean
18015b9c547cSRui Paulo ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
18025b9c547cSRui Paulo {
18035b9c547cSRui Paulo 	return TRUE;
18045b9c547cSRui Paulo }
18055b9c547cSRui Paulo 
18065b9c547cSRui Paulo 
18075b9c547cSRui Paulo /**
18085b9c547cSRui Paulo  * ieee802_1x_kay_get_icv_length
18095b9c547cSRui Paulo  */
18105b9c547cSRui Paulo static int
18115b9c547cSRui Paulo ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
18125b9c547cSRui Paulo {
18135b9c547cSRui Paulo 	int length;
18145b9c547cSRui Paulo 
18154bc52338SCy Schubert 	/* Determine if we need space for the ICV Indicator */
18164bc52338SCy Schubert 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
18174bc52338SCy Schubert 	    DEFAULT_ICV_LEN)
18185b9c547cSRui Paulo 		length = sizeof(struct ieee802_1x_mka_icv_body);
18194bc52338SCy Schubert 	else
18204bc52338SCy Schubert 		length = 0;
18215b9c547cSRui Paulo 	length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
18225b9c547cSRui Paulo 
1823780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
18245b9c547cSRui Paulo }
18255b9c547cSRui Paulo 
18265b9c547cSRui Paulo 
18275b9c547cSRui Paulo /**
18285b9c547cSRui Paulo  * ieee802_1x_mka_encode_icv_body -
18295b9c547cSRui Paulo  */
18305b9c547cSRui Paulo static int
18315b9c547cSRui Paulo ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
18325b9c547cSRui Paulo 			       struct wpabuf *buf)
18335b9c547cSRui Paulo {
18345b9c547cSRui Paulo 	struct ieee802_1x_mka_icv_body *body;
18355b9c547cSRui Paulo 	unsigned int length;
18365b9c547cSRui Paulo 	u8 cmac[MAX_ICV_LEN];
18375b9c547cSRui Paulo 
18385b9c547cSRui Paulo 	length = ieee802_1x_mka_get_icv_length(participant);
18394bc52338SCy Schubert 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
18404bc52338SCy Schubert 	    DEFAULT_ICV_LEN)  {
18414bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: ICV Indicator");
18425b9c547cSRui Paulo 		body = wpabuf_put(buf, MKA_HDR_LEN);
18435b9c547cSRui Paulo 		body->type = MKA_ICV_INDICATOR;
18444bc52338SCy Schubert 		length -= MKA_HDR_LEN;
18454bc52338SCy Schubert 		set_mka_param_body_len(body, length);
18465b9c547cSRui Paulo 	}
18475b9c547cSRui Paulo 
18485b9c547cSRui Paulo 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
18494bc52338SCy Schubert 		    participant->ick.key, participant->ick.len,
18504bc52338SCy Schubert 		    wpabuf_head(buf), wpabuf_len(buf), cmac)) {
18514bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV");
18525b9c547cSRui Paulo 		return -1;
18535b9c547cSRui Paulo 	}
18544bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length);
18555b9c547cSRui Paulo 
18565b9c547cSRui Paulo 	os_memcpy(wpabuf_put(buf, length), cmac, length);
18575b9c547cSRui Paulo 
18585b9c547cSRui Paulo 	return 0;
18595b9c547cSRui Paulo }
18605b9c547cSRui Paulo 
18615b9c547cSRui Paulo /**
18625b9c547cSRui Paulo  * ieee802_1x_mka_decode_icv_body -
18635b9c547cSRui Paulo  */
18644bc52338SCy Schubert static const u8 *
18655b9c547cSRui Paulo ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
18665b9c547cSRui Paulo 			       const u8 *mka_msg, size_t msg_len)
18675b9c547cSRui Paulo {
18684bc52338SCy Schubert 	const struct ieee802_1x_mka_hdr *hdr;
18694bc52338SCy Schubert 	const struct ieee802_1x_mka_icv_body *body;
18705b9c547cSRui Paulo 	size_t body_len;
18715b9c547cSRui Paulo 	size_t left_len;
1872780fb4a2SCy Schubert 	u8 body_type;
18735b9c547cSRui Paulo 	const u8 *pos;
18745b9c547cSRui Paulo 
18755b9c547cSRui Paulo 	pos = mka_msg;
18765b9c547cSRui Paulo 	left_len = msg_len;
18774bc52338SCy Schubert 	while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
18784bc52338SCy Schubert 		hdr = (const struct ieee802_1x_mka_hdr *) pos;
18794bc52338SCy Schubert 		body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
18805b9c547cSRui Paulo 		body_type = get_mka_param_body_type(hdr);
18815b9c547cSRui Paulo 
18824bc52338SCy Schubert 		if (left_len < body_len + MKA_HDR_LEN)
18835b9c547cSRui Paulo 			break;
18845b9c547cSRui Paulo 
18855b9c547cSRui Paulo 		if (body_type != MKA_ICV_INDICATOR) {
18865b9c547cSRui Paulo 			left_len -= MKA_HDR_LEN + body_len;
18875b9c547cSRui Paulo 			pos += MKA_HDR_LEN + body_len;
18885b9c547cSRui Paulo 			continue;
18895b9c547cSRui Paulo 		}
18905b9c547cSRui Paulo 
18914bc52338SCy Schubert 		body = (const struct ieee802_1x_mka_icv_body *) pos;
18925b9c547cSRui Paulo 		if (body_len
18934bc52338SCy Schubert 		    < mka_alg_tbl[participant->kay->mka_algindex].icv_len)
18945b9c547cSRui Paulo 			return NULL;
18955b9c547cSRui Paulo 
18965b9c547cSRui Paulo 		return body->icv;
18975b9c547cSRui Paulo 	}
18985b9c547cSRui Paulo 
18994bc52338SCy Schubert 	return mka_msg + msg_len - DEFAULT_ICV_LEN;
19005b9c547cSRui Paulo }
19015b9c547cSRui Paulo 
19025b9c547cSRui Paulo 
19035b9c547cSRui Paulo /**
19045b9c547cSRui Paulo  * ieee802_1x_mka_decode_dist_cak_body-
19055b9c547cSRui Paulo  */
19065b9c547cSRui Paulo static int
19075b9c547cSRui Paulo ieee802_1x_mka_decode_dist_cak_body(
19085b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
19095b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
19105b9c547cSRui Paulo {
19115b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
19125b9c547cSRui Paulo 	size_t body_len;
19135b9c547cSRui Paulo 
19145b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
19155b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
19165b9c547cSRui Paulo 	if (body_len < 28) {
19175b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
19184bc52338SCy Schubert 			   "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets",
1919780fb4a2SCy Schubert 			   body_len);
19205b9c547cSRui Paulo 		return -1;
19215b9c547cSRui Paulo 	}
19225b9c547cSRui Paulo 
19235b9c547cSRui Paulo 	return 0;
19245b9c547cSRui Paulo }
19255b9c547cSRui Paulo 
19265b9c547cSRui Paulo 
19275b9c547cSRui Paulo /**
19285b9c547cSRui Paulo  * ieee802_1x_mka_decode_kmd_body -
19295b9c547cSRui Paulo  */
19305b9c547cSRui Paulo static int
19315b9c547cSRui Paulo ieee802_1x_mka_decode_kmd_body(
19325b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
19335b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
19345b9c547cSRui Paulo {
19355b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
19365b9c547cSRui Paulo 	size_t body_len;
19375b9c547cSRui Paulo 
19385b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
19395b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
19405b9c547cSRui Paulo 	if (body_len < 5) {
19415b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
19424bc52338SCy Schubert 			   "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets",
1943780fb4a2SCy Schubert 			   body_len);
19445b9c547cSRui Paulo 		return -1;
19455b9c547cSRui Paulo 	}
19465b9c547cSRui Paulo 
19475b9c547cSRui Paulo 	return 0;
19485b9c547cSRui Paulo }
19495b9c547cSRui Paulo 
19505b9c547cSRui Paulo 
19515b9c547cSRui Paulo /**
19525b9c547cSRui Paulo  * ieee802_1x_mka_decode_announce_body -
19535b9c547cSRui Paulo  */
19545b9c547cSRui Paulo static int ieee802_1x_mka_decode_announce_body(
19555b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
19565b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
19575b9c547cSRui Paulo {
19585b9c547cSRui Paulo 	return 0;
19595b9c547cSRui Paulo }
19605b9c547cSRui Paulo 
19615b9c547cSRui Paulo 
1962780fb4a2SCy Schubert struct mka_param_body_handler {
1963780fb4a2SCy Schubert 	int (*body_tx)(struct ieee802_1x_mka_participant *participant,
1964780fb4a2SCy Schubert 		       struct wpabuf *buf);
1965780fb4a2SCy Schubert 	int (*body_rx)(struct ieee802_1x_mka_participant *participant,
1966780fb4a2SCy Schubert 		       const u8 *mka_msg, size_t msg_len);
1967780fb4a2SCy Schubert 	int (*body_length)(struct ieee802_1x_mka_participant *participant);
1968780fb4a2SCy Schubert 	Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
1969780fb4a2SCy Schubert };
1970780fb4a2SCy Schubert 
1971780fb4a2SCy Schubert 
1972780fb4a2SCy Schubert static struct mka_param_body_handler mka_body_handler[] = {
19734bc52338SCy Schubert 	/* Basic parameter set */
19745b9c547cSRui Paulo 	{
1975780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_basic_body,
1976780fb4a2SCy Schubert 		.body_rx      = NULL,
1977780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_basic_body_length,
1978780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_basic_body_present
19795b9c547cSRui Paulo 	},
19805b9c547cSRui Paulo 
19814bc52338SCy Schubert 	/* Live Peer List parameter set */
19825b9c547cSRui Paulo 	{
1983780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_live_peer_body,
1984780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_live_peer_body,
1985780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_live_peer_length,
1986780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_live_peer_body_present
19875b9c547cSRui Paulo 	},
19885b9c547cSRui Paulo 
19894bc52338SCy Schubert 	/* Potential Peer List parameter set */
19905b9c547cSRui Paulo 	{
1991780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_potential_peer_body,
1992780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_potential_peer_body,
1993780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_potential_peer_length,
1994780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_potential_peer_body_present
19955b9c547cSRui Paulo 	},
19965b9c547cSRui Paulo 
19974bc52338SCy Schubert 	/* MACsec SAK Use parameter set */
19985b9c547cSRui Paulo 	{
1999780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_sak_use_body,
2000780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_sak_use_body,
2001780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_sak_use_length,
2002780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_sak_use_body_present
20035b9c547cSRui Paulo 	},
20045b9c547cSRui Paulo 
20054bc52338SCy Schubert 	/* Distributed SAK parameter set */
20065b9c547cSRui Paulo 	{
2007780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_dist_sak_body,
2008780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_dist_sak_body,
2009780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_dist_sak_length,
2010780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_dist_sak_body_present
20115b9c547cSRui Paulo 	},
20125b9c547cSRui Paulo 
20134bc52338SCy Schubert 	/* Distribute CAK parameter set */
20145b9c547cSRui Paulo 	{
2015780fb4a2SCy Schubert 		.body_tx      = NULL,
2016780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_dist_cak_body,
2017780fb4a2SCy Schubert 		.body_length  = NULL,
2018780fb4a2SCy Schubert 		.body_present = NULL
20195b9c547cSRui Paulo 	},
20205b9c547cSRui Paulo 
20214bc52338SCy Schubert 	/* KMD parameter set */
20225b9c547cSRui Paulo 	{
2023780fb4a2SCy Schubert 		.body_tx      = NULL,
2024780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_kmd_body,
2025780fb4a2SCy Schubert 		.body_length  = NULL,
2026780fb4a2SCy Schubert 		.body_present = NULL
20275b9c547cSRui Paulo 	},
20285b9c547cSRui Paulo 
20294bc52338SCy Schubert 	/* Announcement parameter set */
20305b9c547cSRui Paulo 	{
2031780fb4a2SCy Schubert 		.body_tx      = NULL,
2032780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_announce_body,
2033780fb4a2SCy Schubert 		.body_length  = NULL,
2034780fb4a2SCy Schubert 		.body_present = NULL
20355b9c547cSRui Paulo 	},
20365b9c547cSRui Paulo 
20374bc52338SCy Schubert 	/* ICV Indicator parameter set */
20385b9c547cSRui Paulo 	{
2039780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_icv_body,
2040780fb4a2SCy Schubert 		.body_rx      = NULL,
2041780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_icv_length,
2042780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_icv_body_present
20435b9c547cSRui Paulo 	},
20445b9c547cSRui Paulo };
20455b9c547cSRui Paulo 
20465b9c547cSRui Paulo 
20475b9c547cSRui Paulo /**
204885732ac8SCy Schubert  * ieee802_1x_kay_use_data_key - Take reference on a key
204985732ac8SCy Schubert  */
205085732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
205185732ac8SCy Schubert {
205285732ac8SCy Schubert 	pkey->user++;
205385732ac8SCy Schubert }
205485732ac8SCy Schubert 
205585732ac8SCy Schubert 
205685732ac8SCy Schubert /**
205785732ac8SCy Schubert  * ieee802_1x_kay_deinit_data_key - Release reference on a key and
205885732ac8SCy Schubert  * free if there are no remaining users
20595b9c547cSRui Paulo  */
2060780fb4a2SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
20615b9c547cSRui Paulo {
20625b9c547cSRui Paulo 	if (!pkey)
20635b9c547cSRui Paulo 		return;
20645b9c547cSRui Paulo 
20655b9c547cSRui Paulo 	pkey->user--;
20665b9c547cSRui Paulo 	if (pkey->user > 1)
20675b9c547cSRui Paulo 		return;
20685b9c547cSRui Paulo 
20695b9c547cSRui Paulo 	os_free(pkey->key);
20705b9c547cSRui Paulo 	os_free(pkey);
20715b9c547cSRui Paulo }
20725b9c547cSRui Paulo 
20735b9c547cSRui Paulo 
20745b9c547cSRui Paulo /**
20755b9c547cSRui Paulo  * ieee802_1x_kay_generate_new_sak -
20765b9c547cSRui Paulo  */
20775b9c547cSRui Paulo static int
20785b9c547cSRui Paulo ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
20795b9c547cSRui Paulo {
20805b9c547cSRui Paulo 	struct data_key *sa_key = NULL;
20815b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
20825b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
20835b9c547cSRui Paulo 	int ctx_len, ctx_offset;
20845b9c547cSRui Paulo 	u8 *context;
2085780fb4a2SCy Schubert 	unsigned int key_len;
2086780fb4a2SCy Schubert 	u8 *key;
2087780fb4a2SCy Schubert 	struct macsec_ciphersuite *cs;
20885b9c547cSRui Paulo 
20895b9c547cSRui Paulo 	/* check condition for generating a fresh SAK:
20905b9c547cSRui Paulo 	 * must have one live peer
20915b9c547cSRui Paulo 	 * and MKA life time elapse since last distribution
20925b9c547cSRui Paulo 	 * or potential peer is empty
20935b9c547cSRui Paulo 	 */
20945b9c547cSRui Paulo 	if (dl_list_empty(&participant->live_peers)) {
20955b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
20964bc52338SCy Schubert 			   "KaY: Live peers list must not be empty when generating fresh SAK");
20975b9c547cSRui Paulo 		return -1;
20985b9c547cSRui Paulo 	}
20995b9c547cSRui Paulo 
21005b9c547cSRui Paulo 	/* FIXME: A fresh SAK not generated until
21015b9c547cSRui Paulo 	 * the live peer list contains at least one peer and
21025b9c547cSRui Paulo 	 * MKA life time has elapsed since the prior SAK was first distributed,
21035b9c547cSRui Paulo 	 * or the Key server's potential peer is empty
21045b9c547cSRui Paulo 	 * but I can't understand the second item, so
21055b9c547cSRui Paulo 	 * here only check first item and ingore
21065b9c547cSRui Paulo 	 *   && (!dl_list_empty(&participant->potential_peers))) {
21075b9c547cSRui Paulo 	 */
21085b9c547cSRui Paulo 	if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
21095b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
21104bc52338SCy Schubert 			   "KaY: Life time has not elapsed since prior SAK distributed");
21115b9c547cSRui Paulo 		return -1;
21125b9c547cSRui Paulo 	}
21135b9c547cSRui Paulo 
2114780fb4a2SCy Schubert 	cs = &cipher_suite_tbl[kay->macsec_csindex];
2115780fb4a2SCy Schubert 	key_len = cs->sak_len;
2116780fb4a2SCy Schubert 	key = os_zalloc(key_len);
2117780fb4a2SCy Schubert 	if (!key) {
21185b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
21195b9c547cSRui Paulo 		return -1;
21205b9c547cSRui Paulo 	}
21215b9c547cSRui Paulo 
2122780fb4a2SCy Schubert 	ctx_len = key_len + sizeof(kay->dist_kn);
21235b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
21245b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list)
21255b9c547cSRui Paulo 		ctx_len += sizeof(peer->mi);
21265b9c547cSRui Paulo 	ctx_len += sizeof(participant->mi);
21275b9c547cSRui Paulo 
21285b9c547cSRui Paulo 	context = os_zalloc(ctx_len);
2129780fb4a2SCy Schubert 	if (!context)
2130780fb4a2SCy Schubert 		goto fail;
2131780fb4a2SCy Schubert 
21325b9c547cSRui Paulo 	ctx_offset = 0;
2133780fb4a2SCy Schubert 	if (os_get_random(context + ctx_offset, key_len) < 0)
2134780fb4a2SCy Schubert 		goto fail;
2135780fb4a2SCy Schubert 
2136780fb4a2SCy Schubert 	ctx_offset += key_len;
21375b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
21385b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
21395b9c547cSRui Paulo 		os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
21405b9c547cSRui Paulo 		ctx_offset += sizeof(peer->mi);
21415b9c547cSRui Paulo 	}
21425b9c547cSRui Paulo 	os_memcpy(context + ctx_offset, participant->mi,
21435b9c547cSRui Paulo 		  sizeof(participant->mi));
21445b9c547cSRui Paulo 	ctx_offset += sizeof(participant->mi);
21455b9c547cSRui Paulo 	os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
21465b9c547cSRui Paulo 
21474bc52338SCy Schubert 	if (key_len == 16 || key_len == 32) {
21484bc52338SCy Schubert 		if (ieee802_1x_sak_aes_cmac(participant->cak.key,
21494bc52338SCy Schubert 					    participant->cak.len,
21504bc52338SCy Schubert 					    context, ctx_len,
21514bc52338SCy Schubert 					    key, key_len)) {
21524bc52338SCy Schubert 			wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK");
21534bc52338SCy Schubert 			goto fail;
21544bc52338SCy Schubert 		}
21555b9c547cSRui Paulo 	} else {
21564bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported",
21574bc52338SCy Schubert 			   key_len);
2158780fb4a2SCy Schubert 		goto fail;
21595b9c547cSRui Paulo 	}
216085732ac8SCy Schubert 	wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
2161780fb4a2SCy Schubert 	os_free(context);
2162780fb4a2SCy Schubert 	context = NULL;
21635b9c547cSRui Paulo 
2164780fb4a2SCy Schubert 	sa_key = os_zalloc(sizeof(*sa_key));
21655b9c547cSRui Paulo 	if (!sa_key) {
2166780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
2167780fb4a2SCy Schubert 		goto fail;
21685b9c547cSRui Paulo 	}
2169780fb4a2SCy Schubert 
2170780fb4a2SCy Schubert 	sa_key->key = key;
2171780fb4a2SCy Schubert 	sa_key->key_len = key_len;
2172780fb4a2SCy Schubert 	os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN);
2173780fb4a2SCy Schubert 	sa_key->key_identifier.kn = kay->dist_kn;
2174780fb4a2SCy Schubert 
2175780fb4a2SCy Schubert 	sa_key->confidentiality_offset = kay->macsec_confidentiality;
2176780fb4a2SCy Schubert 	sa_key->an = kay->dist_an;
2177780fb4a2SCy Schubert 	ieee802_1x_kay_init_data_key(sa_key);
2178780fb4a2SCy Schubert 
21795b9c547cSRui Paulo 	participant->new_key = sa_key;
21805b9c547cSRui Paulo 
218185732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(sa_key);
21825b9c547cSRui Paulo 	dl_list_add(&participant->sak_list, &sa_key->list);
218385732ac8SCy Schubert 
2184780fb4a2SCy Schubert 	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
21855b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(kay->cp);
2186780fb4a2SCy Schubert 	ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
21875b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(kay->cp);
2188780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
2189780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an);
21905b9c547cSRui Paulo 	ieee802_1x_cp_signal_newsak(kay->cp);
21915b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(kay->cp);
21925b9c547cSRui Paulo 
21935b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
21945b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list)
21955b9c547cSRui Paulo 		peer->sak_used = FALSE;
21965b9c547cSRui Paulo 
2197780fb4a2SCy Schubert 	kay->dist_kn++;
2198780fb4a2SCy Schubert 	kay->dist_an++;
2199780fb4a2SCy Schubert 	if (kay->dist_an > 3)
2200780fb4a2SCy Schubert 		kay->dist_an = 0;
22015b9c547cSRui Paulo 
2202780fb4a2SCy Schubert 	kay->dist_time = time(NULL);
22035b9c547cSRui Paulo 
22045b9c547cSRui Paulo 	return 0;
2205780fb4a2SCy Schubert 
2206780fb4a2SCy Schubert fail:
2207780fb4a2SCy Schubert 	os_free(key);
2208780fb4a2SCy Schubert 	os_free(context);
2209780fb4a2SCy Schubert 	return -1;
2210780fb4a2SCy Schubert }
2211780fb4a2SCy Schubert 
2212780fb4a2SCy Schubert 
2213780fb4a2SCy Schubert static int compare_priorities(const struct ieee802_1x_kay_peer *peer,
2214780fb4a2SCy Schubert 			      const struct ieee802_1x_kay_peer *other)
2215780fb4a2SCy Schubert {
2216780fb4a2SCy Schubert 	if (peer->key_server_priority < other->key_server_priority)
2217780fb4a2SCy Schubert 		return -1;
2218780fb4a2SCy Schubert 	if (other->key_server_priority < peer->key_server_priority)
2219780fb4a2SCy Schubert 		return 1;
2220780fb4a2SCy Schubert 
2221780fb4a2SCy Schubert 	return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN);
22225b9c547cSRui Paulo }
22235b9c547cSRui Paulo 
22245b9c547cSRui Paulo 
22255b9c547cSRui Paulo /**
22265b9c547cSRui Paulo  * ieee802_1x_kay_elect_key_server - elect the key server
22275b9c547cSRui Paulo  * when to elect: whenever the live peers list changes
22285b9c547cSRui Paulo  */
22295b9c547cSRui Paulo static int
22305b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
22315b9c547cSRui Paulo {
22325b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
22335b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *key_server = NULL;
22345b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
22355b9c547cSRui Paulo 	Boolean i_is_key_server;
223685732ac8SCy Schubert 	int priority_comparison;
22375b9c547cSRui Paulo 
22385b9c547cSRui Paulo 	if (participant->is_obliged_key_server) {
22395b9c547cSRui Paulo 		participant->new_sak = TRUE;
22405b9c547cSRui Paulo 		participant->to_dist_sak = FALSE;
22415b9c547cSRui Paulo 		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
22425b9c547cSRui Paulo 		return 0;
22435b9c547cSRui Paulo 	}
22445b9c547cSRui Paulo 
22455b9c547cSRui Paulo 	/* elect the key server among the peers */
22465b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
22475b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
22485b9c547cSRui Paulo 		if (!peer->is_key_server)
22495b9c547cSRui Paulo 			continue;
22505b9c547cSRui Paulo 
22515b9c547cSRui Paulo 		if (!key_server) {
22525b9c547cSRui Paulo 			key_server = peer;
22535b9c547cSRui Paulo 			continue;
22545b9c547cSRui Paulo 		}
22555b9c547cSRui Paulo 
2256780fb4a2SCy Schubert 		if (compare_priorities(peer, key_server) < 0)
22575b9c547cSRui Paulo 			key_server = peer;
22585b9c547cSRui Paulo 	}
22595b9c547cSRui Paulo 
22605b9c547cSRui Paulo 	/* elect the key server between me and the above elected peer */
22615b9c547cSRui Paulo 	i_is_key_server = FALSE;
22625b9c547cSRui Paulo 	if (key_server && participant->can_be_key_server) {
2263780fb4a2SCy Schubert 		struct ieee802_1x_kay_peer tmp;
22645b9c547cSRui Paulo 
2265780fb4a2SCy Schubert 		tmp.key_server_priority = kay->actor_priority;
2266780fb4a2SCy Schubert 		os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
226785732ac8SCy Schubert 		priority_comparison = compare_priorities(&tmp, key_server);
226885732ac8SCy Schubert 		if (priority_comparison < 0) {
2269780fb4a2SCy Schubert 			i_is_key_server = TRUE;
227085732ac8SCy Schubert 		} else if (priority_comparison == 0) {
227185732ac8SCy Schubert 			wpa_printf(MSG_WARNING,
227285732ac8SCy Schubert 				   "KaY: Cannot elect key server between me and peer, duplicate MAC detected");
227385732ac8SCy Schubert 			key_server = NULL;
227485732ac8SCy Schubert 		}
2275780fb4a2SCy Schubert 	} else if (participant->can_be_key_server) {
2276780fb4a2SCy Schubert 		i_is_key_server = TRUE;
22775b9c547cSRui Paulo 	}
22785b9c547cSRui Paulo 
22795b9c547cSRui Paulo 	if (i_is_key_server) {
22805b9c547cSRui Paulo 		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
2281780fb4a2SCy Schubert 		if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) {
22825b9c547cSRui Paulo 			ieee802_1x_cp_signal_chgdserver(kay->cp);
22835b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(kay->cp);
22845b9c547cSRui Paulo 		}
22855b9c547cSRui Paulo 
22865b9c547cSRui Paulo 		participant->is_key_server = TRUE;
22875b9c547cSRui Paulo 		participant->principal = TRUE;
22885b9c547cSRui Paulo 		participant->new_sak = TRUE;
22894bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: I am elected as key server");
22905b9c547cSRui Paulo 		participant->to_dist_sak = FALSE;
22915b9c547cSRui Paulo 		participant->is_elected = TRUE;
22925b9c547cSRui Paulo 
22935b9c547cSRui Paulo 		os_memcpy(&kay->key_server_sci, &kay->actor_sci,
22945b9c547cSRui Paulo 			  sizeof(kay->key_server_sci));
22955b9c547cSRui Paulo 		kay->key_server_priority = kay->actor_priority;
2296780fb4a2SCy Schubert 	} else if (key_server) {
22974bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
22984bc52338SCy Schubert 			   "KaY: Peer %s was elected as the key server",
22994bc52338SCy Schubert 			   mi_txt(key_server->mi));
23005b9c547cSRui Paulo 		ieee802_1x_cp_set_electedself(kay->cp, FALSE);
2301780fb4a2SCy Schubert 		if (!sci_equal(&kay->key_server_sci, &key_server->sci)) {
23025b9c547cSRui Paulo 			ieee802_1x_cp_signal_chgdserver(kay->cp);
23035b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(kay->cp);
23045b9c547cSRui Paulo 		}
23055b9c547cSRui Paulo 
23065b9c547cSRui Paulo 		participant->is_key_server = FALSE;
23075b9c547cSRui Paulo 		participant->principal = TRUE;
23085b9c547cSRui Paulo 		participant->is_elected = TRUE;
23095b9c547cSRui Paulo 
23105b9c547cSRui Paulo 		os_memcpy(&kay->key_server_sci, &key_server->sci,
23115b9c547cSRui Paulo 			  sizeof(kay->key_server_sci));
23125b9c547cSRui Paulo 		kay->key_server_priority = key_server->key_server_priority;
2313780fb4a2SCy Schubert 	} else {
2314780fb4a2SCy Schubert 		participant->principal = FALSE;
2315780fb4a2SCy Schubert 		participant->is_key_server = FALSE;
2316780fb4a2SCy Schubert 		participant->is_elected = FALSE;
23175b9c547cSRui Paulo 	}
23185b9c547cSRui Paulo 
23195b9c547cSRui Paulo 	return 0;
23205b9c547cSRui Paulo }
23215b9c547cSRui Paulo 
23225b9c547cSRui Paulo 
23235b9c547cSRui Paulo /**
23245b9c547cSRui Paulo  * ieee802_1x_kay_decide_macsec_use - the key server determinate
23255b9c547cSRui Paulo  *		 how to use MACsec: whether use MACsec and its capability
23265b9c547cSRui Paulo  * protectFrames will be advised if the key server and one of its live peers are
23275b9c547cSRui Paulo  * MACsec capable and one of those request MACsec protection
23285b9c547cSRui Paulo  */
23295b9c547cSRui Paulo static int
23305b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(
23315b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
23325b9c547cSRui Paulo {
23335b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
23345b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
23355b9c547cSRui Paulo 	enum macsec_cap less_capability;
23365b9c547cSRui Paulo 	Boolean has_peer;
23375b9c547cSRui Paulo 
23385b9c547cSRui Paulo 	if (!participant->is_key_server)
23395b9c547cSRui Paulo 		return -1;
23405b9c547cSRui Paulo 
23415b9c547cSRui Paulo 	/* key server self is MACsec-desired and requesting MACsec */
23425b9c547cSRui Paulo 	if (!kay->macsec_desired) {
23435b9c547cSRui Paulo 		participant->advised_desired = FALSE;
23445b9c547cSRui Paulo 		return -1;
23455b9c547cSRui Paulo 	}
23465b9c547cSRui Paulo 	if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
23475b9c547cSRui Paulo 		participant->advised_desired = FALSE;
23485b9c547cSRui Paulo 		return -1;
23495b9c547cSRui Paulo 	}
23505b9c547cSRui Paulo 	less_capability = kay->macsec_capable;
23515b9c547cSRui Paulo 
23525b9c547cSRui Paulo 	/* at least one of peers is MACsec-desired and requesting MACsec */
23535b9c547cSRui Paulo 	has_peer = FALSE;
23545b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
23555b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
23565b9c547cSRui Paulo 		if (!peer->macsec_desired)
23575b9c547cSRui Paulo 			continue;
23585b9c547cSRui Paulo 
2359780fb4a2SCy Schubert 		if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED)
23605b9c547cSRui Paulo 			continue;
23615b9c547cSRui Paulo 
2362780fb4a2SCy Schubert 		less_capability = (less_capability < peer->macsec_capability) ?
2363780fb4a2SCy Schubert 			less_capability : peer->macsec_capability;
23645b9c547cSRui Paulo 		has_peer = TRUE;
23655b9c547cSRui Paulo 	}
23665b9c547cSRui Paulo 
23675b9c547cSRui Paulo 	if (has_peer) {
23685b9c547cSRui Paulo 		participant->advised_desired = TRUE;
23695b9c547cSRui Paulo 		participant->advised_capability = less_capability;
23705b9c547cSRui Paulo 		kay->authenticated = FALSE;
23715b9c547cSRui Paulo 		kay->secured = TRUE;
23725b9c547cSRui Paulo 		kay->failed = FALSE;
23735b9c547cSRui Paulo 		ieee802_1x_cp_connect_secure(kay->cp);
23745b9c547cSRui Paulo 		ieee802_1x_cp_sm_step(kay->cp);
23755b9c547cSRui Paulo 	} else {
23765b9c547cSRui Paulo 		participant->advised_desired = FALSE;
23775b9c547cSRui Paulo 		participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
23785b9c547cSRui Paulo 		participant->to_use_sak = FALSE;
23795b9c547cSRui Paulo 		kay->authenticated = TRUE;
23805b9c547cSRui Paulo 		kay->secured = FALSE;
23815b9c547cSRui Paulo 		kay->failed = FALSE;
23825b9c547cSRui Paulo 		kay->ltx_kn = 0;
23835b9c547cSRui Paulo 		kay->ltx_an = 0;
23845b9c547cSRui Paulo 		kay->lrx_kn = 0;
23855b9c547cSRui Paulo 		kay->lrx_an = 0;
23865b9c547cSRui Paulo 		kay->otx_kn = 0;
23875b9c547cSRui Paulo 		kay->otx_an = 0;
23885b9c547cSRui Paulo 		kay->orx_kn = 0;
23895b9c547cSRui Paulo 		kay->orx_an = 0;
23905b9c547cSRui Paulo 		ieee802_1x_cp_connect_authenticated(kay->cp);
23915b9c547cSRui Paulo 		ieee802_1x_cp_sm_step(kay->cp);
23925b9c547cSRui Paulo 	}
23935b9c547cSRui Paulo 
23945b9c547cSRui Paulo 	return 0;
23955b9c547cSRui Paulo }
23965b9c547cSRui Paulo 
23975b9c547cSRui Paulo static const u8 pae_group_addr[ETH_ALEN] = {
23985b9c547cSRui Paulo 	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
23995b9c547cSRui Paulo };
24005b9c547cSRui Paulo 
24015b9c547cSRui Paulo 
24025b9c547cSRui Paulo /**
24035b9c547cSRui Paulo  * ieee802_1x_kay_encode_mkpdu -
24045b9c547cSRui Paulo  */
24055b9c547cSRui Paulo static int
24065b9c547cSRui Paulo ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
24075b9c547cSRui Paulo 			    struct wpabuf *pbuf)
24085b9c547cSRui Paulo {
24095b9c547cSRui Paulo 	unsigned int i;
24105b9c547cSRui Paulo 	struct ieee8023_hdr *ether_hdr;
24115b9c547cSRui Paulo 	struct ieee802_1x_hdr *eapol_hdr;
24125b9c547cSRui Paulo 
24135b9c547cSRui Paulo 	ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
24145b9c547cSRui Paulo 	os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
24155b9c547cSRui Paulo 	os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
24165b9c547cSRui Paulo 		  sizeof(ether_hdr->dest));
24175b9c547cSRui Paulo 	ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
24184bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
24194bc52338SCy Schubert 		   " Ethertype=0x%x",
24204bc52338SCy Schubert 		   MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src),
24214bc52338SCy Schubert 		   be_to_host16(ether_hdr->ethertype));
24225b9c547cSRui Paulo 
24235b9c547cSRui Paulo 	eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
24245b9c547cSRui Paulo 	eapol_hdr->version = EAPOL_VERSION;
24255b9c547cSRui Paulo 	eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
24264bc52338SCy Schubert 	eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf));
24274bc52338SCy Schubert 	wpa_printf(MSG_DEBUG,
24284bc52338SCy Schubert 		   "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
24294bc52338SCy Schubert 		   eapol_hdr->version, eapol_hdr->type,
24304bc52338SCy Schubert 		   be_to_host16(eapol_hdr->length));
24315b9c547cSRui Paulo 
2432780fb4a2SCy Schubert 	for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
2433780fb4a2SCy Schubert 		if (mka_body_handler[i].body_present &&
2434780fb4a2SCy Schubert 		    mka_body_handler[i].body_present(participant)) {
2435780fb4a2SCy Schubert 			if (mka_body_handler[i].body_tx(participant, pbuf))
24365b9c547cSRui Paulo 				return -1;
24375b9c547cSRui Paulo 		}
24385b9c547cSRui Paulo 	}
24395b9c547cSRui Paulo 
24405b9c547cSRui Paulo 	return 0;
24415b9c547cSRui Paulo }
24425b9c547cSRui Paulo 
24434bc52338SCy Schubert 
24445b9c547cSRui Paulo /**
24455b9c547cSRui Paulo  * ieee802_1x_participant_send_mkpdu -
24465b9c547cSRui Paulo  */
24475b9c547cSRui Paulo static int
24485b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(
24495b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
24505b9c547cSRui Paulo {
24515b9c547cSRui Paulo 	struct wpabuf *buf;
24525b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
24535b9c547cSRui Paulo 	size_t length = 0;
24545b9c547cSRui Paulo 	unsigned int i;
24555b9c547cSRui Paulo 
24564bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)",
24574bc52338SCy Schubert 		   kay->if_name);
24585b9c547cSRui Paulo 	length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
2459780fb4a2SCy Schubert 	for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
2460780fb4a2SCy Schubert 		if (mka_body_handler[i].body_present &&
2461780fb4a2SCy Schubert 		    mka_body_handler[i].body_present(participant))
2462780fb4a2SCy Schubert 			length += mka_body_handler[i].body_length(participant);
24635b9c547cSRui Paulo 	}
24645b9c547cSRui Paulo 
24655b9c547cSRui Paulo 	buf = wpabuf_alloc(length);
24665b9c547cSRui Paulo 	if (!buf) {
24675b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: out of memory");
24685b9c547cSRui Paulo 		return -1;
24695b9c547cSRui Paulo 	}
24705b9c547cSRui Paulo 
24715b9c547cSRui Paulo 	if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
24724bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail");
24735b9c547cSRui Paulo 		return -1;
24745b9c547cSRui Paulo 	}
24755b9c547cSRui Paulo 
24764bc52338SCy Schubert 	wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf);
24775b9c547cSRui Paulo 	l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
24785b9c547cSRui Paulo 	wpabuf_free(buf);
24795b9c547cSRui Paulo 
24805b9c547cSRui Paulo 	kay->active = TRUE;
24815b9c547cSRui Paulo 	participant->active = TRUE;
24825b9c547cSRui Paulo 
24835b9c547cSRui Paulo 	return 0;
24845b9c547cSRui Paulo }
24855b9c547cSRui Paulo 
24865b9c547cSRui Paulo 
24875b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
248885732ac8SCy Schubert 
248985732ac8SCy Schubert static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
249085732ac8SCy Schubert 					  struct transmit_sa *sa)
249185732ac8SCy Schubert {
249285732ac8SCy Schubert 	secy_disable_transmit_sa(kay, sa);
249385732ac8SCy Schubert 	secy_delete_transmit_sa(kay, sa);
249485732ac8SCy Schubert 	ieee802_1x_kay_deinit_transmit_sa(sa);
249585732ac8SCy Schubert }
249685732ac8SCy Schubert 
249785732ac8SCy Schubert 
24985b9c547cSRui Paulo /**
24995b9c547cSRui Paulo  * ieee802_1x_participant_timer -
25005b9c547cSRui Paulo  */
25015b9c547cSRui Paulo static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
25025b9c547cSRui Paulo {
25035b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
25045b9c547cSRui Paulo 	struct ieee802_1x_kay *kay;
25055b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer, *pre_peer;
25065b9c547cSRui Paulo 	time_t now = time(NULL);
25075b9c547cSRui Paulo 	Boolean lp_changed;
25085b9c547cSRui Paulo 	struct receive_sc *rxsc, *pre_rxsc;
25095b9c547cSRui Paulo 	struct transmit_sa *txsa, *pre_txsa;
25105b9c547cSRui Paulo 
25115b9c547cSRui Paulo 	participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
25125b9c547cSRui Paulo 	kay = participant->kay;
25134bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)",
25144bc52338SCy Schubert 		   kay->if_name);
25155b9c547cSRui Paulo 	if (participant->cak_life) {
2516780fb4a2SCy Schubert 		if (now > participant->cak_life)
2517780fb4a2SCy Schubert 			goto delete_mka;
25185b9c547cSRui Paulo 	}
25195b9c547cSRui Paulo 
25205b9c547cSRui Paulo 	/* should delete MKA instance if there are not live peers
25215b9c547cSRui Paulo 	 * when the MKA life elapsed since its creating */
25225b9c547cSRui Paulo 	if (participant->mka_life) {
25235b9c547cSRui Paulo 		if (dl_list_empty(&participant->live_peers)) {
2524780fb4a2SCy Schubert 			if (now > participant->mka_life)
2525780fb4a2SCy Schubert 				goto delete_mka;
25265b9c547cSRui Paulo 		} else {
25275b9c547cSRui Paulo 			participant->mka_life = 0;
25285b9c547cSRui Paulo 		}
25295b9c547cSRui Paulo 	}
25305b9c547cSRui Paulo 
25315b9c547cSRui Paulo 	lp_changed = FALSE;
25325b9c547cSRui Paulo 	dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
25335b9c547cSRui Paulo 			      struct ieee802_1x_kay_peer, list) {
25345b9c547cSRui Paulo 		if (now > peer->expire) {
25355b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
25365b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
25375b9c547cSRui Paulo 				    sizeof(peer->mi));
25385b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
25395b9c547cSRui Paulo 			dl_list_for_each_safe(rxsc, pre_rxsc,
25405b9c547cSRui Paulo 					      &participant->rxsc_list,
25415b9c547cSRui Paulo 					      struct receive_sc, list) {
2542780fb4a2SCy Schubert 				if (sci_equal(&rxsc->sci, &peer->sci)) {
25435b9c547cSRui Paulo 					ieee802_1x_kay_deinit_receive_sc(
25445b9c547cSRui Paulo 						participant, rxsc);
25455b9c547cSRui Paulo 				}
25465b9c547cSRui Paulo 			}
25475b9c547cSRui Paulo 			dl_list_del(&peer->list);
25485b9c547cSRui Paulo 			os_free(peer);
25495b9c547cSRui Paulo 			lp_changed = TRUE;
25505b9c547cSRui Paulo 		}
25515b9c547cSRui Paulo 	}
25525b9c547cSRui Paulo 
25535b9c547cSRui Paulo 	if (lp_changed) {
25545b9c547cSRui Paulo 		if (dl_list_empty(&participant->live_peers)) {
25555b9c547cSRui Paulo 			participant->advised_desired = FALSE;
25565b9c547cSRui Paulo 			participant->advised_capability =
25575b9c547cSRui Paulo 				MACSEC_CAP_NOT_IMPLEMENTED;
25585b9c547cSRui Paulo 			participant->to_use_sak = FALSE;
255985732ac8SCy Schubert 			participant->ltx = FALSE;
256085732ac8SCy Schubert 			participant->lrx = FALSE;
256185732ac8SCy Schubert 			participant->otx = FALSE;
256285732ac8SCy Schubert 			participant->orx = FALSE;
256385732ac8SCy Schubert 			participant->is_key_server = FALSE;
256485732ac8SCy Schubert 			participant->is_elected = FALSE;
256585732ac8SCy Schubert 			kay->authenticated = FALSE;
25665b9c547cSRui Paulo 			kay->secured = FALSE;
25675b9c547cSRui Paulo 			kay->failed = FALSE;
25685b9c547cSRui Paulo 			kay->ltx_kn = 0;
25695b9c547cSRui Paulo 			kay->ltx_an = 0;
25705b9c547cSRui Paulo 			kay->lrx_kn = 0;
25715b9c547cSRui Paulo 			kay->lrx_an = 0;
25725b9c547cSRui Paulo 			kay->otx_kn = 0;
25735b9c547cSRui Paulo 			kay->otx_an = 0;
25745b9c547cSRui Paulo 			kay->orx_kn = 0;
25755b9c547cSRui Paulo 			kay->orx_an = 0;
25765b9c547cSRui Paulo 			dl_list_for_each_safe(txsa, pre_txsa,
25775b9c547cSRui Paulo 					      &participant->txsc->sa_list,
25785b9c547cSRui Paulo 					      struct transmit_sa, list) {
257985732ac8SCy Schubert 				ieee802_1x_delete_transmit_sa(kay, txsa);
25805b9c547cSRui Paulo 			}
25815b9c547cSRui Paulo 
258285732ac8SCy Schubert 			ieee802_1x_cp_connect_pending(kay->cp);
25835b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(kay->cp);
25845b9c547cSRui Paulo 		} else {
25855b9c547cSRui Paulo 			ieee802_1x_kay_elect_key_server(participant);
25865b9c547cSRui Paulo 			ieee802_1x_kay_decide_macsec_use(participant);
25875b9c547cSRui Paulo 		}
25885b9c547cSRui Paulo 	}
25895b9c547cSRui Paulo 
25905b9c547cSRui Paulo 	dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
25915b9c547cSRui Paulo 			      struct ieee802_1x_kay_peer, list) {
25925b9c547cSRui Paulo 		if (now > peer->expire) {
25935b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
25945b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
25955b9c547cSRui Paulo 				    sizeof(peer->mi));
25965b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
25975b9c547cSRui Paulo 			dl_list_del(&peer->list);
25985b9c547cSRui Paulo 			os_free(peer);
25995b9c547cSRui Paulo 		}
26005b9c547cSRui Paulo 	}
26015b9c547cSRui Paulo 
26024bc52338SCy Schubert 	if (participant->new_sak && participant->is_key_server) {
26035b9c547cSRui Paulo 		if (!ieee802_1x_kay_generate_new_sak(participant))
26045b9c547cSRui Paulo 			participant->to_dist_sak = TRUE;
26055b9c547cSRui Paulo 
26065b9c547cSRui Paulo 		participant->new_sak = FALSE;
26075b9c547cSRui Paulo 	}
26085b9c547cSRui Paulo 
260985732ac8SCy Schubert 	if (participant->retry_count < MAX_RETRY_CNT ||
261085732ac8SCy Schubert 	    participant->mode == PSK) {
26115b9c547cSRui Paulo 		ieee802_1x_participant_send_mkpdu(participant);
26125b9c547cSRui Paulo 		participant->retry_count++;
26135b9c547cSRui Paulo 	}
26145b9c547cSRui Paulo 
26154bc52338SCy Schubert 	eloop_register_timeout(kay->mka_hello_time / 1000, 0,
26165b9c547cSRui Paulo 			       ieee802_1x_participant_timer,
26175b9c547cSRui Paulo 			       participant, NULL);
2618780fb4a2SCy Schubert 
2619780fb4a2SCy Schubert 	return;
2620780fb4a2SCy Schubert 
2621780fb4a2SCy Schubert delete_mka:
2622780fb4a2SCy Schubert 	kay->authenticated = FALSE;
2623780fb4a2SCy Schubert 	kay->secured = FALSE;
2624780fb4a2SCy Schubert 	kay->failed = TRUE;
2625780fb4a2SCy Schubert 	ieee802_1x_kay_delete_mka(kay, &participant->ckn);
26265b9c547cSRui Paulo }
26275b9c547cSRui Paulo 
26285b9c547cSRui Paulo 
26295b9c547cSRui Paulo /**
26305b9c547cSRui Paulo  * ieee802_1x_kay_init_transmit_sa -
26315b9c547cSRui Paulo  */
26325b9c547cSRui Paulo static struct transmit_sa *
26335b9c547cSRui Paulo ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
26345b9c547cSRui Paulo 				struct data_key *key)
26355b9c547cSRui Paulo {
26365b9c547cSRui Paulo 	struct transmit_sa *psa;
26375b9c547cSRui Paulo 
26385b9c547cSRui Paulo 	key->tx_latest = TRUE;
26395b9c547cSRui Paulo 	key->rx_latest = TRUE;
26405b9c547cSRui Paulo 
26415b9c547cSRui Paulo 	psa = os_zalloc(sizeof(*psa));
26425b9c547cSRui Paulo 	if (!psa) {
26435b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
26445b9c547cSRui Paulo 		return NULL;
26455b9c547cSRui Paulo 	}
26465b9c547cSRui Paulo 
26475b9c547cSRui Paulo 	if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
26485b9c547cSRui Paulo 	    key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
26495b9c547cSRui Paulo 		psa->confidentiality = TRUE;
26505b9c547cSRui Paulo 	else
26515b9c547cSRui Paulo 		psa->confidentiality = FALSE;
26525b9c547cSRui Paulo 
26535b9c547cSRui Paulo 	psa->an = an;
265485732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(key);
26555b9c547cSRui Paulo 	psa->pkey = key;
26565b9c547cSRui Paulo 	psa->next_pn = next_PN;
26575b9c547cSRui Paulo 	psa->sc = psc;
26585b9c547cSRui Paulo 
26595b9c547cSRui Paulo 	os_get_time(&psa->created_time);
26605b9c547cSRui Paulo 	psa->in_use = FALSE;
26615b9c547cSRui Paulo 
26625b9c547cSRui Paulo 	dl_list_add(&psc->sa_list, &psa->list);
26635b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
26644bc52338SCy Schubert 		   "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC",
266585732ac8SCy Schubert 		   an, next_PN);
26665b9c547cSRui Paulo 
26675b9c547cSRui Paulo 	return psa;
26685b9c547cSRui Paulo }
26695b9c547cSRui Paulo 
26705b9c547cSRui Paulo 
26715b9c547cSRui Paulo /**
26725b9c547cSRui Paulo  * ieee802_1x_kay_deinit_transmit_sa -
26735b9c547cSRui Paulo  */
26745b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
26755b9c547cSRui Paulo {
267685732ac8SCy Schubert 	ieee802_1x_kay_deinit_data_key(psa->pkey);
26775b9c547cSRui Paulo 	psa->pkey = NULL;
26785b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
2679780fb4a2SCy Schubert 		   "KaY: Delete transmit SA(an: %hhu) of SC",
2680780fb4a2SCy Schubert 		   psa->an);
26815b9c547cSRui Paulo 	dl_list_del(&psa->list);
26825b9c547cSRui Paulo 	os_free(psa);
26835b9c547cSRui Paulo }
26845b9c547cSRui Paulo 
26855b9c547cSRui Paulo 
26865b9c547cSRui Paulo /**
26875b9c547cSRui Paulo  * init_transmit_sc -
26885b9c547cSRui Paulo  */
26895b9c547cSRui Paulo static struct transmit_sc *
269085732ac8SCy Schubert ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
26915b9c547cSRui Paulo {
26925b9c547cSRui Paulo 	struct transmit_sc *psc;
26935b9c547cSRui Paulo 
26945b9c547cSRui Paulo 	psc = os_zalloc(sizeof(*psc));
26955b9c547cSRui Paulo 	if (!psc) {
26965b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
26975b9c547cSRui Paulo 		return NULL;
26985b9c547cSRui Paulo 	}
26995b9c547cSRui Paulo 	os_memcpy(&psc->sci, sci, sizeof(psc->sci));
27005b9c547cSRui Paulo 
27015b9c547cSRui Paulo 	os_get_time(&psc->created_time);
27025b9c547cSRui Paulo 	psc->transmitting = FALSE;
27035b9c547cSRui Paulo 	psc->encoding_sa = FALSE;
27045b9c547cSRui Paulo 	psc->enciphering_sa = FALSE;
27055b9c547cSRui Paulo 
27065b9c547cSRui Paulo 	dl_list_init(&psc->sa_list);
27074bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s",
27084bc52338SCy Schubert 		   sci_txt(&psc->sci));
27095b9c547cSRui Paulo 
27105b9c547cSRui Paulo 	return psc;
27115b9c547cSRui Paulo }
27125b9c547cSRui Paulo 
27135b9c547cSRui Paulo 
27145b9c547cSRui Paulo /**
27155b9c547cSRui Paulo  * ieee802_1x_kay_deinit_transmit_sc -
27165b9c547cSRui Paulo  */
27175b9c547cSRui Paulo static void
27185b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc(
27195b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
27205b9c547cSRui Paulo {
27215b9c547cSRui Paulo 	struct transmit_sa *psa, *tmp;
27225b9c547cSRui Paulo 
272385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
272485732ac8SCy Schubert 	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
272585732ac8SCy Schubert 		ieee802_1x_delete_transmit_sa(participant->kay, psa);
27265b9c547cSRui Paulo 
272785732ac8SCy Schubert 	secy_delete_transmit_sc(participant->kay, psc);
27285b9c547cSRui Paulo 	os_free(psc);
27295b9c547cSRui Paulo }
27305b9c547cSRui Paulo 
27315b9c547cSRui Paulo 
27325b9c547cSRui Paulo /****************** Interface between CP and KAY *********************/
27335b9c547cSRui Paulo /**
27345b9c547cSRui Paulo  * ieee802_1x_kay_set_latest_sa_attr -
27355b9c547cSRui Paulo  */
27365b9c547cSRui Paulo int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
27375b9c547cSRui Paulo 				      struct ieee802_1x_mka_ki *lki, u8 lan,
27385b9c547cSRui Paulo 				      Boolean ltx, Boolean lrx)
27395b9c547cSRui Paulo {
27405b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
27415b9c547cSRui Paulo 
27425b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
27435b9c547cSRui Paulo 	if (!principal)
27445b9c547cSRui Paulo 		return -1;
27455b9c547cSRui Paulo 
27465b9c547cSRui Paulo 	if (!lki)
27475b9c547cSRui Paulo 		os_memset(&principal->lki, 0, sizeof(principal->lki));
27485b9c547cSRui Paulo 	else
27495b9c547cSRui Paulo 		os_memcpy(&principal->lki, lki, sizeof(principal->lki));
27505b9c547cSRui Paulo 
27515b9c547cSRui Paulo 	principal->lan = lan;
27525b9c547cSRui Paulo 	principal->ltx = ltx;
27535b9c547cSRui Paulo 	principal->lrx = lrx;
27545b9c547cSRui Paulo 	if (!lki) {
27555b9c547cSRui Paulo 		kay->ltx_kn = 0;
27565b9c547cSRui Paulo 		kay->lrx_kn = 0;
27575b9c547cSRui Paulo 	} else {
27585b9c547cSRui Paulo 		kay->ltx_kn = lki->kn;
27595b9c547cSRui Paulo 		kay->lrx_kn = lki->kn;
27605b9c547cSRui Paulo 	}
27615b9c547cSRui Paulo 	kay->ltx_an = lan;
27625b9c547cSRui Paulo 	kay->lrx_an = lan;
27635b9c547cSRui Paulo 
27645b9c547cSRui Paulo 	return 0;
27655b9c547cSRui Paulo }
27665b9c547cSRui Paulo 
27675b9c547cSRui Paulo 
27685b9c547cSRui Paulo /**
27695b9c547cSRui Paulo  * ieee802_1x_kay_set_old_sa_attr -
27705b9c547cSRui Paulo  */
27715b9c547cSRui Paulo int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
27725b9c547cSRui Paulo 				   struct ieee802_1x_mka_ki *oki,
27735b9c547cSRui Paulo 				   u8 oan, Boolean otx, Boolean orx)
27745b9c547cSRui Paulo {
27755b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
27765b9c547cSRui Paulo 
27775b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
27785b9c547cSRui Paulo 	if (!principal)
27795b9c547cSRui Paulo 		return -1;
27805b9c547cSRui Paulo 
27815b9c547cSRui Paulo 	if (!oki)
27825b9c547cSRui Paulo 		os_memset(&principal->oki, 0, sizeof(principal->oki));
27835b9c547cSRui Paulo 	else
27845b9c547cSRui Paulo 		os_memcpy(&principal->oki, oki, sizeof(principal->oki));
27855b9c547cSRui Paulo 
27865b9c547cSRui Paulo 	principal->oan = oan;
27875b9c547cSRui Paulo 	principal->otx = otx;
27885b9c547cSRui Paulo 	principal->orx = orx;
27895b9c547cSRui Paulo 
27905b9c547cSRui Paulo 	if (!oki) {
27915b9c547cSRui Paulo 		kay->otx_kn = 0;
27925b9c547cSRui Paulo 		kay->orx_kn = 0;
27935b9c547cSRui Paulo 	} else {
27945b9c547cSRui Paulo 		kay->otx_kn = oki->kn;
27955b9c547cSRui Paulo 		kay->orx_kn = oki->kn;
27965b9c547cSRui Paulo 	}
27975b9c547cSRui Paulo 	kay->otx_an = oan;
27985b9c547cSRui Paulo 	kay->orx_an = oan;
27995b9c547cSRui Paulo 
28005b9c547cSRui Paulo 	return 0;
28015b9c547cSRui Paulo }
28025b9c547cSRui Paulo 
28035b9c547cSRui Paulo 
280485732ac8SCy Schubert static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
280585732ac8SCy Schubert {
280685732ac8SCy Schubert 	struct transmit_sa *txsa;
280785732ac8SCy Schubert 
280885732ac8SCy Schubert 	dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
280985732ac8SCy Schubert 		if (txsa->an == an)
281085732ac8SCy Schubert 			return txsa;
281185732ac8SCy Schubert 	}
281285732ac8SCy Schubert 
281385732ac8SCy Schubert 	return NULL;
281485732ac8SCy Schubert }
281585732ac8SCy Schubert 
281685732ac8SCy Schubert 
281785732ac8SCy Schubert static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
281885732ac8SCy Schubert {
281985732ac8SCy Schubert 	struct receive_sa *rxsa;
282085732ac8SCy Schubert 
282185732ac8SCy Schubert 	dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
282285732ac8SCy Schubert 		if (rxsa->an == an)
282385732ac8SCy Schubert 			return rxsa;
282485732ac8SCy Schubert 	}
282585732ac8SCy Schubert 
282685732ac8SCy Schubert 	return NULL;
282785732ac8SCy Schubert }
282885732ac8SCy Schubert 
282985732ac8SCy Schubert 
28305b9c547cSRui Paulo /**
28315b9c547cSRui Paulo  * ieee802_1x_kay_create_sas -
28325b9c547cSRui Paulo  */
28335b9c547cSRui Paulo int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
28345b9c547cSRui Paulo 			      struct ieee802_1x_mka_ki *lki)
28355b9c547cSRui Paulo {
28365b9c547cSRui Paulo 	struct data_key *sa_key, *latest_sak;
28375b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
28385b9c547cSRui Paulo 	struct receive_sc *rxsc;
28395b9c547cSRui Paulo 	struct receive_sa *rxsa;
28405b9c547cSRui Paulo 	struct transmit_sa *txsa;
28415b9c547cSRui Paulo 
28425b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
28435b9c547cSRui Paulo 	if (!principal)
28445b9c547cSRui Paulo 		return -1;
28455b9c547cSRui Paulo 
28465b9c547cSRui Paulo 	latest_sak = NULL;
28475b9c547cSRui Paulo 	dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
28485b9c547cSRui Paulo 		if (is_ki_equal(&sa_key->key_identifier, lki)) {
28495b9c547cSRui Paulo 			sa_key->rx_latest = TRUE;
28505b9c547cSRui Paulo 			sa_key->tx_latest = TRUE;
28515b9c547cSRui Paulo 			latest_sak = sa_key;
28525b9c547cSRui Paulo 			principal->to_use_sak = TRUE;
28535b9c547cSRui Paulo 		} else {
28545b9c547cSRui Paulo 			sa_key->rx_latest = FALSE;
28555b9c547cSRui Paulo 			sa_key->tx_latest = FALSE;
28565b9c547cSRui Paulo 		}
28575b9c547cSRui Paulo 	}
28585b9c547cSRui Paulo 	if (!latest_sak) {
28594bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: lki related sak not found");
28605b9c547cSRui Paulo 		return -1;
28615b9c547cSRui Paulo 	}
28625b9c547cSRui Paulo 
28635b9c547cSRui Paulo 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
286485732ac8SCy Schubert 		while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
286585732ac8SCy Schubert 			ieee802_1x_delete_receive_sa(kay, rxsa);
286685732ac8SCy Schubert 
28675b9c547cSRui Paulo 		rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
28685b9c547cSRui Paulo 						      latest_sak);
28695b9c547cSRui Paulo 		if (!rxsa)
28705b9c547cSRui Paulo 			return -1;
28715b9c547cSRui Paulo 
28725b9c547cSRui Paulo 		secy_create_receive_sa(kay, rxsa);
28735b9c547cSRui Paulo 	}
28745b9c547cSRui Paulo 
287585732ac8SCy Schubert 	while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
287685732ac8SCy Schubert 	       NULL)
287785732ac8SCy Schubert 		ieee802_1x_delete_transmit_sa(kay, txsa);
287885732ac8SCy Schubert 
28795b9c547cSRui Paulo 	txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
28804bc52338SCy Schubert 					       latest_sak->next_pn ?
28814bc52338SCy Schubert 					       latest_sak->next_pn : 1,
28824bc52338SCy Schubert 					       latest_sak);
28835b9c547cSRui Paulo 	if (!txsa)
28845b9c547cSRui Paulo 		return -1;
28855b9c547cSRui Paulo 
28865b9c547cSRui Paulo 	secy_create_transmit_sa(kay, txsa);
28875b9c547cSRui Paulo 
28885b9c547cSRui Paulo 
28895b9c547cSRui Paulo 
28905b9c547cSRui Paulo 	return 0;
28915b9c547cSRui Paulo }
28925b9c547cSRui Paulo 
28935b9c547cSRui Paulo 
28945b9c547cSRui Paulo /**
28955b9c547cSRui Paulo  * ieee802_1x_kay_delete_sas -
28965b9c547cSRui Paulo  */
28975b9c547cSRui Paulo int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
28985b9c547cSRui Paulo 			      struct ieee802_1x_mka_ki *ki)
28995b9c547cSRui Paulo {
29005b9c547cSRui Paulo 	struct data_key *sa_key, *pre_key;
29015b9c547cSRui Paulo 	struct transmit_sa *txsa, *pre_txsa;
29025b9c547cSRui Paulo 	struct receive_sa *rxsa, *pre_rxsa;
29035b9c547cSRui Paulo 	struct receive_sc *rxsc;
29045b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
29055b9c547cSRui Paulo 
29065b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
29075b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
29085b9c547cSRui Paulo 	if (!principal)
29095b9c547cSRui Paulo 		return -1;
29105b9c547cSRui Paulo 
29115b9c547cSRui Paulo 	/* remove the transmit sa */
29125b9c547cSRui Paulo 	dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
29135b9c547cSRui Paulo 			      struct transmit_sa, list) {
291485732ac8SCy Schubert 		if (is_ki_equal(&txsa->pkey->key_identifier, ki))
291585732ac8SCy Schubert 			ieee802_1x_delete_transmit_sa(kay, txsa);
29165b9c547cSRui Paulo 	}
29175b9c547cSRui Paulo 
29185b9c547cSRui Paulo 	/* remove the receive sa */
29195b9c547cSRui Paulo 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
29205b9c547cSRui Paulo 		dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
29215b9c547cSRui Paulo 				      struct receive_sa, list) {
292285732ac8SCy Schubert 			if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
292385732ac8SCy Schubert 				ieee802_1x_delete_receive_sa(kay, rxsa);
29245b9c547cSRui Paulo 		}
29255b9c547cSRui Paulo 	}
29265b9c547cSRui Paulo 
29275b9c547cSRui Paulo 	/* remove the sak */
29285b9c547cSRui Paulo 	dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
29295b9c547cSRui Paulo 			      struct data_key, list) {
29305b9c547cSRui Paulo 		if (is_ki_equal(&sa_key->key_identifier, ki)) {
29314bc52338SCy Schubert 			if (principal->new_key == sa_key)
29324bc52338SCy Schubert 				principal->new_key = NULL;
293385732ac8SCy Schubert 			dl_list_del(&sa_key->list);
29345b9c547cSRui Paulo 			ieee802_1x_kay_deinit_data_key(sa_key);
29355b9c547cSRui Paulo 			break;
29365b9c547cSRui Paulo 		}
29375b9c547cSRui Paulo 	}
29385b9c547cSRui Paulo 
29395b9c547cSRui Paulo 	return 0;
29405b9c547cSRui Paulo }
29415b9c547cSRui Paulo 
29425b9c547cSRui Paulo 
29435b9c547cSRui Paulo /**
29445b9c547cSRui Paulo  * ieee802_1x_kay_enable_tx_sas -
29455b9c547cSRui Paulo  */
29465b9c547cSRui Paulo int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
29475b9c547cSRui Paulo 				 struct ieee802_1x_mka_ki *lki)
29485b9c547cSRui Paulo {
29495b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
29505b9c547cSRui Paulo 	struct transmit_sa *txsa;
29515b9c547cSRui Paulo 
29525b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
29535b9c547cSRui Paulo 	if (!principal)
29545b9c547cSRui Paulo 		return -1;
29555b9c547cSRui Paulo 
29565b9c547cSRui Paulo 	dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
29575b9c547cSRui Paulo 			 list) {
29585b9c547cSRui Paulo 		if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
29595b9c547cSRui Paulo 			txsa->in_use = TRUE;
29605b9c547cSRui Paulo 			secy_enable_transmit_sa(kay, txsa);
29615b9c547cSRui Paulo 			ieee802_1x_cp_set_usingtransmitas(
29625b9c547cSRui Paulo 				principal->kay->cp, TRUE);
29635b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(principal->kay->cp);
29645b9c547cSRui Paulo 		}
29655b9c547cSRui Paulo 	}
29665b9c547cSRui Paulo 
29675b9c547cSRui Paulo 	return 0;
29685b9c547cSRui Paulo }
29695b9c547cSRui Paulo 
29705b9c547cSRui Paulo 
29715b9c547cSRui Paulo /**
29725b9c547cSRui Paulo  * ieee802_1x_kay_enable_rx_sas -
29735b9c547cSRui Paulo  */
29745b9c547cSRui Paulo int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
29755b9c547cSRui Paulo 				 struct ieee802_1x_mka_ki *lki)
29765b9c547cSRui Paulo {
29775b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
29785b9c547cSRui Paulo 	struct receive_sa *rxsa;
29795b9c547cSRui Paulo 	struct receive_sc *rxsc;
29805b9c547cSRui Paulo 
29815b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
29825b9c547cSRui Paulo 	if (!principal)
29835b9c547cSRui Paulo 		return -1;
29845b9c547cSRui Paulo 
29855b9c547cSRui Paulo 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
29865b9c547cSRui Paulo 		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
29875b9c547cSRui Paulo 		{
29885b9c547cSRui Paulo 			if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
29895b9c547cSRui Paulo 				rxsa->in_use = TRUE;
29905b9c547cSRui Paulo 				secy_enable_receive_sa(kay, rxsa);
29915b9c547cSRui Paulo 				ieee802_1x_cp_set_usingreceivesas(
29925b9c547cSRui Paulo 					principal->kay->cp, TRUE);
29935b9c547cSRui Paulo 				ieee802_1x_cp_sm_step(principal->kay->cp);
29945b9c547cSRui Paulo 			}
29955b9c547cSRui Paulo 		}
29965b9c547cSRui Paulo 	}
29975b9c547cSRui Paulo 
29985b9c547cSRui Paulo 	return 0;
29995b9c547cSRui Paulo }
30005b9c547cSRui Paulo 
30015b9c547cSRui Paulo 
30025b9c547cSRui Paulo /**
30035b9c547cSRui Paulo  * ieee802_1x_kay_enable_new_info -
30045b9c547cSRui Paulo  */
30055b9c547cSRui Paulo int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
30065b9c547cSRui Paulo {
30075b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
30085b9c547cSRui Paulo 
30095b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
30105b9c547cSRui Paulo 	if (!principal)
30115b9c547cSRui Paulo 		return -1;
30125b9c547cSRui Paulo 
301385732ac8SCy Schubert 	if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
30145b9c547cSRui Paulo 		ieee802_1x_participant_send_mkpdu(principal);
30155b9c547cSRui Paulo 		principal->retry_count++;
30165b9c547cSRui Paulo 	}
30175b9c547cSRui Paulo 
30185b9c547cSRui Paulo 	return 0;
30195b9c547cSRui Paulo }
30205b9c547cSRui Paulo 
30215b9c547cSRui Paulo 
30225b9c547cSRui Paulo /**
30235b9c547cSRui Paulo  * ieee802_1x_kay_mkpdu_sanity_check -
30244bc52338SCy Schubert  * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of
30254bc52338SCy Schubert  * MKPDUs)
30265b9c547cSRui Paulo  */
30275b9c547cSRui Paulo static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
30285b9c547cSRui Paulo 					     const u8 *buf, size_t len)
30295b9c547cSRui Paulo {
30305b9c547cSRui Paulo 	struct ieee8023_hdr *eth_hdr;
30315b9c547cSRui Paulo 	struct ieee802_1x_hdr *eapol_hdr;
30325b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *mka_hdr;
30335b9c547cSRui Paulo 	struct ieee802_1x_mka_basic_body *body;
30345b9c547cSRui Paulo 	size_t mka_msg_len;
30355b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
30365b9c547cSRui Paulo 	size_t body_len;
303785732ac8SCy Schubert 	size_t ckn_len;
30385b9c547cSRui Paulo 	u8 icv[MAX_ICV_LEN];
30394bc52338SCy Schubert 	const u8 *msg_icv;
30405b9c547cSRui Paulo 
30414bc52338SCy Schubert 	/* len > eth+eapol header already verified in kay_l2_receive();
30424bc52338SCy Schubert 	 * likewise, eapol_hdr->length validated there */
30435b9c547cSRui Paulo 	eth_hdr = (struct ieee8023_hdr *) buf;
30445b9c547cSRui Paulo 	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
30455b9c547cSRui Paulo 	mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
30465b9c547cSRui Paulo 
30474bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
30484bc52338SCy Schubert 		   " Ethertype=0x%x",
30494bc52338SCy Schubert 		   MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src),
30504bc52338SCy Schubert 		   be_to_host16(eth_hdr->ethertype));
30514bc52338SCy Schubert 
30524bc52338SCy Schubert 	/* the destination address shall not be an individual address */
30535b9c547cSRui Paulo 	if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
30544bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
30555b9c547cSRui Paulo 			   "KaY: ethernet destination address is not PAE group address");
30565b9c547cSRui Paulo 		return -1;
30575b9c547cSRui Paulo 	}
30585b9c547cSRui Paulo 
30594bc52338SCy Schubert 	wpa_printf(MSG_DEBUG,
30604bc52338SCy Schubert 		   "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
30614bc52338SCy Schubert 		   eapol_hdr->version, eapol_hdr->type,
30624bc52338SCy Schubert 		   be_to_host16(eapol_hdr->length));
30634bc52338SCy Schubert 
30644bc52338SCy Schubert 	/* MKPDU shall not be less than 32 octets */
30655b9c547cSRui Paulo 	mka_msg_len = be_to_host16(eapol_hdr->length);
30665b9c547cSRui Paulo 	if (mka_msg_len < 32) {
30674bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets");
30685b9c547cSRui Paulo 		return -1;
30695b9c547cSRui Paulo 	}
30704bc52338SCy Schubert 	/* MKPDU shall be a multiple of 4 octets */
30715b9c547cSRui Paulo 	if ((mka_msg_len % 4) != 0) {
30724bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
30735b9c547cSRui Paulo 			   "KaY: MKPDU is not multiple of 4 octets");
30745b9c547cSRui Paulo 		return -1;
30755b9c547cSRui Paulo 	}
30765b9c547cSRui Paulo 
30774bc52338SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)",
30784bc52338SCy Schubert 		    mka_hdr, mka_msg_len);
30794bc52338SCy Schubert 
30804bc52338SCy Schubert 	/* Room for body_len already verified in kay_l2_receive() */
30815b9c547cSRui Paulo 	body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
30825b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
30835b9c547cSRui Paulo 	/* EAPOL-MKA body should comprise basic parameter set and ICV */
30845b9c547cSRui Paulo 	if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
30855b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
3086780fb4a2SCy Schubert 			   "KaY: Received EAPOL-MKA Packet Body Length (%zu bytes) is less than the Basic Parameter Set Header Length (%zu bytes) + the Basic Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
3087780fb4a2SCy Schubert 			   mka_msg_len, MKA_HDR_LEN,
3088780fb4a2SCy Schubert 			   body_len, DEFAULT_ICV_LEN);
30895b9c547cSRui Paulo 		return -1;
30905b9c547cSRui Paulo 	}
30915b9c547cSRui Paulo 
309285732ac8SCy Schubert 	if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
309385732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
309485732ac8SCy Schubert 			   body_len);
309585732ac8SCy Schubert 		return -1;
309685732ac8SCy Schubert 	}
309785732ac8SCy Schubert 	ckn_len = body_len -
309885732ac8SCy Schubert 		(sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
309985732ac8SCy Schubert 	if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
31004bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
310185732ac8SCy Schubert 			   "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
310285732ac8SCy Schubert 			   ckn_len, MAX_CKN_LEN);
310385732ac8SCy Schubert 		return -1;
310485732ac8SCy Schubert 	}
310585732ac8SCy Schubert 
31064bc52338SCy Schubert 	ieee802_1x_mka_dump_basic_body(body);
31074bc52338SCy Schubert 
31085b9c547cSRui Paulo 	/* CKN should be owned by I */
310985732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
31105b9c547cSRui Paulo 	if (!participant) {
31114bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA");
31125b9c547cSRui Paulo 		return -1;
31135b9c547cSRui Paulo 	}
31145b9c547cSRui Paulo 
31155b9c547cSRui Paulo 	/* algorithm agility check */
31165b9c547cSRui Paulo 	if (os_memcmp(body->algo_agility, mka_algo_agility,
31175b9c547cSRui Paulo 		      sizeof(body->algo_agility)) != 0) {
31184bc52338SCy Schubert 		wpa_printf(MSG_INFO,
31194bc52338SCy Schubert 			   "KaY: Peer's algorithm agility (%s) not supported",
31204bc52338SCy Schubert 			   algo_agility_txt(body->algo_agility));
31215b9c547cSRui Paulo 		return -1;
31225b9c547cSRui Paulo 	}
31235b9c547cSRui Paulo 
31245b9c547cSRui Paulo 	/* ICV check */
31255b9c547cSRui Paulo 	/*
31265b9c547cSRui Paulo 	 * The ICV will comprise the final octets of the packet body, whatever
31275b9c547cSRui Paulo 	 * its size, not the fixed length 16 octets, indicated by the EAPOL
31285b9c547cSRui Paulo 	 * packet body length.
31295b9c547cSRui Paulo 	 */
31304bc52338SCy Schubert 	if (len < mka_alg_tbl[kay->mka_algindex].icv_len ||
31314bc52338SCy Schubert 	    mka_alg_tbl[kay->mka_algindex].icv_hash(
31324bc52338SCy Schubert 		    participant->ick.key, participant->ick.len,
31335b9c547cSRui Paulo 		    buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
31344bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV");
31355b9c547cSRui Paulo 		return -1;
31365b9c547cSRui Paulo 	}
3137780fb4a2SCy Schubert 
31384bc52338SCy Schubert 	msg_icv = ieee802_1x_mka_decode_icv_body(participant,
31394bc52338SCy Schubert 						 (const u8 *) mka_hdr,
31405b9c547cSRui Paulo 						 mka_msg_len);
3141780fb4a2SCy Schubert 	if (!msg_icv) {
31424bc52338SCy Schubert 		wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it");
31435b9c547cSRui Paulo 		return -1;
31445b9c547cSRui Paulo 	}
31454bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: Received ICV",
31464bc52338SCy Schubert 		    msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len);
3147780fb4a2SCy Schubert 	if (os_memcmp_const(msg_icv, icv,
3148780fb4a2SCy Schubert 			    mka_alg_tbl[kay->mka_algindex].icv_len) != 0) {
31494bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
3150780fb4a2SCy Schubert 			   "KaY: Computed ICV is not equal to Received ICV");
31514bc52338SCy Schubert 		wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV",
31524bc52338SCy Schubert 			    icv, mka_alg_tbl[kay->mka_algindex].icv_len);
31535b9c547cSRui Paulo 		return -1;
31545b9c547cSRui Paulo 	}
31555b9c547cSRui Paulo 
31565b9c547cSRui Paulo 	return 0;
31575b9c547cSRui Paulo }
31585b9c547cSRui Paulo 
31595b9c547cSRui Paulo 
31605b9c547cSRui Paulo /**
31615b9c547cSRui Paulo  * ieee802_1x_kay_decode_mkpdu -
31625b9c547cSRui Paulo  */
31635b9c547cSRui Paulo static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
31645b9c547cSRui Paulo 				       const u8 *buf, size_t len)
31655b9c547cSRui Paulo {
31665b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
31675b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
31684bc52338SCy Schubert 	struct ieee802_1x_kay_peer *peer;
31695b9c547cSRui Paulo 	size_t body_len;
31705b9c547cSRui Paulo 	size_t left_len;
3171780fb4a2SCy Schubert 	u8 body_type;
31725b9c547cSRui Paulo 	int i;
31735b9c547cSRui Paulo 	const u8 *pos;
31745b9c547cSRui Paulo 	Boolean handled[256];
31754bc52338SCy Schubert 	Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use
31764bc52338SCy Schubert 				      * parameter set */
31774bc52338SCy Schubert 	Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer;
31785b9c547cSRui Paulo 
31794bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)",
31804bc52338SCy Schubert 		   kay->if_name);
31815b9c547cSRui Paulo 	if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
31825b9c547cSRui Paulo 		return -1;
31835b9c547cSRui Paulo 
31845b9c547cSRui Paulo 	/* handle basic parameter set */
31855b9c547cSRui Paulo 	pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
31865b9c547cSRui Paulo 	left_len = len - sizeof(struct ieee8023_hdr) -
31875b9c547cSRui Paulo 		sizeof(struct ieee802_1x_hdr);
31885b9c547cSRui Paulo 	participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
31895b9c547cSRui Paulo 	if (!participant)
31905b9c547cSRui Paulo 		return -1;
31915b9c547cSRui Paulo 
31925b9c547cSRui Paulo 	/* to skip basic parameter set */
31935b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) pos;
31944bc52338SCy Schubert 	body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
31954bc52338SCy Schubert 	if (left_len < body_len + MKA_HDR_LEN)
31964bc52338SCy Schubert 		return -1;
31975b9c547cSRui Paulo 	pos += body_len + MKA_HDR_LEN;
31985b9c547cSRui Paulo 	left_len -= body_len + MKA_HDR_LEN;
31995b9c547cSRui Paulo 
32005b9c547cSRui Paulo 	/* check i am in the peer's peer list */
32014bc52338SCy Schubert 	i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos,
32024bc52338SCy Schubert 						     left_len);
32034bc52338SCy Schubert 	is_in_live_peer = ieee802_1x_kay_is_in_live_peer(
32044bc52338SCy Schubert 		participant, participant->current_peer_id.mi);
32054bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s",
32064bc52338SCy Schubert 		   yes_no(i_in_peerlist), yes_no(is_in_live_peer));
32074bc52338SCy Schubert 	if (i_in_peerlist && !is_in_live_peer) {
3208780fb4a2SCy Schubert 		/* accept the peer as live peer */
32094bc52338SCy Schubert 		is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer(
32104bc52338SCy Schubert 			participant, participant->current_peer_id.mi);
32114bc52338SCy Schubert 		if (is_in_potential_peer) {
3212780fb4a2SCy Schubert 			if (!ieee802_1x_kay_move_live_peer(
3213780fb4a2SCy Schubert 				    participant,
3214780fb4a2SCy Schubert 				    participant->current_peer_id.mi,
3215780fb4a2SCy Schubert 				    be_to_host32(participant->
3216780fb4a2SCy Schubert 						 current_peer_id.mn)))
3217780fb4a2SCy Schubert 				return -1;
3218780fb4a2SCy Schubert 		} else if (!ieee802_1x_kay_create_live_peer(
32195b9c547cSRui Paulo 				   participant, participant->current_peer_id.mi,
3220780fb4a2SCy Schubert 				   be_to_host32(participant->
3221780fb4a2SCy Schubert 						current_peer_id.mn))) {
3222780fb4a2SCy Schubert 				return -1;
3223780fb4a2SCy Schubert 		}
3224780fb4a2SCy Schubert 
32255b9c547cSRui Paulo 		ieee802_1x_kay_elect_key_server(participant);
32265b9c547cSRui Paulo 		ieee802_1x_kay_decide_macsec_use(participant);
32275b9c547cSRui Paulo 	}
32285b9c547cSRui Paulo 
32295b9c547cSRui Paulo 	/*
32305b9c547cSRui Paulo 	 * Handle other parameter set than basic parameter set.
32315b9c547cSRui Paulo 	 * Each parameter set should be present only once.
32325b9c547cSRui Paulo 	 */
32335b9c547cSRui Paulo 	for (i = 0; i < 256; i++)
32345b9c547cSRui Paulo 		handled[i] = FALSE;
32355b9c547cSRui Paulo 
32365b9c547cSRui Paulo 	handled[0] = TRUE;
3237780fb4a2SCy Schubert 	for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
3238780fb4a2SCy Schubert 	     pos += body_len + MKA_HDR_LEN,
3239780fb4a2SCy Schubert 		     left_len -= body_len + MKA_HDR_LEN) {
32405b9c547cSRui Paulo 		hdr = (struct ieee802_1x_mka_hdr *) pos;
32414bc52338SCy Schubert 		body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
32425b9c547cSRui Paulo 		body_type = get_mka_param_body_type(hdr);
32435b9c547cSRui Paulo 
32445b9c547cSRui Paulo 		if (body_type == MKA_ICV_INDICATOR)
32455b9c547cSRui Paulo 			return 0;
32465b9c547cSRui Paulo 
32475b9c547cSRui Paulo 		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
32485b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
3249780fb4a2SCy Schubert 				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
3250780fb4a2SCy Schubert 				   left_len, MKA_HDR_LEN,
3251780fb4a2SCy Schubert 				   body_len, DEFAULT_ICV_LEN);
325285732ac8SCy Schubert 			return -1;
32535b9c547cSRui Paulo 		}
32545b9c547cSRui Paulo 
32554bc52338SCy Schubert 		if (handled[body_type]) {
32564bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
32574bc52338SCy Schubert 				   "KaY: Ignore duplicated body type %u",
32584bc52338SCy Schubert 				   body_type);
3259780fb4a2SCy Schubert 			continue;
32604bc52338SCy Schubert 		}
32615b9c547cSRui Paulo 
32625b9c547cSRui Paulo 		handled[body_type] = TRUE;
3263780fb4a2SCy Schubert 		if (body_type < ARRAY_SIZE(mka_body_handler) &&
3264780fb4a2SCy Schubert 		    mka_body_handler[body_type].body_rx) {
32654bc52338SCy Schubert 			if (mka_body_handler[body_type].body_rx
32664bc52338SCy Schubert 				(participant, pos, left_len) != 0) {
32674bc52338SCy Schubert 				/* Handle parameter set failure */
32684bc52338SCy Schubert 				if (body_type != MKA_SAK_USE) {
32694bc52338SCy Schubert 					wpa_printf(MSG_INFO,
32704bc52338SCy Schubert 						   "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
32714bc52338SCy Schubert 						   body_type);
32724bc52338SCy Schubert 					return -1;
32734bc52338SCy Schubert 				}
32744bc52338SCy Schubert 
32754bc52338SCy Schubert 				/* Ideally DIST-SAK should be processed before
32764bc52338SCy Schubert 				 * SAK-USE. Unfortunately IEEE Std 802.1X-2010,
32774bc52338SCy Schubert 				 * 11.11.3 (Encoding MKPDUs) states SAK-USE(3)
32784bc52338SCy Schubert 				 * must always be encoded before DIST-SAK(4).
32794bc52338SCy Schubert 				 * Rather than redesigning mka_body_handler so
32804bc52338SCy Schubert 				 * that it somehow processes DIST-SAK before
32814bc52338SCy Schubert 				 * SAK-USE, just ignore SAK-USE failures if
32824bc52338SCy Schubert 				 * DIST-SAK is also present in this MKPDU. */
32834bc52338SCy Schubert 				bad_sak_use = TRUE;
32844bc52338SCy Schubert 			}
32855b9c547cSRui Paulo 		} else {
32865b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
32874bc52338SCy Schubert 				   "KaY: The body type %d is not supported in this MKA version %d",
32885b9c547cSRui Paulo 				   body_type, MKA_VERSION_ID);
32895b9c547cSRui Paulo 		}
32905b9c547cSRui Paulo 	}
32915b9c547cSRui Paulo 
32924bc52338SCy Schubert 	if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) {
32934bc52338SCy Schubert 		wpa_printf(MSG_INFO,
32944bc52338SCy Schubert 			   "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
32954bc52338SCy Schubert 			   MKA_SAK_USE);
32964bc52338SCy Schubert 		if (!reset_participant_mi(participant))
32974bc52338SCy Schubert 			wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
32984bc52338SCy Schubert 		else
32994bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
33004bc52338SCy Schubert 				   "KaY: Selected a new random MI: %s",
33014bc52338SCy Schubert 				   mi_txt(participant->mi));
33024bc52338SCy Schubert 		return -1;
33034bc52338SCy Schubert 	}
33044bc52338SCy Schubert 
33054bc52338SCy Schubert 	/* Detect missing parameter sets */
33064bc52338SCy Schubert 	peer = ieee802_1x_kay_get_live_peer(participant,
33074bc52338SCy Schubert 					    participant->current_peer_id.mi);
33084bc52338SCy Schubert 	if (peer) {
33094bc52338SCy Schubert 		/* MKPDU is from live peer */
33104bc52338SCy Schubert 		if (!handled[MKA_SAK_USE]) {
33114bc52338SCy Schubert 			/* Once a live peer starts sending SAK-USE, it should be
33124bc52338SCy Schubert 			 * sent every time. */
33134bc52338SCy Schubert 			if (peer->sak_used) {
33144bc52338SCy Schubert 				wpa_printf(MSG_INFO,
33154bc52338SCy Schubert 					   "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE");
33164bc52338SCy Schubert 				return -1;
33174bc52338SCy Schubert 			}
33184bc52338SCy Schubert 
33194bc52338SCy Schubert 			/* Live peer is probably hung if it hasn't sent SAK-USE
33204bc52338SCy Schubert 			 * after a reasonable number of MKPDUs. Drop the MKPDU,
33214bc52338SCy Schubert 			 * which will eventually force an timeout. */
33224bc52338SCy Schubert 			if (++peer->missing_sak_use_count >
33234bc52338SCy Schubert 			    MAX_MISSING_SAK_USE) {
33244bc52338SCy Schubert 				wpa_printf(MSG_INFO,
33254bc52338SCy Schubert 					   "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE");
33264bc52338SCy Schubert 				return -1;
33274bc52338SCy Schubert 			}
33284bc52338SCy Schubert 		} else {
33294bc52338SCy Schubert 			peer->missing_sak_use_count = 0;
33304bc52338SCy Schubert 
33314bc52338SCy Schubert 			/* Only update live peer watchdog after successful
33324bc52338SCy Schubert 			 * decode of all parameter sets */
33334bc52338SCy Schubert 			peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
33344bc52338SCy Schubert 		}
33354bc52338SCy Schubert 	} else {
33364bc52338SCy Schubert 		/* MKPDU is from new or potential peer */
33374bc52338SCy Schubert 		peer = ieee802_1x_kay_get_peer(participant,
33384bc52338SCy Schubert 					       participant->current_peer_id.mi);
33394bc52338SCy Schubert 		if (!peer) {
33404bc52338SCy Schubert 			wpa_printf(MSG_DEBUG, "KaY: No peer entry found");
33414bc52338SCy Schubert 			return -1;
33424bc52338SCy Schubert 		}
33434bc52338SCy Schubert 
33444bc52338SCy Schubert 		/* Do not update potential peer watchdog. Per IEEE Std
33454bc52338SCy Schubert 		 * 802.1X-2010, 9.4.3, potential peers need to show liveness by
33464bc52338SCy Schubert 		 * including our MI/MN in their transmitted MKPDU (within
33474bc52338SCy Schubert 		 * potential or live parameter sets). Whena potential peer does
33484bc52338SCy Schubert 		 * include our MI/MN in an MKPDU, we respond by moving the peer
33494bc52338SCy Schubert 		 * from 'potential_peers' to 'live_peers'. */
33504bc52338SCy Schubert 	}
33514bc52338SCy Schubert 
33525b9c547cSRui Paulo 	kay->active = TRUE;
33535b9c547cSRui Paulo 	participant->retry_count = 0;
33545b9c547cSRui Paulo 	participant->active = TRUE;
33555b9c547cSRui Paulo 
33565b9c547cSRui Paulo 	return 0;
33575b9c547cSRui Paulo }
33585b9c547cSRui Paulo 
33595b9c547cSRui Paulo 
33605b9c547cSRui Paulo 
33615b9c547cSRui Paulo static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
33625b9c547cSRui Paulo 			   size_t len)
33635b9c547cSRui Paulo {
33645b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = ctx;
33655b9c547cSRui Paulo 	struct ieee8023_hdr *eth_hdr;
33665b9c547cSRui Paulo 	struct ieee802_1x_hdr *eapol_hdr;
33674bc52338SCy Schubert 	size_t calc_len;
33684bc52338SCy Schubert 
33694bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */
33705b9c547cSRui Paulo 
33715b9c547cSRui Paulo 	/* must contain at least ieee8023_hdr + ieee802_1x_hdr */
33725b9c547cSRui Paulo 	if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
33735b9c547cSRui Paulo 		wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
33745b9c547cSRui Paulo 			   (unsigned long) len);
33755b9c547cSRui Paulo 		return;
33765b9c547cSRui Paulo 	}
33775b9c547cSRui Paulo 
33785b9c547cSRui Paulo 	eth_hdr = (struct ieee8023_hdr *) buf;
33795b9c547cSRui Paulo 	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
33804bc52338SCy Schubert 	calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
33814bc52338SCy Schubert 		be_to_host16(eapol_hdr->length);
33824bc52338SCy Schubert 	if (len < calc_len) {
33834bc52338SCy Schubert 		wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)",
33845b9c547cSRui Paulo 			   (unsigned long) len,
33854bc52338SCy Schubert 			   (unsigned long) calc_len,
33864bc52338SCy Schubert 			   be_to_host16(eapol_hdr->length));
33875b9c547cSRui Paulo 		return;
33885b9c547cSRui Paulo 	}
33894bc52338SCy Schubert 	if (len > calc_len) {
33904bc52338SCy Schubert 		wpa_hexdump(MSG_DEBUG,
33914bc52338SCy Schubert 			    "KaY: Ignore extra octets following the Packey Body field",
33924bc52338SCy Schubert 			    &buf[calc_len], len - calc_len);
33934bc52338SCy Schubert 		len = calc_len;
33944bc52338SCy Schubert 	}
33955b9c547cSRui Paulo 
33965b9c547cSRui Paulo 	if (eapol_hdr->version < EAPOL_VERSION) {
33975b9c547cSRui Paulo 		wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
33985b9c547cSRui Paulo 			   eapol_hdr->version);
33995b9c547cSRui Paulo 		return;
34005b9c547cSRui Paulo 	}
3401780fb4a2SCy Schubert 	if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE ||
34025b9c547cSRui Paulo 	    eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
34034bc52338SCy Schubert 		return; /* ignore other EAPOL types silently here */
34045b9c547cSRui Paulo 
34054bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len);
34065b9c547cSRui Paulo 	if (dl_list_empty(&kay->participant_list)) {
34074bc52338SCy Schubert 		wpa_printf(MSG_ERROR,
34084bc52338SCy Schubert 			   "KaY: No MKA participant instance - ignore EAPOL-MKA");
34095b9c547cSRui Paulo 		return;
34105b9c547cSRui Paulo 	}
34115b9c547cSRui Paulo 
34125b9c547cSRui Paulo 	ieee802_1x_kay_decode_mkpdu(kay, buf, len);
34135b9c547cSRui Paulo }
34145b9c547cSRui Paulo 
34155b9c547cSRui Paulo 
34165b9c547cSRui Paulo /**
34175b9c547cSRui Paulo  * ieee802_1x_kay_init -
34185b9c547cSRui Paulo  */
34195b9c547cSRui Paulo struct ieee802_1x_kay *
34205b9c547cSRui Paulo ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
34214bc52338SCy Schubert 		    Boolean macsec_replay_protect, u32 macsec_replay_window,
342285732ac8SCy Schubert 		    u16 port, u8 priority, const char *ifname, const u8 *addr)
34235b9c547cSRui Paulo {
34245b9c547cSRui Paulo 	struct ieee802_1x_kay *kay;
34255b9c547cSRui Paulo 
34264bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR
34274bc52338SCy Schubert 		   " port=%u priority=%u",
34284bc52338SCy Schubert 		   ifname, MAC2STR(addr), port, priority);
34295b9c547cSRui Paulo 	kay = os_zalloc(sizeof(*kay));
34305b9c547cSRui Paulo 	if (!kay) {
34315b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
343285732ac8SCy Schubert 		os_free(ctx);
34335b9c547cSRui Paulo 		return NULL;
34345b9c547cSRui Paulo 	}
34355b9c547cSRui Paulo 
34365b9c547cSRui Paulo 	kay->ctx = ctx;
34375b9c547cSRui Paulo 
34385b9c547cSRui Paulo 	kay->enable = TRUE;
34395b9c547cSRui Paulo 	kay->active = FALSE;
34405b9c547cSRui Paulo 
34415b9c547cSRui Paulo 	kay->authenticated = FALSE;
34425b9c547cSRui Paulo 	kay->secured = FALSE;
34435b9c547cSRui Paulo 	kay->failed = FALSE;
34445b9c547cSRui Paulo 	kay->policy = policy;
34455b9c547cSRui Paulo 
34465b9c547cSRui Paulo 	os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
34475b9c547cSRui Paulo 	os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
344885732ac8SCy Schubert 	kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
34494bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s",
34504bc52338SCy Schubert 		   sci_txt(&kay->actor_sci));
345185732ac8SCy Schubert 	kay->actor_priority = priority;
34525b9c547cSRui Paulo 
34535b9c547cSRui Paulo 	/* While actor acts as a key server, shall distribute sakey */
34545b9c547cSRui Paulo 	kay->dist_kn = 1;
34555b9c547cSRui Paulo 	kay->dist_an = 0;
34565b9c547cSRui Paulo 	kay->dist_time = 0;
34575b9c547cSRui Paulo 
34585b9c547cSRui Paulo 	kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
34595b9c547cSRui Paulo 	kay->macsec_csindex = DEFAULT_CS_INDEX;
34605b9c547cSRui Paulo 	kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
34615b9c547cSRui Paulo 	kay->mka_version = MKA_VERSION_ID;
34625b9c547cSRui Paulo 
34635b9c547cSRui Paulo 	os_memcpy(kay->algo_agility, mka_algo_agility,
34645b9c547cSRui Paulo 		  sizeof(kay->algo_agility));
34655b9c547cSRui Paulo 
34665b9c547cSRui Paulo 	dl_list_init(&kay->participant_list);
34675b9c547cSRui Paulo 
346885732ac8SCy Schubert 	if (policy != DO_NOT_SECURE &&
346985732ac8SCy Schubert 	    secy_get_capability(kay, &kay->macsec_capable) < 0)
347085732ac8SCy Schubert 		goto error;
347185732ac8SCy Schubert 
347285732ac8SCy Schubert 	if (policy == DO_NOT_SECURE ||
347385732ac8SCy Schubert 	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
34745b9c547cSRui Paulo 		kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
34755b9c547cSRui Paulo 		kay->macsec_desired = FALSE;
34765b9c547cSRui Paulo 		kay->macsec_protect = FALSE;
34774bc52338SCy Schubert 		kay->macsec_encrypt = FALSE;
34785b9c547cSRui Paulo 		kay->macsec_validate = Disabled;
34795b9c547cSRui Paulo 		kay->macsec_replay_protect = FALSE;
34805b9c547cSRui Paulo 		kay->macsec_replay_window = 0;
34815b9c547cSRui Paulo 		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
34824bc52338SCy Schubert 		kay->mka_hello_time = MKA_HELLO_TIME;
34835b9c547cSRui Paulo 	} else {
34845b9c547cSRui Paulo 		kay->macsec_desired = TRUE;
34855b9c547cSRui Paulo 		kay->macsec_protect = TRUE;
34864bc52338SCy Schubert 		if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF &&
34874bc52338SCy Schubert 		    policy == SHOULD_ENCRYPT) {
34884bc52338SCy Schubert 			kay->macsec_encrypt = TRUE;
34895b9c547cSRui Paulo 			kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
34904bc52338SCy Schubert 		} else { /* SHOULD_SECURE */
34914bc52338SCy Schubert 			kay->macsec_encrypt = FALSE;
349285732ac8SCy Schubert 			kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
34935b9c547cSRui Paulo 		}
34944bc52338SCy Schubert 		kay->macsec_validate = Strict;
34954bc52338SCy Schubert 		kay->macsec_replay_protect = macsec_replay_protect;
34964bc52338SCy Schubert 		kay->macsec_replay_window = macsec_replay_window;
34974bc52338SCy Schubert 		kay->mka_hello_time = MKA_HELLO_TIME;
34984bc52338SCy Schubert 	}
34995b9c547cSRui Paulo 
35005b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: state machine created");
35015b9c547cSRui Paulo 
35025b9c547cSRui Paulo 	/* Initialize the SecY must be prio to CP, as CP will control SecY */
350385732ac8SCy Schubert 	if (secy_init_macsec(kay) < 0) {
350485732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
350585732ac8SCy Schubert 		goto error;
350685732ac8SCy Schubert 	}
35075b9c547cSRui Paulo 
35085b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
35095b9c547cSRui Paulo 
35105b9c547cSRui Paulo 	/* init CP */
3511780fb4a2SCy Schubert 	kay->cp = ieee802_1x_cp_sm_init(kay);
351285732ac8SCy Schubert 	if (kay->cp == NULL)
351385732ac8SCy Schubert 		goto error;
35145b9c547cSRui Paulo 
35155b9c547cSRui Paulo 	if (policy == DO_NOT_SECURE) {
35165b9c547cSRui Paulo 		ieee802_1x_cp_connect_authenticated(kay->cp);
35175b9c547cSRui Paulo 		ieee802_1x_cp_sm_step(kay->cp);
35185b9c547cSRui Paulo 	} else {
35195b9c547cSRui Paulo 		kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
35205b9c547cSRui Paulo 					     kay_l2_receive, kay, 1);
35215b9c547cSRui Paulo 		if (kay->l2_mka == NULL) {
35225b9c547cSRui Paulo 			wpa_printf(MSG_WARNING,
35235b9c547cSRui Paulo 				   "KaY: Failed to initialize L2 packet processing for MKA packet");
352485732ac8SCy Schubert 			goto error;
35255b9c547cSRui Paulo 		}
35265b9c547cSRui Paulo 	}
35275b9c547cSRui Paulo 
35285b9c547cSRui Paulo 	return kay;
352985732ac8SCy Schubert 
353085732ac8SCy Schubert error:
353185732ac8SCy Schubert 	ieee802_1x_kay_deinit(kay);
353285732ac8SCy Schubert 	return NULL;
35335b9c547cSRui Paulo }
35345b9c547cSRui Paulo 
35355b9c547cSRui Paulo 
35365b9c547cSRui Paulo /**
35375b9c547cSRui Paulo  * ieee802_1x_kay_deinit -
35385b9c547cSRui Paulo  */
35395b9c547cSRui Paulo void
35405b9c547cSRui Paulo ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
35415b9c547cSRui Paulo {
35425b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
35435b9c547cSRui Paulo 
35445b9c547cSRui Paulo 	if (!kay)
35455b9c547cSRui Paulo 		return;
35465b9c547cSRui Paulo 
35475b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: state machine removed");
35485b9c547cSRui Paulo 
35495b9c547cSRui Paulo 	while (!dl_list_empty(&kay->participant_list)) {
35505b9c547cSRui Paulo 		participant = dl_list_entry(kay->participant_list.next,
35515b9c547cSRui Paulo 					    struct ieee802_1x_mka_participant,
35525b9c547cSRui Paulo 					    list);
35535b9c547cSRui Paulo 		ieee802_1x_kay_delete_mka(kay, &participant->ckn);
35545b9c547cSRui Paulo 	}
35555b9c547cSRui Paulo 
35565b9c547cSRui Paulo 	ieee802_1x_cp_sm_deinit(kay->cp);
35575b9c547cSRui Paulo 	secy_deinit_macsec(kay);
35585b9c547cSRui Paulo 
35595b9c547cSRui Paulo 	if (kay->l2_mka) {
35605b9c547cSRui Paulo 		l2_packet_deinit(kay->l2_mka);
35615b9c547cSRui Paulo 		kay->l2_mka = NULL;
35625b9c547cSRui Paulo 	}
35635b9c547cSRui Paulo 
35645b9c547cSRui Paulo 	os_free(kay->ctx);
35655b9c547cSRui Paulo 	os_free(kay);
35665b9c547cSRui Paulo }
35675b9c547cSRui Paulo 
35685b9c547cSRui Paulo 
35694bc52338SCy Schubert static const char * mode_txt(enum mka_created_mode mode)
35704bc52338SCy Schubert {
35714bc52338SCy Schubert 	switch (mode) {
35724bc52338SCy Schubert 	case PSK:
35734bc52338SCy Schubert 		return "PSK";
35744bc52338SCy Schubert 	case EAP_EXCHANGE:
35754bc52338SCy Schubert 		return "EAP";
35764bc52338SCy Schubert 	}
35774bc52338SCy Schubert 
35784bc52338SCy Schubert 	return "?";
35794bc52338SCy Schubert }
35804bc52338SCy Schubert 
35814bc52338SCy Schubert 
35825b9c547cSRui Paulo /**
35835b9c547cSRui Paulo  * ieee802_1x_kay_create_mka -
35845b9c547cSRui Paulo  */
35855b9c547cSRui Paulo struct ieee802_1x_mka_participant *
358685732ac8SCy Schubert ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
358785732ac8SCy Schubert 			  const struct mka_key_name *ckn,
358885732ac8SCy Schubert 			  const struct mka_key *cak, u32 life,
35895b9c547cSRui Paulo 			  enum mka_created_mode mode, Boolean is_authenticator)
35905b9c547cSRui Paulo {
35915b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
35925b9c547cSRui Paulo 	unsigned int usecs;
35935b9c547cSRui Paulo 
35944bc52338SCy Schubert 	wpa_printf(MSG_DEBUG,
35954bc52338SCy Schubert 		   "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)",
35964bc52338SCy Schubert 		   kay->if_name, mode_txt(mode), yes_no(is_authenticator));
35974bc52338SCy Schubert 
35985b9c547cSRui Paulo 	if (!kay || !ckn || !cak) {
35995b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
36005b9c547cSRui Paulo 		return NULL;
36015b9c547cSRui Paulo 	}
36025b9c547cSRui Paulo 
36034bc52338SCy Schubert 	if (cak->len != 16 && cak->len != 32) {
36044bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u",
36054bc52338SCy Schubert 			   (unsigned int) cak->len);
36065b9c547cSRui Paulo 		return NULL;
36075b9c547cSRui Paulo 	}
36085b9c547cSRui Paulo 	if (ckn->len > MAX_CKN_LEN) {
36094bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)");
36105b9c547cSRui Paulo 		return NULL;
36115b9c547cSRui Paulo 	}
36125b9c547cSRui Paulo 	if (!kay->enable) {
36135b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
36145b9c547cSRui Paulo 		return NULL;
36155b9c547cSRui Paulo 	}
36165b9c547cSRui Paulo 
36175b9c547cSRui Paulo 	participant = os_zalloc(sizeof(*participant));
36185b9c547cSRui Paulo 	if (!participant) {
36195b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
36205b9c547cSRui Paulo 		return NULL;
36215b9c547cSRui Paulo 	}
36225b9c547cSRui Paulo 
36235b9c547cSRui Paulo 	participant->ckn.len = ckn->len;
36245b9c547cSRui Paulo 	os_memcpy(participant->ckn.name, ckn->name, ckn->len);
36254bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name,
36264bc52338SCy Schubert 		    participant->ckn.len);
36275b9c547cSRui Paulo 	participant->cak.len = cak->len;
36285b9c547cSRui Paulo 	os_memcpy(participant->cak.key, cak->key, cak->len);
36294bc52338SCy Schubert 	wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key,
36304bc52338SCy Schubert 			participant->cak.len);
36315b9c547cSRui Paulo 	if (life)
36325b9c547cSRui Paulo 		participant->cak_life = life + time(NULL);
36335b9c547cSRui Paulo 
36345b9c547cSRui Paulo 	switch (mode) {
36355b9c547cSRui Paulo 	case EAP_EXCHANGE:
36365b9c547cSRui Paulo 		if (is_authenticator) {
36375b9c547cSRui Paulo 			participant->is_obliged_key_server = TRUE;
36385b9c547cSRui Paulo 			participant->can_be_key_server = TRUE;
36395b9c547cSRui Paulo 			participant->is_key_server = TRUE;
36405b9c547cSRui Paulo 			participant->principal = TRUE;
36415b9c547cSRui Paulo 
36425b9c547cSRui Paulo 			os_memcpy(&kay->key_server_sci, &kay->actor_sci,
36435b9c547cSRui Paulo 				  sizeof(kay->key_server_sci));
36445b9c547cSRui Paulo 			kay->key_server_priority = kay->actor_priority;
36455b9c547cSRui Paulo 			participant->is_elected = TRUE;
36465b9c547cSRui Paulo 		} else {
36475b9c547cSRui Paulo 			participant->is_obliged_key_server = FALSE;
36485b9c547cSRui Paulo 			participant->can_be_key_server = FALSE;
36495b9c547cSRui Paulo 			participant->is_key_server = FALSE;
36505b9c547cSRui Paulo 			participant->is_elected = TRUE;
36515b9c547cSRui Paulo 		}
36525b9c547cSRui Paulo 		break;
36535b9c547cSRui Paulo 
36545b9c547cSRui Paulo 	default:
36555b9c547cSRui Paulo 		participant->is_obliged_key_server = FALSE;
36565b9c547cSRui Paulo 		participant->can_be_key_server = TRUE;
3657780fb4a2SCy Schubert 		participant->is_key_server = TRUE;
36585b9c547cSRui Paulo 		participant->is_elected = FALSE;
36595b9c547cSRui Paulo 		break;
36605b9c547cSRui Paulo 	}
36615b9c547cSRui Paulo 
36625b9c547cSRui Paulo 	participant->cached = FALSE;
36635b9c547cSRui Paulo 
36645b9c547cSRui Paulo 	participant->active = FALSE;
36655b9c547cSRui Paulo 	participant->participant = FALSE;
36665b9c547cSRui Paulo 	participant->retain = FALSE;
36675b9c547cSRui Paulo 	participant->activate = DEFAULT;
36685b9c547cSRui Paulo 
36695b9c547cSRui Paulo 	if (participant->is_key_server)
36705b9c547cSRui Paulo 		participant->principal = TRUE;
36715b9c547cSRui Paulo 
36725b9c547cSRui Paulo 	dl_list_init(&participant->live_peers);
36735b9c547cSRui Paulo 	dl_list_init(&participant->potential_peers);
36745b9c547cSRui Paulo 
36755b9c547cSRui Paulo 	participant->retry_count = 0;
36765b9c547cSRui Paulo 	participant->kay = kay;
36775b9c547cSRui Paulo 
3678780fb4a2SCy Schubert 	if (!reset_participant_mi(participant))
36795b9c547cSRui Paulo 		goto fail;
36804bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s",
36814bc52338SCy Schubert 		   mi_txt(participant->mi));
36825b9c547cSRui Paulo 
36835b9c547cSRui Paulo 	participant->lrx = FALSE;
36845b9c547cSRui Paulo 	participant->ltx = FALSE;
36855b9c547cSRui Paulo 	participant->orx = FALSE;
36865b9c547cSRui Paulo 	participant->otx = FALSE;
36875b9c547cSRui Paulo 	participant->to_dist_sak = FALSE;
36885b9c547cSRui Paulo 	participant->to_use_sak = FALSE;
36895b9c547cSRui Paulo 	participant->new_sak = FALSE;
36905b9c547cSRui Paulo 	dl_list_init(&participant->sak_list);
36915b9c547cSRui Paulo 	participant->new_key = NULL;
36925b9c547cSRui Paulo 	dl_list_init(&participant->rxsc_list);
369385732ac8SCy Schubert 	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
36945b9c547cSRui Paulo 	secy_cp_control_protect_frames(kay, kay->macsec_protect);
36955b9c547cSRui Paulo 	secy_cp_control_replay(kay, kay->macsec_replay_protect,
36965b9c547cSRui Paulo 			       kay->macsec_replay_window);
36974bc52338SCy Schubert 	if (secy_create_transmit_sc(kay, participant->txsc))
36984bc52338SCy Schubert 		goto fail;
36995b9c547cSRui Paulo 
37005b9c547cSRui Paulo 	/* to derive KEK from CAK and CKN */
37014bc52338SCy Schubert 	participant->kek.len = participant->cak.len;
37025b9c547cSRui Paulo 	if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
37034bc52338SCy Schubert 						    participant->cak.len,
37045b9c547cSRui Paulo 						    participant->ckn.name,
37055b9c547cSRui Paulo 						    participant->ckn.len,
37064bc52338SCy Schubert 						    participant->kek.key,
37074bc52338SCy Schubert 						    participant->kek.len)) {
37084bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: KEK derivation failed");
37095b9c547cSRui Paulo 		goto fail;
37105b9c547cSRui Paulo 	}
37115b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
37125b9c547cSRui Paulo 			participant->kek.key, participant->kek.len);
37135b9c547cSRui Paulo 
37145b9c547cSRui Paulo 	/* to derive ICK from CAK and CKN */
37154bc52338SCy Schubert 	participant->ick.len = participant->cak.len;
37165b9c547cSRui Paulo 	if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
37174bc52338SCy Schubert 						    participant->cak.len,
37185b9c547cSRui Paulo 						    participant->ckn.name,
37195b9c547cSRui Paulo 						    participant->ckn.len,
37204bc52338SCy Schubert 						    participant->ick.key,
37214bc52338SCy Schubert 						    participant->ick.len)) {
37224bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: ICK derivation failed");
37235b9c547cSRui Paulo 		goto fail;
37245b9c547cSRui Paulo 	}
37255b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
37265b9c547cSRui Paulo 			participant->ick.key, participant->ick.len);
37275b9c547cSRui Paulo 
37285b9c547cSRui Paulo 	dl_list_add(&kay->participant_list, &participant->list);
37295b9c547cSRui Paulo 
37304bc52338SCy Schubert 	usecs = os_random() % (kay->mka_hello_time * 1000);
37315b9c547cSRui Paulo 	eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
37325b9c547cSRui Paulo 			       participant, NULL);
373385732ac8SCy Schubert 
373485732ac8SCy Schubert 	/* Disable MKA lifetime for PSK mode.
373585732ac8SCy Schubert 	 * The peer(s) can take a long time to come up, because we
373685732ac8SCy Schubert 	 * create a "standby" MKA, and we need it to remain live until
373785732ac8SCy Schubert 	 * some peer appears.
373885732ac8SCy Schubert 	 */
373985732ac8SCy Schubert 	if (mode != PSK) {
37405b9c547cSRui Paulo 		participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
37415b9c547cSRui Paulo 			usecs / 1000000;
374285732ac8SCy Schubert 	}
374385732ac8SCy Schubert 	participant->mode = mode;
37445b9c547cSRui Paulo 
37455b9c547cSRui Paulo 	return participant;
37465b9c547cSRui Paulo 
37475b9c547cSRui Paulo fail:
37484bc52338SCy Schubert 	os_free(participant->txsc);
37495b9c547cSRui Paulo 	os_free(participant);
37505b9c547cSRui Paulo 	return NULL;
37515b9c547cSRui Paulo }
37525b9c547cSRui Paulo 
37535b9c547cSRui Paulo 
37545b9c547cSRui Paulo /**
37555b9c547cSRui Paulo  * ieee802_1x_kay_delete_mka -
37565b9c547cSRui Paulo  */
37575b9c547cSRui Paulo void
37585b9c547cSRui Paulo ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
37595b9c547cSRui Paulo {
37605b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
37615b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
37625b9c547cSRui Paulo 	struct data_key *sak;
37635b9c547cSRui Paulo 	struct receive_sc *rxsc;
37645b9c547cSRui Paulo 
37655b9c547cSRui Paulo 	if (!kay || !ckn)
37665b9c547cSRui Paulo 		return;
37675b9c547cSRui Paulo 
37685b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: participant removed");
37695b9c547cSRui Paulo 
37705b9c547cSRui Paulo 	/* get the participant */
377185732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
37725b9c547cSRui Paulo 	if (!participant) {
37735b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
37745b9c547cSRui Paulo 			    ckn->name, ckn->len);
37755b9c547cSRui Paulo 		return;
37765b9c547cSRui Paulo 	}
37775b9c547cSRui Paulo 
3778780fb4a2SCy Schubert 	eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL);
37795b9c547cSRui Paulo 	dl_list_del(&participant->list);
37805b9c547cSRui Paulo 
37815b9c547cSRui Paulo 	/* remove live peer */
37825b9c547cSRui Paulo 	while (!dl_list_empty(&participant->live_peers)) {
37835b9c547cSRui Paulo 		peer = dl_list_entry(participant->live_peers.next,
37845b9c547cSRui Paulo 				     struct ieee802_1x_kay_peer, list);
37855b9c547cSRui Paulo 		dl_list_del(&peer->list);
37865b9c547cSRui Paulo 		os_free(peer);
37875b9c547cSRui Paulo 	}
37885b9c547cSRui Paulo 
37895b9c547cSRui Paulo 	/* remove potential peer */
37905b9c547cSRui Paulo 	while (!dl_list_empty(&participant->potential_peers)) {
37915b9c547cSRui Paulo 		peer = dl_list_entry(participant->potential_peers.next,
37925b9c547cSRui Paulo 				     struct ieee802_1x_kay_peer, list);
37935b9c547cSRui Paulo 		dl_list_del(&peer->list);
37945b9c547cSRui Paulo 		os_free(peer);
37955b9c547cSRui Paulo 	}
37965b9c547cSRui Paulo 
37975b9c547cSRui Paulo 	/* remove sak */
37985b9c547cSRui Paulo 	while (!dl_list_empty(&participant->sak_list)) {
37995b9c547cSRui Paulo 		sak = dl_list_entry(participant->sak_list.next,
38005b9c547cSRui Paulo 				    struct data_key, list);
38015b9c547cSRui Paulo 		dl_list_del(&sak->list);
380285732ac8SCy Schubert 		ieee802_1x_kay_deinit_data_key(sak);
38035b9c547cSRui Paulo 	}
38045b9c547cSRui Paulo 	while (!dl_list_empty(&participant->rxsc_list)) {
38055b9c547cSRui Paulo 		rxsc = dl_list_entry(participant->rxsc_list.next,
38065b9c547cSRui Paulo 				     struct receive_sc, list);
38075b9c547cSRui Paulo 		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
38085b9c547cSRui Paulo 	}
38095b9c547cSRui Paulo 	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
38105b9c547cSRui Paulo 
38115b9c547cSRui Paulo 	os_memset(&participant->cak, 0, sizeof(participant->cak));
38125b9c547cSRui Paulo 	os_memset(&participant->kek, 0, sizeof(participant->kek));
38135b9c547cSRui Paulo 	os_memset(&participant->ick, 0, sizeof(participant->ick));
38145b9c547cSRui Paulo 	os_free(participant);
38155b9c547cSRui Paulo }
38165b9c547cSRui Paulo 
38175b9c547cSRui Paulo 
38185b9c547cSRui Paulo /**
38195b9c547cSRui Paulo  * ieee802_1x_kay_mka_participate -
38205b9c547cSRui Paulo  */
38215b9c547cSRui Paulo void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
38225b9c547cSRui Paulo 				    struct mka_key_name *ckn,
38235b9c547cSRui Paulo 				    Boolean status)
38245b9c547cSRui Paulo {
38255b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
38265b9c547cSRui Paulo 
38275b9c547cSRui Paulo 	if (!kay || !ckn)
38285b9c547cSRui Paulo 		return;
38295b9c547cSRui Paulo 
383085732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
38315b9c547cSRui Paulo 	if (!participant)
38325b9c547cSRui Paulo 		return;
38335b9c547cSRui Paulo 
38345b9c547cSRui Paulo 	participant->active = status;
38355b9c547cSRui Paulo }
38365b9c547cSRui Paulo 
38375b9c547cSRui Paulo 
38385b9c547cSRui Paulo /**
38395b9c547cSRui Paulo  * ieee802_1x_kay_new_sak -
38405b9c547cSRui Paulo  */
38415b9c547cSRui Paulo int
38425b9c547cSRui Paulo ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
38435b9c547cSRui Paulo {
38445b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
38455b9c547cSRui Paulo 
38465b9c547cSRui Paulo 	if (!kay)
38475b9c547cSRui Paulo 		return -1;
38485b9c547cSRui Paulo 
38495b9c547cSRui Paulo 	participant = ieee802_1x_kay_get_principal_participant(kay);
38505b9c547cSRui Paulo 	if (!participant)
38515b9c547cSRui Paulo 		return -1;
38525b9c547cSRui Paulo 
38535b9c547cSRui Paulo 	participant->new_sak = TRUE;
38545b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
38555b9c547cSRui Paulo 
38565b9c547cSRui Paulo 	return 0;
38575b9c547cSRui Paulo }
38585b9c547cSRui Paulo 
38595b9c547cSRui Paulo 
38605b9c547cSRui Paulo /**
38615b9c547cSRui Paulo  * ieee802_1x_kay_change_cipher_suite -
38625b9c547cSRui Paulo  */
38635b9c547cSRui Paulo int
3864780fb4a2SCy Schubert ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
3865780fb4a2SCy Schubert 				   unsigned int cs_index)
38665b9c547cSRui Paulo {
38675b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
386885732ac8SCy Schubert 	enum macsec_cap secy_cap;
38695b9c547cSRui Paulo 
38705b9c547cSRui Paulo 	if (!kay)
38715b9c547cSRui Paulo 		return -1;
38725b9c547cSRui Paulo 
3873780fb4a2SCy Schubert 	if (cs_index >= CS_TABLE_SIZE) {
38745b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
38755b9c547cSRui Paulo 			   "KaY: Configured cipher suite index is out of range");
38765b9c547cSRui Paulo 		return -1;
38775b9c547cSRui Paulo 	}
38785b9c547cSRui Paulo 	if (kay->macsec_csindex == cs_index)
38795b9c547cSRui Paulo 		return -2;
38805b9c547cSRui Paulo 
38815b9c547cSRui Paulo 	if (cs_index == 0)
38825b9c547cSRui Paulo 		kay->macsec_desired = FALSE;
38835b9c547cSRui Paulo 
38845b9c547cSRui Paulo 	kay->macsec_csindex = cs_index;
38855b9c547cSRui Paulo 	kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
38865b9c547cSRui Paulo 
388785732ac8SCy Schubert 	if (secy_get_capability(kay, &secy_cap) < 0)
388885732ac8SCy Schubert 		return -3;
388985732ac8SCy Schubert 
389085732ac8SCy Schubert 	if (kay->macsec_capable > secy_cap)
389185732ac8SCy Schubert 		kay->macsec_capable = secy_cap;
389285732ac8SCy Schubert 
38935b9c547cSRui Paulo 	participant = ieee802_1x_kay_get_principal_participant(kay);
38945b9c547cSRui Paulo 	if (participant) {
38955b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
38965b9c547cSRui Paulo 		participant->new_sak = TRUE;
38975b9c547cSRui Paulo 	}
38985b9c547cSRui Paulo 
38995b9c547cSRui Paulo 	return 0;
39005b9c547cSRui Paulo }
390185732ac8SCy Schubert 
390285732ac8SCy Schubert 
390385732ac8SCy Schubert #ifdef CONFIG_CTRL_IFACE
39044bc52338SCy Schubert 
390585732ac8SCy Schubert /**
390685732ac8SCy Schubert  * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
390785732ac8SCy Schubert  * @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
390885732ac8SCy Schubert  * @buf: Buffer for status information
390985732ac8SCy Schubert  * @buflen: Maximum buffer length
391085732ac8SCy Schubert  * @verbose: Whether to include verbose status information
391185732ac8SCy Schubert  * Returns: Number of bytes written to buf.
391285732ac8SCy Schubert  *
39134bc52338SCy Schubert  * Query KaY status information. This function fills in a text area with current
391485732ac8SCy Schubert  * status information. If the buffer (buf) is not large enough, status
391585732ac8SCy Schubert  * information will be truncated to fit the buffer.
391685732ac8SCy Schubert  */
391785732ac8SCy Schubert int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
391885732ac8SCy Schubert 			      size_t buflen)
391985732ac8SCy Schubert {
39204bc52338SCy Schubert 	char *pos, *end;
39214bc52338SCy Schubert 	int res, count;
39224bc52338SCy Schubert 	struct ieee802_1x_mka_participant *p;
392385732ac8SCy Schubert 
392485732ac8SCy Schubert 	if (!kay)
392585732ac8SCy Schubert 		return 0;
392685732ac8SCy Schubert 
39274bc52338SCy Schubert 	pos = buf;
39284bc52338SCy Schubert 	end = buf + buflen;
39294bc52338SCy Schubert 
39304bc52338SCy Schubert 	res = os_snprintf(pos, end - pos,
393185732ac8SCy Schubert 			  "PAE KaY status=%s\n"
393285732ac8SCy Schubert 			  "Authenticated=%s\n"
393385732ac8SCy Schubert 			  "Secured=%s\n"
393485732ac8SCy Schubert 			  "Failed=%s\n"
393585732ac8SCy Schubert 			  "Actor Priority=%u\n"
393685732ac8SCy Schubert 			  "Key Server Priority=%u\n"
393785732ac8SCy Schubert 			  "Is Key Server=%s\n"
393885732ac8SCy Schubert 			  "Number of Keys Distributed=%u\n"
39394bc52338SCy Schubert 			  "Number of Keys Received=%u\n"
39404bc52338SCy Schubert 			  "MKA Hello Time=%u\n",
394185732ac8SCy Schubert 			  kay->active ? "Active" : "Not-Active",
394285732ac8SCy Schubert 			  kay->authenticated ? "Yes" : "No",
394385732ac8SCy Schubert 			  kay->secured ? "Yes" : "No",
394485732ac8SCy Schubert 			  kay->failed ? "Yes" : "No",
394585732ac8SCy Schubert 			  kay->actor_priority,
394685732ac8SCy Schubert 			  kay->key_server_priority,
394785732ac8SCy Schubert 			  kay->is_key_server ? "Yes" : "No",
394885732ac8SCy Schubert 			  kay->dist_kn - 1,
39494bc52338SCy Schubert 			  kay->rcvd_keys,
39504bc52338SCy Schubert 			  kay->mka_hello_time);
39514bc52338SCy Schubert 	if (os_snprintf_error(buflen, res))
39524bc52338SCy Schubert 		return 0;
39534bc52338SCy Schubert 	pos += res;
39544bc52338SCy Schubert 
39554bc52338SCy Schubert 	res = os_snprintf(pos, end - pos,
39564bc52338SCy Schubert 			  "actor_sci=%s\n", sci_txt(&kay->actor_sci));
39574bc52338SCy Schubert 	if (os_snprintf_error(buflen, res))
39584bc52338SCy Schubert 		return end - pos;
39594bc52338SCy Schubert 	pos += res;
39604bc52338SCy Schubert 
39614bc52338SCy Schubert 	res = os_snprintf(pos, end - pos,
39624bc52338SCy Schubert 			  "key_server_sci=%s\n", sci_txt(&kay->key_server_sci));
39634bc52338SCy Schubert 	if (os_snprintf_error(buflen, res))
39644bc52338SCy Schubert 		return end - pos;
39654bc52338SCy Schubert 	pos += res;
39664bc52338SCy Schubert 
39674bc52338SCy Schubert 	count = 0;
39684bc52338SCy Schubert 	dl_list_for_each(p, &kay->participant_list,
39694bc52338SCy Schubert 			 struct ieee802_1x_mka_participant, list) {
39704bc52338SCy Schubert 		char *pos2 = pos;
39714bc52338SCy Schubert 
39724bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=",
39734bc52338SCy Schubert 			count);
39744bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
39754bc52338SCy Schubert 			return end - pos;
39764bc52338SCy Schubert 		pos2 += res;
39774bc52338SCy Schubert 		count++;
39784bc52338SCy Schubert 
39794bc52338SCy Schubert 		pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
39804bc52338SCy Schubert 					 p->ckn.len);
39814bc52338SCy Schubert 
39824bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2,
39834bc52338SCy Schubert 				  "\nmi=%s\n"
39844bc52338SCy Schubert 				  "mn=%u\n"
39854bc52338SCy Schubert 				  "active=%s\n"
39864bc52338SCy Schubert 				  "participant=%s\n"
39874bc52338SCy Schubert 				  "retain=%s\n"
39884bc52338SCy Schubert 				  "live_peers=%u\n"
39894bc52338SCy Schubert 				  "potential_peers=%u\n"
39904bc52338SCy Schubert 				  "is_key_server=%s\n"
39914bc52338SCy Schubert 				  "is_elected=%s\n",
39924bc52338SCy Schubert 				  mi_txt(p->mi), p->mn,
39934bc52338SCy Schubert 				  yes_no(p->active),
39944bc52338SCy Schubert 				  yes_no(p->participant),
39954bc52338SCy Schubert 				  yes_no(p->retain),
39964bc52338SCy Schubert 				  dl_list_len(&p->live_peers),
39974bc52338SCy Schubert 				  dl_list_len(&p->potential_peers),
39984bc52338SCy Schubert 				  yes_no(p->is_key_server),
39994bc52338SCy Schubert 				  yes_no(p->is_elected));
40004bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
40014bc52338SCy Schubert 			return end - pos;
40024bc52338SCy Schubert 		pos2 += res;
40034bc52338SCy Schubert 		pos = pos2;
40044bc52338SCy Schubert 	}
40054bc52338SCy Schubert 
40064bc52338SCy Schubert 	return pos - buf;
40074bc52338SCy Schubert }
40084bc52338SCy Schubert 
40094bc52338SCy Schubert 
40104bc52338SCy Schubert static const char * true_false(Boolean val)
40114bc52338SCy Schubert {
40124bc52338SCy Schubert 	return val ? "true" : "false";
40134bc52338SCy Schubert }
40144bc52338SCy Schubert 
40154bc52338SCy Schubert 
40164bc52338SCy Schubert static const char * activate_control_txt(enum activate_ctrl activate)
40174bc52338SCy Schubert {
40184bc52338SCy Schubert 	switch (activate) {
40194bc52338SCy Schubert 	case DEFAULT:
40204bc52338SCy Schubert 		return "default";
40214bc52338SCy Schubert 	case DISABLED:
40224bc52338SCy Schubert 		return "disabled";
40234bc52338SCy Schubert 	case ON_OPER_UP:
40244bc52338SCy Schubert 		return "onOperUp";
40254bc52338SCy Schubert 	case ALWAYS:
40264bc52338SCy Schubert 		return "always";
40274bc52338SCy Schubert 	}
40284bc52338SCy Schubert 
40294bc52338SCy Schubert 	return "?";
40304bc52338SCy Schubert }
40314bc52338SCy Schubert 
40324bc52338SCy Schubert 
40334bc52338SCy Schubert static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf,
40344bc52338SCy Schubert 			   char *end)
40354bc52338SCy Schubert {
40364bc52338SCy Schubert 	char *pos = buf;
40374bc52338SCy Schubert 	struct ieee802_1x_kay_peer *p;
40384bc52338SCy Schubert 	int res;
40394bc52338SCy Schubert 
40404bc52338SCy Schubert 	dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) {
40414bc52338SCy Schubert 		res = os_snprintf(pos, end - pos,
40424bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListMI=%s\n"
40434bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListMN=%u\n"
40444bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListType=%u\n"
40454bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListSCI=%s\n",
40464bc52338SCy Schubert 				  mi_txt(p->mi),
40474bc52338SCy Schubert 				  p->mn,
40484bc52338SCy Schubert 				  live ? 1 : 2,
40494bc52338SCy Schubert 				  sci_txt(&p->sci));
40504bc52338SCy Schubert 		if (os_snprintf_error(end - pos, res))
40514bc52338SCy Schubert 			return pos;
40524bc52338SCy Schubert 		pos += res;
40534bc52338SCy Schubert 	}
40544bc52338SCy Schubert 
40554bc52338SCy Schubert 	return pos;
40564bc52338SCy Schubert }
40574bc52338SCy Schubert 
40584bc52338SCy Schubert 
40594bc52338SCy Schubert int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
40604bc52338SCy Schubert 			   size_t buflen)
40614bc52338SCy Schubert {
40624bc52338SCy Schubert 	char *pos, *end;
40634bc52338SCy Schubert 	int res;
40644bc52338SCy Schubert 	struct ieee802_1x_mka_participant *p;
40654bc52338SCy Schubert 
40664bc52338SCy Schubert 	if (!kay)
406785732ac8SCy Schubert 		return 0;
406885732ac8SCy Schubert 
40694bc52338SCy Schubert 	pos = buf;
40704bc52338SCy Schubert 	end = buf + buflen;
40714bc52338SCy Schubert 
40724bc52338SCy Schubert 	dl_list_for_each(p, &kay->participant_list,
40734bc52338SCy Schubert 			 struct ieee802_1x_mka_participant, list) {
40744bc52338SCy Schubert 		char *pos2 = pos;
40754bc52338SCy Schubert 
40764bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN=");
40774bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
40784bc52338SCy Schubert 			return end - pos;
40794bc52338SCy Schubert 		pos2 += res;
40804bc52338SCy Schubert 
40814bc52338SCy Schubert 		pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
40824bc52338SCy Schubert 					 p->ckn.len);
40834bc52338SCy Schubert 
40844bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2,
40854bc52338SCy Schubert 				  "\nieee8021XKayMkaPartCached=%s\n"
40864bc52338SCy Schubert 				  "ieee8021XKayMkaPartActive=%s\n"
40874bc52338SCy Schubert 				  "ieee8021XKayMkaPartRetain=%s\n"
40884bc52338SCy Schubert 				  "ieee8021XKayMkaPartActivateControl=%s\n"
40894bc52338SCy Schubert 				  "ieee8021XKayMkaPartPrincipal=%s\n",
40904bc52338SCy Schubert 				  true_false(p->cached),
40914bc52338SCy Schubert 				  true_false(p->active),
40924bc52338SCy Schubert 				  true_false(p->retain),
40934bc52338SCy Schubert 				  activate_control_txt(p->activate),
40944bc52338SCy Schubert 				  true_false(p->principal));
40954bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
40964bc52338SCy Schubert 			return end - pos;
40974bc52338SCy Schubert 		pos2 += res;
40984bc52338SCy Schubert 		pos = pos2;
40994bc52338SCy Schubert 
41004bc52338SCy Schubert 		pos = mka_mib_peer(&p->live_peers, TRUE, pos, end);
41014bc52338SCy Schubert 		pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end);
410285732ac8SCy Schubert 	}
41034bc52338SCy Schubert 
41044bc52338SCy Schubert 	return pos - buf;
41054bc52338SCy Schubert }
41064bc52338SCy Schubert 
410785732ac8SCy Schubert #endif /* CONFIG_CTRL_IFACE */
4108