From 2e7b926827ad199c264b9d6e771de757cb4f2937 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 May 2024 13:04:45 +0200 Subject: [PATCH 01/15] nrUE NAS: remove mm_counter and sm_counter, replace with nas_count Specs (24.501, 33.501) don't seem to mention mm_counter and sm_counter, simply nas_count, let's use this. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 8 ++++---- openair3/NAS/NR_UE/nr_nas_msg_sim.h | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 97f9ca1aef..544abf4676 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -603,7 +603,7 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.mm_counter++; + stream_cipher.count = nas->security.nas_count++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); @@ -703,7 +703,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia initialNasMsg->length = length; stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.mm_counter++; + stream_cipher.count = nas->security.nas_count++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); @@ -765,7 +765,7 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi nas_stream_cipher_t stream_cipher = { .context = nas->security_container->integrity_context, - .count = nas->security.mm_counter++, + .count = nas->security.nas_count++, .bearer = 1, .direction = 0, .message = (unsigned char *)(initialNasMsg->data + 6), @@ -855,7 +855,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.mm_counter++; + stream_cipher.count = nas->security.nas_count++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg_sim.h index 0d2d8f5cc9..dbb18e1425 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.h @@ -92,8 +92,7 @@ typedef struct { uint8_t res[16]; uint8_t rand[16]; uint8_t kgnb[32]; - uint32_t mm_counter; - uint32_t sm_counter; + uint32_t nas_count; } ue_sa_security_key_t; typedef struct { From b615162630d1625ae83f06c3c296f941bd93b41a Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 May 2024 16:35:00 +0200 Subject: [PATCH 02/15] nrUE NAS: there are actually two NAS COUNT, one for DL and one for UL --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 8 ++++---- openair3/NAS/NR_UE/nr_nas_msg_sim.h | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 544abf4676..a63d448f1f 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -603,7 +603,7 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.nas_count++; + stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); @@ -703,7 +703,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia initialNasMsg->length = length; stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.nas_count++; + stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); @@ -765,7 +765,7 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi nas_stream_cipher_t stream_cipher = { .context = nas->security_container->integrity_context, - .count = nas->security.nas_count++, + .count = nas->security.nas_count_ul++, .bearer = 1, .direction = 0, .message = (unsigned char *)(initialNasMsg->data + 6), @@ -855,7 +855,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.nas_count++; + stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg_sim.h index dbb18e1425..8bc8a724c5 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.h @@ -92,7 +92,8 @@ typedef struct { uint8_t res[16]; uint8_t rand[16]; uint8_t kgnb[32]; - uint32_t nas_count; + uint32_t nas_count_ul; + uint32_t nas_count_dl; } ue_sa_security_key_t; typedef struct { From 41037d57b5d78b962bd2a2a6053fdc3b5df3d066 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 May 2024 18:15:19 +0200 Subject: [PATCH 03/15] nrUE NAS: handle SecurityModeCommand Get the security algorithms from SecurityModeCommand. Restructure the code a bit: add the function handle_security_mode_command() and adapt the code to use it. Derive also knas_enc (not used yet). Do the derivation for knas_enc and knas_int only after reception of SecurityModeCommand. We need the algorithms configured by core network to derive those keys. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 62 +++++++++++++++++++---------- openair3/NAS/NR_UE/nr_nas_msg_sim.h | 1 + 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index a63d448f1f..99b7f9c51d 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -303,7 +303,8 @@ void derive_kamf(uint8_t *kseaf, uint8_t *kamf, uint16_t abba, uicc_t* uicc) { } //------------------------------------------------------------------------------ -void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t kamf[32], uint8_t *knas_int) { +void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t kamf[32], uint8_t *knas) +{ uint8_t S[20] = {0}; uint8_t out[32] = { 0 }; S[0] = 0x69; //FC @@ -317,7 +318,7 @@ void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t byte_array_t data = {.buf = S, .len = 7}; kdf(kamf, data, 32, out); - memcpy(knas_int, out+16, 16); + memcpy(knas, out+16, 16); } void derive_kgnb(uint8_t kamf[32], uint32_t count, uint8_t *kgnb){ @@ -362,7 +363,6 @@ void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) { uint8_t *kausf = nas->security.kausf; uint8_t *kseaf = nas->security.kseaf; uint8_t *kamf = nas->security.kamf; - uint8_t *knas_int = nas->security.knas_int; uint8_t *output = nas->security.res; uint8_t *rand = nas->security.rand; uint8_t *kgnb = nas->security.kgnb; @@ -385,7 +385,6 @@ void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) { derive_kausf(ck, ik, sqn, kausf, nas->uicc); derive_kseaf(kausf, kseaf, nas->uicc); derive_kamf(kseaf, kamf, 0x0000, nas->uicc); - derive_knas(0x02, 2, kamf, knas_int); derive_kgnb(kamf,0,kgnb); printf("kausf:"); @@ -406,12 +405,6 @@ void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) { printf("%x ", kamf[i]); } printf("\n"); - - printf("knas_int:\n"); - for(int i = 0; i < 16; i++){ - printf("%x ", knas_int[i]); - } - printf("\n"); } nr_ue_nas_t *get_ue_nas_info(module_id_t module_id) @@ -515,10 +508,6 @@ void generateIdentityResponse(as_nas_info_t *initialNasMsg, uint8_t identitytype static void generateAuthenticationResp(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *buf) { derive_ue_keys(buf, nas); - /* todo: as of now, nia2 is hardcoded in derive_ue_keys(), remove this hardcoding, use NAS signalling for getting proper algorithm */ - /* todo: deal with ciphering for this stream_security_container_init() (handle ciphering in general) */ - /* todo: stream_security_container_delete() is not called anywhere, deal with that */ - nas->security_container = stream_security_container_init(0, 2 /* hardcoded: nia2 */, NULL, nas->security.knas_int); OctetString res; res.length = 16; res.value = calloc(1,16); @@ -610,8 +599,7 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia /* length in bits */ stream_cipher.blength = (initialNasMsg->length - 6) << 3; - // only for Type of integrity protection algorithm: 128-5G-IA2 (2) - stream_compute_integrity(EIA2_128_ALG_ID, &stream_cipher, mac); + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); for(int i = 0; i < 4; i++){ @@ -619,6 +607,40 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia } } +static void handle_security_mode_command(instance_t instance, nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *pdu, int pdu_length) +{ + /* retrieve integrity and ciphering algorithms */ + AssertFatal(pdu_length > 10, "nas: bad pdu\n"); + int ciphering_algorithm = (pdu[10] >> 4) & 0x0f; + int integrity_algorithm = pdu[10] & 0x0f; + + uint8_t *kamf = nas->security.kamf; + uint8_t *knas_enc = nas->security.knas_enc; + uint8_t *knas_int = nas->security.knas_int; + + /* derive keys */ + derive_knas(0x01, ciphering_algorithm, kamf, knas_enc); + derive_knas(0x02, integrity_algorithm, kamf, knas_int); + + printf("knas_int: "); + for(int i = 0; i < 16; i++){ + printf("%x ", knas_int[i]); + } + printf("\n"); + + printf("knas_enc: "); + for(int i = 0; i < 16; i++){ + printf("%x ", knas_enc[i]); + } + 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); + + nas_itti_kgnb_refresh_req(instance, nas->security.kgnb); + generateSecurityModeComplete(nas, initialNasMsg); +} + static void decodeRegistrationAccept(const uint8_t *buf, int len, nr_ue_nas_t *nas) { registration_accept_msg reg_acc = {0}; @@ -1203,7 +1225,8 @@ void *nas_nrue(void *args_p) as_nas_info_t initialNasMsg = {0}; uint8_t *pdu_buffer = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data; - int msg_type = get_msg_type(pdu_buffer, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length); + int pdu_length = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length; + int msg_type = get_msg_type(pdu_buffer, pdu_length); nr_ue_nas_t *nas = get_ue_nas_info(0); switch (msg_type) { @@ -1214,14 +1237,13 @@ void *nas_nrue(void *args_p) generateAuthenticationResp(nas, &initialNasMsg, pdu_buffer); break; case FGS_SECURITY_MODE_COMMAND: - nas_itti_kgnb_refresh_req(instance, nas->security.kgnb); - generateSecurityModeComplete(nas, &initialNasMsg); + handle_security_mode_command(instance, nas, &initialNasMsg, pdu_buffer, pdu_length); break; case FGS_DOWNLINK_NAS_TRANSPORT: decodeDownlinkNASTransport(&initialNasMsg, pdu_buffer); break; case REGISTRATION_ACCEPT: - handle_registration_accept(instance, nas, pdu_buffer, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length); + handle_registration_accept(instance, nas, pdu_buffer, pdu_length); break; case FGS_DEREGISTRATION_ACCEPT: LOG_I(NAS, "received deregistration accept\n"); diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg_sim.h index 8bc8a724c5..d6f7430bdd 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.h @@ -89,6 +89,7 @@ typedef struct { uint8_t kseaf[32]; uint8_t kamf[32]; uint8_t knas_int[16]; + uint8_t knas_enc[16]; uint8_t res[16]; uint8_t rand[16]; uint8_t kgnb[32]; From ce771a17dcea65caa465e1b8d6e323f7271b6855 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 May 2024 18:26:31 +0200 Subject: [PATCH 04/15] nrUE NAS: adapt code to use configured integrity algorithm --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 99b7f9c51d..952b886a3d 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -733,7 +733,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia stream_cipher.blength = (initialNasMsg->length - 6) << 3; // only for Type of integrity protection algorithm: 128-5G-IA2 (2) - stream_compute_integrity(EIA2_128_ALG_ID, &stream_cipher, mac); + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); for(int i = 0; i < 4; i++){ @@ -885,7 +885,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * stream_cipher.blength = (initialNasMsg->length - 6) << 3; // only for Type of integrity protection algorithm: 128-5G-IA2 (2) - stream_compute_integrity(EIA2_128_ALG_ID, &stream_cipher, mac); + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); for(int i = 0; i < 4; i++){ From 6c42ea62935ae641a98e7c74527afe048cf8e0dd Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 May 2024 18:34:32 +0200 Subject: [PATCH 05/15] nrUE NAS: don't hardcode sequence number, get value from NAS COUNT UL as per standard (see 33.501 6.4.3.1, see 24.501 4.4.3) --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 952b886a3d..42caa30075 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -680,7 +680,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia sp_msg->header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; sp_msg->header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED; sp_msg->header.message_authentication_code = 0; - sp_msg->header.sequence_number = 1; + sp_msg->header.sequence_number = nas->security.nas_count_ul & 0xff; length = 7; sp_msg->plain.mm_msg.registration_complete.protocoldiscriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; length += 1; @@ -762,7 +762,7 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi sp_msg->header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; sp_msg->header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED; sp_msg->header.message_authentication_code = 0; - sp_msg->header.sequence_number = 3; + sp_msg->header.sequence_number = nas->security.nas_count_ul & 0xff; int size = sizeof(fgs_nas_message_security_header_t); fgs_deregistration_request_ue_originating_msg *dereg_req = &sp_msg->plain.mm_msg.fgs_deregistration_request_ue_originating; @@ -824,7 +824,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * uint8_t mac[4]; nas_msg.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; nas_msg.header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED; - nas_msg.header.sequence_number = 2; + nas_msg.header.sequence_number = nas->security.nas_count_ul & 0xff; size += 7; From 4cc1bb5433a8f51557b1601c584055c78cc1e780 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 23 May 2024 16:51:58 +0200 Subject: [PATCH 06/15] nrUE NAS: hotfix for get_msg_type() The version before this commit is wrong, taking wrong byte for msg_type in some cases. I don't have much time to implement a proper NAS PDU parser, so let's go with hardcoded values. Some later work will be done to have a cleaner solution. As of now, it's too much work. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 49 ++++++++++++++++++----------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 42caa30075..99ea2aef85 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -894,30 +894,41 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * } -uint8_t get_msg_type(uint8_t *pdu_buffer, uint32_t length) { - uint8_t msg_type = 0; - uint8_t offset = 0; +static uint8_t get_msg_type(uint8_t *pdu_buffer, uint32_t length) +{ + if (pdu_buffer == NULL) + goto error; - if ((pdu_buffer != NULL) && (length > 0)) { - if (((nas_msg_header_t *)(pdu_buffer))->choice.security_protected_nas_msg_header_t.security_header_type > 0) { - offset += SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; - if (offset < length) { - msg_type = ((mm_msg_header_t *)(pdu_buffer + offset))->message_type; + /* get security header type */ + if (length < 2) + goto error; - if (msg_type == FGS_DOWNLINK_NAS_TRANSPORT) { - dl_nas_transport_t tmp; - memcpy(&tmp, pdu_buffer + offset, sizeof(tmp)); - msg_type = tmp.sm_nas_msg_header.message_type; - } - } - } else { // plain 5GS NAS message - msg_type = ((nas_msg_header_t *)(pdu_buffer))->choice.plain_nas_msg_header.message_type; - } - } else { - LOG_I(NAS, "[UE] Received invalid downlink message\n"); + int security_header_type = pdu_buffer[1]; + + if (security_header_type == 0) { + /* plain NAS message */ + if (length < 3) + goto error; + return pdu_buffer[2]; + } + + if (length < 10) + goto error; + + int msg_type = pdu_buffer[9]; + + if (msg_type == FGS_DOWNLINK_NAS_TRANSPORT) { + if (length < 17) + goto error; + + msg_type = pdu_buffer[16]; } return msg_type; + +error: + LOG_E(NAS, "[UE] Received invalid downlink message\n"); + return 0; } static void send_nas_uplink_data_req(instance_t instance, const as_nas_info_t *initial_nas_msg) From 075f8330f6efc132bf90652a576b156d6e336993 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 10:58:03 +0200 Subject: [PATCH 07/15] nrUE NAS: implement downlink security processing Maybe not totally ok (code has some comments). To be refined if needed. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 80 ++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 99ea2aef85..4a87729188 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -60,6 +60,76 @@ extern uint16_t NB_UE_INST; static nr_ue_nas_t nr_ue_nas = {0}; static nr_nas_msg_snssai_t nas_allowed_nssai[8]; +typedef enum { + NAS_SECURITY_NO_SECURITY_CONTEXT, + NAS_SECURITY_INTEGRITY_FAILED, + NAS_SECURITY_INTEGRITY_PASSED, + NAS_SECURITY_BAD_INPUT +} security_state_t; + +security_state_t nas_security_rx_process(nr_ue_nas_t *nas, uint8_t *pdu_buffer, int pdu_length) +{ + if (nas->security_container == NULL) + return NAS_SECURITY_NO_SECURITY_CONTEXT; + /* header is 7 bytes, require at least one byte of payload */ + if (pdu_length < 8) + return NAS_SECURITY_BAD_INPUT; + /* only accept "integrity protected and ciphered" messages */ + if (pdu_buffer[1] != 2) { + LOG_E(NAS, "todo: unhandled security type %d\n", pdu_buffer[2]); + return NAS_SECURITY_BAD_INPUT; + } + + /* synchronize NAS SQN, based on 24.501 4.4.3.1 */ + int nas_sqn = pdu_buffer[6]; + int target_sqn = nas->security.nas_count_dl & 0xff; + if (nas_sqn != target_sqn) { + if (nas_sqn < target_sqn) + nas->security.nas_count_dl += 256; + nas->security.nas_count_dl &= ~255; + nas->security.nas_count_dl |= nas_sqn; + } + if (nas->security.nas_count_dl > 0x00ffffff) { + /* it's doubtful that this will happen, so let's simply exit for the time being */ + /* to be refined if needed */ + LOG_E(NAS, "max NAS COUNT DL reached\n"); + exit(1); + } + + /* check integrity */ + uint8_t computed_mac[4]; + 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 = pdu_buffer + 6; + /* length in bits */ + stream_cipher.blength = (pdu_length - 6) << 3; + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, computed_mac); + + uint8_t *received_mac = pdu_buffer + 2; + + if (memcmp(received_mac, computed_mac, 4) != 0) + return NAS_SECURITY_INTEGRITY_FAILED; + + /* decipher */ + uint8_t buf[pdu_length - 7]; + 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 = pdu_buffer + 7; + /* length in bits */ + stream_cipher.blength = (pdu_length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(pdu_buffer + 7, buf, pdu_length - 7); + + nas->security.nas_count_dl++; + + return NAS_SECURITY_INTEGRITY_PASSED; +} + static int nas_protected_security_header_encode( char *buffer, const fgs_nas_message_security_header_t *header, @@ -1234,11 +1304,19 @@ void *nas_nrue(void *args_p) NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data); as_nas_info_t initialNasMsg = {0}; + nr_ue_nas_t *nas = get_ue_nas_info(0); uint8_t *pdu_buffer = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data; int pdu_length = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length; + + security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); + if (security_state != NAS_SECURITY_INTEGRITY_PASSED + && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { + LOG_E(NAS, "NAS integrity failed, discard incoming message\n"); + break; + } + int msg_type = get_msg_type(pdu_buffer, pdu_length); - nr_ue_nas_t *nas = get_ue_nas_info(0); switch (msg_type) { case FGS_IDENTITY_REQUEST: From 245f430ad92af96042af79c129860e4989f4c6f8 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 16:28:48 +0200 Subject: [PATCH 08/15] nrUE NAS: apply ciphering to uplink messages Before this commit, only integrity protection was applied. This was working only if the NAS connection is using NEA0. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 87 +++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 4a87729188..eb832cb609 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -661,6 +661,19 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->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 */ stream_cipher.context = nas->security_container->integrity_context; stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; @@ -766,6 +779,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia // encode the message initialNasMsg->data = (Byte_t *)malloc(length * sizeof(Byte_t)); + initialNasMsg->length = length; /* Encode the first octet of the header (extended protocol discriminator) */ ENCODE_U8(initialNasMsg->data + size, sp_msg->header.protocol_discriminator, size); @@ -792,8 +806,20 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia if(sortransparentcontainer) { encode_registration_complete(&sp_msg->plain.mm_msg.registration_complete, initialNasMsg->data + size, length - size); } - - initialNasMsg->length = length; + + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->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 */ stream_cipher.context = nas->security_container->integrity_context; stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; @@ -801,8 +827,6 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); /* length in bits */ stream_cipher.blength = (initialNasMsg->length - 6) << 3; - - // only for Type of integrity protection algorithm: 128-5G-IA2 (2) stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); @@ -855,19 +879,35 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi initialNasMsg->length = security_header_len + mm_msg_encode(&sp_msg->plain.mm_msg, (uint8_t *)(initialNasMsg->data + security_header_len), size - security_header_len); - nas_stream_cipher_t stream_cipher = { - .context = nas->security_container->integrity_context, - .count = nas->security.nas_count_ul++, - .bearer = 1, - .direction = 0, - .message = (unsigned char *)(initialNasMsg->data + 6), - .blength = (initialNasMsg->length - 6) << 3, /* length in bits */ - }; - uint8_t mac[4]; - nas_stream_encrypt_eia2(&stream_cipher, mac); + nas_stream_cipher_t stream_cipher; - for(int i = 0; i < 4; i++) - initialNasMsg->data[2 + i] = mac[i]; + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->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 */ + 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->data + 6); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 6) << 3; + uint8_t mac[4]; + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); + + printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); + for(int i = 0; i < 4; i++){ + initialNasMsg->data[2+i] = mac[i]; + } } static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, nas_pdu_session_req_t *pdu_req) @@ -946,6 +986,19 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->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 */ stream_cipher.context = nas->security_container->integrity_context; stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; @@ -953,8 +1006,6 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); /* length in bits */ stream_cipher.blength = (initialNasMsg->length - 6) << 3; - - // only for Type of integrity protection algorithm: 128-5G-IA2 (2) stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); From 56abe6c1187bb3d72bd4d902b1332ac0ccbdd83d Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 16:32:47 +0200 Subject: [PATCH 09/15] nrUE NAS: deal with security for NAS_CONN_ESTABLI_CNF This message is ciphered + integrity protected, let's decipher it and integrity check it. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index eb832cb609..c2de7fd8d3 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -1291,13 +1291,23 @@ void *nas_nrue(void *args_p) NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); uint8_t *pdu_buffer = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.data; - int msg_type = get_msg_type(pdu_buffer, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); + int pdu_length = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length; + + nr_ue_nas_t *nas = get_ue_nas_info(0); + + security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); + if (security_state != NAS_SECURITY_INTEGRITY_PASSED + && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { + LOG_E(NAS, "NAS integrity failed, discard incoming message\n"); + break; + } + + int msg_type = get_msg_type(pdu_buffer, pdu_length); if (msg_type == REGISTRATION_ACCEPT) { - nr_ue_nas_t *nas = get_ue_nas_info(0); - handle_registration_accept(instance, nas, pdu_buffer, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); + handle_registration_accept(instance, nas, pdu_buffer, pdu_length); } else if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) { - capture_pdu_session_establishment_accept_msg(pdu_buffer, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); + capture_pdu_session_establishment_accept_msg(pdu_buffer, pdu_length); } break; From a10092906734936c643ec9383d80859e58383003 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 16:34:20 +0200 Subject: [PATCH 10/15] nrUE NAS: accept some messages without integrity+ciphering OAI core network is sending Deregistation Accept in clear, even after security context activation. The standards is not clear to me, so let's accept it. To be removed if incorrect with respect to the standards. (The core network will need to be fixed then.) --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index c2de7fd8d3..3155d82234 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -62,6 +62,7 @@ static nr_nas_msg_snssai_t nas_allowed_nssai[8]; typedef enum { NAS_SECURITY_NO_SECURITY_CONTEXT, + NAS_SECURITY_UNPROTECTED, NAS_SECURITY_INTEGRITY_FAILED, NAS_SECURITY_INTEGRITY_PASSED, NAS_SECURITY_BAD_INPUT @@ -71,6 +72,8 @@ security_state_t nas_security_rx_process(nr_ue_nas_t *nas, uint8_t *pdu_buffer, { if (nas->security_container == NULL) return NAS_SECURITY_NO_SECURITY_CONTEXT; + if (pdu_buffer[1] == 0) + return NAS_SECURITY_UNPROTECTED; /* header is 7 bytes, require at least one byte of payload */ if (pdu_length < 8) return NAS_SECURITY_BAD_INPUT; @@ -1371,6 +1374,14 @@ void *nas_nrue(void *args_p) int pdu_length = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length; security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); + /* special cases accepted without protection */ + if (security_state == NAS_SECURITY_UNPROTECTED) { + int msg_type = get_msg_type(pdu_buffer, pdu_length); + /* for the moment, only FGS_DEREGISTRATION_ACCEPT is accepted */ + if (msg_type == FGS_DEREGISTRATION_ACCEPT) + security_state = NAS_SECURITY_INTEGRITY_PASSED; + } + if (security_state != NAS_SECURITY_INTEGRITY_PASSED && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { LOG_E(NAS, "NAS integrity failed, discard incoming message\n"); From 04027b9265210665684164db8d28611b5eedf19e Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 16:50:39 +0200 Subject: [PATCH 11/15] nrUE NAS: minor changes in get_allowed_nssai() This function was very wrong, now it's just wrong. (It does not sanitize the input properly. Some bad input may have unexpected effects on the nrUE.) It needs a serious rewrite. I just wanted to "fix" it so that it processes correctly some correct input data. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 3155d82234..db747610ee 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -1157,11 +1157,13 @@ static void get_allowed_nssai(nr_nas_msg_snssai_t nssai[8], const uint8_t *pdu_b case 0x15: // allowed NSSAI length = *pdu_buffer++; parse_allowed_nssai(nssai, pdu_buffer, length); + pdu_buffer += length; break; default: - LOG_W(NAS, "This NAS IEI is not handled when extracting list of allowed NSSAI\n"); - pdu_buffer = end; + LOG_W(NAS, "This NAS IEI (0x%2.2x) is not handled when extracting list of allowed NSSAI\n", type); + length = *pdu_buffer++; + pdu_buffer += length; break; } } From 0147bb2e08b77911dbe009f6cb9d79d6bc6fe5c8 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 16:58:08 +0200 Subject: [PATCH 12/15] nrUE NAS: add NEA1 and NIA1 in nrUE security capabilities --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index db747610ee..aa2c4ddde4 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -532,8 +532,8 @@ void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) mm_msg->registration_request.presencemask |= REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_PRESENT; mm_msg->registration_request.nruesecuritycapability.iei = REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_IEI; mm_msg->registration_request.nruesecuritycapability.length = 8; - mm_msg->registration_request.nruesecuritycapability.fg_EA = 0xa0; - mm_msg->registration_request.nruesecuritycapability.fg_IA = 0x20; + mm_msg->registration_request.nruesecuritycapability.fg_EA = 0xe0; + mm_msg->registration_request.nruesecuritycapability.fg_IA = 0x60; mm_msg->registration_request.nruesecuritycapability.EEA = 0; mm_msg->registration_request.nruesecuritycapability.EIA = 0; size += 10; From 4c248a433ecc15dec66573a324716fc285e27195 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 23:10:14 +0200 Subject: [PATCH 13/15] nrUE NAS: bugfix: add sequence number to generateSecurityModeComplete It was working because on initial connection, it is 0. But it's probably a bad assumption. --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index aa2c4ddde4..2594bbad92 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -634,6 +634,7 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia // set security protected header nas_msg.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; nas_msg.header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX; + nas_msg.header.sequence_number = nas->security.nas_count_ul & 0xff; size += 7; mm_msg = &nas_msg.security_protected.plain.mm_msg; From a1c4c356bd7f3febc3454ea80d121d41fbbb41fd Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 24 May 2024 23:12:08 +0200 Subject: [PATCH 14/15] nrUE NAS: check NAS COUNT UL It should not be > 0xffffff. Normally the core network would prevent this case from happening. Plus to reach this value means to send a lot of NAS messages, which is very doubtful in practice. Anyway, it's better to deal with the case. We exit if that happens. To be refined if needed (very doubtful). --- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 2594bbad92..05feaafac2 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -668,6 +668,7 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia /* ciphering */ uint8_t buf[initialNasMsg->length - 7]; 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; @@ -814,6 +815,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia /* ciphering */ uint8_t buf[initialNasMsg->length - 7]; 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; @@ -888,6 +890,7 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi /* ciphering */ uint8_t buf[initialNasMsg->length - 7]; 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; @@ -993,6 +996,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * /* ciphering */ uint8_t buf[initialNasMsg->length - 7]; 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; From fc42a2a5f15030467eee776dfcc92bc4df7a6925 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Mon, 27 May 2024 17:25:59 +0200 Subject: [PATCH 15/15] security EIA1: bugfix: memory alignment + read out of array Compiling the nrUE with -fsanitize=undefined leads to a crash because the function nas_stream_encrypt_eia1() accesses the data as uint32_t but the data is not aligned. Actually this function wants 64 bits (big-endian) of data, so let's introduce a simple function to read a big-endian 64 bits value. It may seem bad (unoptimized), but looking at the output of gcc without -fsanitize=undefined this will be translated as just one movbeq instruction (on the machine where I did the test), which is actually less instructions than the previous version which was doing too movbel plus orq. Moreover, after the main loop we need to process the last remaining bytes (so less than 8). The code was wrong, reading past the input data (and also not caring about alignment). It may work, it may fail, it depends on many things. But it's plain wrong. So this was replaced for something better, simpler. No need for mask32bit() anymore, only one call to U64() is needed. And now -fsanitize=undefined is happy, so all is good. We also restrict the input length to be multiple of 8 bits. I don't think it's a problem. To be refined if I'm wrong. (I think RRC and NAS messages are 8-bits aligned, so it shouldn't be a problem.) --- openair3/SECU/nas_stream_eia1.c | 60 ++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/openair3/SECU/nas_stream_eia1.c b/openair3/SECU/nas_stream_eia1.c index c648ee585f..dd514c777b 100644 --- a/openair3/SECU/nas_stream_eia1.c +++ b/openair3/SECU/nas_stream_eia1.c @@ -88,25 +88,36 @@ static uint64_t MUL64(uint64_t V, uint64_t P, uint64_t c) return result; } -/* mask32bit. -* Input n: an integer in 1-32. -* Output : a 32 bit mask. -* Prepares a 32 bit mask with required number of 1 bits on the MSB side. -*/ -static uint32_t mask32bit(int n) +/* read a big endian uint64_t at given address (potentially not 64-bits aligned) + * don't read more than 'available_bytes' + * (use 0 if no byte to read) + * (note: the compiler will optimize this, no need to do better) + */ +static inline uint64_t U64(uint8_t *p, int available_bytes) { - uint32_t mask=0x0; + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + uint64_t e = 0; + uint64_t f = 0; + uint64_t g = 0; + uint64_t h = 0; + switch (available_bytes) { + case 8: h = p[7]; /* falltrough */ + case 7: g = p[6]; /* falltrough */ + case 6: f = p[5]; /* falltrough */ + case 5: e = p[4]; /* falltrough */ + case 4: d = p[3]; /* falltrough */ + case 3: c = p[2]; /* falltrough */ + case 2: b = p[1]; /* falltrough */ + case 1: a = p[0]; + } - if ( n%32 == 0 ) - return 0xffffffff; - - while (n--) - mask = (mask>>1) ^ 0x80000000; - - return mask; + return (a << (32+24)) | (b << (32+16)) | (c << (32+8)) | (d << 32) + | (e << 24) | (f << 16) | (g << 8) | h; } - /*! * @brief Create integrity cmac t for a given message. * @param[in] stream_cipher Structure containing various variables to setup encoding @@ -125,11 +136,8 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const *stream_cipher, uint8_t o uint64_t c; uint64_t M_D_2; int rem_bits; - uint32_t mask = 0; - uint32_t *message; uint8_t *key = (uint8_t *)stream_cipher->context; - message = (uint32_t*)stream_cipher->message; /* To operate 32 bit message internally. */ /* Load the Integrity Key for SNOW3G initialization as in section 4.4. */ memcpy(K+3,key+0,4); /*K[3] = key[0]; we assume K[3]=key[0]||key[1]||...||key[31] , with key[0] the @@ -181,11 +189,14 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const *stream_cipher, uint8_t o EVAL = 0; c = 0x1b; + AssertFatal(stream_cipher->blength % 8 == 0, "unsupported buffer length\n"); + + uint8_t *message = stream_cipher->message; + /* for 0 <= i <= D-3 */ for (i=0; i 32) { - M_D_2 = ( (uint64_t) hton_int32(message[2*(D-2)]) << 32 ) | - (uint64_t) (hton_int32(message[2*(D-2)+1]) & mask); - } else { - M_D_2 = ( (uint64_t) hton_int32(message[2*(D-2)]) & mask) << 32 ; - } + M_D_2 = U64(&message[4 * (2*(D-2))], rem_bits/8); V = EVAL ^ M_D_2; EVAL = MUL64(V,P,c);