Initializing repository

This commit is contained in:
2025-09-23 15:51:56 +03:00
commit 58130b21a9
871 changed files with 604586 additions and 0 deletions

8
src/pae/Makefile Normal file
View File

@@ -0,0 +1,8 @@
all:
@echo Nothing to be made.
clean:
rm -f *~ *.o *.d
install:
@echo Nothing to be made.

738
src/pae/ieee802_1x_cp.c Normal file
View File

@@ -0,0 +1,738 @@
/*
* IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine
* Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
#include "utils/state_machine.h"
#include "ieee802_1x_kay.h"
#include "ieee802_1x_secy_ops.h"
#include "pae/ieee802_1x_cp.h"
#define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
#define STATE_MACHINE_DEBUG_PREFIX "CP"
static u64 cs_id[] = { CS_ID_GCM_AES_128, CS_ID_GCM_AES_256 };
/* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
struct ieee802_1x_cp_sm {
enum cp_states {
CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED,
CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT,
CP_TRANSMITTING, CP_ABANDON, CP_RETIRE
} CP_state;
bool changed;
/* CP -> Client */
bool port_valid;
/* Logon -> CP */
enum connect_type connect;
/* KaY -> CP */
bool chgd_server; /* clear by CP */
bool elected_self;
enum confidentiality_offset cipher_offset;
u64 cipher_suite;
bool new_sak; /* clear by CP */
struct ieee802_1x_mka_ki distributed_ki;
u8 distributed_an;
bool using_receive_sas;
bool all_receiving;
bool server_transmitting;
bool using_transmit_sa;
/* CP -> KaY */
struct ieee802_1x_mka_ki *lki;
u8 lan;
bool ltx;
bool lrx;
struct ieee802_1x_mka_ki *oki;
u8 oan;
bool otx;
bool orx;
/* CP -> SecY */
bool protect_frames;
enum validate_frames validate_frames;
bool replay_protect;
u32 replay_window;
u64 current_cipher_suite;
enum confidentiality_offset confidentiality_offset;
bool controlled_port_enabled;
/* SecY -> CP */
bool port_enabled; /* SecY->CP */
/* private */
u32 transmit_when;
u32 transmit_delay;
u32 retire_when;
u32 retire_delay;
/* not defined IEEE Std 802.1X-2010 */
struct ieee802_1x_kay *kay;
u8 offload;
};
static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
void *timeout_ctx);
static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
void *timeout_ctx);
static int changed_cipher(struct ieee802_1x_cp_sm *sm)
{
return sm->confidentiality_offset != sm->cipher_offset ||
sm->current_cipher_suite != sm->cipher_suite;
}
static int changed_connect(struct ieee802_1x_cp_sm *sm)
{
return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
}
SM_STATE(CP, INIT)
{
SM_ENTRY(CP, INIT);
sm->controlled_port_enabled = false;
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
sm->port_valid = false;
os_free(sm->lki);
sm->lki = NULL;
sm->ltx = false;
sm->lrx = false;
os_free(sm->oki);
sm->oki = NULL;
sm->otx = false;
sm->orx = false;
sm->port_enabled = true;
sm->chgd_server = false;
}
SM_STATE(CP, CHANGE)
{
SM_ENTRY(CP, CHANGE);
sm->port_valid = false;
sm->controlled_port_enabled = false;
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
if (sm->lki)
ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
if (sm->oki)
ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
/* The standard doesn't say it but we should clear out the latest
* and old key values. Why would we keep advertising them if
* they've been deleted and the key server has been changed?
*/
os_free(sm->oki);
sm->oki = NULL;
sm->otx = false;
sm->orx = false;
sm->oan = 0;
ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
sm->otx, sm->orx);
os_free(sm->lki);
sm->lki = NULL;
sm->lrx = false;
sm->ltx = false;
sm->lan = 0;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
}
SM_STATE(CP, ALLOWED)
{
SM_ENTRY(CP, ALLOWED);
sm->protect_frames = false;
sm->replay_protect = false;
sm->validate_frames = Checked;
sm->port_valid = false;
sm->controlled_port_enabled = true;
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
}
SM_STATE(CP, AUTHENTICATED)
{
SM_ENTRY(CP, AUTHENTICATED);
sm->protect_frames = false;
sm->replay_protect = false;
sm->validate_frames = Checked;
sm->offload = sm->kay->macsec_offload;
sm->port_valid = false;
sm->controlled_port_enabled = true;
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
secy_cp_control_offload(sm->kay, sm->offload);
}
SM_STATE(CP, SECURED)
{
SM_ENTRY(CP, SECURED);
sm->chgd_server = false;
sm->protect_frames = sm->kay->macsec_protect;
sm->replay_protect = sm->kay->macsec_replay_protect;
sm->offload = sm->kay->macsec_offload;
sm->validate_frames = sm->kay->macsec_validate;
sm->current_cipher_suite = sm->cipher_suite;
secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
sm->confidentiality_offset = sm->cipher_offset;
sm->port_valid = true;
secy_cp_control_confidentiality_offset(sm->kay,
sm->confidentiality_offset);
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
secy_cp_control_offload(sm->kay, sm->offload);
}
SM_STATE(CP, RECEIVE)
{
SM_ENTRY(CP, RECEIVE);
sm->lki = os_malloc(sizeof(*sm->lki));
if (!sm->lki) {
wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
return;
}
os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
sm->lan = sm->distributed_an;
sm->ltx = false;
sm->lrx = false;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
ieee802_1x_kay_create_sas(sm->kay, sm->lki);
ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
sm->new_sak = false;
sm->all_receiving = false;
}
SM_STATE(CP, RECEIVING)
{
SM_ENTRY(CP, RECEIVING);
sm->lrx = true;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
sm->transmit_when = sm->transmit_delay;
eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
eloop_register_timeout(sm->transmit_when / 1000, 0,
ieee802_1x_cp_transmit_when_timeout, sm, NULL);
/* the electedSelf have been set before CP entering to RECEIVING
* but the CP will transmit from RECEIVING to READY under
* the !electedSelf when KaY is not key server */
ieee802_1x_cp_sm_step(sm);
sm->using_receive_sas = false;
sm->server_transmitting = false;
}
SM_STATE(CP, READY)
{
SM_ENTRY(CP, READY);
ieee802_1x_kay_enable_new_info(sm->kay);
}
SM_STATE(CP, TRANSMIT)
{
SM_ENTRY(CP, TRANSMIT);
sm->controlled_port_enabled = true;
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
sm->ltx = true;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
ieee802_1x_kay_enable_tx_sas(sm->kay, sm->lki);
sm->all_receiving = false;
sm->server_transmitting = false;
}
SM_STATE(CP, TRANSMITTING)
{
SM_ENTRY(CP, TRANSMITTING);
sm->retire_when = sm->orx ? sm->retire_delay : 0;
sm->otx = false;
ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
sm->otx, sm->orx);
ieee802_1x_kay_enable_new_info(sm->kay);
eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
eloop_register_timeout(sm->retire_when / 1000, 0,
ieee802_1x_cp_retire_when_timeout, sm, NULL);
sm->using_transmit_sa = false;
}
SM_STATE(CP, ABANDON)
{
SM_ENTRY(CP, ABANDON);
sm->lrx = false;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
os_free(sm->lki);
sm->lki = NULL;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
}
SM_STATE(CP, RETIRE)
{
SM_ENTRY(CP, RETIRE);
if (sm->oki) {
ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
os_free(sm->oki);
sm->oki = NULL;
}
sm->oki = sm->lki;
sm->otx = sm->ltx;
sm->orx = sm->lrx;
sm->oan = sm->lan;
ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
sm->otx, sm->orx);
sm->lki = NULL;
sm->ltx = false;
sm->lrx = false;
sm->lan = 0;
ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
sm->ltx, sm->lrx);
}
/**
* CP state machine handler entry
*/
SM_STEP(CP)
{
if (!sm->port_enabled)
SM_ENTER(CP, INIT);
switch (sm->CP_state) {
case CP_BEGIN:
SM_ENTER(CP, INIT);
break;
case CP_INIT:
SM_ENTER(CP, CHANGE);
break;
case CP_CHANGE:
if (sm->connect == UNAUTHENTICATED)
SM_ENTER(CP, ALLOWED);
else if (sm->connect == AUTHENTICATED)
SM_ENTER(CP, AUTHENTICATED);
else if (sm->connect == SECURE)
SM_ENTER(CP, SECURED);
break;
case CP_ALLOWED:
if (sm->connect != UNAUTHENTICATED)
SM_ENTER(CP, CHANGE);
break;
case CP_AUTHENTICATED:
if (sm->connect != AUTHENTICATED)
SM_ENTER(CP, CHANGE);
break;
case CP_SECURED:
if (changed_connect(sm))
SM_ENTER(CP, CHANGE);
else if (sm->new_sak)
SM_ENTER(CP, RECEIVE);
break;
case CP_RECEIVE:
if (sm->using_receive_sas)
SM_ENTER(CP, RECEIVING);
break;
case CP_RECEIVING:
if (sm->new_sak || changed_connect(sm))
SM_ENTER(CP, ABANDON);
if (!sm->elected_self)
SM_ENTER(CP, READY);
if (sm->elected_self &&
(sm->all_receiving || !sm->controlled_port_enabled ||
!sm->transmit_when))
SM_ENTER(CP, TRANSMIT);
break;
case CP_TRANSMIT:
if (sm->using_transmit_sa)
SM_ENTER(CP, TRANSMITTING);
break;
case CP_TRANSMITTING:
if (!sm->retire_when || changed_connect(sm))
SM_ENTER(CP, RETIRE);
break;
case CP_RETIRE:
if (changed_connect(sm))
SM_ENTER(CP, CHANGE);
else if (sm->new_sak)
SM_ENTER(CP, RECEIVE);
break;
case CP_READY:
if (sm->new_sak || changed_connect(sm))
SM_ENTER(CP, ABANDON);
if (sm->server_transmitting || !sm->controlled_port_enabled)
SM_ENTER(CP, TRANSMIT);
break;
case CP_ABANDON:
if (changed_connect(sm))
SM_ENTER(CP, RETIRE);
else if (sm->new_sak)
SM_ENTER(CP, RECEIVE);
break;
default:
wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
break;
}
}
/**
* ieee802_1x_cp_sm_init -
*/
struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
{
struct ieee802_1x_cp_sm *sm;
sm = os_zalloc(sizeof(*sm));
if (sm == NULL) {
wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
return NULL;
}
sm->kay = kay;
sm->port_valid = false;
sm->chgd_server = false;
sm->protect_frames = kay->macsec_protect;
sm->validate_frames = kay->macsec_validate;
sm->replay_protect = kay->macsec_replay_protect;
sm->replay_window = kay->macsec_replay_window;
sm->offload = kay->macsec_offload;
sm->controlled_port_enabled = false;
sm->lki = NULL;
sm->lrx = false;
sm->ltx = false;
sm->oki = NULL;
sm->orx = false;
sm->otx = false;
sm->current_cipher_suite = cs_id[kay->macsec_csindex];
sm->cipher_suite = cs_id[kay->macsec_csindex];
sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
sm->confidentiality_offset = sm->cipher_offset;
sm->transmit_delay = MKA_LIFE_TIME;
sm->retire_delay = MKA_SAK_RETIRE_TIME;
sm->CP_state = CP_BEGIN;
sm->changed = false;
wpa_printf(MSG_DEBUG, "CP: state machine created");
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
secy_cp_control_confidentiality_offset(sm->kay,
sm->confidentiality_offset);
secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
secy_cp_control_offload(sm->kay, sm->offload);
SM_STEP_RUN(CP);
return sm;
}
static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
{
enum cp_states prev_state;
int i;
for (i = 0; i < 100; i++) {
prev_state = sm->CP_state;
SM_STEP_RUN(CP);
if (prev_state == sm->CP_state)
break;
}
}
static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
{
struct ieee802_1x_cp_sm *sm = eloop_ctx;
ieee802_1x_cp_step_run(sm);
}
/**
* ieee802_1x_cp_sm_deinit -
*/
void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
{
wpa_printf(MSG_DEBUG, "CP: state machine removed");
if (!sm)
return;
eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
os_free(sm->lki);
os_free(sm->oki);
os_free(sm);
}
/**
* ieee802_1x_cp_connect_pending
*/
void ieee802_1x_cp_connect_pending(void *cp_ctx)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->connect = PENDING;
}
/**
* ieee802_1x_cp_connect_unauthenticated
*/
void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
{
struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
sm->connect = UNAUTHENTICATED;
}
/**
* ieee802_1x_cp_connect_authenticated
*/
void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->connect = AUTHENTICATED;
}
/**
* ieee802_1x_cp_connect_secure
*/
void ieee802_1x_cp_connect_secure(void *cp_ctx)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->connect = SECURE;
}
/**
* ieee802_1x_cp_set_chgdserver -
*/
void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->chgd_server = true;
}
/**
* ieee802_1x_cp_set_electedself -
*/
void ieee802_1x_cp_set_electedself(void *cp_ctx, bool status)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->elected_self = status;
}
/**
* ieee802_1x_cp_set_ciphersuite -
*/
void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->cipher_suite = cs;
}
/**
* ieee802_1x_cp_set_offset -
*/
void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->cipher_offset = offset;
}
/**
* ieee802_1x_cp_signal_newsak -
*/
void ieee802_1x_cp_signal_newsak(void *cp_ctx)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->new_sak = true;
}
/**
* ieee802_1x_cp_set_distributedki -
*/
void ieee802_1x_cp_set_distributedki(void *cp_ctx,
const struct ieee802_1x_mka_ki *dki)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
}
/**
* ieee802_1x_cp_set_distributedan -
*/
void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->distributed_an = an;
}
/**
* ieee802_1x_cp_set_usingreceivesas -
*/
void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, bool status)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->using_receive_sas = status;
}
/**
* ieee802_1x_cp_set_allreceiving -
*/
void ieee802_1x_cp_set_allreceiving(void *cp_ctx, bool status)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->all_receiving = status;
}
/**
* ieee802_1x_cp_set_servertransmitting -
*/
void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, bool status)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->server_transmitting = status;
}
/**
* ieee802_1x_cp_set_usingtransmitsas -
*/
void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, bool status)
{
struct ieee802_1x_cp_sm *sm = cp_ctx;
sm->using_transmit_sa = status;
}
/**
* ieee802_1x_cp_sm_step - Advance EAPOL state machines
* @sm: EAPOL state machine
*
* This function is called to advance CP state machines after any change
* that could affect their state.
*/
void ieee802_1x_cp_sm_step(void *cp_ctx)
{
/*
* Run ieee802_1x_cp_step_run from a registered timeout
* to make sure that other possible timeouts/events are processed
* and to avoid long function call chains.
*/
struct ieee802_1x_cp_sm *sm = cp_ctx;
eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
}
static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct ieee802_1x_cp_sm *sm = eloop_ctx;
sm->retire_when = 0;
ieee802_1x_cp_step_run(sm);
}
static void
ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct ieee802_1x_cp_sm *sm = eloop_ctx;
sm->transmit_when = 0;
ieee802_1x_cp_step_run(sm);
}

