diff --git a/openair2/COMMON/pdcp_messages_types.h b/openair2/COMMON/pdcp_messages_types.h index d7c84a24a1..b97fdef986 100644 --- a/openair2/COMMON/pdcp_messages_types.h +++ b/openair2/COMMON/pdcp_messages_types.h @@ -88,14 +88,18 @@ typedef struct NRRrcDcchDataReq_s { uint8_t gNB_index; } NRRrcDcchDataReq; +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_data.h" + typedef struct NRRrcDcchDataInd_s { uint32_t frame; uint8_t dcch_index; uint32_t sdu_size; uint8_t *sdu_p; - uint16_t rnti; - uint8_t module_id; - uint8_t gNB_index; // LG: needed in UE + uint16_t rnti; + uint8_t module_id; + uint8_t gNB_index; // LG: needed in UE + /* 'msg_integrity' is needed for RRC to check integrity of the PDCP SDU */ + nr_pdcp_integrity_data_t msg_integrity; } NRRrcDcchDataInd; typedef struct RrcPcchDataReq_s { diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c index 2a197c3c4f..98162d003c 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c @@ -121,6 +121,16 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, rcvd_count = (rcvd_hfn << entity->sn_size) | rcvd_sn; + nr_pdcp_integrity_data_t msg_integrity = { 0 }; + + /* the MAC-I/header/rcvd_count is needed by some RRC procedures, store it */ + if (entity->has_integrity || entity->type == NR_PDCP_SRB) { + msg_integrity.count = rcvd_count; + memcpy(msg_integrity.mac, &buffer[size-4], 4); + msg_integrity.header_size = header_size; + memcpy(msg_integrity.header, buffer, header_size); + } + if (entity->has_ciphering) entity->cipher(entity->security_context, buffer + header_size + sdap_header_size, @@ -152,7 +162,8 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, sdu = nr_pdcp_new_sdu(rcvd_count, (char *)buffer + header_size, - size - header_size - integrity_size); + size - header_size - integrity_size, + &msg_integrity); entity->rx_list = nr_pdcp_sdu_list_add(entity->rx_list, sdu); entity->rx_size += size-header_size; @@ -168,7 +179,8 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, while (entity->rx_list != NULL && count == entity->rx_list->count) { nr_pdcp_sdu_t *cur = entity->rx_list; entity->deliver_sdu(entity->deliver_sdu_data, entity, - cur->buffer, cur->size); + cur->buffer, cur->size, + &cur->msg_integrity); entity->rx_list = cur->next; entity->rx_size -= cur->size; entity->stats.txsdu_pkts++; @@ -261,13 +273,11 @@ static int nr_pdcp_entity_process_sdu(nr_pdcp_entity_t *entity, memset(buf + header_size + size, 0, PDCP_INTEGRITY_SIZE); } - if (entity->has_ciphering && (entity->is_gnb || entity->security_mode_completed)) { + if (entity->has_ciphering) { entity->cipher(entity->security_context, (unsigned char *)buf + header_size + sdap_header_size, size + integrity_size - sdap_header_size, entity->rb_id, count, entity->is_gnb ? 1 : 0); - } else { - entity->security_mode_completed = true; } entity->tx_next++; @@ -279,6 +289,31 @@ static int nr_pdcp_entity_process_sdu(nr_pdcp_entity_t *entity, return header_size + size + integrity_size; } +static bool nr_pdcp_entity_check_integrity(struct nr_pdcp_entity_t *entity, + const uint8_t *buffer, + int buffer_size, + const nr_pdcp_integrity_data_t *msg_integrity) +{ + if (!entity->has_integrity) + return false; + + int header_size = msg_integrity->header_size; + + uint8_t b[buffer_size + header_size]; + + for (int i = 0; i < header_size; i++) + b[i] = msg_integrity->header[i]; + + memcpy(b + header_size, buffer, buffer_size); + + unsigned char mac[4]; + entity->integrity(entity->integrity_context, mac, + b, buffer_size + header_size, + entity->rb_id, msg_integrity->count, entity->is_gnb ? 0 : 1); + + return memcmp(mac, msg_integrity->mac, 4) == 0; +} + /* may be called several times, take care to clean previous settings */ static void nr_pdcp_entity_set_security(nr_pdcp_entity_t *entity, int integrity_algorithm, @@ -360,7 +395,8 @@ static void check_t_reordering(nr_pdcp_entity_t *entity) while (entity->rx_list != NULL && entity->rx_list->count < entity->rx_reord) { nr_pdcp_sdu_t *cur = entity->rx_list; entity->deliver_sdu(entity->deliver_sdu_data, entity, - cur->buffer, cur->size); + cur->buffer, cur->size, + &cur->msg_integrity); entity->rx_list = cur->next; entity->rx_size -= cur->size; entity->stats.txsdu_pkts++; @@ -373,7 +409,8 @@ static void check_t_reordering(nr_pdcp_entity_t *entity) while (entity->rx_list != NULL && count == entity->rx_list->count) { nr_pdcp_sdu_t *cur = entity->rx_list; entity->deliver_sdu(entity->deliver_sdu_data, entity, - cur->buffer, cur->size); + cur->buffer, cur->size, + &cur->msg_integrity); entity->rx_list = cur->next; entity->rx_size -= cur->size; entity->stats.txsdu_pkts++; @@ -403,7 +440,8 @@ static void deliver_all_sdus(nr_pdcp_entity_t *entity) while (entity->rx_list != NULL) { nr_pdcp_sdu_t *cur = entity->rx_list; entity->deliver_sdu(entity->deliver_sdu_data, entity, - cur->buffer, cur->size); + cur->buffer, cur->size, + &cur->msg_integrity); entity->rx_list = cur->next; entity->rx_size -= cur->size; entity->stats.txsdu_pkts++; @@ -524,7 +562,8 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( bool has_sdap_rx, bool has_sdap_tx, void (*deliver_sdu)(void *deliver_sdu_data, struct nr_pdcp_entity_t *entity, - char *buf, int size), + char *buf, int size, + const nr_pdcp_integrity_data_t *msg_integrity), void *deliver_sdu_data, void (*deliver_pdu)(void *deliver_pdu_data, ue_id_t ue_id, int rb_id, char *buf, int size, int sdu_id), @@ -547,10 +586,11 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( ret->type = type; - ret->recv_pdu = nr_pdcp_entity_recv_pdu; - ret->process_sdu = nr_pdcp_entity_process_sdu; - ret->set_security = nr_pdcp_entity_set_security; - ret->set_time = nr_pdcp_entity_set_time; + ret->recv_pdu = nr_pdcp_entity_recv_pdu; + ret->process_sdu = nr_pdcp_entity_process_sdu; + ret->set_security = nr_pdcp_entity_set_security; + ret->check_integrity = nr_pdcp_entity_check_integrity; + ret->set_time = nr_pdcp_entity_set_time; ret->delete_entity = nr_pdcp_entity_delete; ret->release_entity = nr_pdcp_entity_release; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h index aae1ffb317..5d2fd56f70 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h @@ -104,11 +104,17 @@ typedef struct nr_pdcp_entity_t { int ciphering_algorithm, char *ciphering_key); + /* check_integrity is used by RRC */ + bool (*check_integrity)(struct nr_pdcp_entity_t *entity, + const uint8_t *buffer, int buffer_length, + const nr_pdcp_integrity_data_t *msg_integrity); + void (*set_time)(struct nr_pdcp_entity_t *entity, uint64_t now); /* callbacks provided to the PDCP module */ void (*deliver_sdu)(void *deliver_sdu_data, struct nr_pdcp_entity_t *entity, - char *buf, int size); + char *buf, int size, + const nr_pdcp_integrity_data_t *msg_integrity); void *deliver_sdu_data; void (*deliver_pdu)(void *deliver_pdu_data, ue_id_t ue_id, int rb_id, char *buf, int size, int sdu_id); @@ -167,19 +173,6 @@ typedef struct nr_pdcp_entity_t { int rx_maxsize; nr_pdcp_statistics_t stats; - // WARNING: This is a hack! - // 3GPP TS 38.331 (RRC) version 15.3 - // Section 5.3.4.3 Reception of the SecurityModeCommand by the UE - // The UE needs to send the Security Mode Complete message. However, the message - // needs to be sent without being ciphered. - // However: - // 1- The Security Mode Command arrives to the UE with the cipher algo (e.g., nea2). - // 2- The UE is configured with the cipher algo. - // 3- The Security Mode Complete message is sent to the itti task queue. - // 4- The ITTI task, forwards the message ciphering (e.g., nea2) it. - // 5- The gNB cannot understand the ciphered Security Mode Complete message. - bool security_mode_completed; - /* Keep tracks of whether the PDCP entity was suspended or not */ bool entity_suspended; } nr_pdcp_entity_t; @@ -192,7 +185,8 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( bool has_sdap_rx, bool has_sdap_tx, void (*deliver_sdu)(void *deliver_sdu_data, struct nr_pdcp_entity_t *entity, - char *buf, int size), + char *buf, int size, + const nr_pdcp_integrity_data_t *msg_integrity), void *deliver_sdu_data, void (*deliver_pdu)(void *deliver_pdu_data, ue_id_t ue_id, int rb_id, char *buf, int size, int sdu_id), diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_data.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_data.h new file mode 100644 index 0000000000..6f14a7092a --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_data.h @@ -0,0 +1,34 @@ +/* + * 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 _NR_PDCP_INTEGRITY_DATA_H_ +#define _NR_PDCP_INTEGRITY_DATA_H_ + +#include + +typedef struct { + uint32_t count; + uint8_t mac[4]; + uint8_t header_size; + uint8_t header[3]; +} nr_pdcp_integrity_data_t; + +#endif /* _NR_PDCP_INTEGRITY_DATA_H_ */ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index b2380e3b2f..c2e49f7856 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -658,7 +658,8 @@ uint64_t nr_pdcp_module_init(uint64_t _pdcp_optmask, int id) } static void deliver_sdu_drb(void *_ue, nr_pdcp_entity_t *entity, - char *buf, int size) + char *buf, int size, + const nr_pdcp_integrity_data_t *msg_integrity) { nr_pdcp_ue_t *ue = _ue; int rb_id; @@ -741,7 +742,8 @@ static void deliver_pdu_drb_gnb(void *deliver_pdu_data, ue_id_t ue_id, int rb_id } static void deliver_sdu_srb(void *_ue, nr_pdcp_entity_t *entity, - char *buf, int size) + char *buf, int size, + const nr_pdcp_integrity_data_t *msg_integrity) { nr_pdcp_ue_t *ue = _ue; int srb_id; @@ -782,6 +784,7 @@ srb_found: NR_RRC_DCCH_DATA_IND(message_p).dcch_index = srb_id; NR_RRC_DCCH_DATA_IND(message_p).sdu_p = rrc_buffer_p; NR_RRC_DCCH_DATA_IND(message_p).sdu_size = size; + memcpy(&NR_RRC_DCCH_DATA_IND(message_p).msg_integrity, msg_integrity, sizeof(*msg_integrity)); ue_id_t ue_id = ue->ue_id; itti_send_msg_to_task(TASK_RRC_NRUE, ue_id, message_p); } @@ -1047,8 +1050,8 @@ void nr_pdcp_config_set_security(ue_id_t ue_id, rb = nr_pdcp_get_rb(ue, rb_id, true); if (rb == NULL) { - LOG_E(PDCP, "no SRB found (ue_id %ld, rb_id %ld)\n", ue_id, rb_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "no SRB found (ue_id %ld, rb_id %ld)\n", ue_id, rb_id); return; } @@ -1056,11 +1059,38 @@ void nr_pdcp_config_set_security(ue_id_t ue_id, ciphering_algorithm = security_modeP & 0x0f; rb->set_security(rb, integrity_algorithm, (char *)kRRCint_pP, ciphering_algorithm, (char *)kRRCenc_pP); - rb->security_mode_completed = false; nr_pdcp_manager_unlock(nr_pdcp_ue_manager); } +bool nr_pdcp_check_integrity_srb(ue_id_t ue_id, + int srb_id, + const uint8_t *msg, + int msg_size, + const nr_pdcp_integrity_data_t *msg_integrity) +{ + nr_pdcp_ue_t *ue; + nr_pdcp_entity_t *rb; + + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + + ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); + + rb = nr_pdcp_get_rb(ue, srb_id, true); + + if (rb == NULL) { + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "no SRB found (ue_id %ld, rb_id %d)\n", ue_id, srb_id); + return false; + } + + bool ret = rb->check_integrity(rb, msg, msg_size, msg_integrity); + + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + + return ret; +} + bool nr_pdcp_data_req_srb(ue_id_t ue_id, const rb_id_t rb_id, const mui_t muiP, @@ -1079,8 +1109,8 @@ bool nr_pdcp_data_req_srb(ue_id_t ue_id, rb = nr_pdcp_get_rb(ue, rb_id, true); if (rb == NULL) { - LOG_E(PDCP, "no SRB found (ue_id %ld, rb_id %ld)\n", ue_id, rb_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "no SRB found (ue_id %ld, rb_id %ld)\n", ue_id, rb_id); return 0; } @@ -1106,8 +1136,8 @@ void nr_pdcp_suspend_srb(ue_id_t ue_id, int srb_id) nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); nr_pdcp_entity_t *srb = nr_pdcp_get_rb(ue, srb_id, true); if (srb == NULL) { - LOG_E(PDCP, "Trying to suspend SRB with ID %d but it is not established\n", srb_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "Trying to suspend SRB with ID %d but it is not established\n", srb_id); return; } srb->suspend_entity(srb); @@ -1121,8 +1151,8 @@ void nr_pdcp_suspend_drb(ue_id_t ue_id, int drb_id) nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); nr_pdcp_entity_t *drb = nr_pdcp_get_rb(ue, drb_id, false); if (drb == NULL) { - LOG_E(PDCP, "Trying to suspend DRB with ID %d but it is not established\n", drb_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "Trying to suspend DRB with ID %d but it is not established\n", drb_id); return; } drb->suspend_entity(drb); @@ -1136,8 +1166,8 @@ void nr_pdcp_reconfigure_srb(ue_id_t ue_id, int srb_id, long t_Reordering) nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); nr_pdcp_entity_t *srb = nr_pdcp_get_rb(ue, srb_id, true); if (srb == NULL) { - LOG_E(PDCP, "Trying to reconfigure SRB with ID %d but it is not established\n", srb_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "Trying to reconfigure SRB with ID %d but it is not established\n", srb_id); return; } int decoded_t_reordering = decode_t_reordering(t_Reordering); @@ -1154,8 +1184,8 @@ void nr_pdcp_reconfigure_drb(ue_id_t ue_id, int drb_id, NR_PDCP_Config_t *pdcp_c nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); nr_pdcp_entity_t *drb = nr_pdcp_get_rb(ue, drb_id, false); if (drb == NULL) { - LOG_E(PDCP, "Trying to reconfigure DRB with ID %d but it is not established\n", drb_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "Trying to reconfigure DRB with ID %d but it is not established\n", drb_id); return; } if (pdcp_config) { @@ -1268,8 +1298,8 @@ bool nr_pdcp_data_req_drb(protocol_ctxt_t *ctxt_pP, rb = nr_pdcp_get_rb(ue, rb_id, false); if (rb == NULL) { - LOG_E(PDCP, "[UE %lx] DRB %ld not found\n", rb_id, ue_id); nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + LOG_E(PDCP, "[UE %lx] DRB %ld not found\n", ue_id, rb_id); return 0; } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h index be1604dabd..99f7fcdc81 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h @@ -87,6 +87,12 @@ void nr_pdcp_config_set_security(ue_id_t ue_id, uint8_t *const kRRCint_pP, uint8_t *const kUPenc_pP); +bool nr_pdcp_check_integrity_srb(ue_id_t ue_id, + int srb_id, + const uint8_t *msg, + int msg_size, + const nr_pdcp_integrity_data_t *msg_integrity); + bool cu_f1u_data_req(protocol_ctxt_t *ctxt_pP, const srb_flag_t srb_flagP, const rb_id_t rb_id, diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.c index 47d95156e3..783bc1c3bc 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.c @@ -24,7 +24,8 @@ #include #include -nr_pdcp_sdu_t *nr_pdcp_new_sdu(uint32_t count, char *buffer, int size) +nr_pdcp_sdu_t *nr_pdcp_new_sdu(uint32_t count, char *buffer, int size, + const nr_pdcp_integrity_data_t *msg_integrity) { nr_pdcp_sdu_t *ret = calloc(1, sizeof(nr_pdcp_sdu_t)); if (ret == NULL) @@ -35,6 +36,7 @@ nr_pdcp_sdu_t *nr_pdcp_new_sdu(uint32_t count, char *buffer, int size) exit(1); memcpy(ret->buffer, buffer, size); ret->size = size; + memcpy(&ret->msg_integrity, msg_integrity, sizeof(*msg_integrity)); return ret; } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.h index 3d4e50a4ce..0452e71cfa 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_sdu.h @@ -24,14 +24,18 @@ #include +#include "nr_pdcp_integrity_data.h" + typedef struct nr_pdcp_sdu_t { - uint32_t count; - char *buffer; - int size; - struct nr_pdcp_sdu_t *next; + uint32_t count; + char *buffer; + int size; + nr_pdcp_integrity_data_t msg_integrity; + struct nr_pdcp_sdu_t *next; } nr_pdcp_sdu_t; -nr_pdcp_sdu_t *nr_pdcp_new_sdu(uint32_t count, char *buffer, int size); +nr_pdcp_sdu_t *nr_pdcp_new_sdu(uint32_t count, char *buffer, int size, + const nr_pdcp_integrity_data_t *msg_integrity); nr_pdcp_sdu_t *nr_pdcp_sdu_list_add(nr_pdcp_sdu_t *l, nr_pdcp_sdu_t *sdu); int nr_pdcp_sdu_in_list(nr_pdcp_sdu_t *l, uint32_t count); void nr_pdcp_free_sdu(nr_pdcp_sdu_t *sdu); diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 42540222f0..9e09db1a72 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1004,11 +1004,19 @@ static int8_t nr_rrc_ue_decode_ccch(NR_UE_RRC_INST_t *rrc, } static void nr_rrc_ue_process_securityModeCommand(NR_UE_RRC_INST_t *ue_rrc, - NR_SecurityModeCommand_t *const securityModeCommand) + NR_SecurityModeCommand_t *const securityModeCommand, + int srb_id, + const uint8_t *msg, + int msg_size, + const nr_pdcp_integrity_data_t *msg_integrity) { - int securityMode = 0; LOG_I(NR_RRC, "Receiving from SRB1 (DL-DCCH), Processing securityModeCommand\n"); + AssertFatal(securityModeCommand->criticalExtensions.present == NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand, + "securityModeCommand->criticalExtensions.present (%d) != " + "NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand\n", + securityModeCommand->criticalExtensions.present); + NR_SecurityConfigSMC_t *securityConfigSMC = &securityModeCommand->criticalExtensions.choice.securityModeCommand->securityConfigSMC; @@ -1016,53 +1024,28 @@ static void nr_rrc_ue_process_securityModeCommand(NR_UE_RRC_INST_t *ue_rrc, case NR_CipheringAlgorithm_nea0: case NR_CipheringAlgorithm_nea1: case NR_CipheringAlgorithm_nea2: - LOG_I(NR_RRC, "Security algorithm is set to nea%d\n", securityMode); - securityMode = securityConfigSMC->securityAlgorithmConfig.cipheringAlgorithm; + LOG_I(NR_RRC, "Security algorithm is set to nea%ld\n", + securityConfigSMC->securityAlgorithmConfig.cipheringAlgorithm); break; default: - LOG_W(NR_RRC, "Security algorithm is set to none\n"); - securityMode = NR_CipheringAlgorithm_spare1; - break; + AssertFatal(0, "Security algorithm not known/supported\n"); } ue_rrc->cipheringAlgorithm = securityConfigSMC->securityAlgorithmConfig.cipheringAlgorithm; + ue_rrc->integrityProtAlgorithm = 0; if (securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm != NULL) { switch (*securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm) { + case NR_IntegrityProtAlgorithm_nia0: case NR_IntegrityProtAlgorithm_nia1: - LOG_I(NR_RRC, "Integrity protection algorithm is set to nia1\n"); - securityMode |= 1 << 5; - break; - case NR_IntegrityProtAlgorithm_nia2: - LOG_I(NR_RRC, "Integrity protection algorithm is set to nia2\n"); - securityMode |= 1 << 6; + LOG_I(NR_RRC, "Integrity protection algorithm is set to nia%ld\n", *securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm); break; - default: - LOG_I(NR_RRC, "Integrity protection algorithm is set to none\n"); - securityMode |= 0x70; - break; + AssertFatal(0, "Integrity algorithm not known/supported\n"); } - ue_rrc->integrityProtAlgorithm = *securityConfigSMC->securityAlgorithmConfig.integrityProtAlgorithm; } - LOG_D(NR_RRC, "security mode is %x \n", securityMode); - NR_UL_DCCH_Message_t ul_dcch_msg = {0}; - - // memset((void *)&SecurityModeCommand,0,sizeof(SecurityModeCommand_t)); - ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1; - asn1cCalloc(ul_dcch_msg.message.choice.c1, c1); - - if (securityMode >= NO_SECURITY_MODE) { - LOG_I(NR_RRC, "rrc_ue_process_securityModeCommand, security mode complete case \n"); - c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete; - } else { - LOG_I(NR_RRC, "rrc_ue_process_securityModeCommand, security mode failure case \n"); - c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeFailure; - c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete; - } - uint8_t kRRCenc[NR_K_KEY_SIZE] = {0}; uint8_t kUPenc[NR_K_KEY_SIZE] = {0}; uint8_t kRRCint[NR_K_KEY_SIZE] = {0}; @@ -1070,55 +1053,98 @@ static void nr_rrc_ue_process_securityModeCommand(NR_UE_RRC_INST_t *ue_rrc, nr_derive_key(RRC_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, kRRCenc); nr_derive_key(RRC_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, kRRCint); - log_dump(NR_RRC, ue_rrc->kgnb, 32, LOG_DUMP_CHAR, "driving kRRCenc, kRRCint and kUPenc from KgNB="); + log_dump(NR_RRC, ue_rrc->kgnb, 32, LOG_DUMP_CHAR, "deriving kRRCenc, kRRCint and kUPenc from KgNB="); - if (securityMode != 0xff) { - uint8_t security_mode = ue_rrc->cipheringAlgorithm | (ue_rrc->integrityProtAlgorithm << 4); - // configure lower layers to apply SRB integrity protection and ciphering - for (int i = 1; i < NR_NUM_SRB; i++) { - if (ue_rrc->Srb[i] == RB_ESTABLISHED) - nr_pdcp_config_set_security(ue_rrc->ue_id, i, security_mode, kRRCenc, kRRCint, kUPenc); - } - } else { - LOG_I(NR_RRC, "skipped pdcp_config_set_security() as securityMode == 0x%02x", securityMode); + /* for SecurityModeComplete, ciphering is not activated yet, only integrity */ + uint8_t security_mode = ue_rrc->integrityProtAlgorithm << 4; + // configure lower layers to apply SRB integrity protection and ciphering + for (int i = 1; i < NR_NUM_SRB; i++) { + if (ue_rrc->Srb[i] == RB_ESTABLISHED) + nr_pdcp_config_set_security(ue_rrc->ue_id, i, security_mode, kRRCenc, kRRCint, kUPenc); } - if (securityModeCommand->criticalExtensions.present == NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand) { - asn1cCalloc(c1->choice.securityModeComplete, modeComplete); - modeComplete->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier; - modeComplete->criticalExtensions.present = NR_SecurityModeComplete__criticalExtensions_PR_securityModeComplete; - asn1cCalloc(modeComplete->criticalExtensions.choice.securityModeComplete, ext); + NR_UL_DCCH_Message_t ul_dcch_msg = {0}; + + ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1; + asn1cCalloc(ul_dcch_msg.message.choice.c1, c1); + + // the SecurityModeCommand message needs to pass the integrity protection check + // for the UE to declare AS security to be activated + bool integrity_pass = nr_pdcp_check_integrity_srb(ue_rrc->ue_id, srb_id, msg, msg_size, msg_integrity); + if (!integrity_pass) { + /* - continue using the configuration used prior to the reception of the SecurityModeCommand message, i.e. + * neither apply integrity protection nor ciphering. + * - submit the SecurityModeFailure message to lower layers for transmission, upon which the procedure ends. + */ + LOG_E(NR_RRC, "integrity of SecurityModeCommand failed, reply with SecurityModeFailure\n"); + c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeFailure; + asn1cCalloc(c1->choice.securityModeFailure, modeFailure); + modeFailure->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier; + modeFailure->criticalExtensions.present = NR_SecurityModeFailure__criticalExtensions_PR_securityModeFailure; + asn1cCalloc(modeFailure->criticalExtensions.choice.securityModeFailure, ext); ext->nonCriticalExtension = NULL; - LOG_I(NR_RRC, - "Receiving from SRB1 (DL-DCCH), encoding securityModeComplete, rrc_TransactionIdentifier: %ld\n", - securityModeCommand->rrc_TransactionIdentifier); + uint8_t buffer[200]; asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, sizeof(buffer)); AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded); - - if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); - } - log_dump(NR_RRC, buffer, 16, LOG_DUMP_CHAR, "securityModeComplete payload: "); - LOG_D(NR_RRC, "securityModeComplete Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded + 7) / 8); ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg); - for (int i = 0; i < (enc_rval.encoded + 7) / 8; i++) { - LOG_T(NR_RRC, "%02x.", buffer[i]); + /* disable both ciphering and integrity */ + security_mode = 0; + for (int i = 1; i < NR_NUM_SRB; i++) { + if (ue_rrc->Srb[i] == RB_ESTABLISHED) + nr_pdcp_config_set_security(ue_rrc->ue_id, i, security_mode, NULL, NULL, NULL); } - LOG_T(NR_RRC, "\n"); - // TODO the SecurityModeCommand message needs to pass the integrity protection check - // for the UE to declare AS security to be activated - ue_rrc->as_security_activated = true; - int srb_id = 1; // SecurityModeComplete in SRB1 + srb_id = 1; // SecurityModeFailure in SRB1 nr_pdcp_data_req_srb(ue_rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL); - } else - LOG_W(NR_RRC, - "securityModeCommand->criticalExtensions.present (%d) != " - "NR_SecurityModeCommand__criticalExtensions_PR_securityModeCommand\n", - securityModeCommand->criticalExtensions.present); + + return; + } + + /* integrity passed, send SecurityModeComplete */ + c1->present = NR_UL_DCCH_MessageType__c1_PR_securityModeComplete; + + asn1cCalloc(c1->choice.securityModeComplete, modeComplete); + modeComplete->rrc_TransactionIdentifier = securityModeCommand->rrc_TransactionIdentifier; + modeComplete->criticalExtensions.present = NR_SecurityModeComplete__criticalExtensions_PR_securityModeComplete; + asn1cCalloc(modeComplete->criticalExtensions.choice.securityModeComplete, ext); + ext->nonCriticalExtension = NULL; + LOG_I(NR_RRC, + "Receiving from SRB1 (DL-DCCH), encoding securityModeComplete, rrc_TransactionIdentifier: %ld\n", + securityModeCommand->rrc_TransactionIdentifier); + uint8_t buffer[200]; + asn_enc_rval_t enc_rval = + uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, sizeof(buffer)); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); + } + log_dump(NR_RRC, buffer, 16, LOG_DUMP_CHAR, "securityModeComplete payload: "); + LOG_D(NR_RRC, "securityModeComplete Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded + 7) / 8); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg); + + for (int i = 0; i < (enc_rval.encoded + 7) / 8; i++) { + LOG_T(NR_RRC, "%02x.", buffer[i]); + } + LOG_T(NR_RRC, "\n"); + + ue_rrc->as_security_activated = true; + srb_id = 1; // SecurityModeComplete in SRB1 + nr_pdcp_data_req_srb(ue_rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL); + + /* after encoding SecurityModeComplete we activate both ciphering and integrity */ + security_mode = ue_rrc->cipheringAlgorithm | (ue_rrc->integrityProtAlgorithm << 4); + // configure lower layers to apply SRB integrity protection and ciphering + for (int i = 1; i < NR_NUM_SRB; i++) { + if (ue_rrc->Srb[i] == RB_ESTABLISHED) + /* pass NULL to keep current keys */ + nr_pdcp_config_set_security(ue_rrc->ue_id, i, security_mode, NULL, NULL, NULL); + } } static void handle_meas_reporting_remove(rrcPerNB_t *rrc, int id, NR_UE_Timers_Constants_t *timers) @@ -1391,6 +1417,9 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc, uint8_t kRRCenc[NR_K_KEY_SIZE] = {0}; uint8_t kRRCint[NR_K_KEY_SIZE] = {0}; + uint8_t kUPenc[NR_K_KEY_SIZE] = {0}; + uint8_t kUPint[NR_K_KEY_SIZE] = {0}; + if (ue_rrc->as_security_activated) { if (radioBearerConfig->securityConfig != NULL) { // When the field is not included, continue to use the currently configured keyToUse @@ -1407,6 +1436,8 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc, } nr_derive_key(RRC_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, kRRCenc); nr_derive_key(RRC_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, kRRCint); + nr_derive_key(UP_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, kUPenc); + nr_derive_key(UP_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, kUPint); } if (radioBearerConfig->srb_ToAddModList != NULL) { @@ -1477,8 +1508,8 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc, radioBearerConfig->drb_ToAddModList->list.array[cnt], ue_rrc->cipheringAlgorithm, ue_rrc->integrityProtAlgorithm, - kRRCenc, - kRRCint); + kUPenc, + kUPint); } } } // drb_ToAddModList // @@ -1555,8 +1586,8 @@ static int nr_rrc_ue_decode_dcch(NR_UE_RRC_INST_t *rrc, const srb_id_t Srb_id, const uint8_t *const Buffer, size_t Buffer_size, - const uint8_t gNB_indexP) - + const uint8_t gNB_indexP, + const nr_pdcp_integrity_data_t *msg_integrity) { NR_DL_DCCH_Message_t *dl_dcch_msg = NULL; if (Srb_id != 1 && Srb_id != 2) { @@ -1639,7 +1670,8 @@ static int nr_rrc_ue_decode_dcch(NR_UE_RRC_INST_t *rrc, break; case NR_DL_DCCH_MessageType__c1_PR_securityModeCommand: LOG_I(NR_RRC, "Received securityModeCommand (gNB %d)\n", gNB_indexP); - nr_rrc_ue_process_securityModeCommand(rrc, c1->choice.securityModeCommand); + nr_rrc_ue_process_securityModeCommand(rrc, c1->choice.securityModeCommand, + Srb_id, Buffer, Buffer_size, msg_integrity); break; } } break; @@ -1744,7 +1776,8 @@ void *rrc_nrue(void *notUsed) NR_RRC_DCCH_DATA_IND(msg_p).dcch_index, NR_RRC_DCCH_DATA_IND(msg_p).sdu_p, NR_RRC_DCCH_DATA_IND(msg_p).sdu_size, - NR_RRC_DCCH_DATA_IND(msg_p).gNB_index); + NR_RRC_DCCH_DATA_IND(msg_p).gNB_index, + &NR_RRC_DCCH_DATA_IND(msg_p).msg_integrity); break; case NAS_KENB_REFRESH_REQ: diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index cf1897ae16..57c3e1df58 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -466,7 +466,7 @@ 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 = 0x80; + mm_msg->registration_request.nruesecuritycapability.fg_EA = 0xa0; mm_msg->registration_request.nruesecuritycapability.fg_IA = 0x20; mm_msg->registration_request.nruesecuritycapability.EEA = 0; mm_msg->registration_request.nruesecuritycapability.EIA = 0;