diff --git a/src/linux_daemon/dbus_server.c b/src/linux_daemon/dbus_server.c index 04683fe..9c47444 100644 --- a/src/linux_daemon/dbus_server.c +++ b/src/linux_daemon/dbus_server.c @@ -273,11 +273,12 @@ on_bus_acquired (GDBusConnection *connection, strcpy(intf_name, "/de/technica_engineering/mkad/"); char **busname_split = g_strsplit(bus_names[bus], ".", -1); char *busname = g_strjoinv("", busname_split); - g_strfreev(busname_split); //strncat(intf_name, g_dbus_escape_object_path(busname), IFNAMSIZ); // g_dbus_escape object is only available on glib >=2.68 strncat(intf_name, busname, IFNAMSIZ); strncat(intf_name, "/BUS", 4); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interfaces[bus]), connection, intf_name, &error); + g_strfreev(busname_split); + g_free(busname); } } diff --git a/src/linux_daemon/mka_timers_event.c b/src/linux_daemon/mka_timers_event.c index 7f57d54..b6d6b0d 100644 --- a/src/linux_daemon/mka_timers_event.c +++ b/src/linux_daemon/mka_timers_event.c @@ -131,8 +131,8 @@ bool mka_timer_running(t_mka_timer const* const timer) bool mka_timer_expired(t_mka_timer const* const timer) { - MKA_ASSERT(NULL != timer->ref, "Attempt to call a getter on a non-initialised timer."); - return (mka_tick_time_ms >= timer->ref->expiry); + bool const initialised = (bool)(NULL != timer->ref); + return initialised ? (mka_tick_time_ms >= timer->ref->expiry) : false; } /******************* Func. definition ***********************/ diff --git a/src/mka_private.h b/src/mka_private.h index 3a7f191..eaedd5e 100644 --- a/src/mka_private.h +++ b/src/mka_private.h @@ -264,6 +264,15 @@ static inline bool MKA_sci_equal(t_MKA_sci const* a, t_MKA_sci const* b) return 0 == memcmp(a, b, sizeof(t_MKA_sci)); } +static inline bool MKA_is_mi_null(uint8_t const* a) +{ + uint8_t sum = 0U; + for(uint8_t i=0U; ipeer; + t_mka_peer *const peer_secondary = &participant->peer_secondary; if (mka_timer_expired(&peer->expiry)) { MKA_LOG_INFO("KaY/%i: %s peer timed out.", bus, @@ -147,6 +148,12 @@ void MKA_KAY_MainFunctionTimers(t_MKA_bus bus) mka_peer_cleanup(bus); } + if (mka_timer_expired(&peer_secondary->expiry)) { + MKA_LOG_INFO("KaY/%i: Secondary peer timed out.", bus); + mka_timer_stop(&peer_secondary->expiry); + memset(peer_secondary, 0, sizeof(*peer_secondary)); + } + if (mka_timer_expired(&participant->mka_life) && (MKA_PEER_NONE == peer->state)) { mka_set_mode(bus, MKA_FAILED); } @@ -503,6 +510,7 @@ void mka_handle_mkpdu(t_MKA_bus bus, uint8_t const*packet, uint32_t length) t_mka_peer*const peer = &participant->peer; t_mka_peer_state initial_peer_state = peer->state; uint32_t offset = sizeof(t_MKA_l2_ether_header)+sizeof(t_mka_eapol_header); + bool main_peer = true; // secondary peer: quick renegotiation after peer resets its MI bool header_presence[256U]; bool continue_process; uint16_t param_type = 0U; @@ -515,12 +523,12 @@ void mka_handle_mkpdu(t_MKA_bus bus, uint8_t const*packet, uint32_t length) uint32_t xpn_l_high = 0U; uint32_t xpn_o_high = 0U; - { // Basic Parameter Set is always first - //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data - t_mka_basic_parameter_set const*const bps = (t_mka_basic_parameter_set const*)&packet[offset]; - offset += sizeof(t_mka_param_generic) + MKA_ALIGN_TO_32BIT(((uint32_t)bps->length << 8U) + (uint32_t)bps->length_cont); - continue_process = mka_handle_basic_parameter_set(bus, bps); - } + // Basic Parameter Set is always first + //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data + t_mka_basic_parameter_set const*const bps = (t_mka_basic_parameter_set const*)&packet[offset]; + offset += sizeof(t_mka_param_generic) + MKA_ALIGN_TO_32BIT(((uint32_t)bps->length << 8U) + (uint32_t)bps->length_cont); + continue_process = mka_handle_basic_parameter_set(bus, bps); + main_peer = MKA_mi_equal(peer->mi, bps->actor_mi); /* Re-evaluate in what remote list we are listed, based on the processing * of potential/live peer lists below, which must be populated right after @@ -548,10 +556,12 @@ void mka_handle_mkpdu(t_MKA_bus bus, uint8_t const*packet, uint32_t length) else { switch(param_type) { case PARAMETER_LIVE_PEER_LIST: - continue_process = mka_handle_peer_list(bus, &packet[offset], param_len, MKA_PEER_LIVE); + continue_process = mka_handle_peer_list(bus, &packet[offset], param_len, main_peer, MKA_PEER_LIVE); + main_peer = MKA_mi_equal(peer->mi, bps->actor_mi); // re-evaluate break; case PARAMETER_POTENTIAL_PEER_LIST: - continue_process = mka_handle_peer_list(bus, &packet[offset], param_len, MKA_PEER_POTENTIAL); + continue_process = mka_handle_peer_list(bus, &packet[offset], param_len, main_peer, MKA_PEER_POTENTIAL); + main_peer = MKA_mi_equal(peer->mi, bps->actor_mi); // re-evaluate break; case PARAMETER_SAK_USE: // if we are not key server, a SAK USE parameter could be meaningless unless @@ -569,11 +579,15 @@ void mka_handle_mkpdu(t_MKA_bus bus, uint8_t const*packet, uint32_t length) break; case PARAMETER_ANNOUNCEMENT: - continue_process = mka_handle_announcements(bus, &packet[offset], param_len); + if (main_peer) { + continue_process = mka_handle_announcements(bus, &packet[offset], param_len); + } break; case PARAMETER_XPN: - continue_process = mka_handle_xpn(bus, &packet[offset], param_len, &xpn_o_high, &xpn_l_high); + if (main_peer) { + continue_process = mka_handle_xpn(bus, &packet[offset], param_len, &xpn_o_high, &xpn_l_high); + } break; case PARAMETER_DISTRIBUTED_CAK: @@ -597,6 +611,13 @@ void mka_handle_mkpdu(t_MKA_bus bus, uint8_t const*packet, uint32_t length) } } + // case 'secondary' peer has not become 'main' peer at this point + if (!main_peer) { + // does not make sense to continue processing, discard all logic past this point, + // (DIST SAK/SAK USE). its life timer is already updated while processing basic parameter set + continue_process = false; + } + // Conditions to not perform MACsec cipher suite negotiation, after all parameters are handled if ((!continue_process) || (MKA_PEER_LIVE != peer->state) || (MKA_CS_INVALID != participant->cipher)) { // No action @@ -886,6 +907,8 @@ void mka_participant_cleanup(t_MKA_bus bus) mka_timer_stop(&participant->mka_life); mka_peer_cleanup(bus); + memset(&participant->peer_secondary, 0, sizeof(participant->peer_secondary)); + mka_timer_init(&participant->peer_secondary.expiry); mka_timer_init(&participant_cak_life); mka_timer_init(&participant_mka_life); @@ -916,6 +939,8 @@ void mka_new_participant_mi(t_MKA_bus bus) // Clear participant's peer mka_peer_cleanup(bus); + memset(&participant->peer_secondary, 0, sizeof(participant->peer_secondary)); + mka_timer_init(&participant->peer_secondary.expiry); MKA_ASSERT(MKA_GetRandomBytes(sizeof(participant->mi), participant->mi), "Cannot generate random numbers"); @@ -1044,7 +1069,11 @@ t_MKA_sak* mka_find_key(t_MKA_bus bus, uint8_t const* mi, uint32_t kn) t_mka_participant*const participant = &ctx->participant; t_MKA_sak* sak; - if (MKA_mi_equal(participant->new_sak.identifier.mi, mi) && + // Case call from CP with empty key, prevent referencing internal kay structures + if (MKA_is_mi_null(mi) && (0U == kn)) { + sak = NULL; + } + else if (MKA_mi_equal(participant->new_sak.identifier.mi, mi) && (participant->new_sak.identifier.kn == kn)) { sak = &participant->new_sak; } @@ -1137,7 +1166,7 @@ void MKA_KAY_DeleteSAs(t_MKA_bus bus, t_MKA_ki const* ki) } // CP is erasing SA associated to current SAK, we just completed a key rotation - if ((key == &participant->current_sak) || (NULL == key)) { + if (key == &participant->current_sak) { memcpy(&participant->current_sak, &participant->new_sak, sizeof(t_MKA_sak)); memset(&participant->new_sak, 0, sizeof(t_MKA_sak)); } diff --git a/src/pae/mka_kay_internal.h b/src/pae/mka_kay_internal.h index c63670f..8ee352d 100644 --- a/src/pae/mka_kay_internal.h +++ b/src/pae/mka_kay_internal.h @@ -188,6 +188,9 @@ typedef struct { // Peer t_mka_peer peer; + // backport: potential peer for quick renegotiation after remote MI reset, without full multipeer + t_mka_peer peer_secondary; + // Life time t_mka_timer cak_life; t_mka_timer mka_life; @@ -403,7 +406,7 @@ bool mka_mkpdu_verify(t_MKA_bus bus, uint8_t const*packet, uint32_t *length); void mka_handle_mkpdu(t_MKA_bus bus, uint8_t const*packet, uint32_t length); bool mka_handle_basic_parameter_set(t_MKA_bus bus, t_mka_basic_parameter_set const*bps); bool mka_encode_basic_parameter_set(t_MKA_bus bus, uint8_t *packet, uint32_t *length); -bool mka_handle_peer_list(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, t_mka_peer_state type); +bool mka_handle_peer_list(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, bool main_peer, t_mka_peer_state type); bool mka_encode_peer_list(t_MKA_bus bus, uint8_t *packet, uint32_t *length); bool mka_handle_sak_use(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, uint32_t xpn_o_high, uint32_t xpn_l_high); bool mka_encode_sak_use(t_MKA_bus bus, uint8_t *packet, uint32_t *length); diff --git a/src/pae/mka_kay_params.c b/src/pae/mka_kay_params.c index 806219a..3bcf33d 100644 --- a/src/pae/mka_kay_params.c +++ b/src/pae/mka_kay_params.c @@ -199,19 +199,21 @@ bool mka_handle_basic_parameter_set(t_MKA_bus bus, t_mka_basic_parameter_set con t_mka_kay*const ctx = &mka_kay[bus]; t_mka_participant*const participant = &ctx->participant; t_mka_peer *const peer = &participant->peer; + t_mka_peer *const peer_secondary = &participant->peer_secondary; // Comparisons for evaluating colliding MI bool const is_sci_addr_same_mine = (0 == memcmp(bps->sci.addr, ctx->actor_sci.addr, MKA_L2_ADDR_SIZE)); bool const is_mi_same_mine = MKA_mi_equal(participant->mi, bps->actor_mi); + bool const peer_mi_match = MKA_mi_equal(peer->mi, bps->actor_mi); // Role verification, enforced when there's a relation with peer - if ((MKA_ROLE_FORCE_KEY_SERVER == ctx->role) && (1U == bps->key_server) && (MKA_PEER_NONE != peer->state)) { + if ((MKA_ROLE_FORCE_KEY_SERVER == ctx->role) && (1U == bps->key_server) && (MKA_PEER_NONE != peer->state) && peer_mi_match) { MKA_LOG_WARNING("KaY/%i: Configured as Key Server, received packet from a Key Server. Discarded.", bus); continue_process = false; } // Role verification, enforced when there's a relation with peer - if ((MKA_ROLE_FORCE_KEY_CLIENT == ctx->role) && (0U == bps->key_server) && (MKA_PEER_NONE != peer->state)) { + if ((MKA_ROLE_FORCE_KEY_CLIENT == ctx->role) && (0U == bps->key_server) && (MKA_PEER_NONE != peer->state) && peer_mi_match) { MKA_LOG_WARNING("KaY/%i: Configured as non Key Server, received packet from a non Key Server. Discarded.", bus); continue_process = false; } @@ -260,9 +262,52 @@ bool mka_handle_basic_parameter_set(t_MKA_bus bus, t_mka_basic_parameter_set con continue_process = false; } // Case MI differs - else if (!MKA_mi_equal(peer->mi, bps->actor_mi)) { - MKA_LOG_INFO("KaY/%i: Received MKPDU from peer with same SCI, different MI, but peer slot is occupied. Discarded.", bus); - continue_process = false; + else if (!peer_mi_match) { + if (MKA_PEER_NONE == peer_secondary->state) { + MKA_LOG_INFO("KaY/%i: Received MKPDU from peer with same SCI, different MI. Learning as secondary until live.", bus); + + // Register. It is now a potential peer + memcpy(peer_secondary->sci.addr, bps->sci.addr, MKA_L2_ADDR_SIZE); + peer_secondary->sci.port = MKA_NTOHS(bps->sci.port); + memcpy(peer_secondary->mi, bps->actor_mi, MKA_MI_LENGTH); + peer_secondary->mn = MKA_NTOHL(bps->actor_mn); + peer_secondary->key_server = (bps->key_server > 0U); + peer_secondary->key_server_priority = bps->priority; + peer_secondary->macsec_desired = (bps->macsec_desired > 0U); + //lint -e{9030} [MISRA 2012 Rule 10.5, advisory] Casting 2-bit number to 4-value enum is controlled, values match to enum's + //lint -e{9034} [MISRA 2012 Rule 10.3, required] Casting 2-bit number to 4-value enum is controlled, values match to enum's + peer_secondary->macsec_capability = (t_MKA_macsec_cap)bps->macsec_capability; + //lint -e{9030} [MISRA 2012 Rule 10.5, advisory] Casting 2-bit number to 4-value enum is controlled, values match to enum's + //lint -e{9034} [MISRA 2012 Rule 10.3, required] Casting 2-bit number to 4-value enum is controlled, values match to enum's + peer_secondary->compatible_capability = (t_MKA_macsec_cap)bps->macsec_capability; + peer_secondary->state = MKA_PEER_POTENTIAL; + + ctx->new_info = true; // Speed up handshake + mka_timer_start(&peer_secondary->expiry, MKA_active_global_config->life_time); + + } // Repeated Message Number + else if (peer_secondary->mn >= MKA_NTOHL(bps->actor_mn)) { + MKA_LOG_WARNING("KaY/%i: Received MKPDU from secondary peer with lower MN than expected. Discarded.", bus); + continue_process = false; + } + else if (MKA_mi_equal(peer_secondary->mi, bps->actor_mi)) { + peer_secondary->mn = MKA_NTOHL(bps->actor_mn); + peer_secondary->key_server = (bps->key_server > 0U); + peer_secondary->key_server_priority = bps->priority; + peer_secondary->macsec_desired = (bps->macsec_desired > 0U); + //lint -e{9030} [MISRA 2012 Rule 10.5, advisory] Casting 2-bit number to 4-value enum is controlled, values match to enum's + //lint -e{9034} [MISRA 2012 Rule 10.3, required] Casting 2-bit number to 4-value enum is controlled, values match to enum's + peer_secondary->macsec_capability = (t_MKA_macsec_cap)bps->macsec_capability; + //lint -e{9030} [MISRA 2012 Rule 10.5, advisory] Casting 2-bit number to 4-value enum is controlled, values match to enum's + //lint -e{9034} [MISRA 2012 Rule 10.3, required] Casting 2-bit number to 4-value enum is controlled, values match to enum's + peer_secondary->compatible_capability = (t_MKA_macsec_cap)bps->macsec_capability; + + mka_timer_start(&peer_secondary->expiry, MKA_active_global_config->life_time); + } + else { + MKA_LOG_INFO("KaY/%i: Received MKPDU with same SCI, different MI, but secondary slot occupied. Discarded.", bus); + continue_process = false; + } } // Repeated Message Number else if (peer->mn >= MKA_NTOHL(bps->actor_mn)) { @@ -347,11 +392,12 @@ bool mka_encode_basic_parameter_set(t_MKA_bus bus, uint8_t *packet, uint32_t *le return continue_process; } -bool mka_handle_peer_list(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, t_mka_peer_state type) +bool mka_handle_peer_list(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, bool main_peer, t_mka_peer_state type) { t_mka_kay*const ctx = &mka_kay[bus]; t_mka_participant*const participant = &ctx->participant; t_mka_peer*const peer = &participant->peer; + t_mka_peer*const peer_secondary = &participant->peer_secondary; bool continue_process = true; bool self_seen = false; @@ -392,12 +438,48 @@ bool mka_handle_peer_list(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, } } - if (self_seen) { + // Case secondary peer became live + if (continue_process && self_seen && (MKA_PEER_POTENTIAL == peer_secondary->state) && (!main_peer)) { + t_MKA_ciphsuite const current_cipher = participant->cipher; + t_MKA_ciphsuite const current_compat_cipher = peer->compatible_cipher; + t_MKA_macsec_cap const current_compat_cap = peer->compatible_capability; + + MKA_LOG_INFO("KaY/%i: Secondary peer is live. Replacing primary.", bus); + // kill main peer and any active SA/SC + mka_peer_cleanup(bus); + + // signal connectivity change to CP via server changed + MKA_CP_SignalChgdServer(bus); + + // temporary connection mode transition + mka_set_mode(bus, MKA_PENDING); // not setting FAILED here on purpose, let timer functions handle + + // make sure to "unfreeze" CP from states RECEIVE and TRANSMIT + MKA_CP_SetUsingReceiveSAs(bus, true); + MKA_CP_SetUsingTransmitSA(bus, true); + + // transform secondary peer into primary + (void)memcpy(peer, peer_secondary, sizeof(*peer)); + (void)memset(&peer->expiry, 0, sizeof(peer->expiry)); + mka_timer_start(&peer->expiry, MKA_active_global_config->life_time); + + // secondary cleanup + memset(peer_secondary, 0, sizeof(*peer_secondary)); + mka_timer_init(&peer_secondary->expiry); + main_peer = true; + + // apply previous ciphersuite / negotiation result + participant->cipher = current_cipher; + peer->compatible_cipher = current_compat_cipher; + peer->compatible_capability = current_compat_cap; + } + + if (main_peer && self_seen) { peer->remote_state = type; } // When a potential peer sees us, peer becomes live - if (continue_process && self_seen && (MKA_PEER_POTENTIAL == peer->state)) { + if (continue_process && self_seen && main_peer && (MKA_PEER_POTENTIAL == peer->state)) { MKA_LOG_INFO("KaY/%i: New live peer.", bus); peer->state = MKA_PEER_LIVE; @@ -431,7 +513,7 @@ bool mka_handle_peer_list(t_MKA_bus bus, uint8_t const*param, uint32_t body_len, } // If a live peer doesn't see us, we ignore the frame - if (continue_process && (!self_seen) && (MKA_PEER_LIVE == peer->state) && + if (continue_process && (!self_seen) && main_peer && (MKA_PEER_LIVE == peer->state) && (PARAMETER_LIVE_PEER_LIST == *param)) { MKA_LOG_WARNING("KaY/%i: We are not listed in our peer live list while peer is live. Discarded.", bus); continue_process = false; @@ -444,11 +526,12 @@ bool mka_encode_peer_list(t_MKA_bus bus, uint8_t *packet, uint32_t *length) { t_mka_kay const*const ctx = &mka_kay[bus]; t_mka_participant const*const participant = &ctx->participant; + t_mka_peer const*const peer_secondary = &participant->peer_secondary; t_mka_peer const*const peer = &participant->peer; //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data - t_mka_param_peer_list *const param = (t_mka_param_peer_list*)&packet[*length]; + t_mka_param_peer_list * param = (t_mka_param_peer_list*)&packet[*length]; //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data - t_mka_peer_id *const param_peer = (t_mka_peer_id*)&packet[(*length) + sizeof(t_mka_param_peer_list)]; + t_mka_peer_id * param_peer = (t_mka_peer_id*)&packet[(*length) + sizeof(t_mka_param_peer_list)]; bool const present = (MKA_PEER_NONE != peer->state); bool const dist_sak_present_xpn = mka_is_cipher_xpn(participant->cipher) && (MKA_SAK_KS_DISTRIBUTING == participant->sak_state); bool continue_process = true; @@ -479,6 +562,32 @@ bool mka_encode_peer_list(t_MKA_bus bus, uint8_t *packet, uint32_t *length) param_peer->mn = MKA_HTONL(peer->mn); } + //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data + param = (t_mka_param_peer_list*)&packet[*length]; + //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data + param_peer = (t_mka_peer_id*)&packet[(*length) + sizeof(t_mka_param_peer_list)]; + + // Case not necessary to include potential peer list for quick renegotiation + if (!present || !continue_process || (MKA_PEER_NONE == peer_secondary->state)) { + // No action + } + else if (!mka_frame_account_space(length, sizeof(t_mka_param_peer_list) + sizeof(t_mka_peer_id))) { + MKA_LOG_WARNING("KaY/%i: Could not generate MKPDU due to Peer List encoding error.", bus); + continue_process = false; + } + else { + memset(param, 0, sizeof(t_mka_param_peer_list)); + param->type = PARAMETER_POTENTIAL_PEER_LIST; + param->key_server_ssci = 0U; + param->unused = 0U; + param->length = 0U; + param->length_cont = 16U; + param->key_server_ssci = 0U; + + memcpy(param_peer->mi, peer_secondary->mi, sizeof(peer_secondary->mi)); + param_peer->mn = MKA_HTONL(peer_secondary->mn); + } + return continue_process; } @@ -880,7 +989,7 @@ bool mka_encode_sak_use(t_MKA_bus bus, uint8_t *packet, uint32_t *length) uint8_t oan = 0U; bool otx = false; bool orx = false; - t_MKA_sak const*const old = mka_get_old(bus, &oan, &otx, &orx); + t_MKA_sak const* old = mka_get_old(bus, &oan, &otx, &orx); t_MKA_sak const*const latest = mka_get_latest(bus, &lan, <x, &lrx); //lint -e{9087, 826} [MISRA 2012 Rule 11.3, required] Pointer cast controlled; packed struct representing network data @@ -894,6 +1003,16 @@ bool mka_encode_sak_use(t_MKA_bus bus, uint8_t *packet, uint32_t *length) t_MKA_pn const exhaustion_threshold = is_cipher_xpn ? MKA_XPN_EXHAUSTION : MKA_PN_EXHAUSTION; bool continue_process = true; + // NOTE: Mechanism to detect CP state RETIRE from KaY is really limited. Maybe the way is comparing + // oki/lki, but these are only read when encoding SAK uses. Therefore I'm going to transition KaY SAK + // from new_sak to current_sak here. This is not ideal, but I used to do it on calls to deleteSa() with invalid + // keys (first time), and that strategy seems even worse. It even has side effects when unauthenticated allowed is immediate. + if ((latest == NULL) && (old == &participant->new_sak)) { + memcpy(&participant->current_sak, &participant->new_sak, sizeof(t_MKA_sak)); + memset(&participant->new_sak, 0, sizeof(t_MKA_sak)); + old = &participant->current_sak; // update pointer after rotating + } + // NOTE: Standard does not clearly states WHEN "sak use" is to be transmitted without MACSEC. // However, it considers the possibility of "sak use" being transmited without MACSEC, just imposes // its body length to be 0. Transmitting it would be redundant, so I'm not transmitting it. diff --git a/test/crypto/wscript b/test/crypto/wscript index 2b5d61a..ef0428f 100644 --- a/test/crypto/wscript +++ b/test/crypto/wscript @@ -31,6 +31,10 @@ def test(ut): # Emit friendly errors. isinstance(ut, Build.BuildContext) or ut.fatal("Project is not configured. Please run 'python waf configure --top=../..'") + # Enable compiler sanitisers + for var in (ut.env.CFLAGS, ut.env.CPPFLAGS, ut.env.LDFLAGS): + var.extend(["-fsanitize=address", "-fsanitize=undefined"]) + # Every call to ut() will create a compilation of a unit test. ut( # Give each test compilation a name. Individual tests can be invoked via "python waf test --targets=test_name" diff --git a/test/daemon/wscript b/test/daemon/wscript index 728ffc1..acbfb41 100644 --- a/test/daemon/wscript +++ b/test/daemon/wscript @@ -31,6 +31,10 @@ def test(ut): # Emit friendly errors. isinstance(ut, Build.BuildContext) or ut.fatal("Project is not configured. Please run 'python waf configure --top=../..'") + # Enable compiler sanitisers + for var in (ut.env.CFLAGS, ut.env.CPPFLAGS, ut.env.LDFLAGS): + var.extend(["-fsanitize=address", "-fsanitize=undefined"]) + # Every call to ut() will create a compilation of a unit test. ut( name = "yaml_import", # --> run me with: "python waf test --targets=yaml_import" diff --git a/test/pae/kay_helpers.h b/test/pae/kay_helpers.h index 422cd38..a35c253 100644 --- a/test/pae/kay_helpers.h +++ b/test/pae/kay_helpers.h @@ -604,6 +604,7 @@ struct KayTestBase : public ::testing::Test { t_mka_kay* ctx; t_mka_participant* participant; t_mka_peer* peer; + t_mka_peer* peer_secondary; t_MKA_global_config test_global_active_config = { .hello_time = 2000U, @@ -709,7 +710,8 @@ struct KayTestBase : public ::testing::Test { KayTestBase(void) : ctx(&mka_kay[0]), participant(&ctx->participant), - peer(&participant->peer) + peer(&participant->peer), + peer_secondary(&participant->peer_secondary) { MKA_active_buses_config = &test_buses_active_config; } diff --git a/test/pae/ut_kay_rx.cpp b/test/pae/ut_kay_rx.cpp index e06aaa3..76b055a 100644 --- a/test/pae/ut_kay_rx.cpp +++ b/test/pae/ut_kay_rx.cpp @@ -803,8 +803,13 @@ TEST_F(RxBasicParmSet, PeerWithDifferentMiDiscarded) memcpy(&peer->sci, &bps.sci_, 8); memcpy(&peer->mi, &bps.mi_, sizeof(bps.mi_)); peer->state = MKA_PEER_POTENTIAL; + bps.mi_[1] ^= 0xFFU; - EXPECT_CALL(mocks, print_action(LoggingMessageContains("same SCI, different MI, but peer slot is occupied."), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Received MKPDU from peer with same SCI, different MI. Learning as secondary until live"), _)) .Times(1); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + bps.mi_[1] ^= 0x55U; + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Received MKPDU with same SCI, different MI, but secondary slot occupied. Discarded."), _)) .Times(1); FeedFrame(/*serialise*/true, /*handle_icv*/true); } diff --git a/test/pae/ut_kay_tx.cpp b/test/pae/ut_kay_tx.cpp index 9e2b333..c7d3d4e 100644 --- a/test/pae/ut_kay_tx.cpp +++ b/test/pae/ut_kay_tx.cpp @@ -407,7 +407,7 @@ TEST_F(TxBasic, MnIncreasesAndICVupdates) // Modify MN to expect the next one, this corresponds to byte 45 (see pcap of test with wireshark) ++frame[45]; - + EXPECT_CALL(mocks, MKA_ComputeICV( /* alg. ag */ MKA_ALGORITHM_AGILITY, /* ICK */ &mka_kay[0].participant.ick, @@ -623,6 +623,138 @@ TEST_F(TxPeerList, NewPeerTransitionsToLive) ASSERT_THAT(lpeers.mn_, Eq(0x10AU)); } +TEST_F(TxPeerList, SecondaryPeerLearntAsPotential) +{ + ctx->role = MKA_ROLE_FORCE_KEY_CLIENT; // Configure kay as key client, easier to test and not relevant + HandleTransmission(); + layersReset(); + SetPresentLayers(M_PPEERS); + + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New potential peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New live peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected principal actor"), _)) .Times(AnyNumber()); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected peer as key server."), _)) .Times(1); + EXPECT_CALL(mocks, MKA_CP_SetElectedSelf(0, false)); + EXPECT_CALL(mocks, MKA_CP_SignalChgdServer(0)); + EXPECT_CALL(mocks, MKA_LOGON_SetKayConnectMode(0, MKA_SECURED)); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + rx_mn = 1U; + bps.mi_[11] = 5U; + SetPresentLayers(M_NONE); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Received MKPDU from peer with same SCI, different MI. Learning as secondary until live"), _)); + // transmission expected due to new potential peer + HandlePreTransmission(/*handle_icv*/true, /*handle_rx*/false); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + HandleTransmission(); + ExpectPresentLayers(M_LPEERS + M_PPEERS); + + ASSERT_THAT(ppeers.mi_, MemoryWith({6, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 5})); + ASSERT_THAT(ppeers.mn_, Eq(1U)); + ASSERT_THAT(lpeers.mi_, MemoryWith({6, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 1})); + ASSERT_THAT(lpeers.mn_, Eq(0x10AU)); +} + +TEST_F(TxPeerList, SecondaryPeerTimeout) +{ + ctx->role = MKA_ROLE_FORCE_KEY_CLIENT; // Configure kay as key client, easier to test and not relevant + HandleTransmission(); + layersReset(); + SetPresentLayers(M_PPEERS); + + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New potential peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New live peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected principal actor"), _)) .Times(AnyNumber()); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected peer as key server."), _)) .Times(1); + EXPECT_CALL(mocks, MKA_CP_SetElectedSelf(0, false)); + EXPECT_CALL(mocks, MKA_CP_SignalChgdServer(0)); + EXPECT_CALL(mocks, MKA_LOGON_SetKayConnectMode(0, MKA_SECURED)); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + uint32_t const old_mn = rx_mn; + rx_mn = 1U; + bps.mi_[11] ^= 5U; + SetPresentLayers(M_NONE); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Received MKPDU from peer with same SCI, different MI. Learning as secondary until live"), _)); + // transmission expected due to new potential peer + HandlePreTransmission(/*handle_icv*/true, /*handle_rx*/false); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + //HandleTransmission(); + ExpectPresentLayers(M_LPEERS + M_PPEERS); + + ASSERT_THAT(ppeers.mi_, MemoryWith({6, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 4})); + ASSERT_THAT(ppeers.mn_, Eq(1U)); + ASSERT_THAT(lpeers.mi_, MemoryWith({6, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 1})); + ASSERT_THAT(lpeers.mn_, Eq(0x10AU)); + + mka_tick_time_ms += 3000U; + + // refresh primary + rx_mn = old_mn; + layersReset(); + /* bps.mi_[11] ^= 5U; */ + bps.key_server_ = true; + lpeers.mn_ = 2U; + SetPresentLayers(M_LPEERS); + HandlePreTransmission(/*handle_icv*/true, /*handle_rx*/false); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + mka_tick_time_ms += 3000U; + + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Secondary peer timed out"), _)); + HandleTransmission(); + ExpectPresentLayers(M_LPEERS); + ASSERT_THAT(lpeers.mi_, MemoryWith({6, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 1})); + ASSERT_THAT(lpeers.mn_, Eq(0x10BU)); +} + +TEST_F(TxPeerList, SecondaryPeerReplacesPrimary) +{ + ctx->role = MKA_ROLE_FORCE_KEY_CLIENT; // Configure kay as key client, easier to test and not relevant + HandleTransmission(); + layersReset(); + SetPresentLayers(M_PPEERS); + + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New potential peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New live peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected principal actor"), _)) .Times(AnyNumber()); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected peer as key server."), _)) .Times(1); + EXPECT_CALL(mocks, MKA_CP_SetElectedSelf(0, false)); + EXPECT_CALL(mocks, MKA_CP_SignalChgdServer(0)); + EXPECT_CALL(mocks, MKA_LOGON_SetKayConnectMode(0, MKA_SECURED)); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + rx_mn = 1U; + bps.mi_[11] = 5U; + SetPresentLayers(M_PPEERS); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Received MKPDU from peer with same SCI, different MI. Learning as secondary until live"), _)); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Secondary peer is live. Replacing primary"), _)); + EXPECT_CALL(mocks, MKA_LOGON_SetKayConnectMode(0, MKA_PENDING)); + EXPECT_CALL(mocks, MKA_CP_SetUsingTransmitSA(0, true)); + EXPECT_CALL(mocks, MKA_CP_SetUsingReceiveSAs(0, true)); + + EXPECT_CALL(mocks, print_action(LoggingMessageContains("New live peer"), _)) .Times(1); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected principal actor"), _)) .Times(AnyNumber()); + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Elected peer as key server."), _)) .Times(1); + EXPECT_CALL(mocks, MKA_CP_SetElectedSelf(0, false)); + EXPECT_CALL(mocks, MKA_CP_SignalChgdServer(0)); + EXPECT_CALL(mocks, MKA_LOGON_SetKayConnectMode(0, MKA_SECURED)); + + EXPECT_CALL(mocks, print_action(LoggingMessageContains("Live peer did not sent peer live list. Presence timers not updated."), _)); + + HandlePreTransmission(/*handle_icv*/true, /*handle_rx*/false); + FeedFrame(/*serialise*/true, /*handle_icv*/true); + + HandleTransmission(); + ExpectPresentLayers(M_LPEERS); + + ASSERT_THAT(lpeers.mi_, MemoryWith({6, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 5})); + ASSERT_THAT(lpeers.mn_, Eq(1U)); +} + + TEST_F(TxDistSak, MetaTest) { } @@ -1288,7 +1420,7 @@ TEST_F(TxWhenClient, InstallSak) /* tx */ true, /* rx */ true )) .WillOnce(Return((void*)0x994212)); - + EXPECT_CALL(mocks, MKA_CP_SetCipherSuite(0, MKA_CS_ID_GCM_AES_128)); EXPECT_CALL(mocks, MKA_CP_SetCipherOffset(0, MKA_CONFIDENTIALITY_OFFSET_0)); EXPECT_CALL(mocks, MKA_CP_SetDistributedKI(0, ObjectMatch(identifier))); diff --git a/test/pae/wscript b/test/pae/wscript index 36b06a0..fc3f404 100644 --- a/test/pae/wscript +++ b/test/pae/wscript @@ -31,6 +31,10 @@ def test(ut): # Emit friendly errors. isinstance(ut, Build.BuildContext) or ut.fatal("Project is not configured. Please run 'python waf configure --top=../..'") + # Enable compiler sanitisers + for var in (ut.env.CFLAGS, ut.env.CPPFLAGS, ut.env.LDFLAGS): + var.extend(["-fsanitize=address", "-fsanitize=undefined"]) + # Every call to ut() will create a compilation of a unit test. ut( name = "cp", # --> run me with: "python waf test --targets=cp" diff --git a/test/utils/wscript b/test/utils/wscript index 7d6c939..4b18485 100644 --- a/test/utils/wscript +++ b/test/utils/wscript @@ -31,6 +31,10 @@ def test(ut): # Emit friendly errors. isinstance(ut, Build.BuildContext) or ut.fatal("Project is not configured. Please run 'python waf configure --top=../..'") + # Enable compiler sanitisers + for var in (ut.env.CFLAGS, ut.env.CPPFLAGS, ut.env.LDFLAGS): + var.extend(["-fsanitize=address", "-fsanitize=undefined"]) + # Every call to ut() will create a compilation of a unit test. ut( name = "logging_debug", # --> run me with: "python waf test --targets=logging_debug" diff --git a/wscript b/wscript index e1c1755..126e9df 100644 --- a/wscript +++ b/wscript @@ -180,7 +180,8 @@ def configure(conf): # Debug compilation if os.environ.get('DEBUG', False): - conf.env.CFLAGS += ["-Og", "-ggdb"] + conf.env.CFLAGS += ["-Og", "-ggdb", "-fsanitize=address", "-fsanitize=undefined"] + conf.env.LINKFLAGS += ["-fsanitize=address", "-fsanitize=undefined"] conf.env.DEFINES_STANDALONE += [ "MKA_STANDALONE_COMPILATION",