39
src/pae/ieee802_1x_cp.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* IEEE Std 802.1X-2010 Controlled Port of PAE state machine - CP state machine
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_1X_CP_H
#define IEEE802_1X_CP_H
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
struct ieee802_1x_cp_sm;
struct ieee802_1x_kay;
struct ieee802_1x_mka_ki;
struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay);
void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm);
void ieee802_1x_cp_sm_step(void *cp_ctx);
void ieee802_1x_cp_connect_pending(void *cp_ctx);
void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx);
void ieee802_1x_cp_connect_authenticated(void *cp_ctx);
void ieee802_1x_cp_connect_secure(void *cp_ctx);
void ieee802_1x_cp_signal_chgdserver(void *cp_ctx);
void ieee802_1x_cp_set_electedself(void *cp_ctx, bool status);
void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs);
void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset);
void ieee802_1x_cp_signal_newsak(void *cp_ctx);
void ieee802_1x_cp_set_distributedki(void *cp_ctx,
const struct ieee802_1x_mka_ki *dki);
void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an);
void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, bool status);
void ieee802_1x_cp_set_allreceiving(void *cp_ctx, bool status);
void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, bool status);
void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, bool status);
#endif /* IEEE802_1X_CP_H */

4185
src/pae/ieee802_1x_kay.c Normal file

