diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt index bf47e478b9..0f4445c353 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(fgs_5gmm_lib OBJECT FGSIdentityResponse.c FGSAuthenticationResponse.c FGSNASSecurityModeComplete.c + FGSNASSecurityModeReject.c RegistrationComplete.c FGSUplinkNasTransport.c FGSDeregistrationRequestUEOriginating.c diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/FGSNASSecurityModeReject.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/FGSNASSecurityModeReject.c new file mode 100644 index 0000000000..3f61b14ad7 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/FGSNASSecurityModeReject.c @@ -0,0 +1,50 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include "FGSNASSecurityModeReject.h" +#include "common/utils/ds/byte_array.h" +#include "fgmm_lib.h" + +int encode_fgs_security_mode_reject(byte_array_t *buffer, const fgs_security_mode_reject_msg *msg) +{ + if (buffer->len < 1) + return -1; // nothing to encode + + // 5GMM cause (Mandatory) + return encode_fgs_nas_cause(buffer, &msg->cause); +} + +/** @brief Decode Security Mode Reject (8.2.18 of 3GPP TS 24.501) */ +int decode_fgs_security_mode_reject(fgs_security_mode_reject_msg *msg, const byte_array_t *buffer) +{ + if (buffer->len < 1) + return -1; // nothing to decode + + // 5GMM cause (Mandatory) + return decode_fgs_nas_cause(&msg->cause, buffer); +} + +bool eq_sec_mode_reject(const fgs_security_mode_reject_msg *a, const fgs_security_mode_reject_msg *b) +{ + _NAS_EQ_CHECK_INT(a->cause, b->cause); + return true; +} diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/FGSNASSecurityModeReject.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/FGSNASSecurityModeReject.h new file mode 100644 index 0000000000..28c10e3700 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/FGSNASSecurityModeReject.h @@ -0,0 +1,47 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef FGS_NAS_security_mode_reject_H_ +#define FGS_NAS_security_mode_reject_H_ + +#include +#include "fgmm_lib.h" + +/* + * Message name: security mode reject + * Description: The SECURITY MODE REJECT message is sent by the UE to the AMF to + * indicate that the corresponding security mode command has been rejected. + * See 3GPP TS 24.501 table 8.2.27.1.1 + * + * Significance: dual + * Direction: UE to AMF + */ + +typedef struct { + // 5GMM cause (Mandatory) + cause_id_t cause; +} fgs_security_mode_reject_msg; + +int encode_fgs_security_mode_reject(byte_array_t *buffer, const fgs_security_mode_reject_msg *msg); +int decode_fgs_security_mode_reject(fgs_security_mode_reject_msg *msg, const byte_array_t *buffer); +bool eq_sec_mode_reject(const fgs_security_mode_reject_msg *a, const fgs_security_mode_reject_msg *b); + +#endif /* ! defined(FGS_NAS_security_mode_reject_H_) */ diff --git a/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h b/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h index ab94925f09..2c8ecdf150 100644 --- a/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h +++ b/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h @@ -31,6 +31,7 @@ #include "FGSIdentityResponse.h" #include "FGSMobileIdentity.h" #include "FGSNASSecurityModeComplete.h" +#include "FGSNASSecurityModeReject.h" #include "FGSUplinkNasTransport.h" #include "RegistrationComplete.h" #include "RegistrationRequest.h" @@ -226,6 +227,7 @@ typedef struct { fgmm_auth_failure_t fgmm_auth_failure; fgs_deregistration_request_ue_originating_msg fgs_deregistration_request_ue_originating; fgs_security_mode_complete_msg fgs_security_mode_complete; + fgs_security_mode_reject_msg fgs_security_mode_reject; registration_complete_msg registration_complete; fgs_uplink_nas_transport_msg uplink_nas_transport; } mm_msg; /* 5GS Mobility Management messages */ diff --git a/openair3/NAS/NR_UE/5GS/fgs_nas_lib.c b/openair3/NAS/NR_UE/5GS/fgs_nas_lib.c index ca40131af6..3fcd82aa9b 100644 --- a/openair3/NAS/NR_UE/5GS/fgs_nas_lib.c +++ b/openair3/NAS/NR_UE/5GS/fgs_nas_lib.c @@ -81,6 +81,11 @@ int mm_msg_encode(const fgmm_nas_message_plain_t *p, uint8_t *buffer, uint32_t l case FGS_SECURITY_MODE_COMPLETE: enc_msg = encode_fgs_security_mode_complete(&p->mm_msg.fgs_security_mode_complete, buffer, len); break; + case FGS_SECURITY_MODE_REJECT: { + byte_array_t ba = {.buf = buffer, .len = len}; + enc_msg = encode_fgs_security_mode_reject(&ba, &p->mm_msg.fgs_security_mode_reject); + break; + } case FGS_UPLINK_NAS_TRANSPORT: enc_msg = encode_fgs_uplink_nas_transport(&p->mm_msg.uplink_nas_transport, buffer, len); break; diff --git a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c index 88de4f13aa..f733bdd3a5 100644 --- a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c +++ b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c @@ -8,6 +8,7 @@ #include "fgmm_service_accept.h" #include "fgmm_service_reject.h" #include "fgmm_authentication_failure.h" +#include "FGSNASSecurityModeReject.h" #include "nr_nas_msg.h" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) @@ -266,6 +267,37 @@ static void test_auth_failure(void) free_fgmm_auth_failure(&dec); } +/** @brief Test NAS Security Mode Reject enc/dec */ +static void test_security_mode_reject(void) +{ + // Dummy NAS Security Mode Reject message + const fgs_security_mode_reject_msg orig = { + .cause = Illegal_UE, + }; + + // Expected encoded data + uint8_t expected_enc[] = {0x03}; + + // Buffer + uint8_t buf[8] = {0}; + byte_array_t buffer = {.buf = buf, .len = sizeof(buf)}; + + // Encode + int encoded_length = encode_fgs_security_mode_reject(&buffer, &orig); + AssertFatal(encoded_length == sizeofArray(expected_enc), "encode_fgs_security_mode_reject() failed\n"); + + // Compare the raw encoded buffer with expected encoded data + AssertFatal(memcmp(buffer.buf, expected_enc, encoded_length) == 0, "Encoding mismatch!\n"); + + // Decode + fgs_security_mode_reject_msg dec = {0}; + int decoded_length = decode_fgs_security_mode_reject(&dec, &buffer); + AssertFatal(decoded_length >= 0, "decode_fgs_security_mode_reject() failed\n"); + + // Compare original and decoded messages + AssertFatal(eq_sec_mode_reject(&orig, &dec), "test_sec_mode_reject() failed: original and decoded messages do not match\n"); +} + int main() { test_regression_registration_accept(); @@ -273,5 +305,6 @@ int main() test_service_accept(); test_service_reject(); test_auth_failure(); + test_security_mode_reject(); return 0; } diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 4c5d6fb717..5709d482c6 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -152,6 +152,153 @@ static bool unprotected_allowed(byte_array_t buffer, fgs_nas_msg_t msg_type) } } +/** + * @brief Get the MAC of a Security Protected NAS message + * @param[in] pdu_buffer The buffer containing the NAS message + * @param[in] pdu_length The length of the NAS message + * @param[out] mac The MAC of the NAS message + * @return true if the MAC was successfully extracted, false otherwise + */ +bool nas_security_get_mac(uint8_t *pdu_buffer, int pdu_length, uint8_t *mac) +{ + /* Check for Security Protected Header */ + /* at least 8 bytes for Security Protected MM Message */ + if (pdu_length < 8) + return false; + + /* Only message type, that is not protected (c.f. + TS 24.501 9.3 Security Header Type) */ + if ((Security_header_t)pdu_buffer[1] == PLAIN_5GS_MSG) + return false; + + /* Get the MAC [EPD][SecHdr][MAC0]..[MAC3] - success */ + for (int i = 0; i < 4; i++) + mac[i] = pdu_buffer[2 + i]; + return true; +} + +/* + * @brief Get the Security Header of a NAS message + * @param[in] msg The buffer containing the NAS message + * @param[in] msg_length The length of the NAS message + * @param[out] sec_hdr The Security Header of the NAS message + * @return true if the Security Header was successfully extracted, false otherwise + */ +bool nas_security_get_sec_hdr(uint8_t *msg, int msg_length, Security_header_t *sec_hdr) +{ + /* Shortest message is [EPD][SecHdrType][Payl]*/ + if (msg_length < 3) { + LOG_E(NAS, "Invalid NAS message length %d\n", msg_length); + return false; + } + + /* Get the SecHdr and check for validity */ + uint8_t sec_hdr_type = msg[1] & 0x0f; + if (sec_hdr_type > INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX) { + LOG_E(NAS, "Invalid Security Header Type %d\n", sec_hdr_type); + return false; + } + + /* Check ok. */ + *sec_hdr = (Security_header_t)sec_hdr_type; + return true; +} + +/* + * @brief Compute the MAC of a NAS message + * @param[in] nas The NAS context + * @param[in] pdu_buffer The buffer containing the NAS message + * @param[in] is_uplink True if the message is uplink, false Downlink + * @param[in] is3gpp_access True if the message is 3GPP access, false otherwise + * @param[in] pdu_length The length of the NAS message + * @param[out] mac The MAC of the NAS message + */ +static void nas_security_compute_mac(nr_ue_nas_t *nas, + byte_array_t buffer, + const bool is_uplink, + const bool is3gpp_access, + uint8_t *mac) +{ + uint8_t *buf = buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH - 1; + int len = buffer.len - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH + 1; + + /* Compute the MAC */ + nas_stream_cipher_t stream_cipher; + stream_cipher.context = nas->security_container->integrity_context; + + /* Select the right count in the context */ + if (is_uplink) + stream_cipher.count = nas->security.nas_count_ul; + else + stream_cipher.count = nas->security.nas_count_dl; + + /* 3GPP access is 1, non-3GPP access is 2 - see 3GPP TS 33.501 6.4.2.2 */ + stream_cipher.bearer = is3gpp_access ? 1 : 2; + + /* Configure Direction for MAC protection */ + stream_cipher.direction = is_uplink ? 0 : 1; + + /* Possibly encrypted message */ + stream_cipher.message = buf; + + /* length in bits */ + stream_cipher.blength = len << 3; + + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); +} + +/* + * @brief: Decrypt the payload of a NAS message. The buffer is modified in place + * @param[in] nas The NAS context + * @param[in] pdu_buffer The buffer containing the full (header + payload) NAS message + * @param[in] is_uplink True if the message is uplink, false Downlink + * @param[in] is3gpp_access True if the message is 3GPP access, false otherwise + * @param[in] pdu_length The length of the NAS message + */ +static void nas_security_decrypt_payload(nr_ue_nas_t *nas, byte_array_t buffer, const bool is_uplink, const bool is3gpp_access) +{ + Security_header_t sec_hdr; + + if (!nas_security_get_sec_hdr(buffer.buf, buffer.len, &sec_hdr)) { + LOG_E(NAS, "Failed to get Security Header\n"); + return; + } + + /* Nothing to do for unencrypted msgs */ + if (sec_hdr == PLAIN_5GS_MSG || sec_hdr == INTEGRITY_PROTECTED) { + return; + } + + /* Get integrity keys, and algorithms */ + nas_stream_cipher_t stream_cipher; + stream_cipher.context = nas->security_container->ciphering_context; + + /* Use the estimated count the right count in the context */ + stream_cipher.count = nas->security.nas_count_dl; + /* 3GPP access is 1, non-3GPP access is 2 - see 3GPP TS 33.501 6.4.2.2 */ + stream_cipher.bearer = is3gpp_access ? 1 : 2; + + /* Decryption only in downlink direction */ + stream_cipher.direction = 1; + + /* [EPD][SHR][MAC0]..[MAC3][PDU...]*/ + uint8_t *plain_payload = buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; + stream_cipher.message = plain_payload; + + /* length in bits */ + int plain_length = buffer.len - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; + stream_cipher.blength = plain_length << 3; + + /* Allocate output buffer for body only */ + uint8_t *decrypted = malloc_or_fail(plain_length); + + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, decrypted); + + /* Override and free the decrypted payload */ + memcpy(plain_payload, decrypted, plain_length); + free(decrypted); +} + static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t buffer, fgs_nas_msg_t msg_type) { if (nas->security_container == NULL) @@ -210,35 +357,22 @@ static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t b } /* check integrity */ - uint8_t computed_mac[NAS_INTEGRITY_SIZE]; - nas_stream_cipher_t stream_cipher; - stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.nas_count_dl; - stream_cipher.bearer = 1; /* todo: don't hardcode */ - stream_cipher.direction = 1; - stream_cipher.message = buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH - 1; - /* length in bits */ - stream_cipher.blength = (buffer.len - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH + 1) << 3; - stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, computed_mac); - + /* [EPD][SHD][MAC0]...[MAC3][SEQNO][PAYL]*/ uint8_t *received_mac = buffer.buf + 2; + uint8_t computed_mac[NAS_INTEGRITY_SIZE]; + nas_security_compute_mac(nas, + buffer, + false, + true, + computed_mac); if (memcmp(received_mac, computed_mac, NAS_INTEGRITY_SIZE) != 0) return NAS_SECURITY_INTEGRITY_FAILED; /* decipher */ - uint8_t payload_len = buffer.len - SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; - uint8_t buf[payload_len]; - stream_cipher.context = nas->security_container->ciphering_context; - stream_cipher.count = nas->security.nas_count_dl; - stream_cipher.bearer = 1; /* todo: don't hardcode */ - stream_cipher.direction = 1; - stream_cipher.message = buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; - /* length in bits */ - stream_cipher.blength = (payload_len) << 3; - stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); - memcpy(buffer.buf + SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH, buf, payload_len); + nas_security_decrypt_payload(nas, buffer, false, true); + /* update estimated DL Counter */ nas->security.nas_count_dl++; return NAS_SECURITY_INTEGRITY_PASSED; @@ -1008,10 +1142,103 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia } } +/** @brief Generates the Security Mode Reject message to be sent by the UE to the AMF + * to indicate that the corresponding security mode command has been rejected */ +static void generateSecurityModeReject(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, cause_id_t cause) +{ + LOG_I(NAS, "Send Security Mode Reject\n"); + fgmm_msg_header_t plain_header = set_mm_header(FGS_SECURITY_MODE_REJECT, PLAIN_5GS_MSG); + int size = sizeof(plain_header); + fgs_security_mode_reject_msg msg = {.cause = cause}; + size += 1; + + /** The UE shall apply the 5G NAS security context in use + * before the initiation of the security mode control procedure, + * if any, to protect the SECURITY MODE REJECT message */ + bool has_security_context = nas->security_container && nas->security_container->integrity_context; + if (has_security_context) { + fgmm_nas_msg_security_protected_t sp = {0}; + sp.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; + sp.header.security_header_type = INTEGRITY_PROTECTED; + sp.header.sequence_number = nas->security.nas_count_ul & 0xff; + size += sizeof(sp.header); + sp.plain.header = plain_header; + sp.plain.mm_msg.fgs_security_mode_reject = msg; + // Security protected header encoding + int security_header_len = nas_protected_security_header_encode(initialNasMsg->nas_data, &sp.header, size); + initialNasMsg->length = + security_header_len + mm_msg_encode(&sp.plain, initialNasMsg->nas_data + security_header_len, size - security_header_len); + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + nas_stream_cipher_t stream_cipher; + stream_cipher.context = nas->security_container->ciphering_context; + AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n"); + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->nas_data + 7); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(stream_cipher.message, buf, initialNasMsg->length - 7); + /* integrity protection */ + uint8_t mac[4]; + stream_cipher.context = nas->security_container->integrity_context; + stream_cipher.count = nas->security.nas_count_ul++; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->nas_data + 6); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 6) << 3; + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); + LOG_D(NAS, "Integrity protected initial NAS message: mac = %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); + for (int i = 0; i < 4; i++) + initialNasMsg->nas_data[2 + i] = mac[i]; + } else { + fgmm_nas_message_plain_t plain = {0}; + plain.header = plain_header; + plain.mm_msg.fgs_security_mode_reject = msg; + // encode the message + initialNasMsg->nas_data = malloc_or_fail(size); + initialNasMsg->length = mm_msg_encode(&plain, initialNasMsg->nas_data, size); + } +} + static void handle_security_mode_command(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *pdu, int pdu_length) { + /* Handle security mode command: must be authenticated, especially if no security + context has been previously established. */ + uint8_t recv_mac[4]; + Security_header_t sec_hdr; + + /* Must have valid security header*/ + if(!nas_security_get_sec_hdr(pdu, pdu_length, &sec_hdr)) { + LOG_E(NAS, "Received Security Mode Command without integrity protection.\n"); + generateSecurityModeReject(nas, initialNasMsg, Security_mode_rejected_unspecified); + return; + } + + /* Must be integrity protected with new context (=3), see 3GPP TS 24.501 5.4.2.2 */ + if(sec_hdr != INTEGRITY_PROTECTED_WITH_NEW_SECU_CTX) { + LOG_E(NAS, "Received Security Mode Command with invalid security header type %d.\n", sec_hdr); + generateSecurityModeReject(nas, initialNasMsg, Security_mode_rejected_unspecified); + return; + } + + /* Must have a MAC - checked after deriving keys */ + if(!nas_security_get_mac(pdu, pdu_length, recv_mac)) { + LOG_E(NAS, "Received Security Mode Command with invalid MAC.\n"); + generateSecurityModeReject(nas, initialNasMsg, Security_mode_rejected_unspecified); + return; + } + /* retrieve integrity and ciphering algorithms */ - AssertFatal(pdu_length > 10, "nas: bad pdu\n"); + if (pdu_length < 10) { + LOG_E(NAS, "Invalid pdu_length=%d : send Security Mode Reject\n", pdu_length); + // 3GPP TS 24.501 5.4.2.5 NAS security mode command not accepted by the UE + generateSecurityModeReject(nas, initialNasMsg, Security_mode_rejected_unspecified); + } + int ciphering_algorithm = (pdu[10] >> 4) & 0x0f; int integrity_algorithm = pdu[10] & 0x0f; @@ -1035,8 +1262,38 @@ static void handle_security_mode_command(nr_ue_nas_t *nas, as_nas_info_t *initia } printf("\n"); - /* todo: stream_security_container_delete() is not called anywhere, deal with that */ - nas->security_container = stream_security_container_init(ciphering_algorithm, integrity_algorithm, knas_enc, knas_int); + if (integrity_algorithm != EIA0_ALG_ID) { + nas->security_container = stream_security_container_init(ciphering_algorithm, integrity_algorithm, knas_enc, knas_int); + } else { + LOG_E(NAS, "Rejecting Invalid NULL integrity %d for 5G!\n", + integrity_algorithm); + nas->security_container = NULL; + } + + /* Handle the invalid container with a reject message */ + if(nas->security_container == NULL) { + LOG_W(NAS, "Could not create security container!\n"); + generateSecurityModeReject(nas, initialNasMsg, Security_mode_rejected_unspecified); + return; + } + + /* Check MAC and delete context if it does not match */ + uint8_t computed_mac[NAS_INTEGRITY_SIZE]; + byte_array_t ba = {.buf = pdu, .len = pdu_length}; + nas_security_compute_mac(nas, ba, false, true, computed_mac); + + /* Teardown security container if mismatch. */ + if(memcmp(computed_mac, recv_mac, NAS_INTEGRITY_SIZE) != 0) { + LOG_W(NAS, "MAC does not match\n"); + LOG_W(NAS, "Expected: %x %x %x %x\n", computed_mac[0], computed_mac[1], computed_mac[2], computed_mac[3]); + LOG_W(NAS, "Received: %x %x %x %x\n", recv_mac[0], recv_mac[1], recv_mac[2], recv_mac[3]); + stream_security_container_delete(nas->security_container); + nas->security_container = NULL; + + /* Signal rejection */ + generateSecurityModeReject(nas, initialNasMsg, Security_mode_rejected_unspecified); + return; + } nas_itti_kgnb_refresh_req(nas->UE_id, nas->security.kgnb); generateSecurityModeComplete(nas, initialNasMsg);