xref: /freebsd/contrib/wpa/src/pae/ieee802_1x_kay.c (revision 4bc523382c7e72183c32be2c3aedecc1f5e844dd)
15b9c547cSRui Paulo /*
2*4bc52338SCy 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 
30*4bc52338SCy Schubert #define MAX_MISSING_SAK_USE 10  /* Accept up to 10 inbound MKPDUs without
31*4bc52338SCy Schubert 				 * SAK-USE before dropping */
32*4bc52338SCy 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 
67*4bc52338SCy Schubert 		.cak_trfm = ieee802_1x_cak_aes_cmac,
68*4bc52338SCy Schubert 		.ckn_trfm = ieee802_1x_ckn_aes_cmac,
69*4bc52338SCy Schubert 		.kek_trfm = ieee802_1x_kek_aes_cmac,
70*4bc52338SCy Schubert 		.ick_trfm = ieee802_1x_ick_aes_cmac,
71*4bc52338SCy 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 
107*4bc52338SCy Schubert static const char * mi_txt(const u8 *mi)
108*4bc52338SCy Schubert {
109*4bc52338SCy Schubert 	static char txt[MI_LEN * 2 + 1];
110*4bc52338SCy Schubert 
111*4bc52338SCy Schubert 	wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN);
112*4bc52338SCy Schubert 	return txt;
113*4bc52338SCy Schubert }
114*4bc52338SCy Schubert 
115*4bc52338SCy Schubert 
116*4bc52338SCy Schubert static const char * sci_txt(const struct ieee802_1x_mka_sci *sci)
117*4bc52338SCy Schubert {
118*4bc52338SCy Schubert 	static char txt[ETH_ALEN * 3 + 1 + 5 + 1];
119*4bc52338SCy Schubert 
120*4bc52338SCy Schubert 	os_snprintf(txt, sizeof(txt), MACSTR "@%u",
121*4bc52338SCy Schubert 		    MAC2STR(sci->addr), be_to_host16(sci->port));
122*4bc52338SCy Schubert 	return txt;
123*4bc52338SCy Schubert }
124*4bc52338SCy Schubert 
125*4bc52338SCy Schubert 
126*4bc52338SCy Schubert static const char * algo_agility_txt(const u8 *algo_agility)
127*4bc52338SCy Schubert {
128*4bc52338SCy Schubert 	static char txt[4 * 2 + 1];
129*4bc52338SCy Schubert 
130*4bc52338SCy Schubert 	wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4);
131*4bc52338SCy Schubert 	return txt;
132*4bc52338SCy Schubert }
133*4bc52338SCy Schubert 
134*4bc52338SCy 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 
146*4bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-8 */
1475b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
148*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set");
149*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version);
150*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority);
151*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server);
152*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired);
153*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d",
154*4bc52338SCy Schubert 		   body->macsec_capability);
155*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len);
156*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci));
157*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s",
158*4bc52338SCy Schubert 		   mi_txt(body->actor_mi));
159*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d",
1605b9c547cSRui Paulo 		   be_to_host32(body->actor_mn));
161*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s",
162*4bc52338SCy Schubert 		   algo_agility_txt(body->algo_agility));
163*4bc52338SCy 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 
182*4bc52338SCy 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) {
185*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "Live Peer List parameter set");
186*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
1875b9c547cSRui Paulo 	} else if (body->type == MKA_POTENTIAL_PEER_LIST) {
188*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "Potential Peer List parameter set");
189*4bc52338SCy 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));
195*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "\tMember Id: %s  Message Number: %d",
196*4bc52338SCy 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 
212*4bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */
2135b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
214*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "Distributed SAK parameter set");
215*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan);
216*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d",
2175b9c547cSRui Paulo 		   body->confid_offset);
218*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len);
2195b9c547cSRui Paulo 	if (!body_len)
2205b9c547cSRui Paulo 		return;
2215b9c547cSRui Paulo 
222*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tKey Number............: %d",
2235b9c547cSRui Paulo 		   be_to_host32(body->kn));
224*4bc52338SCy Schubert 	/* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */
225*4bc52338SCy 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 
246*4bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, Figure 11-10 */
2475b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
248*4bc52338SCy 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));
252*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan);
253*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx));
254*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx));
255*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx));
256*4bc52338SCy 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 
263*4bc52338SCy 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));
268*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi));
269*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u",
2705b9c547cSRui Paulo 		   be_to_host32(body->okn));
271*4bc52338SCy 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,
398*4bc52338SCy 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++) {
408*4bc52338SCy Schubert 		if (cipher_suite_tbl[i].id == cs) {
409*4bc52338SCy Schubert 			*idx = i;
4105b9c547cSRui Paulo 			return &cipher_suite_tbl[i];
4115b9c547cSRui Paulo 		}
412*4bc52338SCy 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,
493*4bc52338SCy 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);
540*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s",
541*4bc52338SCy 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 {
578*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "\tMI: %s  MN: %d  SCI: %s",
579*4bc52338SCy 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;
598*4bc52338SCy 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 
627*4bc52338SCy Schubert 	if (secy_create_receive_sc(participant->kay, rxsc)) {
628*4bc52338SCy Schubert 		os_free(rxsc);
629*4bc52338SCy Schubert 		os_free(peer);
630*4bc52338SCy Schubert 		return NULL;
631*4bc52338SCy 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 
657*4bc52338SCy 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 
687*4bc52338SCy 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);
691*4bc52338SCy Schubert 	if (secy_create_receive_sc(participant->kay, rxsc)) {
692*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer");
693*4bc52338SCy Schubert 		os_free(rxsc);
694*4bc52338SCy Schubert 		os_free(peer);
695*4bc52338SCy Schubert 		return NULL;
696*4bc52338SCy 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;
741*4bc52338SCy Schubert 	unsigned int length = sizeof(struct ieee802_1x_mka_basic_body);
7425b9c547cSRui Paulo 
743*4bc52338SCy Schubert 	length += participant->ckn.len;
744*4bc52338SCy 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;
748*4bc52338SCy Schubert 	/* The Key Server flag is set if and only if the participant has not
749*4bc52338SCy 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,
805*4bc52338SCy 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) {
809*4bc52338SCy 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) {
823*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
824*4bc52338SCy 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;
832*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
833*4bc52338SCy Schubert 			   "KaY: Peer using my MI - selected a new random MI: %s",
834*4bc52338SCy 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) {
846*4bc52338SCy Schubert 		/* Check duplicated SCI
847*4bc52338SCy Schubert 		 *
848*4bc52338SCy Schubert 		 * A duplicated SCI indicates either an active attacker or
849*4bc52338SCy Schubert 		 * a valid peer whose MI is being changed. The latter scenario
850*4bc52338SCy Schubert 		 * is more likely because to have gotten this far the received
851*4bc52338SCy Schubert 		 * MKPDU must have had a valid ICV, indicating the peer holds
852*4bc52338SCy Schubert 		 * the same CAK as our participant.
853*4bc52338SCy Schubert 		 *
854*4bc52338SCy Schubert 		 * Before creating a new peer object for the new MI we must
855*4bc52338SCy Schubert 		 * clean up the resources (SCs and SAs) associated with the
856*4bc52338SCy Schubert 		 * old peer. An easy way to do this is to ignore MKPDUs with
857*4bc52338SCy Schubert 		 * the new MI's for now and just wait for the old peer to
858*4bc52338SCy Schubert 		 * time out and clean itself up (within MKA_LIFE_TIME).
859*4bc52338SCy Schubert 		 *
860*4bc52338SCy Schubert 		 * This method is preferable to deleting the old peer here
861*4bc52338SCy Schubert 		 * and now and continuing on with processing because if this
862*4bc52338SCy Schubert 		 * MKPDU is from an attacker it's better to ignore the MKPDU
863*4bc52338SCy 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) {
868*4bc52338SCy Schubert 			time_t new_expire;
869*4bc52338SCy Schubert 
8705b9c547cSRui Paulo 			wpa_printf(MSG_WARNING,
871*4bc52338SCy Schubert 				   "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU");
872*4bc52338SCy Schubert 			/* Reduce timeout to speed up this process but left the
873*4bc52338SCy Schubert 			 * chance for old one to prove aliveness. */
874*4bc52338SCy Schubert 			new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000;
875*4bc52338SCy Schubert 			if (peer->expire > new_expire)
876*4bc52338SCy Schubert 				peer->expire = new_expire;
877*4bc52338SCy 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));
883*4bc52338SCy Schubert 		if (!peer) {
884*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
885*4bc52338SCy Schubert 				   "KaY: No potential peer entry found - ignore MKPDU");
8865b9c547cSRui Paulo 			return NULL;
887*4bc52338SCy 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 {
900*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
901*4bc52338SCy 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;
1046*4bc52338SCy Schubert 	     left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN,
1047*4bc52338SCy 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,
1082*4bc52338SCy Schubert 				      MI_LEN) == 0) {
1083*4bc52338SCy Schubert 				u32 mn = be_to_host32(peer_mi->mn);
1084*4bc52338SCy Schubert 
1085*4bc52338SCy Schubert 				wpa_printf(MSG_DEBUG,
1086*4bc52338SCy Schubert 					   "KaY: My MI - received MN %u, most recently transmitted MN %u",
1087*4bc52338SCy Schubert 					   mn, participant->mn);
1088*4bc52338SCy Schubert 				if (mn == participant->mn)
10895b9c547cSRui Paulo 					return TRUE;
1090780fb4a2SCy Schubert 			}
10915b9c547cSRui Paulo 		}
1092*4bc52338SCy Schubert 	}
10935b9c547cSRui Paulo 
10945b9c547cSRui Paulo 	return FALSE;
10955b9c547cSRui Paulo }
10965b9c547cSRui Paulo 
10975b9c547cSRui Paulo 
10985b9c547cSRui Paulo /**
10995b9c547cSRui Paulo  * ieee802_1x_mka_decode_live_peer_body -
11005b9c547cSRui Paulo  */
11015b9c547cSRui Paulo static int ieee802_1x_mka_decode_live_peer_body(
11025b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
11035b9c547cSRui Paulo 	const u8 *peer_msg, size_t msg_len)
11045b9c547cSRui Paulo {
11055b9c547cSRui Paulo 	const struct ieee802_1x_mka_hdr *hdr;
11065b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
11075b9c547cSRui Paulo 	size_t body_len;
11085b9c547cSRui Paulo 	size_t i;
11095b9c547cSRui Paulo 	Boolean is_included;
11105b9c547cSRui Paulo 
11115b9c547cSRui Paulo 	is_included = ieee802_1x_kay_is_in_live_peer(
11125b9c547cSRui Paulo 		participant, participant->current_peer_id.mi);
11135b9c547cSRui Paulo 
11145b9c547cSRui Paulo 	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
11155b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
1116780fb4a2SCy Schubert 	if (body_len % 16 != 0) {
1117780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
1118780fb4a2SCy Schubert 			   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1119780fb4a2SCy Schubert 			   body_len);
1120780fb4a2SCy Schubert 		return -1;
1121780fb4a2SCy Schubert 	}
11225b9c547cSRui Paulo 
1123780fb4a2SCy Schubert 	for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
1124780fb4a2SCy Schubert 		const struct ieee802_1x_mka_peer_id *peer_mi;
1125780fb4a2SCy Schubert 		u32 peer_mn;
1126780fb4a2SCy Schubert 
1127780fb4a2SCy Schubert 		peer_mi = (const struct ieee802_1x_mka_peer_id *)
1128780fb4a2SCy Schubert 			(peer_msg + MKA_HDR_LEN + i);
1129780fb4a2SCy Schubert 		peer_mn = be_to_host32(peer_mi->mn);
11305b9c547cSRui Paulo 
11315b9c547cSRui Paulo 		/* it is myself */
11325b9c547cSRui Paulo 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
11335b9c547cSRui Paulo 			/* My message id is used by other participant */
1134780fb4a2SCy Schubert 			if (peer_mn > participant->mn &&
1135780fb4a2SCy Schubert 			    !reset_participant_mi(participant))
1136780fb4a2SCy Schubert 				wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
11375b9c547cSRui Paulo 			continue;
11385b9c547cSRui Paulo 		}
1139780fb4a2SCy Schubert 
11405b9c547cSRui Paulo 		if (!is_included)
11415b9c547cSRui Paulo 			continue;
11425b9c547cSRui Paulo 
1143780fb4a2SCy Schubert 		peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi);
1144780fb4a2SCy Schubert 		if (peer) {
11455b9c547cSRui Paulo 			peer->mn = peer_mn;
1146780fb4a2SCy Schubert 		} else if (!ieee802_1x_kay_create_potential_peer(
1147780fb4a2SCy Schubert 				participant, peer_mi->mi, peer_mn)) {
11485b9c547cSRui Paulo 			return -1;
11495b9c547cSRui Paulo 		}
11505b9c547cSRui Paulo 	}
11515b9c547cSRui Paulo 
11525b9c547cSRui Paulo 	return 0;
11535b9c547cSRui Paulo }
11545b9c547cSRui Paulo 
11555b9c547cSRui Paulo 
11565b9c547cSRui Paulo /**
11575b9c547cSRui Paulo  * ieee802_1x_mka_decode_potential_peer_body -
11585b9c547cSRui Paulo  */
11595b9c547cSRui Paulo static int
11605b9c547cSRui Paulo ieee802_1x_mka_decode_potential_peer_body(
11615b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
11625b9c547cSRui Paulo 	const u8 *peer_msg, size_t msg_len)
11635b9c547cSRui Paulo {
1164780fb4a2SCy Schubert 	const struct ieee802_1x_mka_hdr *hdr;
11655b9c547cSRui Paulo 	size_t body_len;
11665b9c547cSRui Paulo 	size_t i;
11675b9c547cSRui Paulo 
1168780fb4a2SCy Schubert 	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
11695b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
1170780fb4a2SCy Schubert 	if (body_len % 16 != 0) {
1171780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
1172780fb4a2SCy Schubert 			   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1173780fb4a2SCy Schubert 			   body_len);
1174780fb4a2SCy Schubert 		return -1;
1175780fb4a2SCy Schubert 	}
11765b9c547cSRui Paulo 
1177780fb4a2SCy Schubert 	for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
1178780fb4a2SCy Schubert 		const struct ieee802_1x_mka_peer_id *peer_mi;
1179780fb4a2SCy Schubert 		u32 peer_mn;
1180780fb4a2SCy Schubert 
1181780fb4a2SCy Schubert 		peer_mi = (struct ieee802_1x_mka_peer_id *)
1182780fb4a2SCy Schubert 			(peer_msg + MKA_HDR_LEN + i);
1183780fb4a2SCy Schubert 		peer_mn = be_to_host32(peer_mi->mn);
11845b9c547cSRui Paulo 
11855b9c547cSRui Paulo 		/* it is myself */
11865b9c547cSRui Paulo 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
11875b9c547cSRui Paulo 			/* My message id is used by other participant */
1188780fb4a2SCy Schubert 			if (peer_mn > participant->mn &&
1189780fb4a2SCy Schubert 			    !reset_participant_mi(participant))
1190780fb4a2SCy Schubert 				wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
11915b9c547cSRui Paulo 			continue;
11925b9c547cSRui Paulo 		}
11935b9c547cSRui Paulo 	}
11945b9c547cSRui Paulo 
11955b9c547cSRui Paulo 	return 0;
11965b9c547cSRui Paulo }
11975b9c547cSRui Paulo 
11985b9c547cSRui Paulo 
11995b9c547cSRui Paulo /**
12005b9c547cSRui Paulo  * ieee802_1x_mka_sak_use_body_present
12015b9c547cSRui Paulo  */
12025b9c547cSRui Paulo static Boolean
12035b9c547cSRui Paulo ieee802_1x_mka_sak_use_body_present(
12045b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
12055b9c547cSRui Paulo {
1206780fb4a2SCy Schubert 	return participant->to_use_sak;
12075b9c547cSRui Paulo }
12085b9c547cSRui Paulo 
12095b9c547cSRui Paulo 
12105b9c547cSRui Paulo /**
12115b9c547cSRui Paulo  * ieee802_1x_mka_get_sak_use_length
12125b9c547cSRui Paulo  */
12135b9c547cSRui Paulo static int
12145b9c547cSRui Paulo ieee802_1x_mka_get_sak_use_length(
12155b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
12165b9c547cSRui Paulo {
12175b9c547cSRui Paulo 	int length = MKA_HDR_LEN;
12185b9c547cSRui Paulo 
12195b9c547cSRui Paulo 	if (participant->kay->macsec_desired && participant->advised_desired)
12205b9c547cSRui Paulo 		length = sizeof(struct ieee802_1x_mka_sak_use_body);
12215b9c547cSRui Paulo 
1222780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
12235b9c547cSRui Paulo }
12245b9c547cSRui Paulo 
12255b9c547cSRui Paulo 
12265b9c547cSRui Paulo /**
1227*4bc52338SCy Schubert  * ieee802_1x_mka_get_lpn
12285b9c547cSRui Paulo  */
12295b9c547cSRui Paulo static u32
12305b9c547cSRui Paulo ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
12315b9c547cSRui Paulo 		       struct ieee802_1x_mka_ki *ki)
12325b9c547cSRui Paulo {
1233*4bc52338SCy Schubert 	struct transmit_sa *txsa;
12345b9c547cSRui Paulo 	u32 lpn = 0;
12355b9c547cSRui Paulo 
1236*4bc52338SCy Schubert 	dl_list_for_each(txsa, &principal->txsc->sa_list,
1237*4bc52338SCy Schubert 			 struct transmit_sa, list) {
1238*4bc52338SCy Schubert 		if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
1239*4bc52338SCy Schubert 			/* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
1240*4bc52338SCy Schubert 			 * MKA to communicate the lowest PN used for
1241*4bc52338SCy Schubert 			 * transmission with the SAK within the last two
1242*4bc52338SCy Schubert 			 * seconds".  Achieve this 2 second delay by setting the
1243*4bc52338SCy Schubert 			 * lpn using the transmit next PN (i.e., txsa->next_pn)
1244*4bc52338SCy Schubert 			 * that was read last time here (i.e., mka_hello_time
1245*4bc52338SCy Schubert 			 * 2 seconds ago).
1246*4bc52338SCy Schubert 			 *
1247*4bc52338SCy Schubert 			 * The lowest acceptable PN is the same as the last
1248*4bc52338SCy Schubert 			 * transmitted PN, which is one less than the next
1249*4bc52338SCy Schubert 			 * transmit PN.
1250*4bc52338SCy Schubert 			 *
1251*4bc52338SCy Schubert 			 * NOTE: This method only works if mka_hello_time is 2s.
1252*4bc52338SCy Schubert 			 */
1253*4bc52338SCy Schubert 			lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
12545b9c547cSRui Paulo 
1255*4bc52338SCy Schubert 			/* Now read the current transmit next PN for use next
1256*4bc52338SCy Schubert 			 * time through. */
1257*4bc52338SCy Schubert 			secy_get_transmit_next_pn(principal->kay, txsa);
12585b9c547cSRui Paulo 			break;
12595b9c547cSRui Paulo 		}
12605b9c547cSRui Paulo 	}
12615b9c547cSRui Paulo 
12625b9c547cSRui Paulo 	if (lpn == 0)
12635b9c547cSRui Paulo 		lpn = 1;
12645b9c547cSRui Paulo 
12655b9c547cSRui Paulo 	return lpn;
12665b9c547cSRui Paulo }
12675b9c547cSRui Paulo 
12685b9c547cSRui Paulo 
12695b9c547cSRui Paulo /**
12705b9c547cSRui Paulo  * ieee802_1x_mka_encode_sak_use_body -
12715b9c547cSRui Paulo  */
12725b9c547cSRui Paulo static int
12735b9c547cSRui Paulo ieee802_1x_mka_encode_sak_use_body(
12745b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
12755b9c547cSRui Paulo 	struct wpabuf *buf)
12765b9c547cSRui Paulo {
12775b9c547cSRui Paulo 	struct ieee802_1x_mka_sak_use_body *body;
1278780fb4a2SCy Schubert 	struct ieee802_1x_kay *kay = participant->kay;
12795b9c547cSRui Paulo 	unsigned int length;
12805b9c547cSRui Paulo 	u32 pn = 1;
12815b9c547cSRui Paulo 
12825b9c547cSRui Paulo 	length = ieee802_1x_mka_get_sak_use_length(participant);
1283780fb4a2SCy Schubert 	body = wpabuf_put(buf, length);
12845b9c547cSRui Paulo 
12855b9c547cSRui Paulo 	body->type = MKA_SAK_USE;
12865b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
12875b9c547cSRui Paulo 
12885b9c547cSRui Paulo 	if (length == MKA_HDR_LEN) {
12895b9c547cSRui Paulo 		body->ptx = TRUE;
12905b9c547cSRui Paulo 		body->prx = TRUE;
12915b9c547cSRui Paulo 		body->lan = 0;
12925b9c547cSRui Paulo 		body->lrx = FALSE;
12935b9c547cSRui Paulo 		body->ltx = FALSE;
12945b9c547cSRui Paulo 		body->delay_protect = FALSE;
12955b9c547cSRui Paulo 		return 0;
12965b9c547cSRui Paulo 	}
12975b9c547cSRui Paulo 
1298*4bc52338SCy Schubert 	/* data delay protect */
1299*4bc52338SCy Schubert 	body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME;
1300*4bc52338SCy Schubert 	/* lowest accept packet number */
13015b9c547cSRui Paulo 	pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
1302780fb4a2SCy Schubert 	if (pn > kay->pn_exhaustion) {
13035b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
13045b9c547cSRui Paulo 		if (participant->is_key_server)
13055b9c547cSRui Paulo 			participant->new_sak = TRUE;
13065b9c547cSRui Paulo 	}
13075b9c547cSRui Paulo 
13085b9c547cSRui Paulo 	body->llpn = host_to_be32(pn);
13095b9c547cSRui Paulo 	pn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
13105b9c547cSRui Paulo 	body->olpn = host_to_be32(pn);
13115b9c547cSRui Paulo 
13125b9c547cSRui Paulo 	/* plain tx, plain rx */
1313780fb4a2SCy Schubert 	body->ptx = !kay->macsec_protect;
1314780fb4a2SCy Schubert 	body->prx = kay->macsec_validate != Strict;
13155b9c547cSRui Paulo 
13165b9c547cSRui Paulo 	/* latest key: rx, tx, key server member identifier key number */
13175b9c547cSRui Paulo 	body->lan = participant->lan;
1318780fb4a2SCy Schubert 	os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi));
13195b9c547cSRui Paulo 	body->lkn = host_to_be32(participant->lki.kn);
13205b9c547cSRui Paulo 	body->lrx = participant->lrx;
13215b9c547cSRui Paulo 	body->ltx = participant->ltx;
13225b9c547cSRui Paulo 
13235b9c547cSRui Paulo 	/* old key: rx, tx, key server member identifier key number */
13245b9c547cSRui Paulo 	body->oan = participant->oan;
13255b9c547cSRui Paulo 	if (participant->oki.kn != participant->lki.kn &&
13265b9c547cSRui Paulo 	    participant->oki.kn != 0) {
13275b9c547cSRui Paulo 		body->otx = TRUE;
13285b9c547cSRui Paulo 		body->orx = TRUE;
13295b9c547cSRui Paulo 		os_memcpy(body->osrv_mi, participant->oki.mi,
13305b9c547cSRui Paulo 			  sizeof(body->osrv_mi));
13315b9c547cSRui Paulo 		body->okn = host_to_be32(participant->oki.kn);
13325b9c547cSRui Paulo 	} else {
13335b9c547cSRui Paulo 		body->otx = FALSE;
13345b9c547cSRui Paulo 		body->orx = FALSE;
13355b9c547cSRui Paulo 	}
13365b9c547cSRui Paulo 
13375b9c547cSRui Paulo 	/* set CP's variable */
13385b9c547cSRui Paulo 	if (body->ltx) {
1339780fb4a2SCy Schubert 		kay->tx_enable = TRUE;
1340780fb4a2SCy Schubert 		kay->port_enable = TRUE;
13415b9c547cSRui Paulo 	}
1342780fb4a2SCy Schubert 	if (body->lrx)
1343780fb4a2SCy Schubert 		kay->rx_enable = TRUE;
13445b9c547cSRui Paulo 
13455b9c547cSRui Paulo 	ieee802_1x_mka_dump_sak_use_body(body);
13465b9c547cSRui Paulo 	return 0;
13475b9c547cSRui Paulo }
13485b9c547cSRui Paulo 
13495b9c547cSRui Paulo 
13505b9c547cSRui Paulo /**
13515b9c547cSRui Paulo  * ieee802_1x_mka_decode_sak_use_body -
13525b9c547cSRui Paulo  */
13535b9c547cSRui Paulo static int
13545b9c547cSRui Paulo ieee802_1x_mka_decode_sak_use_body(
13555b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
13565b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
13575b9c547cSRui Paulo {
13585b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
13595b9c547cSRui Paulo 	struct ieee802_1x_mka_sak_use_body *body;
13605b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
1361*4bc52338SCy Schubert 	struct receive_sc *rxsc;
1362*4bc52338SCy Schubert 	struct receive_sa *rxsa;
13635b9c547cSRui Paulo 	struct data_key *sa_key = NULL;
13645b9c547cSRui Paulo 	size_t body_len;
13655b9c547cSRui Paulo 	struct ieee802_1x_mka_ki ki;
13665b9c547cSRui Paulo 	u32 lpn;
13675b9c547cSRui Paulo 	Boolean all_receiving;
1368780fb4a2SCy Schubert 	Boolean found;
1369780fb4a2SCy Schubert 	struct ieee802_1x_kay *kay = participant->kay;
13705b9c547cSRui Paulo 
13715b9c547cSRui Paulo 	if (!participant->principal) {
13725b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
13735b9c547cSRui Paulo 		return -1;
13745b9c547cSRui Paulo 	}
13755b9c547cSRui Paulo 	peer = ieee802_1x_kay_get_live_peer(participant,
13765b9c547cSRui Paulo 					    participant->current_peer_id.mi);
13775b9c547cSRui Paulo 	if (!peer) {
1378*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
1379*4bc52338SCy Schubert 			   "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set",
1380*4bc52338SCy Schubert 			   mi_txt(participant->current_peer_id.mi));
13815b9c547cSRui Paulo 		return -1;
13825b9c547cSRui Paulo 	}
13835b9c547cSRui Paulo 
13845b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
13855b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
13865b9c547cSRui Paulo 	body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
13875b9c547cSRui Paulo 	ieee802_1x_mka_dump_sak_use_body(body);
13885b9c547cSRui Paulo 
13895b9c547cSRui Paulo 	if ((body_len != 0) && (body_len < 40)) {
13905b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1391780fb4a2SCy Schubert 			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets",
1392780fb4a2SCy Schubert 			   body_len);
13935b9c547cSRui Paulo 		return -1;
13945b9c547cSRui Paulo 	}
13955b9c547cSRui Paulo 
13965b9c547cSRui Paulo 	/* TODO: what action should I take when peer does not support MACsec */
13975b9c547cSRui Paulo 	if (body_len == 0) {
13985b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
13995b9c547cSRui Paulo 		return 0;
14005b9c547cSRui Paulo 	}
14015b9c547cSRui Paulo 
14025b9c547cSRui Paulo 	/* TODO: when the plain tx or rx of peer is true, should I change
14035b9c547cSRui Paulo 	 * the attribute of controlled port
14045b9c547cSRui Paulo 	 */
14055b9c547cSRui Paulo 	if (body->prx)
14065b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
14075b9c547cSRui Paulo 
14085b9c547cSRui Paulo 	if (body->ptx)
14095b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
14105b9c547cSRui Paulo 
14115b9c547cSRui Paulo 	/* check latest key is valid */
14125b9c547cSRui Paulo 	if (body->ltx || body->lrx) {
1413780fb4a2SCy Schubert 		found = FALSE;
14145b9c547cSRui Paulo 		os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
1415780fb4a2SCy Schubert 		ki.kn = be_to_host32(body->lkn);
14165b9c547cSRui Paulo 		dl_list_for_each(sa_key, &participant->sak_list,
14175b9c547cSRui Paulo 				 struct data_key, list) {
14185b9c547cSRui Paulo 			if (is_ki_equal(&sa_key->key_identifier, &ki)) {
1419780fb4a2SCy Schubert 				found = TRUE;
14205b9c547cSRui Paulo 				break;
14215b9c547cSRui Paulo 			}
14225b9c547cSRui Paulo 		}
1423780fb4a2SCy Schubert 		if (!found) {
1424*4bc52338SCy Schubert 			wpa_printf(MSG_INFO, "KaY: Latest key is invalid");
14255b9c547cSRui Paulo 			return -1;
14265b9c547cSRui Paulo 		}
14275b9c547cSRui Paulo 		if (os_memcmp(participant->lki.mi, body->lsrv_mi,
14285b9c547cSRui Paulo 			      sizeof(participant->lki.mi)) == 0 &&
1429780fb4a2SCy Schubert 		    be_to_host32(body->lkn) == participant->lki.kn &&
14305b9c547cSRui Paulo 		    body->lan == participant->lan) {
14315b9c547cSRui Paulo 			peer->sak_used = TRUE;
14325b9c547cSRui Paulo 		}
14335b9c547cSRui Paulo 		if (body->ltx && peer->is_key_server) {
1434780fb4a2SCy Schubert 			ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE);
1435780fb4a2SCy Schubert 			ieee802_1x_cp_sm_step(kay->cp);
14365b9c547cSRui Paulo 		}
14375b9c547cSRui Paulo 	}
14385b9c547cSRui Paulo 
143985732ac8SCy Schubert 	/* check old key is valid (but only if we remember our old key) */
144085732ac8SCy Schubert 	if (participant->oki.kn != 0 && (body->otx || body->orx)) {
14415b9c547cSRui Paulo 		if (os_memcmp(participant->oki.mi, body->osrv_mi,
14425b9c547cSRui Paulo 			      sizeof(participant->oki.mi)) != 0 ||
1443780fb4a2SCy Schubert 		    be_to_host32(body->okn) != participant->oki.kn ||
14445b9c547cSRui Paulo 		    body->oan != participant->oan) {
14455b9c547cSRui Paulo 			wpa_printf(MSG_WARNING, "KaY: Old key is invalid");
14465b9c547cSRui Paulo 			return -1;
14475b9c547cSRui Paulo 		}
14485b9c547cSRui Paulo 	}
14495b9c547cSRui Paulo 
14505b9c547cSRui Paulo 	/* TODO: how to set the MACsec hardware when delay_protect is true */
1451780fb4a2SCy Schubert 	if (body->delay_protect &&
1452780fb4a2SCy Schubert 	    (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
14535b9c547cSRui Paulo 		wpa_printf(MSG_WARNING,
1454*4bc52338SCy Schubert 			   "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE");
14555b9c547cSRui Paulo 		return -1;
14565b9c547cSRui Paulo 	}
14575b9c547cSRui Paulo 
14585b9c547cSRui Paulo 	/* check all live peer have used the sak for receiving sa */
14595b9c547cSRui Paulo 	all_receiving = TRUE;
14605b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
14615b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
14625b9c547cSRui Paulo 		if (!peer->sak_used) {
14635b9c547cSRui Paulo 			all_receiving = FALSE;
14645b9c547cSRui Paulo 			break;
14655b9c547cSRui Paulo 		}
14665b9c547cSRui Paulo 	}
14675b9c547cSRui Paulo 	if (all_receiving) {
14685b9c547cSRui Paulo 		participant->to_dist_sak = FALSE;
1469780fb4a2SCy Schubert 		ieee802_1x_cp_set_allreceiving(kay->cp, TRUE);
1470780fb4a2SCy Schubert 		ieee802_1x_cp_sm_step(kay->cp);
14715b9c547cSRui Paulo 	}
14725b9c547cSRui Paulo 
1473*4bc52338SCy Schubert 	/* if I'm key server, and detects peer member pn exhaustion, rekey. */
1474780fb4a2SCy Schubert 	lpn = be_to_host32(body->llpn);
1475780fb4a2SCy Schubert 	if (lpn > kay->pn_exhaustion) {
14765b9c547cSRui Paulo 		if (participant->is_key_server) {
14775b9c547cSRui Paulo 			participant->new_sak = TRUE;
14785b9c547cSRui Paulo 			wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
14795b9c547cSRui Paulo 		}
14805b9c547cSRui Paulo 	}
14815b9c547cSRui Paulo 
1482*4bc52338SCy Schubert 	if (sa_key)
1483*4bc52338SCy Schubert 		sa_key->next_pn = lpn;
1484780fb4a2SCy Schubert 	found = FALSE;
1485*4bc52338SCy Schubert 	dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc,
1486*4bc52338SCy Schubert 			 list) {
1487*4bc52338SCy Schubert 		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa,
1488*4bc52338SCy Schubert 				 list) {
1489*4bc52338SCy Schubert 			if (sa_key && rxsa->pkey == sa_key) {
1490780fb4a2SCy Schubert 				found = TRUE;
14915b9c547cSRui Paulo 				break;
14925b9c547cSRui Paulo 			}
14935b9c547cSRui Paulo 		}
1494*4bc52338SCy Schubert 		if (found)
1495*4bc52338SCy Schubert 			break;
1496*4bc52338SCy Schubert 	}
1497780fb4a2SCy Schubert 	if (!found) {
1498*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING, "KaY: Can't find rxsa");
14995b9c547cSRui Paulo 		return -1;
15005b9c547cSRui Paulo 	}
15015b9c547cSRui Paulo 
1502*4bc52338SCy Schubert 	if (body->delay_protect) {
1503*4bc52338SCy Schubert 		secy_get_receive_lowest_pn(participant->kay, rxsa);
1504*4bc52338SCy Schubert 		if (lpn > rxsa->lowest_pn) {
1505*4bc52338SCy Schubert 			/* Delay protect window (communicated via MKA) is
1506*4bc52338SCy Schubert 			 * tighter than SecY's current replay protect window,
1507*4bc52338SCy Schubert 			 * so tell SecY the new (and higher) lpn. */
1508*4bc52338SCy Schubert 			rxsa->lowest_pn = lpn;
1509*4bc52338SCy Schubert 			secy_set_receive_lowest_pn(participant->kay, rxsa);
1510*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn);
1511*4bc52338SCy Schubert 		}
1512*4bc52338SCy Schubert 		/* FIX: Delay protection for olpn not implemented.
1513*4bc52338SCy Schubert 		 * Note that Old Key is only active for MKA_SAK_RETIRE_TIME
1514*4bc52338SCy Schubert 		 * (3 seconds) and delay protection does allow PN's within
1515*4bc52338SCy Schubert 		 * a 2 seconds window, so olpn would be a lot of work for
1516*4bc52338SCy Schubert 		 * just 1 second's worth of protection. */
15175b9c547cSRui Paulo 	}
15185b9c547cSRui Paulo 
15195b9c547cSRui Paulo 	return 0;
15205b9c547cSRui Paulo }
15215b9c547cSRui Paulo 
15225b9c547cSRui Paulo 
15235b9c547cSRui Paulo /**
15245b9c547cSRui Paulo  * ieee802_1x_mka_dist_sak_body_present
15255b9c547cSRui Paulo  */
15265b9c547cSRui Paulo static Boolean
15275b9c547cSRui Paulo ieee802_1x_mka_dist_sak_body_present(
15285b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
15295b9c547cSRui Paulo {
1530*4bc52338SCy Schubert 	return participant->is_key_server && participant->to_dist_sak &&
1531*4bc52338SCy Schubert 		participant->new_key;
15325b9c547cSRui Paulo }
15335b9c547cSRui Paulo 
15345b9c547cSRui Paulo 
15355b9c547cSRui Paulo /**
15365b9c547cSRui Paulo  * ieee802_1x_kay_get_dist_sak_length
15375b9c547cSRui Paulo  */
15385b9c547cSRui Paulo static int
15395b9c547cSRui Paulo ieee802_1x_mka_get_dist_sak_length(
15405b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
15415b9c547cSRui Paulo {
1542780fb4a2SCy Schubert 	int length = MKA_HDR_LEN;
1543780fb4a2SCy Schubert 	unsigned int cs_index = participant->kay->macsec_csindex;
15445b9c547cSRui Paulo 
1545780fb4a2SCy Schubert 	if (participant->advised_desired && cs_index < CS_TABLE_SIZE) {
15465b9c547cSRui Paulo 		length = sizeof(struct ieee802_1x_mka_dist_sak_body);
15475b9c547cSRui Paulo 		if (cs_index != DEFAULT_CS_INDEX)
15485b9c547cSRui Paulo 			length += CS_ID_LEN;
15495b9c547cSRui Paulo 
15505b9c547cSRui Paulo 		length += cipher_suite_tbl[cs_index].sak_len + 8;
15515b9c547cSRui Paulo 	}
15525b9c547cSRui Paulo 
1553780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
15545b9c547cSRui Paulo }
15555b9c547cSRui Paulo 
15565b9c547cSRui Paulo 
15575b9c547cSRui Paulo /**
15585b9c547cSRui Paulo  * ieee802_1x_mka_encode_dist_sak_body -
15595b9c547cSRui Paulo  */
15605b9c547cSRui Paulo static int
15615b9c547cSRui Paulo ieee802_1x_mka_encode_dist_sak_body(
15625b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
15635b9c547cSRui Paulo 	struct wpabuf *buf)
15645b9c547cSRui Paulo {
15655b9c547cSRui Paulo 	struct ieee802_1x_mka_dist_sak_body *body;
15665b9c547cSRui Paulo 	struct data_key *sak;
15675b9c547cSRui Paulo 	unsigned int length;
1568780fb4a2SCy Schubert 	unsigned int cs_index;
15695b9c547cSRui Paulo 	int sak_pos;
15705b9c547cSRui Paulo 
15715b9c547cSRui Paulo 	length = ieee802_1x_mka_get_dist_sak_length(participant);
15725b9c547cSRui Paulo 	body = wpabuf_put(buf, length);
15735b9c547cSRui Paulo 	body->type = MKA_DISTRIBUTED_SAK;
15745b9c547cSRui Paulo 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
15755b9c547cSRui Paulo 	if (length == MKA_HDR_LEN) {
15765b9c547cSRui Paulo 		body->confid_offset = 0;
15775b9c547cSRui Paulo 		body->dan = 0;
15785b9c547cSRui Paulo 		return 0;
15795b9c547cSRui Paulo 	}
15805b9c547cSRui Paulo 
15815b9c547cSRui Paulo 	sak = participant->new_key;
1582*4bc52338SCy Schubert 	if (!sak) {
1583*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
1584*4bc52338SCy Schubert 			   "KaY: No SAK available to build Distributed SAK parameter set");
1585*4bc52338SCy Schubert 		return -1;
1586*4bc52338SCy Schubert 	}
15875b9c547cSRui Paulo 	body->confid_offset = sak->confidentiality_offset;
15885b9c547cSRui Paulo 	body->dan = sak->an;
15895b9c547cSRui Paulo 	body->kn = host_to_be32(sak->key_identifier.kn);
15905b9c547cSRui Paulo 	cs_index = participant->kay->macsec_csindex;
15915b9c547cSRui Paulo 	sak_pos = 0;
1592780fb4a2SCy Schubert 	if (cs_index >= CS_TABLE_SIZE)
1593780fb4a2SCy Schubert 		return -1;
15945b9c547cSRui Paulo 	if (cs_index != DEFAULT_CS_INDEX) {
1595780fb4a2SCy Schubert 		be64 cs;
1596780fb4a2SCy Schubert 
1597780fb4a2SCy Schubert 		cs = host_to_be64(cipher_suite_tbl[cs_index].id);
1598780fb4a2SCy Schubert 		os_memcpy(body->sak, &cs, CS_ID_LEN);
15995b9c547cSRui Paulo 		sak_pos = CS_ID_LEN;
16005b9c547cSRui Paulo 	}
1601*4bc52338SCy Schubert 	if (aes_wrap(participant->kek.key, participant->kek.len,
16025b9c547cSRui Paulo 		     cipher_suite_tbl[cs_index].sak_len / 8,
16035b9c547cSRui Paulo 		     sak->key, body->sak + sak_pos)) {
16045b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
16055b9c547cSRui Paulo 		return -1;
16065b9c547cSRui Paulo 	}
16075b9c547cSRui Paulo 
16085b9c547cSRui Paulo 	ieee802_1x_mka_dump_dist_sak_body(body);
16095b9c547cSRui Paulo 
16105b9c547cSRui Paulo 	return 0;
16115b9c547cSRui Paulo }
16125b9c547cSRui Paulo 
16135b9c547cSRui Paulo 
16145b9c547cSRui Paulo /**
16155b9c547cSRui Paulo  * ieee802_1x_kay_init_data_key -
16165b9c547cSRui Paulo  */
1617780fb4a2SCy Schubert static void ieee802_1x_kay_init_data_key(struct data_key *pkey)
16185b9c547cSRui Paulo {
1619780fb4a2SCy Schubert 	pkey->transmits = TRUE;
1620780fb4a2SCy Schubert 	pkey->receives = TRUE;
16215b9c547cSRui Paulo 	os_get_time(&pkey->created_time);
16225b9c547cSRui Paulo 
1623*4bc52338SCy Schubert 	pkey->next_pn = 1;
16245b9c547cSRui Paulo 	pkey->user = 1;
16255b9c547cSRui Paulo }
16265b9c547cSRui Paulo 
16275b9c547cSRui Paulo 
16285b9c547cSRui Paulo /**
16295b9c547cSRui Paulo  * ieee802_1x_kay_decode_dist_sak_body -
16305b9c547cSRui Paulo  */
16315b9c547cSRui Paulo static int
16325b9c547cSRui Paulo ieee802_1x_mka_decode_dist_sak_body(
16335b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
16345b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
16355b9c547cSRui Paulo {
16365b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
16375b9c547cSRui Paulo 	struct ieee802_1x_mka_dist_sak_body *body;
16385b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
16395b9c547cSRui Paulo 	struct macsec_ciphersuite *cs;
16405b9c547cSRui Paulo 	size_t body_len;
16415b9c547cSRui Paulo 	struct data_key *sa_key = NULL;
16425b9c547cSRui Paulo 	int sak_len;
16435b9c547cSRui Paulo 	u8 *wrap_sak;
16445b9c547cSRui Paulo 	u8 *unwrap_sak;
1645780fb4a2SCy Schubert 	struct ieee802_1x_kay *kay = participant->kay;
16465b9c547cSRui Paulo 
16475b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
16485b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
16495b9c547cSRui Paulo 	if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
16505b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1651780fb4a2SCy Schubert 			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets",
1652780fb4a2SCy Schubert 			   body_len);
16535b9c547cSRui Paulo 		return -1;
16545b9c547cSRui Paulo 	}
16555b9c547cSRui Paulo 
16565b9c547cSRui Paulo 	if (!participant->principal) {
16575b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16585b9c547cSRui Paulo 			   "KaY: I can't accept the distributed SAK as I am not principal");
16595b9c547cSRui Paulo 		return -1;
16605b9c547cSRui Paulo 	}
16615b9c547cSRui Paulo 	if (participant->is_key_server) {
16625b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1663*4bc52338SCy Schubert 			   "KaY: Reject distributed SAK since I'm a key server");
16645b9c547cSRui Paulo 		return -1;
16655b9c547cSRui Paulo 	}
1666780fb4a2SCy Schubert 	if (!kay->macsec_desired ||
1667780fb4a2SCy Schubert 	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
16685b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16695b9c547cSRui Paulo 			   "KaY: I am not MACsec-desired or without MACsec capable");
16705b9c547cSRui Paulo 		return -1;
16715b9c547cSRui Paulo 	}
16725b9c547cSRui Paulo 
16735b9c547cSRui Paulo 	peer = ieee802_1x_kay_get_live_peer(participant,
16745b9c547cSRui Paulo 					    participant->current_peer_id.mi);
16755b9c547cSRui Paulo 	if (!peer) {
16765b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
16775b9c547cSRui Paulo 			   "KaY: The key server is not in my live peers list");
16785b9c547cSRui Paulo 		return -1;
16795b9c547cSRui Paulo 	}
1680780fb4a2SCy Schubert 	if (!sci_equal(&kay->key_server_sci, &peer->sci)) {
16815b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
16825b9c547cSRui Paulo 		return -1;
16835b9c547cSRui Paulo 	}
1684780fb4a2SCy Schubert 
16855b9c547cSRui Paulo 	if (body_len == 0) {
1686780fb4a2SCy Schubert 		kay->authenticated = TRUE;
1687780fb4a2SCy Schubert 		kay->secured = FALSE;
1688780fb4a2SCy Schubert 		kay->failed = FALSE;
16895b9c547cSRui Paulo 		participant->advised_desired = FALSE;
1690780fb4a2SCy Schubert 		ieee802_1x_cp_connect_authenticated(kay->cp);
1691780fb4a2SCy Schubert 		ieee802_1x_cp_sm_step(kay->cp);
16925b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec");
169385732ac8SCy Schubert 		participant->to_use_sak = FALSE;
16945b9c547cSRui Paulo 		return 0;
16955b9c547cSRui Paulo 	}
1696780fb4a2SCy Schubert 
16975b9c547cSRui Paulo 	participant->advised_desired = TRUE;
1698780fb4a2SCy Schubert 	kay->authenticated = FALSE;
1699780fb4a2SCy Schubert 	kay->secured = TRUE;
1700780fb4a2SCy Schubert 	kay->failed = FALSE;
1701780fb4a2SCy Schubert 	ieee802_1x_cp_connect_secure(kay->cp);
1702780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
17035b9c547cSRui Paulo 
17045b9c547cSRui Paulo 	body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
17055b9c547cSRui Paulo 	ieee802_1x_mka_dump_dist_sak_body(body);
17065b9c547cSRui Paulo 	dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
17075b9c547cSRui Paulo 	{
17085b9c547cSRui Paulo 		if (os_memcmp(sa_key->key_identifier.mi,
17095b9c547cSRui Paulo 			      participant->current_peer_id.mi, MI_LEN) == 0 &&
17105b9c547cSRui Paulo 		    sa_key->key_identifier.kn == be_to_host32(body->kn)) {
1711*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
1712*4bc52338SCy Schubert 				   "KaY: SAK has already been installed - do not set it again");
17135b9c547cSRui Paulo 			return 0;
17145b9c547cSRui Paulo 		}
17155b9c547cSRui Paulo 	}
1716780fb4a2SCy Schubert 
17175b9c547cSRui Paulo 	if (body_len == 28) {
17185b9c547cSRui Paulo 		sak_len = DEFAULT_SA_KEY_LEN;
17195b9c547cSRui Paulo 		wrap_sak =  body->sak;
1720780fb4a2SCy Schubert 		kay->macsec_csindex = DEFAULT_CS_INDEX;
1721780fb4a2SCy Schubert 		cs = &cipher_suite_tbl[kay->macsec_csindex];
17225b9c547cSRui Paulo 	} else {
1723*4bc52338SCy Schubert 		unsigned int idx;
1724*4bc52338SCy Schubert 
1725*4bc52338SCy Schubert 		cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak,
1726*4bc52338SCy Schubert 						     &idx);
17275b9c547cSRui Paulo 		if (!cs) {
17285b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
17295b9c547cSRui Paulo 				   "KaY: I can't support the Cipher Suite advised by key server");
17305b9c547cSRui Paulo 			return -1;
17315b9c547cSRui Paulo 		}
17325b9c547cSRui Paulo 		sak_len = cs->sak_len;
17335b9c547cSRui Paulo 		wrap_sak = body->sak + CS_ID_LEN;
1734*4bc52338SCy Schubert 		kay->macsec_csindex = idx;
17355b9c547cSRui Paulo 	}
17365b9c547cSRui Paulo 
17375b9c547cSRui Paulo 	unwrap_sak = os_zalloc(sak_len);
17385b9c547cSRui Paulo 	if (!unwrap_sak) {
17395b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
17405b9c547cSRui Paulo 		return -1;
17415b9c547cSRui Paulo 	}
1742*4bc52338SCy Schubert 	if (aes_unwrap(participant->kek.key, participant->kek.len,
1743*4bc52338SCy Schubert 		       sak_len >> 3, wrap_sak, unwrap_sak)) {
17445b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
17455b9c547cSRui Paulo 		os_free(unwrap_sak);
17465b9c547cSRui Paulo 		return -1;
17475b9c547cSRui Paulo 	}
1748*4bc52338SCy Schubert 	wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:",
174985732ac8SCy Schubert 			unwrap_sak, sak_len);
17505b9c547cSRui Paulo 
1751780fb4a2SCy Schubert 	sa_key = os_zalloc(sizeof(*sa_key));
17525b9c547cSRui Paulo 	if (!sa_key) {
17535b9c547cSRui Paulo 		os_free(unwrap_sak);
17545b9c547cSRui Paulo 		return -1;
17555b9c547cSRui Paulo 	}
17565b9c547cSRui Paulo 
1757780fb4a2SCy Schubert 	os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi,
1758780fb4a2SCy Schubert 		  MI_LEN);
1759780fb4a2SCy Schubert 	sa_key->key_identifier.kn = be_to_host32(body->kn);
1760780fb4a2SCy Schubert 
1761780fb4a2SCy Schubert 	sa_key->key = unwrap_sak;
1762780fb4a2SCy Schubert 	sa_key->key_len = sak_len;
1763780fb4a2SCy Schubert 
1764780fb4a2SCy Schubert 	sa_key->confidentiality_offset = body->confid_offset;
1765780fb4a2SCy Schubert 	sa_key->an = body->dan;
1766780fb4a2SCy Schubert 	ieee802_1x_kay_init_data_key(sa_key);
1767780fb4a2SCy Schubert 
176885732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(sa_key);
17695b9c547cSRui Paulo 	dl_list_add(&participant->sak_list, &sa_key->list);
17705b9c547cSRui Paulo 
1771780fb4a2SCy Schubert 	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
1772780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
1773780fb4a2SCy Schubert 	ieee802_1x_cp_set_offset(kay->cp, body->confid_offset);
1774780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
1775780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
1776780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedan(kay->cp, body->dan);
1777780fb4a2SCy Schubert 	ieee802_1x_cp_signal_newsak(kay->cp);
1778780fb4a2SCy Schubert 	ieee802_1x_cp_sm_step(kay->cp);
17795b9c547cSRui Paulo 
178085732ac8SCy Schubert 	kay->rcvd_keys++;
17815b9c547cSRui Paulo 	participant->to_use_sak = TRUE;
17825b9c547cSRui Paulo 
17835b9c547cSRui Paulo 	return 0;
17845b9c547cSRui Paulo }
17855b9c547cSRui Paulo 
17865b9c547cSRui Paulo 
17875b9c547cSRui Paulo /**
17885b9c547cSRui Paulo  * ieee802_1x_mka_icv_body_present
17895b9c547cSRui Paulo  */
17905b9c547cSRui Paulo static Boolean
17915b9c547cSRui Paulo ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
17925b9c547cSRui Paulo {
17935b9c547cSRui Paulo 	return TRUE;
17945b9c547cSRui Paulo }
17955b9c547cSRui Paulo 
17965b9c547cSRui Paulo 
17975b9c547cSRui Paulo /**
17985b9c547cSRui Paulo  * ieee802_1x_kay_get_icv_length
17995b9c547cSRui Paulo  */
18005b9c547cSRui Paulo static int
18015b9c547cSRui Paulo ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
18025b9c547cSRui Paulo {
18035b9c547cSRui Paulo 	int length;
18045b9c547cSRui Paulo 
1805*4bc52338SCy Schubert 	/* Determine if we need space for the ICV Indicator */
1806*4bc52338SCy Schubert 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
1807*4bc52338SCy Schubert 	    DEFAULT_ICV_LEN)
18085b9c547cSRui Paulo 		length = sizeof(struct ieee802_1x_mka_icv_body);
1809*4bc52338SCy Schubert 	else
1810*4bc52338SCy Schubert 		length = 0;
18115b9c547cSRui Paulo 	length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
18125b9c547cSRui Paulo 
1813780fb4a2SCy Schubert 	return MKA_ALIGN_LENGTH(length);
18145b9c547cSRui Paulo }
18155b9c547cSRui Paulo 
18165b9c547cSRui Paulo 
18175b9c547cSRui Paulo /**
18185b9c547cSRui Paulo  * ieee802_1x_mka_encode_icv_body -
18195b9c547cSRui Paulo  */
18205b9c547cSRui Paulo static int
18215b9c547cSRui Paulo ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
18225b9c547cSRui Paulo 			       struct wpabuf *buf)
18235b9c547cSRui Paulo {
18245b9c547cSRui Paulo 	struct ieee802_1x_mka_icv_body *body;
18255b9c547cSRui Paulo 	unsigned int length;
18265b9c547cSRui Paulo 	u8 cmac[MAX_ICV_LEN];
18275b9c547cSRui Paulo 
18285b9c547cSRui Paulo 	length = ieee802_1x_mka_get_icv_length(participant);
1829*4bc52338SCy Schubert 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
1830*4bc52338SCy Schubert 	    DEFAULT_ICV_LEN)  {
1831*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: ICV Indicator");
18325b9c547cSRui Paulo 		body = wpabuf_put(buf, MKA_HDR_LEN);
18335b9c547cSRui Paulo 		body->type = MKA_ICV_INDICATOR;
1834*4bc52338SCy Schubert 		length -= MKA_HDR_LEN;
1835*4bc52338SCy Schubert 		set_mka_param_body_len(body, length);
18365b9c547cSRui Paulo 	}
18375b9c547cSRui Paulo 
18385b9c547cSRui Paulo 	if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
1839*4bc52338SCy Schubert 		    participant->ick.key, participant->ick.len,
1840*4bc52338SCy Schubert 		    wpabuf_head(buf), wpabuf_len(buf), cmac)) {
1841*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV");
18425b9c547cSRui Paulo 		return -1;
18435b9c547cSRui Paulo 	}
1844*4bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length);
18455b9c547cSRui Paulo 
18465b9c547cSRui Paulo 	os_memcpy(wpabuf_put(buf, length), cmac, length);
18475b9c547cSRui Paulo 
18485b9c547cSRui Paulo 	return 0;
18495b9c547cSRui Paulo }
18505b9c547cSRui Paulo 
18515b9c547cSRui Paulo /**
18525b9c547cSRui Paulo  * ieee802_1x_mka_decode_icv_body -
18535b9c547cSRui Paulo  */
1854*4bc52338SCy Schubert static const u8 *
18555b9c547cSRui Paulo ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
18565b9c547cSRui Paulo 			       const u8 *mka_msg, size_t msg_len)
18575b9c547cSRui Paulo {
1858*4bc52338SCy Schubert 	const struct ieee802_1x_mka_hdr *hdr;
1859*4bc52338SCy Schubert 	const struct ieee802_1x_mka_icv_body *body;
18605b9c547cSRui Paulo 	size_t body_len;
18615b9c547cSRui Paulo 	size_t left_len;
1862780fb4a2SCy Schubert 	u8 body_type;
18635b9c547cSRui Paulo 	const u8 *pos;
18645b9c547cSRui Paulo 
18655b9c547cSRui Paulo 	pos = mka_msg;
18665b9c547cSRui Paulo 	left_len = msg_len;
1867*4bc52338SCy Schubert 	while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
1868*4bc52338SCy Schubert 		hdr = (const struct ieee802_1x_mka_hdr *) pos;
1869*4bc52338SCy Schubert 		body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
18705b9c547cSRui Paulo 		body_type = get_mka_param_body_type(hdr);
18715b9c547cSRui Paulo 
1872*4bc52338SCy Schubert 		if (left_len < body_len + MKA_HDR_LEN)
18735b9c547cSRui Paulo 			break;
18745b9c547cSRui Paulo 
18755b9c547cSRui Paulo 		if (body_type != MKA_ICV_INDICATOR) {
18765b9c547cSRui Paulo 			left_len -= MKA_HDR_LEN + body_len;
18775b9c547cSRui Paulo 			pos += MKA_HDR_LEN + body_len;
18785b9c547cSRui Paulo 			continue;
18795b9c547cSRui Paulo 		}
18805b9c547cSRui Paulo 
1881*4bc52338SCy Schubert 		body = (const struct ieee802_1x_mka_icv_body *) pos;
18825b9c547cSRui Paulo 		if (body_len
1883*4bc52338SCy Schubert 		    < mka_alg_tbl[participant->kay->mka_algindex].icv_len)
18845b9c547cSRui Paulo 			return NULL;
18855b9c547cSRui Paulo 
18865b9c547cSRui Paulo 		return body->icv;
18875b9c547cSRui Paulo 	}
18885b9c547cSRui Paulo 
1889*4bc52338SCy Schubert 	return mka_msg + msg_len - DEFAULT_ICV_LEN;
18905b9c547cSRui Paulo }
18915b9c547cSRui Paulo 
18925b9c547cSRui Paulo 
18935b9c547cSRui Paulo /**
18945b9c547cSRui Paulo  * ieee802_1x_mka_decode_dist_cak_body-
18955b9c547cSRui Paulo  */
18965b9c547cSRui Paulo static int
18975b9c547cSRui Paulo ieee802_1x_mka_decode_dist_cak_body(
18985b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
18995b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
19005b9c547cSRui Paulo {
19015b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
19025b9c547cSRui Paulo 	size_t body_len;
19035b9c547cSRui Paulo 
19045b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
19055b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
19065b9c547cSRui Paulo 	if (body_len < 28) {
19075b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1908*4bc52338SCy Schubert 			   "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets",
1909780fb4a2SCy Schubert 			   body_len);
19105b9c547cSRui Paulo 		return -1;
19115b9c547cSRui Paulo 	}
19125b9c547cSRui Paulo 
19135b9c547cSRui Paulo 	return 0;
19145b9c547cSRui Paulo }
19155b9c547cSRui Paulo 
19165b9c547cSRui Paulo 
19175b9c547cSRui Paulo /**
19185b9c547cSRui Paulo  * ieee802_1x_mka_decode_kmd_body -
19195b9c547cSRui Paulo  */
19205b9c547cSRui Paulo static int
19215b9c547cSRui Paulo ieee802_1x_mka_decode_kmd_body(
19225b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
19235b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
19245b9c547cSRui Paulo {
19255b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
19265b9c547cSRui Paulo 	size_t body_len;
19275b9c547cSRui Paulo 
19285b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
19295b9c547cSRui Paulo 	body_len = get_mka_param_body_len(hdr);
19305b9c547cSRui Paulo 	if (body_len < 5) {
19315b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1932*4bc52338SCy Schubert 			   "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets",
1933780fb4a2SCy Schubert 			   body_len);
19345b9c547cSRui Paulo 		return -1;
19355b9c547cSRui Paulo 	}
19365b9c547cSRui Paulo 
19375b9c547cSRui Paulo 	return 0;
19385b9c547cSRui Paulo }
19395b9c547cSRui Paulo 
19405b9c547cSRui Paulo 
19415b9c547cSRui Paulo /**
19425b9c547cSRui Paulo  * ieee802_1x_mka_decode_announce_body -
19435b9c547cSRui Paulo  */
19445b9c547cSRui Paulo static int ieee802_1x_mka_decode_announce_body(
19455b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant,
19465b9c547cSRui Paulo 	const u8 *mka_msg, size_t msg_len)
19475b9c547cSRui Paulo {
19485b9c547cSRui Paulo 	return 0;
19495b9c547cSRui Paulo }
19505b9c547cSRui Paulo 
19515b9c547cSRui Paulo 
1952780fb4a2SCy Schubert struct mka_param_body_handler {
1953780fb4a2SCy Schubert 	int (*body_tx)(struct ieee802_1x_mka_participant *participant,
1954780fb4a2SCy Schubert 		       struct wpabuf *buf);
1955780fb4a2SCy Schubert 	int (*body_rx)(struct ieee802_1x_mka_participant *participant,
1956780fb4a2SCy Schubert 		       const u8 *mka_msg, size_t msg_len);
1957780fb4a2SCy Schubert 	int (*body_length)(struct ieee802_1x_mka_participant *participant);
1958780fb4a2SCy Schubert 	Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
1959780fb4a2SCy Schubert };
1960780fb4a2SCy Schubert 
1961780fb4a2SCy Schubert 
1962780fb4a2SCy Schubert static struct mka_param_body_handler mka_body_handler[] = {
1963*4bc52338SCy Schubert 	/* Basic parameter set */
19645b9c547cSRui Paulo 	{
1965780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_basic_body,
1966780fb4a2SCy Schubert 		.body_rx      = NULL,
1967780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_basic_body_length,
1968780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_basic_body_present
19695b9c547cSRui Paulo 	},
19705b9c547cSRui Paulo 
1971*4bc52338SCy Schubert 	/* Live Peer List parameter set */
19725b9c547cSRui Paulo 	{
1973780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_live_peer_body,
1974780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_live_peer_body,
1975780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_live_peer_length,
1976780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_live_peer_body_present
19775b9c547cSRui Paulo 	},
19785b9c547cSRui Paulo 
1979*4bc52338SCy Schubert 	/* Potential Peer List parameter set */
19805b9c547cSRui Paulo 	{
1981780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_potential_peer_body,
1982780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_potential_peer_body,
1983780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_potential_peer_length,
1984780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_potential_peer_body_present
19855b9c547cSRui Paulo 	},
19865b9c547cSRui Paulo 
1987*4bc52338SCy Schubert 	/* MACsec SAK Use parameter set */
19885b9c547cSRui Paulo 	{
1989780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_sak_use_body,
1990780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_sak_use_body,
1991780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_sak_use_length,
1992780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_sak_use_body_present
19935b9c547cSRui Paulo 	},
19945b9c547cSRui Paulo 
1995*4bc52338SCy Schubert 	/* Distributed SAK parameter set */
19965b9c547cSRui Paulo 	{
1997780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_dist_sak_body,
1998780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_dist_sak_body,
1999780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_dist_sak_length,
2000780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_dist_sak_body_present
20015b9c547cSRui Paulo 	},
20025b9c547cSRui Paulo 
2003*4bc52338SCy Schubert 	/* Distribute CAK parameter set */
20045b9c547cSRui Paulo 	{
2005780fb4a2SCy Schubert 		.body_tx      = NULL,
2006780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_dist_cak_body,
2007780fb4a2SCy Schubert 		.body_length  = NULL,
2008780fb4a2SCy Schubert 		.body_present = NULL
20095b9c547cSRui Paulo 	},
20105b9c547cSRui Paulo 
2011*4bc52338SCy Schubert 	/* KMD parameter set */
20125b9c547cSRui Paulo 	{
2013780fb4a2SCy Schubert 		.body_tx      = NULL,
2014780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_kmd_body,
2015780fb4a2SCy Schubert 		.body_length  = NULL,
2016780fb4a2SCy Schubert 		.body_present = NULL
20175b9c547cSRui Paulo 	},
20185b9c547cSRui Paulo 
2019*4bc52338SCy Schubert 	/* Announcement parameter set */
20205b9c547cSRui Paulo 	{
2021780fb4a2SCy Schubert 		.body_tx      = NULL,
2022780fb4a2SCy Schubert 		.body_rx      = ieee802_1x_mka_decode_announce_body,
2023780fb4a2SCy Schubert 		.body_length  = NULL,
2024780fb4a2SCy Schubert 		.body_present = NULL
20255b9c547cSRui Paulo 	},
20265b9c547cSRui Paulo 
2027*4bc52338SCy Schubert 	/* ICV Indicator parameter set */
20285b9c547cSRui Paulo 	{
2029780fb4a2SCy Schubert 		.body_tx      = ieee802_1x_mka_encode_icv_body,
2030780fb4a2SCy Schubert 		.body_rx      = NULL,
2031780fb4a2SCy Schubert 		.body_length  = ieee802_1x_mka_get_icv_length,
2032780fb4a2SCy Schubert 		.body_present = ieee802_1x_mka_icv_body_present
20335b9c547cSRui Paulo 	},
20345b9c547cSRui Paulo };
20355b9c547cSRui Paulo 
20365b9c547cSRui Paulo 
20375b9c547cSRui Paulo /**
203885732ac8SCy Schubert  * ieee802_1x_kay_use_data_key - Take reference on a key
203985732ac8SCy Schubert  */
204085732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
204185732ac8SCy Schubert {
204285732ac8SCy Schubert 	pkey->user++;
204385732ac8SCy Schubert }
204485732ac8SCy Schubert 
204585732ac8SCy Schubert 
204685732ac8SCy Schubert /**
204785732ac8SCy Schubert  * ieee802_1x_kay_deinit_data_key - Release reference on a key and
204885732ac8SCy Schubert  * free if there are no remaining users
20495b9c547cSRui Paulo  */
2050780fb4a2SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
20515b9c547cSRui Paulo {
20525b9c547cSRui Paulo 	if (!pkey)
20535b9c547cSRui Paulo 		return;
20545b9c547cSRui Paulo 
20555b9c547cSRui Paulo 	pkey->user--;
20565b9c547cSRui Paulo 	if (pkey->user > 1)
20575b9c547cSRui Paulo 		return;
20585b9c547cSRui Paulo 
20595b9c547cSRui Paulo 	os_free(pkey->key);
20605b9c547cSRui Paulo 	os_free(pkey);
20615b9c547cSRui Paulo }
20625b9c547cSRui Paulo 
20635b9c547cSRui Paulo 
20645b9c547cSRui Paulo /**
20655b9c547cSRui Paulo  * ieee802_1x_kay_generate_new_sak -
20665b9c547cSRui Paulo  */
20675b9c547cSRui Paulo static int
20685b9c547cSRui Paulo ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
20695b9c547cSRui Paulo {
20705b9c547cSRui Paulo 	struct data_key *sa_key = NULL;
20715b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
20725b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
20735b9c547cSRui Paulo 	int ctx_len, ctx_offset;
20745b9c547cSRui Paulo 	u8 *context;
2075780fb4a2SCy Schubert 	unsigned int key_len;
2076780fb4a2SCy Schubert 	u8 *key;
2077780fb4a2SCy Schubert 	struct macsec_ciphersuite *cs;
20785b9c547cSRui Paulo 
20795b9c547cSRui Paulo 	/* check condition for generating a fresh SAK:
20805b9c547cSRui Paulo 	 * must have one live peer
20815b9c547cSRui Paulo 	 * and MKA life time elapse since last distribution
20825b9c547cSRui Paulo 	 * or potential peer is empty
20835b9c547cSRui Paulo 	 */
20845b9c547cSRui Paulo 	if (dl_list_empty(&participant->live_peers)) {
20855b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
2086*4bc52338SCy Schubert 			   "KaY: Live peers list must not be empty when generating fresh SAK");
20875b9c547cSRui Paulo 		return -1;
20885b9c547cSRui Paulo 	}
20895b9c547cSRui Paulo 
20905b9c547cSRui Paulo 	/* FIXME: A fresh SAK not generated until
20915b9c547cSRui Paulo 	 * the live peer list contains at least one peer and
20925b9c547cSRui Paulo 	 * MKA life time has elapsed since the prior SAK was first distributed,
20935b9c547cSRui Paulo 	 * or the Key server's potential peer is empty
20945b9c547cSRui Paulo 	 * but I can't understand the second item, so
20955b9c547cSRui Paulo 	 * here only check first item and ingore
20965b9c547cSRui Paulo 	 *   && (!dl_list_empty(&participant->potential_peers))) {
20975b9c547cSRui Paulo 	 */
20985b9c547cSRui Paulo 	if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
20995b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
2100*4bc52338SCy Schubert 			   "KaY: Life time has not elapsed since prior SAK distributed");
21015b9c547cSRui Paulo 		return -1;
21025b9c547cSRui Paulo 	}
21035b9c547cSRui Paulo 
2104780fb4a2SCy Schubert 	cs = &cipher_suite_tbl[kay->macsec_csindex];
2105780fb4a2SCy Schubert 	key_len = cs->sak_len;
2106780fb4a2SCy Schubert 	key = os_zalloc(key_len);
2107780fb4a2SCy Schubert 	if (!key) {
21085b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
21095b9c547cSRui Paulo 		return -1;
21105b9c547cSRui Paulo 	}
21115b9c547cSRui Paulo 
2112780fb4a2SCy Schubert 	ctx_len = key_len + sizeof(kay->dist_kn);
21135b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
21145b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list)
21155b9c547cSRui Paulo 		ctx_len += sizeof(peer->mi);
21165b9c547cSRui Paulo 	ctx_len += sizeof(participant->mi);
21175b9c547cSRui Paulo 
21185b9c547cSRui Paulo 	context = os_zalloc(ctx_len);
2119780fb4a2SCy Schubert 	if (!context)
2120780fb4a2SCy Schubert 		goto fail;
2121780fb4a2SCy Schubert 
21225b9c547cSRui Paulo 	ctx_offset = 0;
2123780fb4a2SCy Schubert 	if (os_get_random(context + ctx_offset, key_len) < 0)
2124780fb4a2SCy Schubert 		goto fail;
2125780fb4a2SCy Schubert 
2126780fb4a2SCy Schubert 	ctx_offset += key_len;
21275b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
21285b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
21295b9c547cSRui Paulo 		os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
21305b9c547cSRui Paulo 		ctx_offset += sizeof(peer->mi);
21315b9c547cSRui Paulo 	}
21325b9c547cSRui Paulo 	os_memcpy(context + ctx_offset, participant->mi,
21335b9c547cSRui Paulo 		  sizeof(participant->mi));
21345b9c547cSRui Paulo 	ctx_offset += sizeof(participant->mi);
21355b9c547cSRui Paulo 	os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
21365b9c547cSRui Paulo 
2137*4bc52338SCy Schubert 	if (key_len == 16 || key_len == 32) {
2138*4bc52338SCy Schubert 		if (ieee802_1x_sak_aes_cmac(participant->cak.key,
2139*4bc52338SCy Schubert 					    participant->cak.len,
2140*4bc52338SCy Schubert 					    context, ctx_len,
2141*4bc52338SCy Schubert 					    key, key_len)) {
2142*4bc52338SCy Schubert 			wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK");
2143*4bc52338SCy Schubert 			goto fail;
2144*4bc52338SCy Schubert 		}
21455b9c547cSRui Paulo 	} else {
2146*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported",
2147*4bc52338SCy Schubert 			   key_len);
2148780fb4a2SCy Schubert 		goto fail;
21495b9c547cSRui Paulo 	}
215085732ac8SCy Schubert 	wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
2151780fb4a2SCy Schubert 	os_free(context);
2152780fb4a2SCy Schubert 	context = NULL;
21535b9c547cSRui Paulo 
2154780fb4a2SCy Schubert 	sa_key = os_zalloc(sizeof(*sa_key));
21555b9c547cSRui Paulo 	if (!sa_key) {
2156780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
2157780fb4a2SCy Schubert 		goto fail;
21585b9c547cSRui Paulo 	}
2159780fb4a2SCy Schubert 
2160780fb4a2SCy Schubert 	sa_key->key = key;
2161780fb4a2SCy Schubert 	sa_key->key_len = key_len;
2162780fb4a2SCy Schubert 	os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN);
2163780fb4a2SCy Schubert 	sa_key->key_identifier.kn = kay->dist_kn;
2164780fb4a2SCy Schubert 
2165780fb4a2SCy Schubert 	sa_key->confidentiality_offset = kay->macsec_confidentiality;
2166780fb4a2SCy Schubert 	sa_key->an = kay->dist_an;
2167780fb4a2SCy Schubert 	ieee802_1x_kay_init_data_key(sa_key);
2168780fb4a2SCy Schubert 
21695b9c547cSRui Paulo 	participant->new_key = sa_key;
21705b9c547cSRui Paulo 
217185732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(sa_key);
21725b9c547cSRui Paulo 	dl_list_add(&participant->sak_list, &sa_key->list);
217385732ac8SCy Schubert 
2174780fb4a2SCy Schubert 	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
21755b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(kay->cp);
2176780fb4a2SCy Schubert 	ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
21775b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(kay->cp);
2178780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
2179780fb4a2SCy Schubert 	ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an);
21805b9c547cSRui Paulo 	ieee802_1x_cp_signal_newsak(kay->cp);
21815b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(kay->cp);
21825b9c547cSRui Paulo 
21835b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
21845b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list)
21855b9c547cSRui Paulo 		peer->sak_used = FALSE;
21865b9c547cSRui Paulo 
2187780fb4a2SCy Schubert 	kay->dist_kn++;
2188780fb4a2SCy Schubert 	kay->dist_an++;
2189780fb4a2SCy Schubert 	if (kay->dist_an > 3)
2190780fb4a2SCy Schubert 		kay->dist_an = 0;
21915b9c547cSRui Paulo 
2192780fb4a2SCy Schubert 	kay->dist_time = time(NULL);
21935b9c547cSRui Paulo 
21945b9c547cSRui Paulo 	return 0;
2195780fb4a2SCy Schubert 
2196780fb4a2SCy Schubert fail:
2197780fb4a2SCy Schubert 	os_free(key);
2198780fb4a2SCy Schubert 	os_free(context);
2199780fb4a2SCy Schubert 	return -1;
2200780fb4a2SCy Schubert }
2201780fb4a2SCy Schubert 
2202780fb4a2SCy Schubert 
2203780fb4a2SCy Schubert static int compare_priorities(const struct ieee802_1x_kay_peer *peer,
2204780fb4a2SCy Schubert 			      const struct ieee802_1x_kay_peer *other)
2205780fb4a2SCy Schubert {
2206780fb4a2SCy Schubert 	if (peer->key_server_priority < other->key_server_priority)
2207780fb4a2SCy Schubert 		return -1;
2208780fb4a2SCy Schubert 	if (other->key_server_priority < peer->key_server_priority)
2209780fb4a2SCy Schubert 		return 1;
2210780fb4a2SCy Schubert 
2211780fb4a2SCy Schubert 	return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN);
22125b9c547cSRui Paulo }
22135b9c547cSRui Paulo 
22145b9c547cSRui Paulo 
22155b9c547cSRui Paulo /**
22165b9c547cSRui Paulo  * ieee802_1x_kay_elect_key_server - elect the key server
22175b9c547cSRui Paulo  * when to elect: whenever the live peers list changes
22185b9c547cSRui Paulo  */
22195b9c547cSRui Paulo static int
22205b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
22215b9c547cSRui Paulo {
22225b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
22235b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *key_server = NULL;
22245b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
22255b9c547cSRui Paulo 	Boolean i_is_key_server;
222685732ac8SCy Schubert 	int priority_comparison;
22275b9c547cSRui Paulo 
22285b9c547cSRui Paulo 	if (participant->is_obliged_key_server) {
22295b9c547cSRui Paulo 		participant->new_sak = TRUE;
22305b9c547cSRui Paulo 		participant->to_dist_sak = FALSE;
22315b9c547cSRui Paulo 		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
22325b9c547cSRui Paulo 		return 0;
22335b9c547cSRui Paulo 	}
22345b9c547cSRui Paulo 
22355b9c547cSRui Paulo 	/* elect the key server among the peers */
22365b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
22375b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
22385b9c547cSRui Paulo 		if (!peer->is_key_server)
22395b9c547cSRui Paulo 			continue;
22405b9c547cSRui Paulo 
22415b9c547cSRui Paulo 		if (!key_server) {
22425b9c547cSRui Paulo 			key_server = peer;
22435b9c547cSRui Paulo 			continue;
22445b9c547cSRui Paulo 		}
22455b9c547cSRui Paulo 
2246780fb4a2SCy Schubert 		if (compare_priorities(peer, key_server) < 0)
22475b9c547cSRui Paulo 			key_server = peer;
22485b9c547cSRui Paulo 	}
22495b9c547cSRui Paulo 
22505b9c547cSRui Paulo 	/* elect the key server between me and the above elected peer */
22515b9c547cSRui Paulo 	i_is_key_server = FALSE;
22525b9c547cSRui Paulo 	if (key_server && participant->can_be_key_server) {
2253780fb4a2SCy Schubert 		struct ieee802_1x_kay_peer tmp;
22545b9c547cSRui Paulo 
2255780fb4a2SCy Schubert 		tmp.key_server_priority = kay->actor_priority;
2256780fb4a2SCy Schubert 		os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
225785732ac8SCy Schubert 		priority_comparison = compare_priorities(&tmp, key_server);
225885732ac8SCy Schubert 		if (priority_comparison < 0) {
2259780fb4a2SCy Schubert 			i_is_key_server = TRUE;
226085732ac8SCy Schubert 		} else if (priority_comparison == 0) {
226185732ac8SCy Schubert 			wpa_printf(MSG_WARNING,
226285732ac8SCy Schubert 				   "KaY: Cannot elect key server between me and peer, duplicate MAC detected");
226385732ac8SCy Schubert 			key_server = NULL;
226485732ac8SCy Schubert 		}
2265780fb4a2SCy Schubert 	} else if (participant->can_be_key_server) {
2266780fb4a2SCy Schubert 		i_is_key_server = TRUE;
22675b9c547cSRui Paulo 	}
22685b9c547cSRui Paulo 
22695b9c547cSRui Paulo 	if (i_is_key_server) {
22705b9c547cSRui Paulo 		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
2271780fb4a2SCy Schubert 		if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) {
22725b9c547cSRui Paulo 			ieee802_1x_cp_signal_chgdserver(kay->cp);
22735b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(kay->cp);
22745b9c547cSRui Paulo 		}
22755b9c547cSRui Paulo 
22765b9c547cSRui Paulo 		participant->is_key_server = TRUE;
22775b9c547cSRui Paulo 		participant->principal = TRUE;
22785b9c547cSRui Paulo 		participant->new_sak = TRUE;
2279*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: I am elected as key server");
22805b9c547cSRui Paulo 		participant->to_dist_sak = FALSE;
22815b9c547cSRui Paulo 		participant->is_elected = TRUE;
22825b9c547cSRui Paulo 
22835b9c547cSRui Paulo 		os_memcpy(&kay->key_server_sci, &kay->actor_sci,
22845b9c547cSRui Paulo 			  sizeof(kay->key_server_sci));
22855b9c547cSRui Paulo 		kay->key_server_priority = kay->actor_priority;
2286780fb4a2SCy Schubert 	} else if (key_server) {
2287*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
2288*4bc52338SCy Schubert 			   "KaY: Peer %s was elected as the key server",
2289*4bc52338SCy Schubert 			   mi_txt(key_server->mi));
22905b9c547cSRui Paulo 		ieee802_1x_cp_set_electedself(kay->cp, FALSE);
2291780fb4a2SCy Schubert 		if (!sci_equal(&kay->key_server_sci, &key_server->sci)) {
22925b9c547cSRui Paulo 			ieee802_1x_cp_signal_chgdserver(kay->cp);
22935b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(kay->cp);
22945b9c547cSRui Paulo 		}
22955b9c547cSRui Paulo 
22965b9c547cSRui Paulo 		participant->is_key_server = FALSE;
22975b9c547cSRui Paulo 		participant->principal = TRUE;
22985b9c547cSRui Paulo 		participant->is_elected = TRUE;
22995b9c547cSRui Paulo 
23005b9c547cSRui Paulo 		os_memcpy(&kay->key_server_sci, &key_server->sci,
23015b9c547cSRui Paulo 			  sizeof(kay->key_server_sci));
23025b9c547cSRui Paulo 		kay->key_server_priority = key_server->key_server_priority;
2303780fb4a2SCy Schubert 	} else {
2304780fb4a2SCy Schubert 		participant->principal = FALSE;
2305780fb4a2SCy Schubert 		participant->is_key_server = FALSE;
2306780fb4a2SCy Schubert 		participant->is_elected = FALSE;
23075b9c547cSRui Paulo 	}
23085b9c547cSRui Paulo 
23095b9c547cSRui Paulo 	return 0;
23105b9c547cSRui Paulo }
23115b9c547cSRui Paulo 
23125b9c547cSRui Paulo 
23135b9c547cSRui Paulo /**
23145b9c547cSRui Paulo  * ieee802_1x_kay_decide_macsec_use - the key server determinate
23155b9c547cSRui Paulo  *		 how to use MACsec: whether use MACsec and its capability
23165b9c547cSRui Paulo  * protectFrames will be advised if the key server and one of its live peers are
23175b9c547cSRui Paulo  * MACsec capable and one of those request MACsec protection
23185b9c547cSRui Paulo  */
23195b9c547cSRui Paulo static int
23205b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(
23215b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
23225b9c547cSRui Paulo {
23235b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
23245b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
23255b9c547cSRui Paulo 	enum macsec_cap less_capability;
23265b9c547cSRui Paulo 	Boolean has_peer;
23275b9c547cSRui Paulo 
23285b9c547cSRui Paulo 	if (!participant->is_key_server)
23295b9c547cSRui Paulo 		return -1;
23305b9c547cSRui Paulo 
23315b9c547cSRui Paulo 	/* key server self is MACsec-desired and requesting MACsec */
23325b9c547cSRui Paulo 	if (!kay->macsec_desired) {
23335b9c547cSRui Paulo 		participant->advised_desired = FALSE;
23345b9c547cSRui Paulo 		return -1;
23355b9c547cSRui Paulo 	}
23365b9c547cSRui Paulo 	if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
23375b9c547cSRui Paulo 		participant->advised_desired = FALSE;
23385b9c547cSRui Paulo 		return -1;
23395b9c547cSRui Paulo 	}
23405b9c547cSRui Paulo 	less_capability = kay->macsec_capable;
23415b9c547cSRui Paulo 
23425b9c547cSRui Paulo 	/* at least one of peers is MACsec-desired and requesting MACsec */
23435b9c547cSRui Paulo 	has_peer = FALSE;
23445b9c547cSRui Paulo 	dl_list_for_each(peer, &participant->live_peers,
23455b9c547cSRui Paulo 			 struct ieee802_1x_kay_peer, list) {
23465b9c547cSRui Paulo 		if (!peer->macsec_desired)
23475b9c547cSRui Paulo 			continue;
23485b9c547cSRui Paulo 
2349780fb4a2SCy Schubert 		if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED)
23505b9c547cSRui Paulo 			continue;
23515b9c547cSRui Paulo 
2352780fb4a2SCy Schubert 		less_capability = (less_capability < peer->macsec_capability) ?
2353780fb4a2SCy Schubert 			less_capability : peer->macsec_capability;
23545b9c547cSRui Paulo 		has_peer = TRUE;
23555b9c547cSRui Paulo 	}
23565b9c547cSRui Paulo 
23575b9c547cSRui Paulo 	if (has_peer) {
23585b9c547cSRui Paulo 		participant->advised_desired = TRUE;
23595b9c547cSRui Paulo 		participant->advised_capability = less_capability;
23605b9c547cSRui Paulo 		kay->authenticated = FALSE;
23615b9c547cSRui Paulo 		kay->secured = TRUE;
23625b9c547cSRui Paulo 		kay->failed = FALSE;
23635b9c547cSRui Paulo 		ieee802_1x_cp_connect_secure(kay->cp);
23645b9c547cSRui Paulo 		ieee802_1x_cp_sm_step(kay->cp);
23655b9c547cSRui Paulo 	} else {
23665b9c547cSRui Paulo 		participant->advised_desired = FALSE;
23675b9c547cSRui Paulo 		participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
23685b9c547cSRui Paulo 		participant->to_use_sak = FALSE;
23695b9c547cSRui Paulo 		kay->authenticated = TRUE;
23705b9c547cSRui Paulo 		kay->secured = FALSE;
23715b9c547cSRui Paulo 		kay->failed = FALSE;
23725b9c547cSRui Paulo 		kay->ltx_kn = 0;
23735b9c547cSRui Paulo 		kay->ltx_an = 0;
23745b9c547cSRui Paulo 		kay->lrx_kn = 0;
23755b9c547cSRui Paulo 		kay->lrx_an = 0;
23765b9c547cSRui Paulo 		kay->otx_kn = 0;
23775b9c547cSRui Paulo 		kay->otx_an = 0;
23785b9c547cSRui Paulo 		kay->orx_kn = 0;
23795b9c547cSRui Paulo 		kay->orx_an = 0;
23805b9c547cSRui Paulo 		ieee802_1x_cp_connect_authenticated(kay->cp);
23815b9c547cSRui Paulo 		ieee802_1x_cp_sm_step(kay->cp);
23825b9c547cSRui Paulo 	}
23835b9c547cSRui Paulo 
23845b9c547cSRui Paulo 	return 0;
23855b9c547cSRui Paulo }
23865b9c547cSRui Paulo 
23875b9c547cSRui Paulo static const u8 pae_group_addr[ETH_ALEN] = {
23885b9c547cSRui Paulo 	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
23895b9c547cSRui Paulo };
23905b9c547cSRui Paulo 
23915b9c547cSRui Paulo 
23925b9c547cSRui Paulo /**
23935b9c547cSRui Paulo  * ieee802_1x_kay_encode_mkpdu -
23945b9c547cSRui Paulo  */
23955b9c547cSRui Paulo static int
23965b9c547cSRui Paulo ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
23975b9c547cSRui Paulo 			    struct wpabuf *pbuf)
23985b9c547cSRui Paulo {
23995b9c547cSRui Paulo 	unsigned int i;
24005b9c547cSRui Paulo 	struct ieee8023_hdr *ether_hdr;
24015b9c547cSRui Paulo 	struct ieee802_1x_hdr *eapol_hdr;
24025b9c547cSRui Paulo 
24035b9c547cSRui Paulo 	ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
24045b9c547cSRui Paulo 	os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
24055b9c547cSRui Paulo 	os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
24065b9c547cSRui Paulo 		  sizeof(ether_hdr->dest));
24075b9c547cSRui Paulo 	ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
2408*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
2409*4bc52338SCy Schubert 		   " Ethertype=0x%x",
2410*4bc52338SCy Schubert 		   MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src),
2411*4bc52338SCy Schubert 		   be_to_host16(ether_hdr->ethertype));
24125b9c547cSRui Paulo 
24135b9c547cSRui Paulo 	eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
24145b9c547cSRui Paulo 	eapol_hdr->version = EAPOL_VERSION;
24155b9c547cSRui Paulo 	eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
2416*4bc52338SCy Schubert 	eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf));
2417*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG,
2418*4bc52338SCy Schubert 		   "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
2419*4bc52338SCy Schubert 		   eapol_hdr->version, eapol_hdr->type,
2420*4bc52338SCy Schubert 		   be_to_host16(eapol_hdr->length));
24215b9c547cSRui Paulo 
2422780fb4a2SCy Schubert 	for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
2423780fb4a2SCy Schubert 		if (mka_body_handler[i].body_present &&
2424780fb4a2SCy Schubert 		    mka_body_handler[i].body_present(participant)) {
2425780fb4a2SCy Schubert 			if (mka_body_handler[i].body_tx(participant, pbuf))
24265b9c547cSRui Paulo 				return -1;
24275b9c547cSRui Paulo 		}
24285b9c547cSRui Paulo 	}
24295b9c547cSRui Paulo 
24305b9c547cSRui Paulo 	return 0;
24315b9c547cSRui Paulo }
24325b9c547cSRui Paulo 
2433*4bc52338SCy Schubert 
24345b9c547cSRui Paulo /**
24355b9c547cSRui Paulo  * ieee802_1x_participant_send_mkpdu -
24365b9c547cSRui Paulo  */
24375b9c547cSRui Paulo static int
24385b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(
24395b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant)
24405b9c547cSRui Paulo {
24415b9c547cSRui Paulo 	struct wpabuf *buf;
24425b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = participant->kay;
24435b9c547cSRui Paulo 	size_t length = 0;
24445b9c547cSRui Paulo 	unsigned int i;
24455b9c547cSRui Paulo 
2446*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)",
2447*4bc52338SCy Schubert 		   kay->if_name);
24485b9c547cSRui Paulo 	length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
2449780fb4a2SCy Schubert 	for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
2450780fb4a2SCy Schubert 		if (mka_body_handler[i].body_present &&
2451780fb4a2SCy Schubert 		    mka_body_handler[i].body_present(participant))
2452780fb4a2SCy Schubert 			length += mka_body_handler[i].body_length(participant);
24535b9c547cSRui Paulo 	}
24545b9c547cSRui Paulo 
24555b9c547cSRui Paulo 	buf = wpabuf_alloc(length);
24565b9c547cSRui Paulo 	if (!buf) {
24575b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: out of memory");
24585b9c547cSRui Paulo 		return -1;
24595b9c547cSRui Paulo 	}
24605b9c547cSRui Paulo 
24615b9c547cSRui Paulo 	if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
2462*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail");
24635b9c547cSRui Paulo 		return -1;
24645b9c547cSRui Paulo 	}
24655b9c547cSRui Paulo 
2466*4bc52338SCy Schubert 	wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf);
24675b9c547cSRui Paulo 	l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
24685b9c547cSRui Paulo 	wpabuf_free(buf);
24695b9c547cSRui Paulo 
24705b9c547cSRui Paulo 	kay->active = TRUE;
24715b9c547cSRui Paulo 	participant->active = TRUE;
24725b9c547cSRui Paulo 
24735b9c547cSRui Paulo 	return 0;
24745b9c547cSRui Paulo }
24755b9c547cSRui Paulo 
24765b9c547cSRui Paulo 
24775b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
247885732ac8SCy Schubert 
247985732ac8SCy Schubert static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
248085732ac8SCy Schubert 					  struct transmit_sa *sa)
248185732ac8SCy Schubert {
248285732ac8SCy Schubert 	secy_disable_transmit_sa(kay, sa);
248385732ac8SCy Schubert 	secy_delete_transmit_sa(kay, sa);
248485732ac8SCy Schubert 	ieee802_1x_kay_deinit_transmit_sa(sa);
248585732ac8SCy Schubert }
248685732ac8SCy Schubert 
248785732ac8SCy Schubert 
24885b9c547cSRui Paulo /**
24895b9c547cSRui Paulo  * ieee802_1x_participant_timer -
24905b9c547cSRui Paulo  */
24915b9c547cSRui Paulo static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
24925b9c547cSRui Paulo {
24935b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
24945b9c547cSRui Paulo 	struct ieee802_1x_kay *kay;
24955b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer, *pre_peer;
24965b9c547cSRui Paulo 	time_t now = time(NULL);
24975b9c547cSRui Paulo 	Boolean lp_changed;
24985b9c547cSRui Paulo 	struct receive_sc *rxsc, *pre_rxsc;
24995b9c547cSRui Paulo 	struct transmit_sa *txsa, *pre_txsa;
25005b9c547cSRui Paulo 
25015b9c547cSRui Paulo 	participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
25025b9c547cSRui Paulo 	kay = participant->kay;
2503*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)",
2504*4bc52338SCy Schubert 		   kay->if_name);
25055b9c547cSRui Paulo 	if (participant->cak_life) {
2506780fb4a2SCy Schubert 		if (now > participant->cak_life)
2507780fb4a2SCy Schubert 			goto delete_mka;
25085b9c547cSRui Paulo 	}
25095b9c547cSRui Paulo 
25105b9c547cSRui Paulo 	/* should delete MKA instance if there are not live peers
25115b9c547cSRui Paulo 	 * when the MKA life elapsed since its creating */
25125b9c547cSRui Paulo 	if (participant->mka_life) {
25135b9c547cSRui Paulo 		if (dl_list_empty(&participant->live_peers)) {
2514780fb4a2SCy Schubert 			if (now > participant->mka_life)
2515780fb4a2SCy Schubert 				goto delete_mka;
25165b9c547cSRui Paulo 		} else {
25175b9c547cSRui Paulo 			participant->mka_life = 0;
25185b9c547cSRui Paulo 		}
25195b9c547cSRui Paulo 	}
25205b9c547cSRui Paulo 
25215b9c547cSRui Paulo 	lp_changed = FALSE;
25225b9c547cSRui Paulo 	dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
25235b9c547cSRui Paulo 			      struct ieee802_1x_kay_peer, list) {
25245b9c547cSRui Paulo 		if (now > peer->expire) {
25255b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
25265b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
25275b9c547cSRui Paulo 				    sizeof(peer->mi));
25285b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
25295b9c547cSRui Paulo 			dl_list_for_each_safe(rxsc, pre_rxsc,
25305b9c547cSRui Paulo 					      &participant->rxsc_list,
25315b9c547cSRui Paulo 					      struct receive_sc, list) {
2532780fb4a2SCy Schubert 				if (sci_equal(&rxsc->sci, &peer->sci)) {
25335b9c547cSRui Paulo 					ieee802_1x_kay_deinit_receive_sc(
25345b9c547cSRui Paulo 						participant, rxsc);
25355b9c547cSRui Paulo 				}
25365b9c547cSRui Paulo 			}
25375b9c547cSRui Paulo 			dl_list_del(&peer->list);
25385b9c547cSRui Paulo 			os_free(peer);
25395b9c547cSRui Paulo 			lp_changed = TRUE;
25405b9c547cSRui Paulo 		}
25415b9c547cSRui Paulo 	}
25425b9c547cSRui Paulo 
25435b9c547cSRui Paulo 	if (lp_changed) {
25445b9c547cSRui Paulo 		if (dl_list_empty(&participant->live_peers)) {
25455b9c547cSRui Paulo 			participant->advised_desired = FALSE;
25465b9c547cSRui Paulo 			participant->advised_capability =
25475b9c547cSRui Paulo 				MACSEC_CAP_NOT_IMPLEMENTED;
25485b9c547cSRui Paulo 			participant->to_use_sak = FALSE;
254985732ac8SCy Schubert 			participant->ltx = FALSE;
255085732ac8SCy Schubert 			participant->lrx = FALSE;
255185732ac8SCy Schubert 			participant->otx = FALSE;
255285732ac8SCy Schubert 			participant->orx = FALSE;
255385732ac8SCy Schubert 			participant->is_key_server = FALSE;
255485732ac8SCy Schubert 			participant->is_elected = FALSE;
255585732ac8SCy Schubert 			kay->authenticated = FALSE;
25565b9c547cSRui Paulo 			kay->secured = FALSE;
25575b9c547cSRui Paulo 			kay->failed = FALSE;
25585b9c547cSRui Paulo 			kay->ltx_kn = 0;
25595b9c547cSRui Paulo 			kay->ltx_an = 0;
25605b9c547cSRui Paulo 			kay->lrx_kn = 0;
25615b9c547cSRui Paulo 			kay->lrx_an = 0;
25625b9c547cSRui Paulo 			kay->otx_kn = 0;
25635b9c547cSRui Paulo 			kay->otx_an = 0;
25645b9c547cSRui Paulo 			kay->orx_kn = 0;
25655b9c547cSRui Paulo 			kay->orx_an = 0;
25665b9c547cSRui Paulo 			dl_list_for_each_safe(txsa, pre_txsa,
25675b9c547cSRui Paulo 					      &participant->txsc->sa_list,
25685b9c547cSRui Paulo 					      struct transmit_sa, list) {
256985732ac8SCy Schubert 				ieee802_1x_delete_transmit_sa(kay, txsa);
25705b9c547cSRui Paulo 			}
25715b9c547cSRui Paulo 
257285732ac8SCy Schubert 			ieee802_1x_cp_connect_pending(kay->cp);
25735b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(kay->cp);
25745b9c547cSRui Paulo 		} else {
25755b9c547cSRui Paulo 			ieee802_1x_kay_elect_key_server(participant);
25765b9c547cSRui Paulo 			ieee802_1x_kay_decide_macsec_use(participant);
25775b9c547cSRui Paulo 		}
25785b9c547cSRui Paulo 	}
25795b9c547cSRui Paulo 
25805b9c547cSRui Paulo 	dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
25815b9c547cSRui Paulo 			      struct ieee802_1x_kay_peer, list) {
25825b9c547cSRui Paulo 		if (now > peer->expire) {
25835b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
25845b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
25855b9c547cSRui Paulo 				    sizeof(peer->mi));
25865b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
25875b9c547cSRui Paulo 			dl_list_del(&peer->list);
25885b9c547cSRui Paulo 			os_free(peer);
25895b9c547cSRui Paulo 		}
25905b9c547cSRui Paulo 	}
25915b9c547cSRui Paulo 
2592*4bc52338SCy Schubert 	if (participant->new_sak && participant->is_key_server) {
25935b9c547cSRui Paulo 		if (!ieee802_1x_kay_generate_new_sak(participant))
25945b9c547cSRui Paulo 			participant->to_dist_sak = TRUE;
25955b9c547cSRui Paulo 
25965b9c547cSRui Paulo 		participant->new_sak = FALSE;
25975b9c547cSRui Paulo 	}
25985b9c547cSRui Paulo 
259985732ac8SCy Schubert 	if (participant->retry_count < MAX_RETRY_CNT ||
260085732ac8SCy Schubert 	    participant->mode == PSK) {
26015b9c547cSRui Paulo 		ieee802_1x_participant_send_mkpdu(participant);
26025b9c547cSRui Paulo 		participant->retry_count++;
26035b9c547cSRui Paulo 	}
26045b9c547cSRui Paulo 
2605*4bc52338SCy Schubert 	eloop_register_timeout(kay->mka_hello_time / 1000, 0,
26065b9c547cSRui Paulo 			       ieee802_1x_participant_timer,
26075b9c547cSRui Paulo 			       participant, NULL);
2608780fb4a2SCy Schubert 
2609780fb4a2SCy Schubert 	return;
2610780fb4a2SCy Schubert 
2611780fb4a2SCy Schubert delete_mka:
2612780fb4a2SCy Schubert 	kay->authenticated = FALSE;
2613780fb4a2SCy Schubert 	kay->secured = FALSE;
2614780fb4a2SCy Schubert 	kay->failed = TRUE;
2615780fb4a2SCy Schubert 	ieee802_1x_kay_delete_mka(kay, &participant->ckn);
26165b9c547cSRui Paulo }
26175b9c547cSRui Paulo 
26185b9c547cSRui Paulo 
26195b9c547cSRui Paulo /**
26205b9c547cSRui Paulo  * ieee802_1x_kay_init_transmit_sa -
26215b9c547cSRui Paulo  */
26225b9c547cSRui Paulo static struct transmit_sa *
26235b9c547cSRui Paulo ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
26245b9c547cSRui Paulo 				struct data_key *key)
26255b9c547cSRui Paulo {
26265b9c547cSRui Paulo 	struct transmit_sa *psa;
26275b9c547cSRui Paulo 
26285b9c547cSRui Paulo 	key->tx_latest = TRUE;
26295b9c547cSRui Paulo 	key->rx_latest = TRUE;
26305b9c547cSRui Paulo 
26315b9c547cSRui Paulo 	psa = os_zalloc(sizeof(*psa));
26325b9c547cSRui Paulo 	if (!psa) {
26335b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
26345b9c547cSRui Paulo 		return NULL;
26355b9c547cSRui Paulo 	}
26365b9c547cSRui Paulo 
26375b9c547cSRui Paulo 	if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
26385b9c547cSRui Paulo 	    key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
26395b9c547cSRui Paulo 		psa->confidentiality = TRUE;
26405b9c547cSRui Paulo 	else
26415b9c547cSRui Paulo 		psa->confidentiality = FALSE;
26425b9c547cSRui Paulo 
26435b9c547cSRui Paulo 	psa->an = an;
264485732ac8SCy Schubert 	ieee802_1x_kay_use_data_key(key);
26455b9c547cSRui Paulo 	psa->pkey = key;
26465b9c547cSRui Paulo 	psa->next_pn = next_PN;
26475b9c547cSRui Paulo 	psa->sc = psc;
26485b9c547cSRui Paulo 
26495b9c547cSRui Paulo 	os_get_time(&psa->created_time);
26505b9c547cSRui Paulo 	psa->in_use = FALSE;
26515b9c547cSRui Paulo 
26525b9c547cSRui Paulo 	dl_list_add(&psc->sa_list, &psa->list);
26535b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
2654*4bc52338SCy Schubert 		   "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC",
265585732ac8SCy Schubert 		   an, next_PN);
26565b9c547cSRui Paulo 
26575b9c547cSRui Paulo 	return psa;
26585b9c547cSRui Paulo }
26595b9c547cSRui Paulo 
26605b9c547cSRui Paulo 
26615b9c547cSRui Paulo /**
26625b9c547cSRui Paulo  * ieee802_1x_kay_deinit_transmit_sa -
26635b9c547cSRui Paulo  */
26645b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
26655b9c547cSRui Paulo {
266685732ac8SCy Schubert 	ieee802_1x_kay_deinit_data_key(psa->pkey);
26675b9c547cSRui Paulo 	psa->pkey = NULL;
26685b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
2669780fb4a2SCy Schubert 		   "KaY: Delete transmit SA(an: %hhu) of SC",
2670780fb4a2SCy Schubert 		   psa->an);
26715b9c547cSRui Paulo 	dl_list_del(&psa->list);
26725b9c547cSRui Paulo 	os_free(psa);
26735b9c547cSRui Paulo }
26745b9c547cSRui Paulo 
26755b9c547cSRui Paulo 
26765b9c547cSRui Paulo /**
26775b9c547cSRui Paulo  * init_transmit_sc -
26785b9c547cSRui Paulo  */
26795b9c547cSRui Paulo static struct transmit_sc *
268085732ac8SCy Schubert ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
26815b9c547cSRui Paulo {
26825b9c547cSRui Paulo 	struct transmit_sc *psc;
26835b9c547cSRui Paulo 
26845b9c547cSRui Paulo 	psc = os_zalloc(sizeof(*psc));
26855b9c547cSRui Paulo 	if (!psc) {
26865b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
26875b9c547cSRui Paulo 		return NULL;
26885b9c547cSRui Paulo 	}
26895b9c547cSRui Paulo 	os_memcpy(&psc->sci, sci, sizeof(psc->sci));
26905b9c547cSRui Paulo 
26915b9c547cSRui Paulo 	os_get_time(&psc->created_time);
26925b9c547cSRui Paulo 	psc->transmitting = FALSE;
26935b9c547cSRui Paulo 	psc->encoding_sa = FALSE;
26945b9c547cSRui Paulo 	psc->enciphering_sa = FALSE;
26955b9c547cSRui Paulo 
26965b9c547cSRui Paulo 	dl_list_init(&psc->sa_list);
2697*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s",
2698*4bc52338SCy Schubert 		   sci_txt(&psc->sci));
26995b9c547cSRui Paulo 
27005b9c547cSRui Paulo 	return psc;
27015b9c547cSRui Paulo }
27025b9c547cSRui Paulo 
27035b9c547cSRui Paulo 
27045b9c547cSRui Paulo /**
27055b9c547cSRui Paulo  * ieee802_1x_kay_deinit_transmit_sc -
27065b9c547cSRui Paulo  */
27075b9c547cSRui Paulo static void
27085b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc(
27095b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
27105b9c547cSRui Paulo {
27115b9c547cSRui Paulo 	struct transmit_sa *psa, *tmp;
27125b9c547cSRui Paulo 
271385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
271485732ac8SCy Schubert 	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
271585732ac8SCy Schubert 		ieee802_1x_delete_transmit_sa(participant->kay, psa);
27165b9c547cSRui Paulo 
271785732ac8SCy Schubert 	secy_delete_transmit_sc(participant->kay, psc);
27185b9c547cSRui Paulo 	os_free(psc);
27195b9c547cSRui Paulo }
27205b9c547cSRui Paulo 
27215b9c547cSRui Paulo 
27225b9c547cSRui Paulo /****************** Interface between CP and KAY *********************/
27235b9c547cSRui Paulo /**
27245b9c547cSRui Paulo  * ieee802_1x_kay_set_latest_sa_attr -
27255b9c547cSRui Paulo  */
27265b9c547cSRui Paulo int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
27275b9c547cSRui Paulo 				      struct ieee802_1x_mka_ki *lki, u8 lan,
27285b9c547cSRui Paulo 				      Boolean ltx, Boolean lrx)
27295b9c547cSRui Paulo {
27305b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
27315b9c547cSRui Paulo 
27325b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
27335b9c547cSRui Paulo 	if (!principal)
27345b9c547cSRui Paulo 		return -1;
27355b9c547cSRui Paulo 
27365b9c547cSRui Paulo 	if (!lki)
27375b9c547cSRui Paulo 		os_memset(&principal->lki, 0, sizeof(principal->lki));
27385b9c547cSRui Paulo 	else
27395b9c547cSRui Paulo 		os_memcpy(&principal->lki, lki, sizeof(principal->lki));
27405b9c547cSRui Paulo 
27415b9c547cSRui Paulo 	principal->lan = lan;
27425b9c547cSRui Paulo 	principal->ltx = ltx;
27435b9c547cSRui Paulo 	principal->lrx = lrx;
27445b9c547cSRui Paulo 	if (!lki) {
27455b9c547cSRui Paulo 		kay->ltx_kn = 0;
27465b9c547cSRui Paulo 		kay->lrx_kn = 0;
27475b9c547cSRui Paulo 	} else {
27485b9c547cSRui Paulo 		kay->ltx_kn = lki->kn;
27495b9c547cSRui Paulo 		kay->lrx_kn = lki->kn;
27505b9c547cSRui Paulo 	}
27515b9c547cSRui Paulo 	kay->ltx_an = lan;
27525b9c547cSRui Paulo 	kay->lrx_an = lan;
27535b9c547cSRui Paulo 
27545b9c547cSRui Paulo 	return 0;
27555b9c547cSRui Paulo }
27565b9c547cSRui Paulo 
27575b9c547cSRui Paulo 
27585b9c547cSRui Paulo /**
27595b9c547cSRui Paulo  * ieee802_1x_kay_set_old_sa_attr -
27605b9c547cSRui Paulo  */
27615b9c547cSRui Paulo int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
27625b9c547cSRui Paulo 				   struct ieee802_1x_mka_ki *oki,
27635b9c547cSRui Paulo 				   u8 oan, Boolean otx, Boolean orx)
27645b9c547cSRui Paulo {
27655b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
27665b9c547cSRui Paulo 
27675b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
27685b9c547cSRui Paulo 	if (!principal)
27695b9c547cSRui Paulo 		return -1;
27705b9c547cSRui Paulo 
27715b9c547cSRui Paulo 	if (!oki)
27725b9c547cSRui Paulo 		os_memset(&principal->oki, 0, sizeof(principal->oki));
27735b9c547cSRui Paulo 	else
27745b9c547cSRui Paulo 		os_memcpy(&principal->oki, oki, sizeof(principal->oki));
27755b9c547cSRui Paulo 
27765b9c547cSRui Paulo 	principal->oan = oan;
27775b9c547cSRui Paulo 	principal->otx = otx;
27785b9c547cSRui Paulo 	principal->orx = orx;
27795b9c547cSRui Paulo 
27805b9c547cSRui Paulo 	if (!oki) {
27815b9c547cSRui Paulo 		kay->otx_kn = 0;
27825b9c547cSRui Paulo 		kay->orx_kn = 0;
27835b9c547cSRui Paulo 	} else {
27845b9c547cSRui Paulo 		kay->otx_kn = oki->kn;
27855b9c547cSRui Paulo 		kay->orx_kn = oki->kn;
27865b9c547cSRui Paulo 	}
27875b9c547cSRui Paulo 	kay->otx_an = oan;
27885b9c547cSRui Paulo 	kay->orx_an = oan;
27895b9c547cSRui Paulo 
27905b9c547cSRui Paulo 	return 0;
27915b9c547cSRui Paulo }
27925b9c547cSRui Paulo 
27935b9c547cSRui Paulo 
279485732ac8SCy Schubert static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
279585732ac8SCy Schubert {
279685732ac8SCy Schubert 	struct transmit_sa *txsa;
279785732ac8SCy Schubert 
279885732ac8SCy Schubert 	dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
279985732ac8SCy Schubert 		if (txsa->an == an)
280085732ac8SCy Schubert 			return txsa;
280185732ac8SCy Schubert 	}
280285732ac8SCy Schubert 
280385732ac8SCy Schubert 	return NULL;
280485732ac8SCy Schubert }
280585732ac8SCy Schubert 
280685732ac8SCy Schubert 
280785732ac8SCy Schubert static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
280885732ac8SCy Schubert {
280985732ac8SCy Schubert 	struct receive_sa *rxsa;
281085732ac8SCy Schubert 
281185732ac8SCy Schubert 	dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
281285732ac8SCy Schubert 		if (rxsa->an == an)
281385732ac8SCy Schubert 			return rxsa;
281485732ac8SCy Schubert 	}
281585732ac8SCy Schubert 
281685732ac8SCy Schubert 	return NULL;
281785732ac8SCy Schubert }
281885732ac8SCy Schubert 
281985732ac8SCy Schubert 
28205b9c547cSRui Paulo /**
28215b9c547cSRui Paulo  * ieee802_1x_kay_create_sas -
28225b9c547cSRui Paulo  */
28235b9c547cSRui Paulo int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
28245b9c547cSRui Paulo 			      struct ieee802_1x_mka_ki *lki)
28255b9c547cSRui Paulo {
28265b9c547cSRui Paulo 	struct data_key *sa_key, *latest_sak;
28275b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
28285b9c547cSRui Paulo 	struct receive_sc *rxsc;
28295b9c547cSRui Paulo 	struct receive_sa *rxsa;
28305b9c547cSRui Paulo 	struct transmit_sa *txsa;
28315b9c547cSRui Paulo 
28325b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
28335b9c547cSRui Paulo 	if (!principal)
28345b9c547cSRui Paulo 		return -1;
28355b9c547cSRui Paulo 
28365b9c547cSRui Paulo 	latest_sak = NULL;
28375b9c547cSRui Paulo 	dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
28385b9c547cSRui Paulo 		if (is_ki_equal(&sa_key->key_identifier, lki)) {
28395b9c547cSRui Paulo 			sa_key->rx_latest = TRUE;
28405b9c547cSRui Paulo 			sa_key->tx_latest = TRUE;
28415b9c547cSRui Paulo 			latest_sak = sa_key;
28425b9c547cSRui Paulo 			principal->to_use_sak = TRUE;
28435b9c547cSRui Paulo 		} else {
28445b9c547cSRui Paulo 			sa_key->rx_latest = FALSE;
28455b9c547cSRui Paulo 			sa_key->tx_latest = FALSE;
28465b9c547cSRui Paulo 		}
28475b9c547cSRui Paulo 	}
28485b9c547cSRui Paulo 	if (!latest_sak) {
2849*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: lki related sak not found");
28505b9c547cSRui Paulo 		return -1;
28515b9c547cSRui Paulo 	}
28525b9c547cSRui Paulo 
28535b9c547cSRui Paulo 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
285485732ac8SCy Schubert 		while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
285585732ac8SCy Schubert 			ieee802_1x_delete_receive_sa(kay, rxsa);
285685732ac8SCy Schubert 
28575b9c547cSRui Paulo 		rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
28585b9c547cSRui Paulo 						      latest_sak);
28595b9c547cSRui Paulo 		if (!rxsa)
28605b9c547cSRui Paulo 			return -1;
28615b9c547cSRui Paulo 
28625b9c547cSRui Paulo 		secy_create_receive_sa(kay, rxsa);
28635b9c547cSRui Paulo 	}
28645b9c547cSRui Paulo 
286585732ac8SCy Schubert 	while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
286685732ac8SCy Schubert 	       NULL)
286785732ac8SCy Schubert 		ieee802_1x_delete_transmit_sa(kay, txsa);
286885732ac8SCy Schubert 
28695b9c547cSRui Paulo 	txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
2870*4bc52338SCy Schubert 					       latest_sak->next_pn ?
2871*4bc52338SCy Schubert 					       latest_sak->next_pn : 1,
2872*4bc52338SCy Schubert 					       latest_sak);
28735b9c547cSRui Paulo 	if (!txsa)
28745b9c547cSRui Paulo 		return -1;
28755b9c547cSRui Paulo 
28765b9c547cSRui Paulo 	secy_create_transmit_sa(kay, txsa);
28775b9c547cSRui Paulo 
28785b9c547cSRui Paulo 
28795b9c547cSRui Paulo 
28805b9c547cSRui Paulo 	return 0;
28815b9c547cSRui Paulo }
28825b9c547cSRui Paulo 
28835b9c547cSRui Paulo 
28845b9c547cSRui Paulo /**
28855b9c547cSRui Paulo  * ieee802_1x_kay_delete_sas -
28865b9c547cSRui Paulo  */
28875b9c547cSRui Paulo int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
28885b9c547cSRui Paulo 			      struct ieee802_1x_mka_ki *ki)
28895b9c547cSRui Paulo {
28905b9c547cSRui Paulo 	struct data_key *sa_key, *pre_key;
28915b9c547cSRui Paulo 	struct transmit_sa *txsa, *pre_txsa;
28925b9c547cSRui Paulo 	struct receive_sa *rxsa, *pre_rxsa;
28935b9c547cSRui Paulo 	struct receive_sc *rxsc;
28945b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
28955b9c547cSRui Paulo 
28965b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
28975b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
28985b9c547cSRui Paulo 	if (!principal)
28995b9c547cSRui Paulo 		return -1;
29005b9c547cSRui Paulo 
29015b9c547cSRui Paulo 	/* remove the transmit sa */
29025b9c547cSRui Paulo 	dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
29035b9c547cSRui Paulo 			      struct transmit_sa, list) {
290485732ac8SCy Schubert 		if (is_ki_equal(&txsa->pkey->key_identifier, ki))
290585732ac8SCy Schubert 			ieee802_1x_delete_transmit_sa(kay, txsa);
29065b9c547cSRui Paulo 	}
29075b9c547cSRui Paulo 
29085b9c547cSRui Paulo 	/* remove the receive sa */
29095b9c547cSRui Paulo 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
29105b9c547cSRui Paulo 		dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
29115b9c547cSRui Paulo 				      struct receive_sa, list) {
291285732ac8SCy Schubert 			if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
291385732ac8SCy Schubert 				ieee802_1x_delete_receive_sa(kay, rxsa);
29145b9c547cSRui Paulo 		}
29155b9c547cSRui Paulo 	}
29165b9c547cSRui Paulo 
29175b9c547cSRui Paulo 	/* remove the sak */
29185b9c547cSRui Paulo 	dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
29195b9c547cSRui Paulo 			      struct data_key, list) {
29205b9c547cSRui Paulo 		if (is_ki_equal(&sa_key->key_identifier, ki)) {
2921*4bc52338SCy Schubert 			if (principal->new_key == sa_key)
2922*4bc52338SCy Schubert 				principal->new_key = NULL;
292385732ac8SCy Schubert 			dl_list_del(&sa_key->list);
29245b9c547cSRui Paulo 			ieee802_1x_kay_deinit_data_key(sa_key);
29255b9c547cSRui Paulo 			break;
29265b9c547cSRui Paulo 		}
29275b9c547cSRui Paulo 	}
29285b9c547cSRui Paulo 
29295b9c547cSRui Paulo 	return 0;
29305b9c547cSRui Paulo }
29315b9c547cSRui Paulo 
29325b9c547cSRui Paulo 
29335b9c547cSRui Paulo /**
29345b9c547cSRui Paulo  * ieee802_1x_kay_enable_tx_sas -
29355b9c547cSRui Paulo  */
29365b9c547cSRui Paulo int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
29375b9c547cSRui Paulo 				 struct ieee802_1x_mka_ki *lki)
29385b9c547cSRui Paulo {
29395b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
29405b9c547cSRui Paulo 	struct transmit_sa *txsa;
29415b9c547cSRui Paulo 
29425b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
29435b9c547cSRui Paulo 	if (!principal)
29445b9c547cSRui Paulo 		return -1;
29455b9c547cSRui Paulo 
29465b9c547cSRui Paulo 	dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
29475b9c547cSRui Paulo 			 list) {
29485b9c547cSRui Paulo 		if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
29495b9c547cSRui Paulo 			txsa->in_use = TRUE;
29505b9c547cSRui Paulo 			secy_enable_transmit_sa(kay, txsa);
29515b9c547cSRui Paulo 			ieee802_1x_cp_set_usingtransmitas(
29525b9c547cSRui Paulo 				principal->kay->cp, TRUE);
29535b9c547cSRui Paulo 			ieee802_1x_cp_sm_step(principal->kay->cp);
29545b9c547cSRui Paulo 		}
29555b9c547cSRui Paulo 	}
29565b9c547cSRui Paulo 
29575b9c547cSRui Paulo 	return 0;
29585b9c547cSRui Paulo }
29595b9c547cSRui Paulo 
29605b9c547cSRui Paulo 
29615b9c547cSRui Paulo /**
29625b9c547cSRui Paulo  * ieee802_1x_kay_enable_rx_sas -
29635b9c547cSRui Paulo  */
29645b9c547cSRui Paulo int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
29655b9c547cSRui Paulo 				 struct ieee802_1x_mka_ki *lki)
29665b9c547cSRui Paulo {
29675b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
29685b9c547cSRui Paulo 	struct receive_sa *rxsa;
29695b9c547cSRui Paulo 	struct receive_sc *rxsc;
29705b9c547cSRui Paulo 
29715b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
29725b9c547cSRui Paulo 	if (!principal)
29735b9c547cSRui Paulo 		return -1;
29745b9c547cSRui Paulo 
29755b9c547cSRui Paulo 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
29765b9c547cSRui Paulo 		dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
29775b9c547cSRui Paulo 		{
29785b9c547cSRui Paulo 			if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
29795b9c547cSRui Paulo 				rxsa->in_use = TRUE;
29805b9c547cSRui Paulo 				secy_enable_receive_sa(kay, rxsa);
29815b9c547cSRui Paulo 				ieee802_1x_cp_set_usingreceivesas(
29825b9c547cSRui Paulo 					principal->kay->cp, TRUE);
29835b9c547cSRui Paulo 				ieee802_1x_cp_sm_step(principal->kay->cp);
29845b9c547cSRui Paulo 			}
29855b9c547cSRui Paulo 		}
29865b9c547cSRui Paulo 	}
29875b9c547cSRui Paulo 
29885b9c547cSRui Paulo 	return 0;
29895b9c547cSRui Paulo }
29905b9c547cSRui Paulo 
29915b9c547cSRui Paulo 
29925b9c547cSRui Paulo /**
29935b9c547cSRui Paulo  * ieee802_1x_kay_enable_new_info -
29945b9c547cSRui Paulo  */
29955b9c547cSRui Paulo int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
29965b9c547cSRui Paulo {
29975b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *principal;
29985b9c547cSRui Paulo 
29995b9c547cSRui Paulo 	principal = ieee802_1x_kay_get_principal_participant(kay);
30005b9c547cSRui Paulo 	if (!principal)
30015b9c547cSRui Paulo 		return -1;
30025b9c547cSRui Paulo 
300385732ac8SCy Schubert 	if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
30045b9c547cSRui Paulo 		ieee802_1x_participant_send_mkpdu(principal);
30055b9c547cSRui Paulo 		principal->retry_count++;
30065b9c547cSRui Paulo 	}
30075b9c547cSRui Paulo 
30085b9c547cSRui Paulo 	return 0;
30095b9c547cSRui Paulo }
30105b9c547cSRui Paulo 
30115b9c547cSRui Paulo 
30125b9c547cSRui Paulo /**
30135b9c547cSRui Paulo  * ieee802_1x_kay_mkpdu_sanity_check -
3014*4bc52338SCy Schubert  * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of
3015*4bc52338SCy Schubert  * MKPDUs)
30165b9c547cSRui Paulo  */
30175b9c547cSRui Paulo static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
30185b9c547cSRui Paulo 					     const u8 *buf, size_t len)
30195b9c547cSRui Paulo {
30205b9c547cSRui Paulo 	struct ieee8023_hdr *eth_hdr;
30215b9c547cSRui Paulo 	struct ieee802_1x_hdr *eapol_hdr;
30225b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *mka_hdr;
30235b9c547cSRui Paulo 	struct ieee802_1x_mka_basic_body *body;
30245b9c547cSRui Paulo 	size_t mka_msg_len;
30255b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
30265b9c547cSRui Paulo 	size_t body_len;
302785732ac8SCy Schubert 	size_t ckn_len;
30285b9c547cSRui Paulo 	u8 icv[MAX_ICV_LEN];
3029*4bc52338SCy Schubert 	const u8 *msg_icv;
30305b9c547cSRui Paulo 
3031*4bc52338SCy Schubert 	/* len > eth+eapol header already verified in kay_l2_receive();
3032*4bc52338SCy Schubert 	 * likewise, eapol_hdr->length validated there */
30335b9c547cSRui Paulo 	eth_hdr = (struct ieee8023_hdr *) buf;
30345b9c547cSRui Paulo 	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
30355b9c547cSRui Paulo 	mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
30365b9c547cSRui Paulo 
3037*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
3038*4bc52338SCy Schubert 		   " Ethertype=0x%x",
3039*4bc52338SCy Schubert 		   MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src),
3040*4bc52338SCy Schubert 		   be_to_host16(eth_hdr->ethertype));
3041*4bc52338SCy Schubert 
3042*4bc52338SCy Schubert 	/* the destination address shall not be an individual address */
30435b9c547cSRui Paulo 	if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
3044*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
30455b9c547cSRui Paulo 			   "KaY: ethernet destination address is not PAE group address");
30465b9c547cSRui Paulo 		return -1;
30475b9c547cSRui Paulo 	}
30485b9c547cSRui Paulo 
3049*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG,
3050*4bc52338SCy Schubert 		   "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
3051*4bc52338SCy Schubert 		   eapol_hdr->version, eapol_hdr->type,
3052*4bc52338SCy Schubert 		   be_to_host16(eapol_hdr->length));
3053*4bc52338SCy Schubert 
3054*4bc52338SCy Schubert 	/* MKPDU shall not be less than 32 octets */
30555b9c547cSRui Paulo 	mka_msg_len = be_to_host16(eapol_hdr->length);
30565b9c547cSRui Paulo 	if (mka_msg_len < 32) {
3057*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets");
30585b9c547cSRui Paulo 		return -1;
30595b9c547cSRui Paulo 	}
3060*4bc52338SCy Schubert 	/* MKPDU shall be a multiple of 4 octets */
30615b9c547cSRui Paulo 	if ((mka_msg_len % 4) != 0) {
3062*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG,
30635b9c547cSRui Paulo 			   "KaY: MKPDU is not multiple of 4 octets");
30645b9c547cSRui Paulo 		return -1;
30655b9c547cSRui Paulo 	}
30665b9c547cSRui Paulo 
3067*4bc52338SCy Schubert 	wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)",
3068*4bc52338SCy Schubert 		    mka_hdr, mka_msg_len);
3069*4bc52338SCy Schubert 
3070*4bc52338SCy Schubert 	/* Room for body_len already verified in kay_l2_receive() */
30715b9c547cSRui Paulo 	body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
30725b9c547cSRui Paulo 	body_len = get_mka_param_body_len(body);
30735b9c547cSRui Paulo 	/* EAPOL-MKA body should comprise basic parameter set and ICV */
30745b9c547cSRui Paulo 	if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
30755b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
3076780fb4a2SCy 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",
3077780fb4a2SCy Schubert 			   mka_msg_len, MKA_HDR_LEN,
3078780fb4a2SCy Schubert 			   body_len, DEFAULT_ICV_LEN);
30795b9c547cSRui Paulo 		return -1;
30805b9c547cSRui Paulo 	}
30815b9c547cSRui Paulo 
308285732ac8SCy Schubert 	if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
308385732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
308485732ac8SCy Schubert 			   body_len);
308585732ac8SCy Schubert 		return -1;
308685732ac8SCy Schubert 	}
308785732ac8SCy Schubert 	ckn_len = body_len -
308885732ac8SCy Schubert 		(sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
308985732ac8SCy Schubert 	if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
3090*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
309185732ac8SCy Schubert 			   "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
309285732ac8SCy Schubert 			   ckn_len, MAX_CKN_LEN);
309385732ac8SCy Schubert 		return -1;
309485732ac8SCy Schubert 	}
309585732ac8SCy Schubert 
3096*4bc52338SCy Schubert 	ieee802_1x_mka_dump_basic_body(body);
3097*4bc52338SCy Schubert 
30985b9c547cSRui Paulo 	/* CKN should be owned by I */
309985732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
31005b9c547cSRui Paulo 	if (!participant) {
3101*4bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA");
31025b9c547cSRui Paulo 		return -1;
31035b9c547cSRui Paulo 	}
31045b9c547cSRui Paulo 
31055b9c547cSRui Paulo 	/* algorithm agility check */
31065b9c547cSRui Paulo 	if (os_memcmp(body->algo_agility, mka_algo_agility,
31075b9c547cSRui Paulo 		      sizeof(body->algo_agility)) != 0) {
3108*4bc52338SCy Schubert 		wpa_printf(MSG_INFO,
3109*4bc52338SCy Schubert 			   "KaY: Peer's algorithm agility (%s) not supported",
3110*4bc52338SCy Schubert 			   algo_agility_txt(body->algo_agility));
31115b9c547cSRui Paulo 		return -1;
31125b9c547cSRui Paulo 	}
31135b9c547cSRui Paulo 
31145b9c547cSRui Paulo 	/* ICV check */
31155b9c547cSRui Paulo 	/*
31165b9c547cSRui Paulo 	 * The ICV will comprise the final octets of the packet body, whatever
31175b9c547cSRui Paulo 	 * its size, not the fixed length 16 octets, indicated by the EAPOL
31185b9c547cSRui Paulo 	 * packet body length.
31195b9c547cSRui Paulo 	 */
3120*4bc52338SCy Schubert 	if (len < mka_alg_tbl[kay->mka_algindex].icv_len ||
3121*4bc52338SCy Schubert 	    mka_alg_tbl[kay->mka_algindex].icv_hash(
3122*4bc52338SCy Schubert 		    participant->ick.key, participant->ick.len,
31235b9c547cSRui Paulo 		    buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
3124*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV");
31255b9c547cSRui Paulo 		return -1;
31265b9c547cSRui Paulo 	}
3127780fb4a2SCy Schubert 
3128*4bc52338SCy Schubert 	msg_icv = ieee802_1x_mka_decode_icv_body(participant,
3129*4bc52338SCy Schubert 						 (const u8 *) mka_hdr,
31305b9c547cSRui Paulo 						 mka_msg_len);
3131780fb4a2SCy Schubert 	if (!msg_icv) {
3132*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it");
31335b9c547cSRui Paulo 		return -1;
31345b9c547cSRui Paulo 	}
3135*4bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: Received ICV",
3136*4bc52338SCy Schubert 		    msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len);
3137780fb4a2SCy Schubert 	if (os_memcmp_const(msg_icv, icv,
3138780fb4a2SCy Schubert 			    mka_alg_tbl[kay->mka_algindex].icv_len) != 0) {
3139*4bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
3140780fb4a2SCy Schubert 			   "KaY: Computed ICV is not equal to Received ICV");
3141*4bc52338SCy Schubert 		wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV",
3142*4bc52338SCy Schubert 			    icv, mka_alg_tbl[kay->mka_algindex].icv_len);
31435b9c547cSRui Paulo 		return -1;
31445b9c547cSRui Paulo 	}
31455b9c547cSRui Paulo 
31465b9c547cSRui Paulo 	return 0;
31475b9c547cSRui Paulo }
31485b9c547cSRui Paulo 
31495b9c547cSRui Paulo 
31505b9c547cSRui Paulo /**
31515b9c547cSRui Paulo  * ieee802_1x_kay_decode_mkpdu -
31525b9c547cSRui Paulo  */
31535b9c547cSRui Paulo static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
31545b9c547cSRui Paulo 				       const u8 *buf, size_t len)
31555b9c547cSRui Paulo {
31565b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
31575b9c547cSRui Paulo 	struct ieee802_1x_mka_hdr *hdr;
3158*4bc52338SCy Schubert 	struct ieee802_1x_kay_peer *peer;
31595b9c547cSRui Paulo 	size_t body_len;
31605b9c547cSRui Paulo 	size_t left_len;
3161780fb4a2SCy Schubert 	u8 body_type;
31625b9c547cSRui Paulo 	int i;
31635b9c547cSRui Paulo 	const u8 *pos;
31645b9c547cSRui Paulo 	Boolean handled[256];
3165*4bc52338SCy Schubert 	Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use
3166*4bc52338SCy Schubert 				      * parameter set */
3167*4bc52338SCy Schubert 	Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer;
31685b9c547cSRui Paulo 
3169*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)",
3170*4bc52338SCy Schubert 		   kay->if_name);
31715b9c547cSRui Paulo 	if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
31725b9c547cSRui Paulo 		return -1;
31735b9c547cSRui Paulo 
31745b9c547cSRui Paulo 	/* handle basic parameter set */
31755b9c547cSRui Paulo 	pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
31765b9c547cSRui Paulo 	left_len = len - sizeof(struct ieee8023_hdr) -
31775b9c547cSRui Paulo 		sizeof(struct ieee802_1x_hdr);
31785b9c547cSRui Paulo 	participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
31795b9c547cSRui Paulo 	if (!participant)
31805b9c547cSRui Paulo 		return -1;
31815b9c547cSRui Paulo 
31825b9c547cSRui Paulo 	/* to skip basic parameter set */
31835b9c547cSRui Paulo 	hdr = (struct ieee802_1x_mka_hdr *) pos;
3184*4bc52338SCy Schubert 	body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
3185*4bc52338SCy Schubert 	if (left_len < body_len + MKA_HDR_LEN)
3186*4bc52338SCy Schubert 		return -1;
31875b9c547cSRui Paulo 	pos += body_len + MKA_HDR_LEN;
31885b9c547cSRui Paulo 	left_len -= body_len + MKA_HDR_LEN;
31895b9c547cSRui Paulo 
31905b9c547cSRui Paulo 	/* check i am in the peer's peer list */
3191*4bc52338SCy Schubert 	i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos,
3192*4bc52338SCy Schubert 						     left_len);
3193*4bc52338SCy Schubert 	is_in_live_peer = ieee802_1x_kay_is_in_live_peer(
3194*4bc52338SCy Schubert 		participant, participant->current_peer_id.mi);
3195*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s",
3196*4bc52338SCy Schubert 		   yes_no(i_in_peerlist), yes_no(is_in_live_peer));
3197*4bc52338SCy Schubert 	if (i_in_peerlist && !is_in_live_peer) {
3198780fb4a2SCy Schubert 		/* accept the peer as live peer */
3199*4bc52338SCy Schubert 		is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer(
3200*4bc52338SCy Schubert 			participant, participant->current_peer_id.mi);
3201*4bc52338SCy Schubert 		if (is_in_potential_peer) {
3202780fb4a2SCy Schubert 			if (!ieee802_1x_kay_move_live_peer(
3203780fb4a2SCy Schubert 				    participant,
3204780fb4a2SCy Schubert 				    participant->current_peer_id.mi,
3205780fb4a2SCy Schubert 				    be_to_host32(participant->
3206780fb4a2SCy Schubert 						 current_peer_id.mn)))
3207780fb4a2SCy Schubert 				return -1;
3208780fb4a2SCy Schubert 		} else if (!ieee802_1x_kay_create_live_peer(
32095b9c547cSRui Paulo 				   participant, participant->current_peer_id.mi,
3210780fb4a2SCy Schubert 				   be_to_host32(participant->
3211780fb4a2SCy Schubert 						current_peer_id.mn))) {
3212780fb4a2SCy Schubert 				return -1;
3213780fb4a2SCy Schubert 		}
3214780fb4a2SCy Schubert 
32155b9c547cSRui Paulo 		ieee802_1x_kay_elect_key_server(participant);
32165b9c547cSRui Paulo 		ieee802_1x_kay_decide_macsec_use(participant);
32175b9c547cSRui Paulo 	}
32185b9c547cSRui Paulo 
32195b9c547cSRui Paulo 	/*
32205b9c547cSRui Paulo 	 * Handle other parameter set than basic parameter set.
32215b9c547cSRui Paulo 	 * Each parameter set should be present only once.
32225b9c547cSRui Paulo 	 */
32235b9c547cSRui Paulo 	for (i = 0; i < 256; i++)
32245b9c547cSRui Paulo 		handled[i] = FALSE;
32255b9c547cSRui Paulo 
32265b9c547cSRui Paulo 	handled[0] = TRUE;
3227780fb4a2SCy Schubert 	for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
3228780fb4a2SCy Schubert 	     pos += body_len + MKA_HDR_LEN,
3229780fb4a2SCy Schubert 		     left_len -= body_len + MKA_HDR_LEN) {
32305b9c547cSRui Paulo 		hdr = (struct ieee802_1x_mka_hdr *) pos;
3231*4bc52338SCy Schubert 		body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
32325b9c547cSRui Paulo 		body_type = get_mka_param_body_type(hdr);
32335b9c547cSRui Paulo 
32345b9c547cSRui Paulo 		if (body_type == MKA_ICV_INDICATOR)
32355b9c547cSRui Paulo 			return 0;
32365b9c547cSRui Paulo 
32375b9c547cSRui Paulo 		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
32385b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
3239780fb4a2SCy 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",
3240780fb4a2SCy Schubert 				   left_len, MKA_HDR_LEN,
3241780fb4a2SCy Schubert 				   body_len, DEFAULT_ICV_LEN);
324285732ac8SCy Schubert 			return -1;
32435b9c547cSRui Paulo 		}
32445b9c547cSRui Paulo 
3245*4bc52338SCy Schubert 		if (handled[body_type]) {
3246*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
3247*4bc52338SCy Schubert 				   "KaY: Ignore duplicated body type %u",
3248*4bc52338SCy Schubert 				   body_type);
3249780fb4a2SCy Schubert 			continue;
3250*4bc52338SCy Schubert 		}
32515b9c547cSRui Paulo 
32525b9c547cSRui Paulo 		handled[body_type] = TRUE;
3253780fb4a2SCy Schubert 		if (body_type < ARRAY_SIZE(mka_body_handler) &&
3254780fb4a2SCy Schubert 		    mka_body_handler[body_type].body_rx) {
3255*4bc52338SCy Schubert 			if (mka_body_handler[body_type].body_rx
3256*4bc52338SCy Schubert 				(participant, pos, left_len) != 0) {
3257*4bc52338SCy Schubert 				/* Handle parameter set failure */
3258*4bc52338SCy Schubert 				if (body_type != MKA_SAK_USE) {
3259*4bc52338SCy Schubert 					wpa_printf(MSG_INFO,
3260*4bc52338SCy Schubert 						   "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
3261*4bc52338SCy Schubert 						   body_type);
3262*4bc52338SCy Schubert 					return -1;
3263*4bc52338SCy Schubert 				}
3264*4bc52338SCy Schubert 
3265*4bc52338SCy Schubert 				/* Ideally DIST-SAK should be processed before
3266*4bc52338SCy Schubert 				 * SAK-USE. Unfortunately IEEE Std 802.1X-2010,
3267*4bc52338SCy Schubert 				 * 11.11.3 (Encoding MKPDUs) states SAK-USE(3)
3268*4bc52338SCy Schubert 				 * must always be encoded before DIST-SAK(4).
3269*4bc52338SCy Schubert 				 * Rather than redesigning mka_body_handler so
3270*4bc52338SCy Schubert 				 * that it somehow processes DIST-SAK before
3271*4bc52338SCy Schubert 				 * SAK-USE, just ignore SAK-USE failures if
3272*4bc52338SCy Schubert 				 * DIST-SAK is also present in this MKPDU. */
3273*4bc52338SCy Schubert 				bad_sak_use = TRUE;
3274*4bc52338SCy Schubert 			}
32755b9c547cSRui Paulo 		} else {
32765b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
3277*4bc52338SCy Schubert 				   "KaY: The body type %d is not supported in this MKA version %d",
32785b9c547cSRui Paulo 				   body_type, MKA_VERSION_ID);
32795b9c547cSRui Paulo 		}
32805b9c547cSRui Paulo 	}
32815b9c547cSRui Paulo 
3282*4bc52338SCy Schubert 	if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) {
3283*4bc52338SCy Schubert 		wpa_printf(MSG_INFO,
3284*4bc52338SCy Schubert 			   "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
3285*4bc52338SCy Schubert 			   MKA_SAK_USE);
3286*4bc52338SCy Schubert 		if (!reset_participant_mi(participant))
3287*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
3288*4bc52338SCy Schubert 		else
3289*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
3290*4bc52338SCy Schubert 				   "KaY: Selected a new random MI: %s",
3291*4bc52338SCy Schubert 				   mi_txt(participant->mi));
3292*4bc52338SCy Schubert 		return -1;
3293*4bc52338SCy Schubert 	}
3294*4bc52338SCy Schubert 
3295*4bc52338SCy Schubert 	/* Detect missing parameter sets */
3296*4bc52338SCy Schubert 	peer = ieee802_1x_kay_get_live_peer(participant,
3297*4bc52338SCy Schubert 					    participant->current_peer_id.mi);
3298*4bc52338SCy Schubert 	if (peer) {
3299*4bc52338SCy Schubert 		/* MKPDU is from live peer */
3300*4bc52338SCy Schubert 		if (!handled[MKA_SAK_USE]) {
3301*4bc52338SCy Schubert 			/* Once a live peer starts sending SAK-USE, it should be
3302*4bc52338SCy Schubert 			 * sent every time. */
3303*4bc52338SCy Schubert 			if (peer->sak_used) {
3304*4bc52338SCy Schubert 				wpa_printf(MSG_INFO,
3305*4bc52338SCy Schubert 					   "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE");
3306*4bc52338SCy Schubert 				return -1;
3307*4bc52338SCy Schubert 			}
3308*4bc52338SCy Schubert 
3309*4bc52338SCy Schubert 			/* Live peer is probably hung if it hasn't sent SAK-USE
3310*4bc52338SCy Schubert 			 * after a reasonable number of MKPDUs. Drop the MKPDU,
3311*4bc52338SCy Schubert 			 * which will eventually force an timeout. */
3312*4bc52338SCy Schubert 			if (++peer->missing_sak_use_count >
3313*4bc52338SCy Schubert 			    MAX_MISSING_SAK_USE) {
3314*4bc52338SCy Schubert 				wpa_printf(MSG_INFO,
3315*4bc52338SCy Schubert 					   "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE");
3316*4bc52338SCy Schubert 				return -1;
3317*4bc52338SCy Schubert 			}
3318*4bc52338SCy Schubert 		} else {
3319*4bc52338SCy Schubert 			peer->missing_sak_use_count = 0;
3320*4bc52338SCy Schubert 
3321*4bc52338SCy Schubert 			/* Only update live peer watchdog after successful
3322*4bc52338SCy Schubert 			 * decode of all parameter sets */
3323*4bc52338SCy Schubert 			peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
3324*4bc52338SCy Schubert 		}
3325*4bc52338SCy Schubert 	} else {
3326*4bc52338SCy Schubert 		/* MKPDU is from new or potential peer */
3327*4bc52338SCy Schubert 		peer = ieee802_1x_kay_get_peer(participant,
3328*4bc52338SCy Schubert 					       participant->current_peer_id.mi);
3329*4bc52338SCy Schubert 		if (!peer) {
3330*4bc52338SCy Schubert 			wpa_printf(MSG_DEBUG, "KaY: No peer entry found");
3331*4bc52338SCy Schubert 			return -1;
3332*4bc52338SCy Schubert 		}
3333*4bc52338SCy Schubert 
3334*4bc52338SCy Schubert 		/* Do not update potential peer watchdog. Per IEEE Std
3335*4bc52338SCy Schubert 		 * 802.1X-2010, 9.4.3, potential peers need to show liveness by
3336*4bc52338SCy Schubert 		 * including our MI/MN in their transmitted MKPDU (within
3337*4bc52338SCy Schubert 		 * potential or live parameter sets). Whena potential peer does
3338*4bc52338SCy Schubert 		 * include our MI/MN in an MKPDU, we respond by moving the peer
3339*4bc52338SCy Schubert 		 * from 'potential_peers' to 'live_peers'. */
3340*4bc52338SCy Schubert 	}
3341*4bc52338SCy Schubert 
33425b9c547cSRui Paulo 	kay->active = TRUE;
33435b9c547cSRui Paulo 	participant->retry_count = 0;
33445b9c547cSRui Paulo 	participant->active = TRUE;
33455b9c547cSRui Paulo 
33465b9c547cSRui Paulo 	return 0;
33475b9c547cSRui Paulo }
33485b9c547cSRui Paulo 
33495b9c547cSRui Paulo 
33505b9c547cSRui Paulo 
33515b9c547cSRui Paulo static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
33525b9c547cSRui Paulo 			   size_t len)
33535b9c547cSRui Paulo {
33545b9c547cSRui Paulo 	struct ieee802_1x_kay *kay = ctx;
33555b9c547cSRui Paulo 	struct ieee8023_hdr *eth_hdr;
33565b9c547cSRui Paulo 	struct ieee802_1x_hdr *eapol_hdr;
3357*4bc52338SCy Schubert 	size_t calc_len;
3358*4bc52338SCy Schubert 
3359*4bc52338SCy Schubert 	/* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */
33605b9c547cSRui Paulo 
33615b9c547cSRui Paulo 	/* must contain at least ieee8023_hdr + ieee802_1x_hdr */
33625b9c547cSRui Paulo 	if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
33635b9c547cSRui Paulo 		wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
33645b9c547cSRui Paulo 			   (unsigned long) len);
33655b9c547cSRui Paulo 		return;
33665b9c547cSRui Paulo 	}
33675b9c547cSRui Paulo 
33685b9c547cSRui Paulo 	eth_hdr = (struct ieee8023_hdr *) buf;
33695b9c547cSRui Paulo 	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
3370*4bc52338SCy Schubert 	calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
3371*4bc52338SCy Schubert 		be_to_host16(eapol_hdr->length);
3372*4bc52338SCy Schubert 	if (len < calc_len) {
3373*4bc52338SCy Schubert 		wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)",
33745b9c547cSRui Paulo 			   (unsigned long) len,
3375*4bc52338SCy Schubert 			   (unsigned long) calc_len,
3376*4bc52338SCy Schubert 			   be_to_host16(eapol_hdr->length));
33775b9c547cSRui Paulo 		return;
33785b9c547cSRui Paulo 	}
3379*4bc52338SCy Schubert 	if (len > calc_len) {
3380*4bc52338SCy Schubert 		wpa_hexdump(MSG_DEBUG,
3381*4bc52338SCy Schubert 			    "KaY: Ignore extra octets following the Packey Body field",
3382*4bc52338SCy Schubert 			    &buf[calc_len], len - calc_len);
3383*4bc52338SCy Schubert 		len = calc_len;
3384*4bc52338SCy Schubert 	}
33855b9c547cSRui Paulo 
33865b9c547cSRui Paulo 	if (eapol_hdr->version < EAPOL_VERSION) {
33875b9c547cSRui Paulo 		wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
33885b9c547cSRui Paulo 			   eapol_hdr->version);
33895b9c547cSRui Paulo 		return;
33905b9c547cSRui Paulo 	}
3391780fb4a2SCy Schubert 	if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE ||
33925b9c547cSRui Paulo 	    eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
3393*4bc52338SCy Schubert 		return; /* ignore other EAPOL types silently here */
33945b9c547cSRui Paulo 
3395*4bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len);
33965b9c547cSRui Paulo 	if (dl_list_empty(&kay->participant_list)) {
3397*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR,
3398*4bc52338SCy Schubert 			   "KaY: No MKA participant instance - ignore EAPOL-MKA");
33995b9c547cSRui Paulo 		return;
34005b9c547cSRui Paulo 	}
34015b9c547cSRui Paulo 
34025b9c547cSRui Paulo 	ieee802_1x_kay_decode_mkpdu(kay, buf, len);
34035b9c547cSRui Paulo }
34045b9c547cSRui Paulo 
34055b9c547cSRui Paulo 
34065b9c547cSRui Paulo /**
34075b9c547cSRui Paulo  * ieee802_1x_kay_init -
34085b9c547cSRui Paulo  */
34095b9c547cSRui Paulo struct ieee802_1x_kay *
34105b9c547cSRui Paulo ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
3411*4bc52338SCy Schubert 		    Boolean macsec_replay_protect, u32 macsec_replay_window,
341285732ac8SCy Schubert 		    u16 port, u8 priority, const char *ifname, const u8 *addr)
34135b9c547cSRui Paulo {
34145b9c547cSRui Paulo 	struct ieee802_1x_kay *kay;
34155b9c547cSRui Paulo 
3416*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR
3417*4bc52338SCy Schubert 		   " port=%u priority=%u",
3418*4bc52338SCy Schubert 		   ifname, MAC2STR(addr), port, priority);
34195b9c547cSRui Paulo 	kay = os_zalloc(sizeof(*kay));
34205b9c547cSRui Paulo 	if (!kay) {
34215b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
342285732ac8SCy Schubert 		os_free(ctx);
34235b9c547cSRui Paulo 		return NULL;
34245b9c547cSRui Paulo 	}
34255b9c547cSRui Paulo 
34265b9c547cSRui Paulo 	kay->ctx = ctx;
34275b9c547cSRui Paulo 
34285b9c547cSRui Paulo 	kay->enable = TRUE;
34295b9c547cSRui Paulo 	kay->active = FALSE;
34305b9c547cSRui Paulo 
34315b9c547cSRui Paulo 	kay->authenticated = FALSE;
34325b9c547cSRui Paulo 	kay->secured = FALSE;
34335b9c547cSRui Paulo 	kay->failed = FALSE;
34345b9c547cSRui Paulo 	kay->policy = policy;
34355b9c547cSRui Paulo 
34365b9c547cSRui Paulo 	os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
34375b9c547cSRui Paulo 	os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
343885732ac8SCy Schubert 	kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
3439*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s",
3440*4bc52338SCy Schubert 		   sci_txt(&kay->actor_sci));
344185732ac8SCy Schubert 	kay->actor_priority = priority;
34425b9c547cSRui Paulo 
34435b9c547cSRui Paulo 	/* While actor acts as a key server, shall distribute sakey */
34445b9c547cSRui Paulo 	kay->dist_kn = 1;
34455b9c547cSRui Paulo 	kay->dist_an = 0;
34465b9c547cSRui Paulo 	kay->dist_time = 0;
34475b9c547cSRui Paulo 
34485b9c547cSRui Paulo 	kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
34495b9c547cSRui Paulo 	kay->macsec_csindex = DEFAULT_CS_INDEX;
34505b9c547cSRui Paulo 	kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
34515b9c547cSRui Paulo 	kay->mka_version = MKA_VERSION_ID;
34525b9c547cSRui Paulo 
34535b9c547cSRui Paulo 	os_memcpy(kay->algo_agility, mka_algo_agility,
34545b9c547cSRui Paulo 		  sizeof(kay->algo_agility));
34555b9c547cSRui Paulo 
34565b9c547cSRui Paulo 	dl_list_init(&kay->participant_list);
34575b9c547cSRui Paulo 
345885732ac8SCy Schubert 	if (policy != DO_NOT_SECURE &&
345985732ac8SCy Schubert 	    secy_get_capability(kay, &kay->macsec_capable) < 0)
346085732ac8SCy Schubert 		goto error;
346185732ac8SCy Schubert 
346285732ac8SCy Schubert 	if (policy == DO_NOT_SECURE ||
346385732ac8SCy Schubert 	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
34645b9c547cSRui Paulo 		kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
34655b9c547cSRui Paulo 		kay->macsec_desired = FALSE;
34665b9c547cSRui Paulo 		kay->macsec_protect = FALSE;
3467*4bc52338SCy Schubert 		kay->macsec_encrypt = FALSE;
34685b9c547cSRui Paulo 		kay->macsec_validate = Disabled;
34695b9c547cSRui Paulo 		kay->macsec_replay_protect = FALSE;
34705b9c547cSRui Paulo 		kay->macsec_replay_window = 0;
34715b9c547cSRui Paulo 		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
3472*4bc52338SCy Schubert 		kay->mka_hello_time = MKA_HELLO_TIME;
34735b9c547cSRui Paulo 	} else {
34745b9c547cSRui Paulo 		kay->macsec_desired = TRUE;
34755b9c547cSRui Paulo 		kay->macsec_protect = TRUE;
3476*4bc52338SCy Schubert 		if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF &&
3477*4bc52338SCy Schubert 		    policy == SHOULD_ENCRYPT) {
3478*4bc52338SCy Schubert 			kay->macsec_encrypt = TRUE;
34795b9c547cSRui Paulo 			kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
3480*4bc52338SCy Schubert 		} else { /* SHOULD_SECURE */
3481*4bc52338SCy Schubert 			kay->macsec_encrypt = FALSE;
348285732ac8SCy Schubert 			kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
34835b9c547cSRui Paulo 		}
3484*4bc52338SCy Schubert 		kay->macsec_validate = Strict;
3485*4bc52338SCy Schubert 		kay->macsec_replay_protect = macsec_replay_protect;
3486*4bc52338SCy Schubert 		kay->macsec_replay_window = macsec_replay_window;
3487*4bc52338SCy Schubert 		kay->mka_hello_time = MKA_HELLO_TIME;
3488*4bc52338SCy Schubert 	}
34895b9c547cSRui Paulo 
34905b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: state machine created");
34915b9c547cSRui Paulo 
34925b9c547cSRui Paulo 	/* Initialize the SecY must be prio to CP, as CP will control SecY */
349385732ac8SCy Schubert 	if (secy_init_macsec(kay) < 0) {
349485732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
349585732ac8SCy Schubert 		goto error;
349685732ac8SCy Schubert 	}
34975b9c547cSRui Paulo 
34985b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
34995b9c547cSRui Paulo 
35005b9c547cSRui Paulo 	/* init CP */
3501780fb4a2SCy Schubert 	kay->cp = ieee802_1x_cp_sm_init(kay);
350285732ac8SCy Schubert 	if (kay->cp == NULL)
350385732ac8SCy Schubert 		goto error;
35045b9c547cSRui Paulo 
35055b9c547cSRui Paulo 	if (policy == DO_NOT_SECURE) {
35065b9c547cSRui Paulo 		ieee802_1x_cp_connect_authenticated(kay->cp);
35075b9c547cSRui Paulo 		ieee802_1x_cp_sm_step(kay->cp);
35085b9c547cSRui Paulo 	} else {
35095b9c547cSRui Paulo 		kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
35105b9c547cSRui Paulo 					     kay_l2_receive, kay, 1);
35115b9c547cSRui Paulo 		if (kay->l2_mka == NULL) {
35125b9c547cSRui Paulo 			wpa_printf(MSG_WARNING,
35135b9c547cSRui Paulo 				   "KaY: Failed to initialize L2 packet processing for MKA packet");
351485732ac8SCy Schubert 			goto error;
35155b9c547cSRui Paulo 		}
35165b9c547cSRui Paulo 	}
35175b9c547cSRui Paulo 
35185b9c547cSRui Paulo 	return kay;
351985732ac8SCy Schubert 
352085732ac8SCy Schubert error:
352185732ac8SCy Schubert 	ieee802_1x_kay_deinit(kay);
352285732ac8SCy Schubert 	return NULL;
35235b9c547cSRui Paulo }
35245b9c547cSRui Paulo 
35255b9c547cSRui Paulo 
35265b9c547cSRui Paulo /**
35275b9c547cSRui Paulo  * ieee802_1x_kay_deinit -
35285b9c547cSRui Paulo  */
35295b9c547cSRui Paulo void
35305b9c547cSRui Paulo ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
35315b9c547cSRui Paulo {
35325b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
35335b9c547cSRui Paulo 
35345b9c547cSRui Paulo 	if (!kay)
35355b9c547cSRui Paulo 		return;
35365b9c547cSRui Paulo 
35375b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: state machine removed");
35385b9c547cSRui Paulo 
35395b9c547cSRui Paulo 	while (!dl_list_empty(&kay->participant_list)) {
35405b9c547cSRui Paulo 		participant = dl_list_entry(kay->participant_list.next,
35415b9c547cSRui Paulo 					    struct ieee802_1x_mka_participant,
35425b9c547cSRui Paulo 					    list);
35435b9c547cSRui Paulo 		ieee802_1x_kay_delete_mka(kay, &participant->ckn);
35445b9c547cSRui Paulo 	}
35455b9c547cSRui Paulo 
35465b9c547cSRui Paulo 	ieee802_1x_cp_sm_deinit(kay->cp);
35475b9c547cSRui Paulo 	secy_deinit_macsec(kay);
35485b9c547cSRui Paulo 
35495b9c547cSRui Paulo 	if (kay->l2_mka) {
35505b9c547cSRui Paulo 		l2_packet_deinit(kay->l2_mka);
35515b9c547cSRui Paulo 		kay->l2_mka = NULL;
35525b9c547cSRui Paulo 	}
35535b9c547cSRui Paulo 
35545b9c547cSRui Paulo 	os_free(kay->ctx);
35555b9c547cSRui Paulo 	os_free(kay);
35565b9c547cSRui Paulo }
35575b9c547cSRui Paulo 
35585b9c547cSRui Paulo 
3559*4bc52338SCy Schubert static const char * mode_txt(enum mka_created_mode mode)
3560*4bc52338SCy Schubert {
3561*4bc52338SCy Schubert 	switch (mode) {
3562*4bc52338SCy Schubert 	case PSK:
3563*4bc52338SCy Schubert 		return "PSK";
3564*4bc52338SCy Schubert 	case EAP_EXCHANGE:
3565*4bc52338SCy Schubert 		return "EAP";
3566*4bc52338SCy Schubert 	}
3567*4bc52338SCy Schubert 
3568*4bc52338SCy Schubert 	return "?";
3569*4bc52338SCy Schubert }
3570*4bc52338SCy Schubert 
3571*4bc52338SCy Schubert 
35725b9c547cSRui Paulo /**
35735b9c547cSRui Paulo  * ieee802_1x_kay_create_mka -
35745b9c547cSRui Paulo  */
35755b9c547cSRui Paulo struct ieee802_1x_mka_participant *
357685732ac8SCy Schubert ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
357785732ac8SCy Schubert 			  const struct mka_key_name *ckn,
357885732ac8SCy Schubert 			  const struct mka_key *cak, u32 life,
35795b9c547cSRui Paulo 			  enum mka_created_mode mode, Boolean is_authenticator)
35805b9c547cSRui Paulo {
35815b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
35825b9c547cSRui Paulo 	unsigned int usecs;
35835b9c547cSRui Paulo 
3584*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG,
3585*4bc52338SCy Schubert 		   "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)",
3586*4bc52338SCy Schubert 		   kay->if_name, mode_txt(mode), yes_no(is_authenticator));
3587*4bc52338SCy Schubert 
35885b9c547cSRui Paulo 	if (!kay || !ckn || !cak) {
35895b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
35905b9c547cSRui Paulo 		return NULL;
35915b9c547cSRui Paulo 	}
35925b9c547cSRui Paulo 
3593*4bc52338SCy Schubert 	if (cak->len != 16 && cak->len != 32) {
3594*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u",
3595*4bc52338SCy Schubert 			   (unsigned int) cak->len);
35965b9c547cSRui Paulo 		return NULL;
35975b9c547cSRui Paulo 	}
35985b9c547cSRui Paulo 	if (ckn->len > MAX_CKN_LEN) {
3599*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)");
36005b9c547cSRui Paulo 		return NULL;
36015b9c547cSRui Paulo 	}
36025b9c547cSRui Paulo 	if (!kay->enable) {
36035b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
36045b9c547cSRui Paulo 		return NULL;
36055b9c547cSRui Paulo 	}
36065b9c547cSRui Paulo 
36075b9c547cSRui Paulo 	participant = os_zalloc(sizeof(*participant));
36085b9c547cSRui Paulo 	if (!participant) {
36095b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
36105b9c547cSRui Paulo 		return NULL;
36115b9c547cSRui Paulo 	}
36125b9c547cSRui Paulo 
36135b9c547cSRui Paulo 	participant->ckn.len = ckn->len;
36145b9c547cSRui Paulo 	os_memcpy(participant->ckn.name, ckn->name, ckn->len);
3615*4bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name,
3616*4bc52338SCy Schubert 		    participant->ckn.len);
36175b9c547cSRui Paulo 	participant->cak.len = cak->len;
36185b9c547cSRui Paulo 	os_memcpy(participant->cak.key, cak->key, cak->len);
3619*4bc52338SCy Schubert 	wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key,
3620*4bc52338SCy Schubert 			participant->cak.len);
36215b9c547cSRui Paulo 	if (life)
36225b9c547cSRui Paulo 		participant->cak_life = life + time(NULL);
36235b9c547cSRui Paulo 
36245b9c547cSRui Paulo 	switch (mode) {
36255b9c547cSRui Paulo 	case EAP_EXCHANGE:
36265b9c547cSRui Paulo 		if (is_authenticator) {
36275b9c547cSRui Paulo 			participant->is_obliged_key_server = TRUE;
36285b9c547cSRui Paulo 			participant->can_be_key_server = TRUE;
36295b9c547cSRui Paulo 			participant->is_key_server = TRUE;
36305b9c547cSRui Paulo 			participant->principal = TRUE;
36315b9c547cSRui Paulo 
36325b9c547cSRui Paulo 			os_memcpy(&kay->key_server_sci, &kay->actor_sci,
36335b9c547cSRui Paulo 				  sizeof(kay->key_server_sci));
36345b9c547cSRui Paulo 			kay->key_server_priority = kay->actor_priority;
36355b9c547cSRui Paulo 			participant->is_elected = TRUE;
36365b9c547cSRui Paulo 		} else {
36375b9c547cSRui Paulo 			participant->is_obliged_key_server = FALSE;
36385b9c547cSRui Paulo 			participant->can_be_key_server = FALSE;
36395b9c547cSRui Paulo 			participant->is_key_server = FALSE;
36405b9c547cSRui Paulo 			participant->is_elected = TRUE;
36415b9c547cSRui Paulo 		}
36425b9c547cSRui Paulo 		break;
36435b9c547cSRui Paulo 
36445b9c547cSRui Paulo 	default:
36455b9c547cSRui Paulo 		participant->is_obliged_key_server = FALSE;
36465b9c547cSRui Paulo 		participant->can_be_key_server = TRUE;
3647780fb4a2SCy Schubert 		participant->is_key_server = TRUE;
36485b9c547cSRui Paulo 		participant->is_elected = FALSE;
36495b9c547cSRui Paulo 		break;
36505b9c547cSRui Paulo 	}
36515b9c547cSRui Paulo 
36525b9c547cSRui Paulo 	participant->cached = FALSE;
36535b9c547cSRui Paulo 
36545b9c547cSRui Paulo 	participant->active = FALSE;
36555b9c547cSRui Paulo 	participant->participant = FALSE;
36565b9c547cSRui Paulo 	participant->retain = FALSE;
36575b9c547cSRui Paulo 	participant->activate = DEFAULT;
36585b9c547cSRui Paulo 
36595b9c547cSRui Paulo 	if (participant->is_key_server)
36605b9c547cSRui Paulo 		participant->principal = TRUE;
36615b9c547cSRui Paulo 
36625b9c547cSRui Paulo 	dl_list_init(&participant->live_peers);
36635b9c547cSRui Paulo 	dl_list_init(&participant->potential_peers);
36645b9c547cSRui Paulo 
36655b9c547cSRui Paulo 	participant->retry_count = 0;
36665b9c547cSRui Paulo 	participant->kay = kay;
36675b9c547cSRui Paulo 
3668780fb4a2SCy Schubert 	if (!reset_participant_mi(participant))
36695b9c547cSRui Paulo 		goto fail;
3670*4bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s",
3671*4bc52338SCy Schubert 		   mi_txt(participant->mi));
36725b9c547cSRui Paulo 
36735b9c547cSRui Paulo 	participant->lrx = FALSE;
36745b9c547cSRui Paulo 	participant->ltx = FALSE;
36755b9c547cSRui Paulo 	participant->orx = FALSE;
36765b9c547cSRui Paulo 	participant->otx = FALSE;
36775b9c547cSRui Paulo 	participant->to_dist_sak = FALSE;
36785b9c547cSRui Paulo 	participant->to_use_sak = FALSE;
36795b9c547cSRui Paulo 	participant->new_sak = FALSE;
36805b9c547cSRui Paulo 	dl_list_init(&participant->sak_list);
36815b9c547cSRui Paulo 	participant->new_key = NULL;
36825b9c547cSRui Paulo 	dl_list_init(&participant->rxsc_list);
368385732ac8SCy Schubert 	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
36845b9c547cSRui Paulo 	secy_cp_control_protect_frames(kay, kay->macsec_protect);
36855b9c547cSRui Paulo 	secy_cp_control_replay(kay, kay->macsec_replay_protect,
36865b9c547cSRui Paulo 			       kay->macsec_replay_window);
3687*4bc52338SCy Schubert 	if (secy_create_transmit_sc(kay, participant->txsc))
3688*4bc52338SCy Schubert 		goto fail;
36895b9c547cSRui Paulo 
36905b9c547cSRui Paulo 	/* to derive KEK from CAK and CKN */
3691*4bc52338SCy Schubert 	participant->kek.len = participant->cak.len;
36925b9c547cSRui Paulo 	if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
3693*4bc52338SCy Schubert 						    participant->cak.len,
36945b9c547cSRui Paulo 						    participant->ckn.name,
36955b9c547cSRui Paulo 						    participant->ckn.len,
3696*4bc52338SCy Schubert 						    participant->kek.key,
3697*4bc52338SCy Schubert 						    participant->kek.len)) {
3698*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: KEK derivation failed");
36995b9c547cSRui Paulo 		goto fail;
37005b9c547cSRui Paulo 	}
37015b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
37025b9c547cSRui Paulo 			participant->kek.key, participant->kek.len);
37035b9c547cSRui Paulo 
37045b9c547cSRui Paulo 	/* to derive ICK from CAK and CKN */
3705*4bc52338SCy Schubert 	participant->ick.len = participant->cak.len;
37065b9c547cSRui Paulo 	if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
3707*4bc52338SCy Schubert 						    participant->cak.len,
37085b9c547cSRui Paulo 						    participant->ckn.name,
37095b9c547cSRui Paulo 						    participant->ckn.len,
3710*4bc52338SCy Schubert 						    participant->ick.key,
3711*4bc52338SCy Schubert 						    participant->ick.len)) {
3712*4bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "KaY: ICK derivation failed");
37135b9c547cSRui Paulo 		goto fail;
37145b9c547cSRui Paulo 	}
37155b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
37165b9c547cSRui Paulo 			participant->ick.key, participant->ick.len);
37175b9c547cSRui Paulo 
37185b9c547cSRui Paulo 	dl_list_add(&kay->participant_list, &participant->list);
37195b9c547cSRui Paulo 
3720*4bc52338SCy Schubert 	usecs = os_random() % (kay->mka_hello_time * 1000);
37215b9c547cSRui Paulo 	eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
37225b9c547cSRui Paulo 			       participant, NULL);
372385732ac8SCy Schubert 
372485732ac8SCy Schubert 	/* Disable MKA lifetime for PSK mode.
372585732ac8SCy Schubert 	 * The peer(s) can take a long time to come up, because we
372685732ac8SCy Schubert 	 * create a "standby" MKA, and we need it to remain live until
372785732ac8SCy Schubert 	 * some peer appears.
372885732ac8SCy Schubert 	 */
372985732ac8SCy Schubert 	if (mode != PSK) {
37305b9c547cSRui Paulo 		participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
37315b9c547cSRui Paulo 			usecs / 1000000;
373285732ac8SCy Schubert 	}
373385732ac8SCy Schubert 	participant->mode = mode;
37345b9c547cSRui Paulo 
37355b9c547cSRui Paulo 	return participant;
37365b9c547cSRui Paulo 
37375b9c547cSRui Paulo fail:
3738*4bc52338SCy Schubert 	os_free(participant->txsc);
37395b9c547cSRui Paulo 	os_free(participant);
37405b9c547cSRui Paulo 	return NULL;
37415b9c547cSRui Paulo }
37425b9c547cSRui Paulo 
37435b9c547cSRui Paulo 
37445b9c547cSRui Paulo /**
37455b9c547cSRui Paulo  * ieee802_1x_kay_delete_mka -
37465b9c547cSRui Paulo  */
37475b9c547cSRui Paulo void
37485b9c547cSRui Paulo ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
37495b9c547cSRui Paulo {
37505b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
37515b9c547cSRui Paulo 	struct ieee802_1x_kay_peer *peer;
37525b9c547cSRui Paulo 	struct data_key *sak;
37535b9c547cSRui Paulo 	struct receive_sc *rxsc;
37545b9c547cSRui Paulo 
37555b9c547cSRui Paulo 	if (!kay || !ckn)
37565b9c547cSRui Paulo 		return;
37575b9c547cSRui Paulo 
37585b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: participant removed");
37595b9c547cSRui Paulo 
37605b9c547cSRui Paulo 	/* get the participant */
376185732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
37625b9c547cSRui Paulo 	if (!participant) {
37635b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
37645b9c547cSRui Paulo 			    ckn->name, ckn->len);
37655b9c547cSRui Paulo 		return;
37665b9c547cSRui Paulo 	}
37675b9c547cSRui Paulo 
3768780fb4a2SCy Schubert 	eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL);
37695b9c547cSRui Paulo 	dl_list_del(&participant->list);
37705b9c547cSRui Paulo 
37715b9c547cSRui Paulo 	/* remove live peer */
37725b9c547cSRui Paulo 	while (!dl_list_empty(&participant->live_peers)) {
37735b9c547cSRui Paulo 		peer = dl_list_entry(participant->live_peers.next,
37745b9c547cSRui Paulo 				     struct ieee802_1x_kay_peer, list);
37755b9c547cSRui Paulo 		dl_list_del(&peer->list);
37765b9c547cSRui Paulo 		os_free(peer);
37775b9c547cSRui Paulo 	}
37785b9c547cSRui Paulo 
37795b9c547cSRui Paulo 	/* remove potential peer */
37805b9c547cSRui Paulo 	while (!dl_list_empty(&participant->potential_peers)) {
37815b9c547cSRui Paulo 		peer = dl_list_entry(participant->potential_peers.next,
37825b9c547cSRui Paulo 				     struct ieee802_1x_kay_peer, list);
37835b9c547cSRui Paulo 		dl_list_del(&peer->list);
37845b9c547cSRui Paulo 		os_free(peer);
37855b9c547cSRui Paulo 	}
37865b9c547cSRui Paulo 
37875b9c547cSRui Paulo 	/* remove sak */
37885b9c547cSRui Paulo 	while (!dl_list_empty(&participant->sak_list)) {
37895b9c547cSRui Paulo 		sak = dl_list_entry(participant->sak_list.next,
37905b9c547cSRui Paulo 				    struct data_key, list);
37915b9c547cSRui Paulo 		dl_list_del(&sak->list);
379285732ac8SCy Schubert 		ieee802_1x_kay_deinit_data_key(sak);
37935b9c547cSRui Paulo 	}
37945b9c547cSRui Paulo 	while (!dl_list_empty(&participant->rxsc_list)) {
37955b9c547cSRui Paulo 		rxsc = dl_list_entry(participant->rxsc_list.next,
37965b9c547cSRui Paulo 				     struct receive_sc, list);
37975b9c547cSRui Paulo 		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
37985b9c547cSRui Paulo 	}
37995b9c547cSRui Paulo 	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
38005b9c547cSRui Paulo 
38015b9c547cSRui Paulo 	os_memset(&participant->cak, 0, sizeof(participant->cak));
38025b9c547cSRui Paulo 	os_memset(&participant->kek, 0, sizeof(participant->kek));
38035b9c547cSRui Paulo 	os_memset(&participant->ick, 0, sizeof(participant->ick));
38045b9c547cSRui Paulo 	os_free(participant);
38055b9c547cSRui Paulo }
38065b9c547cSRui Paulo 
38075b9c547cSRui Paulo 
38085b9c547cSRui Paulo /**
38095b9c547cSRui Paulo  * ieee802_1x_kay_mka_participate -
38105b9c547cSRui Paulo  */
38115b9c547cSRui Paulo void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
38125b9c547cSRui Paulo 				    struct mka_key_name *ckn,
38135b9c547cSRui Paulo 				    Boolean status)
38145b9c547cSRui Paulo {
38155b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
38165b9c547cSRui Paulo 
38175b9c547cSRui Paulo 	if (!kay || !ckn)
38185b9c547cSRui Paulo 		return;
38195b9c547cSRui Paulo 
382085732ac8SCy Schubert 	participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
38215b9c547cSRui Paulo 	if (!participant)
38225b9c547cSRui Paulo 		return;
38235b9c547cSRui Paulo 
38245b9c547cSRui Paulo 	participant->active = status;
38255b9c547cSRui Paulo }
38265b9c547cSRui Paulo 
38275b9c547cSRui Paulo 
38285b9c547cSRui Paulo /**
38295b9c547cSRui Paulo  * ieee802_1x_kay_new_sak -
38305b9c547cSRui Paulo  */
38315b9c547cSRui Paulo int
38325b9c547cSRui Paulo ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
38335b9c547cSRui Paulo {
38345b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
38355b9c547cSRui Paulo 
38365b9c547cSRui Paulo 	if (!kay)
38375b9c547cSRui Paulo 		return -1;
38385b9c547cSRui Paulo 
38395b9c547cSRui Paulo 	participant = ieee802_1x_kay_get_principal_participant(kay);
38405b9c547cSRui Paulo 	if (!participant)
38415b9c547cSRui Paulo 		return -1;
38425b9c547cSRui Paulo 
38435b9c547cSRui Paulo 	participant->new_sak = TRUE;
38445b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
38455b9c547cSRui Paulo 
38465b9c547cSRui Paulo 	return 0;
38475b9c547cSRui Paulo }
38485b9c547cSRui Paulo 
38495b9c547cSRui Paulo 
38505b9c547cSRui Paulo /**
38515b9c547cSRui Paulo  * ieee802_1x_kay_change_cipher_suite -
38525b9c547cSRui Paulo  */
38535b9c547cSRui Paulo int
3854780fb4a2SCy Schubert ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
3855780fb4a2SCy Schubert 				   unsigned int cs_index)
38565b9c547cSRui Paulo {
38575b9c547cSRui Paulo 	struct ieee802_1x_mka_participant *participant;
385885732ac8SCy Schubert 	enum macsec_cap secy_cap;
38595b9c547cSRui Paulo 
38605b9c547cSRui Paulo 	if (!kay)
38615b9c547cSRui Paulo 		return -1;
38625b9c547cSRui Paulo 
3863780fb4a2SCy Schubert 	if (cs_index >= CS_TABLE_SIZE) {
38645b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
38655b9c547cSRui Paulo 			   "KaY: Configured cipher suite index is out of range");
38665b9c547cSRui Paulo 		return -1;
38675b9c547cSRui Paulo 	}
38685b9c547cSRui Paulo 	if (kay->macsec_csindex == cs_index)
38695b9c547cSRui Paulo 		return -2;
38705b9c547cSRui Paulo 
38715b9c547cSRui Paulo 	if (cs_index == 0)
38725b9c547cSRui Paulo 		kay->macsec_desired = FALSE;
38735b9c547cSRui Paulo 
38745b9c547cSRui Paulo 	kay->macsec_csindex = cs_index;
38755b9c547cSRui Paulo 	kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
38765b9c547cSRui Paulo 
387785732ac8SCy Schubert 	if (secy_get_capability(kay, &secy_cap) < 0)
387885732ac8SCy Schubert 		return -3;
387985732ac8SCy Schubert 
388085732ac8SCy Schubert 	if (kay->macsec_capable > secy_cap)
388185732ac8SCy Schubert 		kay->macsec_capable = secy_cap;
388285732ac8SCy Schubert 
38835b9c547cSRui Paulo 	participant = ieee802_1x_kay_get_principal_participant(kay);
38845b9c547cSRui Paulo 	if (participant) {
38855b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
38865b9c547cSRui Paulo 		participant->new_sak = TRUE;
38875b9c547cSRui Paulo 	}
38885b9c547cSRui Paulo 
38895b9c547cSRui Paulo 	return 0;
38905b9c547cSRui Paulo }
389185732ac8SCy Schubert 
389285732ac8SCy Schubert 
389385732ac8SCy Schubert #ifdef CONFIG_CTRL_IFACE
3894*4bc52338SCy Schubert 
389585732ac8SCy Schubert /**
389685732ac8SCy Schubert  * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
389785732ac8SCy Schubert  * @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
389885732ac8SCy Schubert  * @buf: Buffer for status information
389985732ac8SCy Schubert  * @buflen: Maximum buffer length
390085732ac8SCy Schubert  * @verbose: Whether to include verbose status information
390185732ac8SCy Schubert  * Returns: Number of bytes written to buf.
390285732ac8SCy Schubert  *
3903*4bc52338SCy Schubert  * Query KaY status information. This function fills in a text area with current
390485732ac8SCy Schubert  * status information. If the buffer (buf) is not large enough, status
390585732ac8SCy Schubert  * information will be truncated to fit the buffer.
390685732ac8SCy Schubert  */
390785732ac8SCy Schubert int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
390885732ac8SCy Schubert 			      size_t buflen)
390985732ac8SCy Schubert {
3910*4bc52338SCy Schubert 	char *pos, *end;
3911*4bc52338SCy Schubert 	int res, count;
3912*4bc52338SCy Schubert 	struct ieee802_1x_mka_participant *p;
391385732ac8SCy Schubert 
391485732ac8SCy Schubert 	if (!kay)
391585732ac8SCy Schubert 		return 0;
391685732ac8SCy Schubert 
3917*4bc52338SCy Schubert 	pos = buf;
3918*4bc52338SCy Schubert 	end = buf + buflen;
3919*4bc52338SCy Schubert 
3920*4bc52338SCy Schubert 	res = os_snprintf(pos, end - pos,
392185732ac8SCy Schubert 			  "PAE KaY status=%s\n"
392285732ac8SCy Schubert 			  "Authenticated=%s\n"
392385732ac8SCy Schubert 			  "Secured=%s\n"
392485732ac8SCy Schubert 			  "Failed=%s\n"
392585732ac8SCy Schubert 			  "Actor Priority=%u\n"
392685732ac8SCy Schubert 			  "Key Server Priority=%u\n"
392785732ac8SCy Schubert 			  "Is Key Server=%s\n"
392885732ac8SCy Schubert 			  "Number of Keys Distributed=%u\n"
3929*4bc52338SCy Schubert 			  "Number of Keys Received=%u\n"
3930*4bc52338SCy Schubert 			  "MKA Hello Time=%u\n",
393185732ac8SCy Schubert 			  kay->active ? "Active" : "Not-Active",
393285732ac8SCy Schubert 			  kay->authenticated ? "Yes" : "No",
393385732ac8SCy Schubert 			  kay->secured ? "Yes" : "No",
393485732ac8SCy Schubert 			  kay->failed ? "Yes" : "No",
393585732ac8SCy Schubert 			  kay->actor_priority,
393685732ac8SCy Schubert 			  kay->key_server_priority,
393785732ac8SCy Schubert 			  kay->is_key_server ? "Yes" : "No",
393885732ac8SCy Schubert 			  kay->dist_kn - 1,
3939*4bc52338SCy Schubert 			  kay->rcvd_keys,
3940*4bc52338SCy Schubert 			  kay->mka_hello_time);
3941*4bc52338SCy Schubert 	if (os_snprintf_error(buflen, res))
3942*4bc52338SCy Schubert 		return 0;
3943*4bc52338SCy Schubert 	pos += res;
3944*4bc52338SCy Schubert 
3945*4bc52338SCy Schubert 	res = os_snprintf(pos, end - pos,
3946*4bc52338SCy Schubert 			  "actor_sci=%s\n", sci_txt(&kay->actor_sci));
3947*4bc52338SCy Schubert 	if (os_snprintf_error(buflen, res))
3948*4bc52338SCy Schubert 		return end - pos;
3949*4bc52338SCy Schubert 	pos += res;
3950*4bc52338SCy Schubert 
3951*4bc52338SCy Schubert 	res = os_snprintf(pos, end - pos,
3952*4bc52338SCy Schubert 			  "key_server_sci=%s\n", sci_txt(&kay->key_server_sci));
3953*4bc52338SCy Schubert 	if (os_snprintf_error(buflen, res))
3954*4bc52338SCy Schubert 		return end - pos;
3955*4bc52338SCy Schubert 	pos += res;
3956*4bc52338SCy Schubert 
3957*4bc52338SCy Schubert 	count = 0;
3958*4bc52338SCy Schubert 	dl_list_for_each(p, &kay->participant_list,
3959*4bc52338SCy Schubert 			 struct ieee802_1x_mka_participant, list) {
3960*4bc52338SCy Schubert 		char *pos2 = pos;
3961*4bc52338SCy Schubert 
3962*4bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=",
3963*4bc52338SCy Schubert 			count);
3964*4bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
3965*4bc52338SCy Schubert 			return end - pos;
3966*4bc52338SCy Schubert 		pos2 += res;
3967*4bc52338SCy Schubert 		count++;
3968*4bc52338SCy Schubert 
3969*4bc52338SCy Schubert 		pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
3970*4bc52338SCy Schubert 					 p->ckn.len);
3971*4bc52338SCy Schubert 
3972*4bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2,
3973*4bc52338SCy Schubert 				  "\nmi=%s\n"
3974*4bc52338SCy Schubert 				  "mn=%u\n"
3975*4bc52338SCy Schubert 				  "active=%s\n"
3976*4bc52338SCy Schubert 				  "participant=%s\n"
3977*4bc52338SCy Schubert 				  "retain=%s\n"
3978*4bc52338SCy Schubert 				  "live_peers=%u\n"
3979*4bc52338SCy Schubert 				  "potential_peers=%u\n"
3980*4bc52338SCy Schubert 				  "is_key_server=%s\n"
3981*4bc52338SCy Schubert 				  "is_elected=%s\n",
3982*4bc52338SCy Schubert 				  mi_txt(p->mi), p->mn,
3983*4bc52338SCy Schubert 				  yes_no(p->active),
3984*4bc52338SCy Schubert 				  yes_no(p->participant),
3985*4bc52338SCy Schubert 				  yes_no(p->retain),
3986*4bc52338SCy Schubert 				  dl_list_len(&p->live_peers),
3987*4bc52338SCy Schubert 				  dl_list_len(&p->potential_peers),
3988*4bc52338SCy Schubert 				  yes_no(p->is_key_server),
3989*4bc52338SCy Schubert 				  yes_no(p->is_elected));
3990*4bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
3991*4bc52338SCy Schubert 			return end - pos;
3992*4bc52338SCy Schubert 		pos2 += res;
3993*4bc52338SCy Schubert 		pos = pos2;
3994*4bc52338SCy Schubert 	}
3995*4bc52338SCy Schubert 
3996*4bc52338SCy Schubert 	return pos - buf;
3997*4bc52338SCy Schubert }
3998*4bc52338SCy Schubert 
3999*4bc52338SCy Schubert 
4000*4bc52338SCy Schubert static const char * true_false(Boolean val)
4001*4bc52338SCy Schubert {
4002*4bc52338SCy Schubert 	return val ? "true" : "false";
4003*4bc52338SCy Schubert }
4004*4bc52338SCy Schubert 
4005*4bc52338SCy Schubert 
4006*4bc52338SCy Schubert static const char * activate_control_txt(enum activate_ctrl activate)
4007*4bc52338SCy Schubert {
4008*4bc52338SCy Schubert 	switch (activate) {
4009*4bc52338SCy Schubert 	case DEFAULT:
4010*4bc52338SCy Schubert 		return "default";
4011*4bc52338SCy Schubert 	case DISABLED:
4012*4bc52338SCy Schubert 		return "disabled";
4013*4bc52338SCy Schubert 	case ON_OPER_UP:
4014*4bc52338SCy Schubert 		return "onOperUp";
4015*4bc52338SCy Schubert 	case ALWAYS:
4016*4bc52338SCy Schubert 		return "always";
4017*4bc52338SCy Schubert 	}
4018*4bc52338SCy Schubert 
4019*4bc52338SCy Schubert 	return "?";
4020*4bc52338SCy Schubert }
4021*4bc52338SCy Schubert 
4022*4bc52338SCy Schubert 
4023*4bc52338SCy Schubert static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf,
4024*4bc52338SCy Schubert 			   char *end)
4025*4bc52338SCy Schubert {
4026*4bc52338SCy Schubert 	char *pos = buf;
4027*4bc52338SCy Schubert 	struct ieee802_1x_kay_peer *p;
4028*4bc52338SCy Schubert 	int res;
4029*4bc52338SCy Schubert 
4030*4bc52338SCy Schubert 	dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) {
4031*4bc52338SCy Schubert 		res = os_snprintf(pos, end - pos,
4032*4bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListMI=%s\n"
4033*4bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListMN=%u\n"
4034*4bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListType=%u\n"
4035*4bc52338SCy Schubert 				  "ieee8021XKayMkaPeerListSCI=%s\n",
4036*4bc52338SCy Schubert 				  mi_txt(p->mi),
4037*4bc52338SCy Schubert 				  p->mn,
4038*4bc52338SCy Schubert 				  live ? 1 : 2,
4039*4bc52338SCy Schubert 				  sci_txt(&p->sci));
4040*4bc52338SCy Schubert 		if (os_snprintf_error(end - pos, res))
4041*4bc52338SCy Schubert 			return pos;
4042*4bc52338SCy Schubert 		pos += res;
4043*4bc52338SCy Schubert 	}
4044*4bc52338SCy Schubert 
4045*4bc52338SCy Schubert 	return pos;
4046*4bc52338SCy Schubert }
4047*4bc52338SCy Schubert 
4048*4bc52338SCy Schubert 
4049*4bc52338SCy Schubert int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
4050*4bc52338SCy Schubert 			   size_t buflen)
4051*4bc52338SCy Schubert {
4052*4bc52338SCy Schubert 	char *pos, *end;
4053*4bc52338SCy Schubert 	int res;
4054*4bc52338SCy Schubert 	struct ieee802_1x_mka_participant *p;
4055*4bc52338SCy Schubert 
4056*4bc52338SCy Schubert 	if (!kay)
405785732ac8SCy Schubert 		return 0;
405885732ac8SCy Schubert 
4059*4bc52338SCy Schubert 	pos = buf;
4060*4bc52338SCy Schubert 	end = buf + buflen;
4061*4bc52338SCy Schubert 
4062*4bc52338SCy Schubert 	dl_list_for_each(p, &kay->participant_list,
4063*4bc52338SCy Schubert 			 struct ieee802_1x_mka_participant, list) {
4064*4bc52338SCy Schubert 		char *pos2 = pos;
4065*4bc52338SCy Schubert 
4066*4bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN=");
4067*4bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
4068*4bc52338SCy Schubert 			return end - pos;
4069*4bc52338SCy Schubert 		pos2 += res;
4070*4bc52338SCy Schubert 
4071*4bc52338SCy Schubert 		pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
4072*4bc52338SCy Schubert 					 p->ckn.len);
4073*4bc52338SCy Schubert 
4074*4bc52338SCy Schubert 		res = os_snprintf(pos2, end - pos2,
4075*4bc52338SCy Schubert 				  "\nieee8021XKayMkaPartCached=%s\n"
4076*4bc52338SCy Schubert 				  "ieee8021XKayMkaPartActive=%s\n"
4077*4bc52338SCy Schubert 				  "ieee8021XKayMkaPartRetain=%s\n"
4078*4bc52338SCy Schubert 				  "ieee8021XKayMkaPartActivateControl=%s\n"
4079*4bc52338SCy Schubert 				  "ieee8021XKayMkaPartPrincipal=%s\n",
4080*4bc52338SCy Schubert 				  true_false(p->cached),
4081*4bc52338SCy Schubert 				  true_false(p->active),
4082*4bc52338SCy Schubert 				  true_false(p->retain),
4083*4bc52338SCy Schubert 				  activate_control_txt(p->activate),
4084*4bc52338SCy Schubert 				  true_false(p->principal));
4085*4bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
4086*4bc52338SCy Schubert 			return end - pos;
4087*4bc52338SCy Schubert 		pos2 += res;
4088*4bc52338SCy Schubert 		pos = pos2;
4089*4bc52338SCy Schubert 
4090*4bc52338SCy Schubert 		pos = mka_mib_peer(&p->live_peers, TRUE, pos, end);
4091*4bc52338SCy Schubert 		pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end);
409285732ac8SCy Schubert 	}
4093*4bc52338SCy Schubert 
4094*4bc52338SCy Schubert 	return pos - buf;
4095*4bc52338SCy Schubert }
4096*4bc52338SCy Schubert 
409785732ac8SCy Schubert #endif /* CONFIG_CTRL_IFACE */
4098