File diff suppressed because it is too large Load Diff

284
src/pae/ieee802_1x_kay.h Normal file
View File

@@ -0,0 +1,284 @@
/*
* IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_1X_KAY_H
#define IEEE802_1X_KAY_H
#include "utils/list.h"
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
struct macsec_init_params;
#define MI_LEN 12 /* 96-bit Member Identifier */
#define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */
#define MAX_CKN_LEN 32 /* 32 bytes, 256 bits */
/* MKA timer, unit: millisecond */
#define MKA_HELLO_TIME 2000
#define MKA_BOUNDED_HELLO_TIME 500
#define MKA_LIFE_TIME 6000
#define MKA_SAK_RETIRE_TIME 3000
/**
* struct ieee802_1x_mka_ki - Key Identifier (KI)
* @mi: Key Server's Member Identifier
* @kn: Key Number, assigned by the Key Server
* IEEE 802.1X-2010 9.8 SAK generation, distribution, and selection
*/
struct ieee802_1x_mka_ki {
u8 mi[MI_LEN];
u32 kn;
};
struct ieee802_1x_mka_sci {
u8 addr[ETH_ALEN];
be16 port;
} STRUCT_PACKED;
struct mka_key {
u8 key[MAX_KEY_LEN];
size_t len;
};
struct mka_key_name {
u8 name[MAX_CKN_LEN];
size_t len;
};
enum mka_created_mode {
PSK,
EAP_EXCHANGE,
};
struct data_key {
u8 *key;
int key_len;
struct ieee802_1x_mka_ki key_identifier;
enum confidentiality_offset confidentiality_offset;
u8 an;
bool transmits;
bool receives;
struct os_time created_time;
u32 next_pn;
/* not defined data */
bool rx_latest;
bool tx_latest;
int user;
struct dl_list list;
};
/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
struct transmit_sc {
struct ieee802_1x_mka_sci sci; /* const SCI sci */
bool transmitting; /* bool transmitting (read only) */
struct os_time created_time; /* Time createdTime */
u8 encoding_sa; /* AN encodingSA (read only) */
u8 enciphering_sa; /* AN encipheringSA (read only) */
/* not defined data */
struct dl_list list;
struct dl_list sa_list;
};
/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
struct transmit_sa {
bool in_use; /* bool inUse (read only) */
u32 next_pn; /* PN nextPN (read only) */
struct os_time created_time; /* Time createdTime */
bool enable_transmit; /* bool EnableTransmit */
u8 an;
bool confidentiality;
struct data_key *pkey;
struct transmit_sc *sc;
struct dl_list list; /* list entry in struct transmit_sc::sa_list */
};
/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
struct receive_sc {
struct ieee802_1x_mka_sci sci; /* const SCI sci */
bool receiving; /* bool receiving (read only) */
struct os_time created_time; /* Time createdTime */
struct dl_list list;
struct dl_list sa_list;
};
/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
struct receive_sa {
bool enable_receive; /* bool enableReceive */
bool in_use; /* bool inUse (read only) */
u32 next_pn; /* PN nextPN (read only) */
u32 lowest_pn; /* PN lowestPN (read only) */
u8 an;
struct os_time created_time;
struct data_key *pkey;
struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
struct dl_list list;
};
struct ieee802_1x_kay_ctx {
/* pointer to arbitrary upper level context */
void *ctx;
/* abstract wpa driver interface */
int (*macsec_init)(void *ctx, struct macsec_init_params *params);
int (*macsec_deinit)(void *ctx);
int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
int (*enable_protect_frames)(void *ctx, bool enabled);
int (*enable_encrypt)(void *ctx, bool enabled);
int (*set_replay_protect)(void *ctx, bool enabled, u32 window);
int (*set_current_cipher_suite)(void *ctx, u64 cs);
int (*enable_controlled_port)(void *ctx, bool enabled);
int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
int (*set_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
enum validate_frames vf,
enum confidentiality_offset co);
int (*delete_receive_sc)(void *ctx, struct receive_sc *sc);
int (*create_receive_sa)(void *ctx, struct receive_sa *sa);
int (*delete_receive_sa)(void *ctx, struct receive_sa *sa);
int (*enable_receive_sa)(void *ctx, struct receive_sa *sa);
int (*disable_receive_sa)(void *ctx, struct receive_sa *sa);
int (*create_transmit_sc)(void *ctx, struct transmit_sc *sc,
enum confidentiality_offset co);
int (*delete_transmit_sc)(void *ctx, struct transmit_sc *sc);
int (*create_transmit_sa)(void *ctx, struct transmit_sa *sa);
int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa);
int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa);
int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa);
int (*set_offload)(void *ctx, u8 offload);
};
struct ieee802_1x_kay {
bool enable;
bool active;
bool authenticated;
bool secured;
bool failed;
struct ieee802_1x_mka_sci actor_sci;
u8 actor_priority;
struct ieee802_1x_mka_sci key_server_sci;
u8 key_server_priority;
enum macsec_cap macsec_capable;
bool macsec_desired;
bool macsec_protect;
bool macsec_encrypt;
bool macsec_replay_protect;
u32 macsec_replay_window;
enum validate_frames macsec_validate;
enum confidentiality_offset macsec_confidentiality;
u32 mka_hello_time;
u32 ltx_kn;
u8 ltx_an;
u32 lrx_kn;
u8 lrx_an;
u32 otx_kn;
u8 otx_an;
u32 orx_kn;
u8 orx_an;
/* not defined in IEEE802.1X */
struct ieee802_1x_kay_ctx *ctx;
bool is_key_server;
bool is_obliged_key_server;
char if_name[IFNAMSIZ];
u8 macsec_offload;
unsigned int macsec_csindex; /* MACsec cipher suite table index */
int mka_algindex; /* MKA alg table index */
u32 dist_kn;
u32 rcvd_keys;
u8 dist_an;
time_t dist_time;
u8 mka_version;
u8 algo_agility[4];
u32 pn_exhaustion;
bool port_enable;
bool rx_enable;
bool tx_enable;
struct dl_list participant_list;
enum macsec_policy policy;
struct ieee802_1x_cp_sm *cp;
struct l2_packet_data *l2_mka;
enum validate_frames vf;
enum confidentiality_offset co;
};
u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci);
struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
bool macsec_replay_protect, u32 macsec_replay_window,
u8 macsec_offload, u16 port, u8 priority,
u32 macsec_csindex, const char *ifname, const u8 *addr);
void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
struct ieee802_1x_mka_participant *
ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
const struct mka_key_name *ckn,
const struct mka_key *cak,
u32 life, enum mka_created_mode mode,
bool is_authenticator);
void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
struct mka_key_name *ckn);
void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
struct mka_key_name *ckn,
bool status);
int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay);
int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
unsigned int cs_index);
int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki, u8 lan,
bool ltx, bool lrx);
int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *oki,
u8 oan, bool otx, bool orx);
int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki);
int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *ki);
int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki);
int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki);
int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
size_t buflen);
int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
size_t buflen);
#endif /* IEEE802_1X_KAY_H */

425
src/pae/ieee802_1x_kay_i.h Normal file
View File

@@ -0,0 +1,425 @@
/*
* IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_1X_KAY_I_H
#define IEEE802_1X_KAY_I_H
#include "utils/list.h"
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
#define MKA_VERSION_ID 1
/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 (MKPDU parameter sets) */
enum mka_packet_type {
MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID,
MKA_LIVE_PEER_LIST = 1,
MKA_POTENTIAL_PEER_LIST = 2,
MKA_SAK_USE = 3,
MKA_DISTRIBUTED_SAK = 4,
MKA_DISTRIBUTED_CAK = 5,
MKA_KMD = 6,
MKA_ANNOUNCEMENT = 7,
MKA_ICV_INDICATOR = 255
};
#define ICV_LEN 16 /* 16 bytes */
#define SAK_WRAPPED_LEN 24
/* KN + Wrapper SAK */
#define DEFAULT_DIS_SAK_BODY_LENGTH (SAK_WRAPPED_LEN + 4)
#define MAX_RETRY_CNT 5
struct ieee802_1x_kay;
struct ieee802_1x_mka_peer_id {
u8 mi[MI_LEN];
be32 mn;
} STRUCT_PACKED;
struct ieee802_1x_kay_peer {
struct ieee802_1x_mka_sci sci;
u8 mi[MI_LEN];
u32 mn;
time_t expire;
bool is_key_server;
u8 key_server_priority;
bool macsec_desired;
enum macsec_cap macsec_capability;
bool sak_used;
int missing_sak_use_count;
struct dl_list list;
};
struct macsec_ciphersuite {
u64 id;
char name[32];
enum macsec_cap capable;
int sak_len; /* unit: byte */
};
struct mka_alg {
u8 parameter[4];
size_t icv_len;
int (*cak_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1,
const u8 *mac2, u8 *cak, size_t cak_bytes);
int (*ckn_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1,
const u8 *mac2, const u8 *sid, size_t sid_len, u8 *ckn);
int (*kek_trfm)(const u8 *cak, size_t cak_bytes,
const u8 *ckn, size_t ckn_len,
u8 *kek, size_t kek_bytes);
int (*ick_trfm)(const u8 *cak, size_t cak_bytes,
const u8 *ckn, size_t ckn_len,
u8 *ick, size_t ick_bytes);
int (*icv_hash)(const u8 *ick, size_t ick_bytes,
const u8 *msg, size_t msg_len, u8 *icv);
};
#define DEFAULT_MKA_ALG_INDEX 0
/* See IEEE Std 802.1X-2010, 9.16 MKA management */
struct ieee802_1x_mka_participant {
/* used for active and potential participant */
struct mka_key_name ckn;
struct mka_key cak;
bool cached;
/* used by management to monitor and control activation */
bool active;
bool participant;
bool retain;
enum mka_created_mode mode;
enum activate_ctrl { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
/* used for active participant */
bool principal;
struct dl_list live_peers;
struct dl_list potential_peers;
/* not defined in IEEE 802.1X */
struct dl_list list;
struct mka_key kek;
struct mka_key ick;
struct ieee802_1x_mka_ki lki;
u8 lan;
bool ltx;
bool lrx;
struct ieee802_1x_mka_ki oki;
u8 oan;
bool otx;
bool orx;
bool is_key_server;
bool is_obliged_key_server;
bool can_be_key_server;
bool is_elected;
struct dl_list sak_list;
struct dl_list rxsc_list;
struct transmit_sc *txsc;
u8 mi[MI_LEN];
u32 mn;
/* Current peer MI and SCI during MKPDU processing */
struct ieee802_1x_mka_peer_id current_peer_id;
struct ieee802_1x_mka_sci current_peer_sci;
time_t cak_life;
time_t mka_life;
bool to_dist_sak;
bool to_use_sak;
bool new_sak;
bool advised_desired;
enum macsec_cap advised_capability;
struct data_key *new_key;
u32 retry_count;
struct ieee802_1x_kay *kay;
};
struct ieee802_1x_mka_hdr {
/* octet 1 */
u8 type;
/* octet 2 */
u8 reserve;
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 reserve1:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 reserve1:4;
u8 length:4;
#else
#error "Please fix <bits/endian.h>"
#endif
/* octet 4 */
u8 length1;
} STRUCT_PACKED;
#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
/**
* struct ieee802_1x_mka_basic_body - Basic Parameter Set (Figure 11-8)
* @version: MKA Version Identifier
* @priority: Key Server Priority
* @length: Parameter set body length
* @macsec_capability: MACsec capability, as defined in ieee802_1x_defs.h
* @macsec_desired: the participant wants MACsec to be used to protect frames
* (9.6.1)
* @key_server: the participant has not decided that another participant is or
* will be the key server (9.5.1)
* @length1: Parameter set body length (cont)
* @actor_mi: Actor's Member Identifier
* @actor_mn: Actor's Message Number
* @algo_agility: Algorithm Agility parameter
* @ckn: CAK Name
*/
struct ieee802_1x_mka_basic_body {
/* octet 1 */
u8 version;
/* octet 2 */
u8 priority;
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 macsec_capability:2;
u8 macsec_desired:1;
u8 key_server:1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 key_server:1;
u8 macsec_desired:1;
u8 macsec_capability:2;
u8 length:4;
#endif
/* octet 4 */
u8 length1;
struct ieee802_1x_mka_sci actor_sci;
u8 actor_mi[MI_LEN];
be32 actor_mn;
u8 algo_agility[4];
/* followed by CAK Name */
u8 ckn[0];
} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List
* parameter sets (Figure 11-9)
* @type: Parameter set type (1 or 2)
* @length: Parameter set body length
* @length1: Parameter set body length (cont)
* @peer: array of (MI, MN) pairs
*/
struct ieee802_1x_mka_peer_body {
/* octet 1 */
u8 type;
/* octet 2 */
u8 reserve;
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 reserve1:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 reserve1:4;
u8 length:4;
#endif
/* octet 4 */
u8 length1;
/* followed by Peers */
u8 peer[0];
} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure
* 11-10)
* @type: MKA message type
* @lan: latest key AN
* @ltx: latest key TX
* @lrx: latest key RX
* @oan: old key AN
* @otx: old key TX
* @orx: old key RX
* @ptx: plain TX, ie protectFrames is False
* @prx: plain RX, ie validateFrames is not Strict
* @delay_protect: True if LPNs are being reported sufficiently frequently to
* allow the recipient to provide data delay protection. If False, the LPN
* can be reported as zero.
* @lsrv_mi: latest key server MI
* @lkn: latest key number (together with MI, form the KI)
* @llpn: latest lowest acceptable PN (LPN)
* @osrv_mi: old key server MI
* @okn: old key number (together with MI, form the KI)
* @olpn: old lowest acceptable PN (LPN)
*/
struct ieee802_1x_mka_sak_use_body {
/* octet 1 */
u8 type;
/* octet 2 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 orx:1;
u8 otx:1;
u8 oan:2;
u8 lrx:1;
u8 ltx:1;
u8 lan:2;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 lan:2;
u8 ltx:1;
u8 lrx:1;
u8 oan:2;
u8 otx:1;
u8 orx:1;
#endif
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 delay_protect:1;
u8 reserve:1;
u8 prx:1;
u8 ptx:1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 ptx:1;
u8 prx:1;
u8 reserve:1;
u8 delay_protect:1;
u8 length:4;
#endif
/* octet 4 */
u8 length1;
/* octet 5 - 16 */
u8 lsrv_mi[MI_LEN];
/* octet 17 - 20 */
be32 lkn;
/* octet 21 - 24 */
be32 llpn;
/* octet 25 - 36 */
u8 osrv_mi[MI_LEN];
/* octet 37 - 40 */
be32 okn;
/* octet 41 - 44 */
be32 olpn;
} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set
* (GCM-AES-128, Figure 11-11)
* @type: Parameter set type (4)
* @length: Parameter set body length
* @length1: Parameter set body length (cont)
* Total parameter body length values:
* - 0 for plain text
* - 28 for GCM-AES-128
* - 36 or more for other cipher suites
* @confid_offset: confidentiality offset, as defined in ieee802_1x_defs.h
* @dan: distributed AN (0 for plain text)
* @kn: Key Number
* @sak: AES Key Wrap of SAK (see 9.8)
*/
struct ieee802_1x_mka_dist_sak_body {
/* octet 1 */
u8 type;
/* octet 2 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 reserve:4;
u8 confid_offset:2;
u8 dan:2;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 dan:2;
u8 confid_offset:2;
u8 reserve:4;
#endif
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 reserve1:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 reserve1:4;
u8 length:4;
#endif
/* octet 4 */
u8 length1;
/* octet 5 - 8 */
be32 kn;
/* for GCM-AES-128: octet 9-32: SAK
* for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK
*/
u8 sak[0];
} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure
* 11-13)
* @type: Parameter set type (5)
* @length: Parameter set body length
* @length1: Parameter set body length (cont)
* Total parameter body length values:
* - 0 for plain text
* - 28 for GCM-AES-128
* - 36 or more for other cipher suites
* @cak: AES Key Wrap of CAK (see 9.8)
* @ckn: CAK Name
*/
struct ieee802_1x_mka_dist_cak_body {
/* octet 1 */
u8 type;
/* octet 2 */
u8 reserve;
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 reserve1:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 reserve1:4;
u8 length:4;
#endif
/* octet 4 */
u8 length1;
/* octet 5 - 28 */
u8 cak[24];
/* followed by CAK Name, 29- */
u8 ckn[0];
} STRUCT_PACKED;
struct ieee802_1x_mka_icv_body {
/* octet 1 */
u8 type;
/* octet 2 */
u8 reserve;
/* octet 3 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
u8 length:4;
u8 reserve1:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u8 reserve1:4;
u8 length:4;
#endif
/* octet 4 */
u8 length1;
/* octet 5 - */
u8 icv[0];
} STRUCT_PACKED;
#endif /* IEEE802_1X_KAY_I_H */

210
src/pae/ieee802_1x_key.c Normal file
View File

@@ -0,0 +1,210 @@
/*
* IEEE 802.1X-2010 Key Hierarchy
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "ieee802_1x_key.h"
static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
{
if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) {
os_memcpy(out, mac1, ETH_ALEN);
os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN);
} else {
os_memcpy(out, mac2, ETH_ALEN);
os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN);
}
}
/* IEEE Std 802.1X-2010, 6.2.1 KDF */
static int aes_kdf(const u8 *kdk, size_t kdk_bits,
const char *label, const u8 *context,
int ctx_bits, int ret_bits, u8 *ret)
{
const int h = 128;
const int r = 8;
int i, n;
int lab_len, ctx_len, ret_len, buf_len;
u8 *buf;
if (kdk_bits != 128 && kdk_bits != 256)
return -1;
lab_len = os_strlen(label);
ctx_len = (ctx_bits + 7) / 8;
ret_len = ((ret_bits & 0xffff) + 7) / 8;
buf_len = lab_len + ctx_len + 4;
os_memset(ret, 0, ret_len);
n = (ret_bits + h - 1) / h;
if (n > ((0x1 << r) - 1))
return -1;
buf = os_zalloc(buf_len);
if (buf == NULL)
return -1;
os_memcpy(buf + 1, label, lab_len);
os_memcpy(buf + lab_len + 2, context, ctx_len);
WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
for (i = 0; i < n; i++) {
int res;
buf[0] = (u8) (i + 1);
if (kdk_bits == 128)
res = omac1_aes_128(kdk, buf, buf_len, ret);
else
res = omac1_aes_256(kdk, buf, buf_len, ret);
if (res) {
os_free(buf);
return -1;
}
ret = ret + h / 8;
}
os_free(buf);
return 0;
}
/**
* ieee802_1x_cak_aes_cmac
*
* IEEE Std 802.1X-2010, 6.2.2
* CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
*/
int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
const u8 *mac2, u8 *cak, size_t cak_bytes)
{
u8 context[2 * ETH_ALEN];
joint_two_mac(mac1, mac2, context);
return aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CAK",
context, sizeof(context) * 8, 8 * cak_bytes, cak);
}
/**
* ieee802_1x_ckn_aes_cmac
*
* IEEE Std 802.1X-2010, 6.2.2
* CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
*/
int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
const u8 *mac2, const u8 *sid,
size_t sid_bytes, u8 *ckn)
{
int res;
u8 *context;
size_t ctx_len = sid_bytes + ETH_ALEN * 2;
context = os_zalloc(ctx_len);
if (!context) {
wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__);
return -1;
}
os_memcpy(context, sid, sid_bytes);
joint_two_mac(mac1, mac2, context + sid_bytes);
res = aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CKN",
context, ctx_len * 8, 128, ckn);
os_free(context);
return res;
}
/**
* ieee802_1x_kek_aes_cmac
*
* IEEE Std 802.1X-2010, 9.3.3
* KEK = KDF(Key, Label, Keyid, KEKLength)
*/
int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
size_t ckn_bytes, u8 *kek, size_t kek_bytes)
{
u8 context[16];
/* First 16 octets of CKN, with null octets appended to pad if needed */
os_memset(context, 0, sizeof(context));
os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
return aes_kdf(cak, 8 * cak_bytes, "IEEE8021 KEK",
context, sizeof(context) * 8,
8 * kek_bytes, kek);
}
/**
* ieee802_1x_ick_aes_cmac
*
* IEEE Std 802.1X-2010, 9.3.3
* ICK = KDF(Key, Label, Keyid, ICKLength)
*/
int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
size_t ckn_bytes, u8 *ick, size_t ick_bytes)
{
u8 context[16];
/* First 16 octets of CKN, with null octets appended to pad if needed */
os_memset(context, 0, sizeof(context));
os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
return aes_kdf(cak, 8 *cak_bytes, "IEEE8021 ICK",
context, sizeof(context) * 8,
8 * ick_bytes, ick);
}
/**
* ieee802_1x_icv_aes_cmac
*
* IEEE Std 802.1X-2010, 9.4.1
* ICV = AES-CMAC(ICK, M, 128)
*/
int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg,
size_t msg_bytes, u8 *icv)
{
int res;
if (ick_bytes == 16)
res = omac1_aes_128(ick, msg, msg_bytes, icv);
else if (ick_bytes == 32)
res = omac1_aes_256(ick, msg, msg_bytes, icv);
else
return -1;
if (res) {
wpa_printf(MSG_ERROR,
"MKA: AES-CMAC failed for ICV calculation");
return -1;
}
return 0;
}
/**
* ieee802_1x_sak_aes_cmac
*
* IEEE Std 802.1X-2010, 9.8.1
* SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
*/
int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx,
size_t ctx_bytes, u8 *sak, size_t sak_bytes)
{
return aes_kdf(cak, cak_bytes * 8, "IEEE8021 SAK", ctx, ctx_bytes * 8,
sak_bytes * 8, sak);
}

26
src/pae/ieee802_1x_key.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* IEEE 802.1X-2010 Key Hierarchy
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_1X_KEY_H
#define IEEE802_1X_KEY_H
int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
const u8 *mac2, u8 *cak, size_t cak_bytes);
int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
const u8 *mac2, const u8 *sid,
size_t sid_bytes, u8 *ckn);
int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
size_t ckn_bytes, u8 *kek, size_t kek_bytes);
int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
size_t ckn_bytes, u8 *ick, size_t ick_bytes);
int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg,
size_t msg_bytes, u8 *icv);
int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx,
size_t ctx_bytes, u8 *sak, size_t sak_bytes);
#endif /* IEEE802_1X_KEY_H */

View File

@@ -0,0 +1,559 @@
/*
* SecY Operations
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/defs.h"
#include "drivers/driver.h"
#include "pae/ieee802_1x_kay.h"
#include "pae/ieee802_1x_kay_i.h"
#include "pae/ieee802_1x_secy_ops.h"
int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
enum validate_frames vf)
{
kay->vf = vf;
return 0;
}
int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, bool enabled)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->enable_protect_frames) {
wpa_printf(MSG_ERROR,
"KaY: secy enable_protect_frames operation not supported");
return -1;
}
return ops->enable_protect_frames(ops->ctx, enabled);
}
int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, bool enabled)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->enable_encrypt) {
wpa_printf(MSG_ERROR,
"KaY: secy enable_encrypt operation not supported");
return -1;
}
return ops->enable_encrypt(ops->ctx, enabled);
}
int secy_cp_control_replay(struct ieee802_1x_kay *kay, bool enabled, u32 win)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->set_replay_protect) {
wpa_printf(MSG_ERROR,
"KaY: secy set_replay_protect operation not supported");
return -1;
}
return ops->set_replay_protect(ops->ctx, enabled, win);
}
int secy_cp_control_offload(struct ieee802_1x_kay *kay, u8 offload)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->set_offload) {
wpa_printf(MSG_ERROR,
"KaY: secy set_offload operation not supported");
return -1;
}
return ops->set_offload(ops->ctx, offload);
}
int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->set_current_cipher_suite) {
wpa_printf(MSG_ERROR,
"KaY: secy set_current_cipher_suite operation not supported");
return -1;
}
return ops->set_current_cipher_suite(ops->ctx, cs);
}
int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
enum confidentiality_offset co)
{
kay->co = co;
return 0;
}
int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, bool enabled)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->enable_controlled_port) {
wpa_printf(MSG_ERROR,
"KaY: secy enable_controlled_port operation not supported");
return -1;
}
return ops->enable_controlled_port(ops->ctx, enabled);
}
int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->macsec_get_capability) {
wpa_printf(MSG_ERROR,
"KaY: secy macsec_get_capability operation not supported");
return -1;
}
return ops->macsec_get_capability(ops->ctx, cap);
}
int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->get_receive_lowest_pn) {
wpa_printf(MSG_ERROR,
"KaY: secy get_receive_lowest_pn operation not supported");
return -1;
}
return ops->get_receive_lowest_pn(ops->ctx, rxsa);
}
int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->get_transmit_next_pn) {
wpa_printf(MSG_ERROR,
"KaY: secy get_transmit_next_pn operation not supported");
return -1;
}
return ops->get_transmit_next_pn(ops->ctx, txsa);
}
int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->set_transmit_next_pn) {
wpa_printf(MSG_ERROR,
"KaY: secy set_transmit_next_pn operation not supported");
return -1;
}
return ops->set_transmit_next_pn(ops->ctx, txsa);
}
int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->set_receive_lowest_pn) {
wpa_printf(MSG_ERROR,
"KaY: secy set_receive_lowest_pn operation not supported");
return -1;
}
return ops->set_receive_lowest_pn(ops->ctx, rxsa);
}
int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsc) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->create_receive_sc) {
wpa_printf(MSG_ERROR,
"KaY: secy create_receive_sc operation not supported");
return -1;
}
return ops->create_receive_sc(ops->ctx, rxsc, kay->vf, kay->co);
}
int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsc) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->delete_receive_sc) {
wpa_printf(MSG_ERROR,
"KaY: secy delete_receive_sc operation not supported");
return -1;
}
return ops->delete_receive_sc(ops->ctx, rxsc);
}
int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->create_receive_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy create_receive_sa operation not supported");
return -1;
}
return ops->create_receive_sa(ops->ctx, rxsa);
}
int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->delete_receive_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy delete_receive_sa operation not supported");
return -1;
}
return ops->delete_receive_sa(ops->ctx, rxsa);
}
int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->enable_receive_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy enable_receive_sa operation not supported");
return -1;
}
rxsa->enable_receive = true;
return ops->enable_receive_sa(ops->ctx, rxsa);
}
int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->disable_receive_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy disable_receive_sa operation not supported");
return -1;
}
rxsa->enable_receive = false;
return ops->disable_receive_sa(ops->ctx, rxsa);
}
int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
struct transmit_sc *txsc)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsc) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->create_transmit_sc) {
wpa_printf(MSG_ERROR,
"KaY: secy create_transmit_sc operation not supported");
return -1;
}
return ops->create_transmit_sc(ops->ctx, txsc, kay->co);
}
int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
struct transmit_sc *txsc)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsc) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->delete_transmit_sc) {
wpa_printf(MSG_ERROR,
"KaY: secy delete_transmit_sc operation not supported");
return -1;
}
return ops->delete_transmit_sc(ops->ctx, txsc);
}
int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->create_transmit_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy create_transmit_sa operation not supported");
return -1;
}
return ops->create_transmit_sa(ops->ctx, txsa);
}
int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->delete_transmit_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy delete_transmit_sa operation not supported");
return -1;
}
return ops->delete_transmit_sa(ops->ctx, txsa);
}
int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->enable_transmit_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy enable_transmit_sa operation not supported");
return -1;
}
txsa->enable_transmit = true;
return ops->enable_transmit_sa(ops->ctx, txsa);
}
int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->disable_transmit_sa) {
wpa_printf(MSG_ERROR,
"KaY: secy disable_transmit_sa operation not supported");
return -1;
}
txsa->enable_transmit = false;
return ops->disable_transmit_sa(ops->ctx, txsa);
}
int secy_init_macsec(struct ieee802_1x_kay *kay)
{
int ret;
struct ieee802_1x_kay_ctx *ops;
struct macsec_init_params params;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->macsec_init) {
wpa_printf(MSG_ERROR,
"KaY: secy macsec_init operation not supported");
return -1;
}
params.use_es = false;
params.use_scb = false;
params.always_include_sci = true;
ret = ops->macsec_init(ops->ctx, &params);
return ret;
}
int secy_deinit_macsec(struct ieee802_1x_kay *kay)
{
struct ieee802_1x_kay_ctx *ops;
if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
if (!ops || !ops->macsec_deinit) {
wpa_printf(MSG_ERROR,
"KaY: secy macsec_deinit operation not supported");
return -1;
}
return ops->macsec_deinit(ops->ctx);
}

View File

@@ -0,0 +1,63 @@
/*
* SecY Operations
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_1X_SECY_OPS_H
#define IEEE802_1X_SECY_OPS_H
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
struct ieee802_1x_kay_conf;
int secy_init_macsec(struct ieee802_1x_kay *kay);
int secy_deinit_macsec(struct ieee802_1x_kay *kay);
/****** CP -> SecY ******/
int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
enum validate_frames vf);
int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, bool flag);
int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, bool enabled);
int secy_cp_control_replay(struct ieee802_1x_kay *kay, bool flag, u32 win);
int secy_cp_control_offload(struct ieee802_1x_kay *kay, u8 offload);
int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs);
int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
enum confidentiality_offset co);
int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, bool flag);
/****** KaY -> SecY *******/
int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap);
int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
struct receive_sa *rxsa);
int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
struct receive_sa *txsa);
int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
struct receive_sa *rxsa);
int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
struct transmit_sc *txsc);
int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
struct transmit_sc *txsc);
int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
#endif /* IEEE802_1X_SECY_OPS_H */