From 18cb68ecc2f03949a0359b3bbfb77a4e0201b0cf Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 17 Mar 2021 16:22:00 +0100 Subject: [PATCH] nr pdcp: first draft of NIA2 integrity This code has not been tested with a COTS UE. We need SRB1/2 (so we need SA) to test. The activation of security and integrity for SRB1 will need more code (new functions) because it happens after some time, not at creation time of the PDCP entity and there is no way to do this with current code. --- cmake_targets/CMakeLists.txt | 1 + openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c | 72 ++++++++++++++---- openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h | 7 +- .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c | 75 +++++++++++++++++++ .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h | 34 +++++++++ openair2/UTIL/OSA/osa_defs.h | 3 +- 6 files changed, 175 insertions(+), 17 deletions(-) create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 4645acf8f1..36d6bb54b1 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -1973,6 +1973,7 @@ set(NR_PDCP_SRC ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_sdu.c ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_timer_thread.c ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c + ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c ${OPENAIR2_DIR}/LAYER2/nr_pdcp/asn1_utils.c ) diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c index c3ccc7710e..1822239171 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.c @@ -26,6 +26,7 @@ #include #include "nr_pdcp_security_nea2.h" +#include "nr_pdcp_integrity_nia2.h" #include "nr_pdcp_sdu.h" #include "LOG/log.h" @@ -54,17 +55,21 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, } if (entity->sn_size == 12) { - rcvd_sn = (((unsigned char)buffer[0] & 0xf) << 8) | - (unsigned char)buffer[1]; + rcvd_sn = ((buffer[0] & 0xf) << 8) | + buffer[1]; header_size = 2; } else { - rcvd_sn = (((unsigned char)buffer[0] & 0x3) << 16) | - ((unsigned char)buffer[1] << 8) | - (unsigned char)buffer[2]; + rcvd_sn = ((buffer[0] & 0x3) << 16) | + (buffer[1] << 8) | + buffer[2]; header_size = 3; } - integrity_size = 0; + if (entity->has_integrity) { + integrity_size = 4; + } else { + integrity_size = 0; + } if (size < header_size + integrity_size + 1) { LOG_E(PDCP, "bad PDU received (size = %d)\n", size); @@ -86,9 +91,20 @@ static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity, if (entity->has_ciphering) entity->cipher(entity->security_context, - (unsigned char *)buffer+header_size, size-header_size, + buffer+header_size, size-header_size, entity->rb_id, rcvd_count, entity->is_gnb ? 0 : 1); + if (entity->has_integrity) { + unsigned char integrity[4]; + entity->integrity(entity->integrity_context, integrity, + buffer, size - integrity_size, + entity->rb_id, rcvd_count, entity->is_gnb ? 0 : 1); + if (memcmp(integrity, buffer, 4) != 0) { + LOG_E(PDCP, "discard NR PDU, integrity failed\n"); + return; + } + } + if (rcvd_count < entity->rx_deliv || nr_pdcp_sdu_in_list(entity->rx_list, rcvd_count)) { LOG_D(PDCP, "discard NR PDU rcvd_count=%d\n", rcvd_count); @@ -139,7 +155,8 @@ static void nr_pdcp_entity_recv_sdu(nr_pdcp_entity_t *entity, uint32_t count; int sn; int header_size; - char buf[size+3+4]; + int integrity_size; + char buf[size + 3 + 4]; int dc_bit; count = entity->tx_next; @@ -163,23 +180,37 @@ static void nr_pdcp_entity_recv_sdu(nr_pdcp_entity_t *entity, header_size = 3; } - memcpy(buf+header_size, buffer, size); + if (entity->has_integrity) { + integrity_size = 4; + } else { + integrity_size = 0; + } + + memcpy(buf + header_size, buffer, size); + + if (entity->has_integrity) + entity->integrity(entity->integrity_context, + (unsigned char *)buf + header_size + size, + (unsigned char *)buf, header_size + size, + entity->rb_id, count, entity->is_gnb ? 1 : 0); if (entity->has_ciphering) entity->cipher(entity->security_context, - (unsigned char *)buf+header_size, size, + (unsigned char *)buf + header_size, size + integrity_size, entity->rb_id, count, entity->is_gnb ? 1 : 0); entity->tx_next++; entity->deliver_pdu(entity->deliver_pdu_data, entity, buf, - size+header_size, sdu_id); + header_size + size + integrity_size, sdu_id); } static void nr_pdcp_entity_set_integrity_key(nr_pdcp_entity_t *entity, char *key) { - memcpy(entity->integrity_key, key, 16); + LOG_E(PDCP, "%s: %d: %s: TODO? to remove?\n", __FILE__, __LINE__, __FUNCTION__); + exit(1); + //memcpy(entity->integrity_key, key, 16); } static void check_t_reordering(nr_pdcp_entity_t *entity) @@ -240,6 +271,8 @@ void nr_pdcp_entity_delete(nr_pdcp_entity_t *entity) } if (entity->free_security != NULL) entity->free_security(entity->security_context); + if (entity->free_integrity != NULL) + entity->free_integrity(entity->integrity_context); free(entity); } @@ -306,9 +339,18 @@ nr_pdcp_entity_t *new_nr_pdcp_entity( } ret->is_gnb = is_gnb; - if (integrity_key != NULL) { - printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); - exit(1); + if (integrity_key != NULL && integrity_algorithm != 0) { + if (integrity_algorithm != 2) { + LOG_E(PDCP, "FATAL: only nia2 supported for the moment\n"); + exit(1); + } + ret->has_integrity = 1; + ret->integrity_algorithm = integrity_algorithm; + memcpy(ret->integrity_key, integrity_key, 16); + + ret->integrity_context = nr_pdcp_integrity_nia2_init(integrity_key); + ret->integrity = nr_pdcp_integrity_nia2_integrity; + ret->free_integrity = nr_pdcp_integrity_nia2_free_integrity; } return ret; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h index 36e7a540c4..4ae7a45abb 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h @@ -85,7 +85,12 @@ typedef struct nr_pdcp_entity_t { unsigned char *buffer, int length, int bearer, int count, int direction); void (*free_security)(void *security_context); - /* security algorithms need to know uplink/downlink information + void *integrity_context; + void (*integrity)(void *integrity_context, unsigned char *out, + unsigned char *buffer, int length, + int bearer, int count, int direction); + void (*free_integrity)(void *integrity_context); + /* security/integrity algorithms need to know uplink/downlink information * which is reverse for gnb and ue, so we need to know if this * pdcp entity is for a gnb or an ue */ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c new file mode 100644 index 0000000000..661dd02b5d --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c @@ -0,0 +1,75 @@ +/* + * 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 "nr_pdcp_integrity_nia2.h" + +#include +#include +#include +#include + +void *nr_pdcp_integrity_nia2_init(unsigned char *integrity_key) +{ + CMAC_CTX *ctx; + + ctx = CMAC_CTX_new(); + if (ctx == NULL) abort(); + + CMAC_Init(ctx, integrity_key, 16, EVP_aes_128_cbc(), NULL); + + return ctx; +} + +static void compute_t(unsigned char *t, uint32_t count, int bearer, + int direction) +{ + t[0] = (count >> 24) & 255; + t[1] = (count >> 16) & 255; + t[2] = (count >> 8) & 255; + t[3] = (count ) & 255; + t[4] = (bearer << 3) | (direction << 2); + memset(&t[5], 0, 8-5); +} + +void nr_pdcp_integrity_nia2_integrity(void *integrity_context, + unsigned char *out, + unsigned char *buffer, int length, + int bearer, int count, int direction) +{ + CMAC_CTX *ctx = integrity_context; + unsigned char t[8]; + unsigned char mac[16]; + size_t maclen; + + compute_t(t, count, bearer, direction); + + CMAC_Update(ctx, t, 8); + CMAC_Update(ctx, buffer, length); + CMAC_Final(ctx, mac, &maclen); + + memcpy(out, mac, 4); +} + +void nr_pdcp_integrity_nia2_free_integrity(void *integrity_context) +{ + CMAC_CTX *ctx = integrity_context; + CMAC_CTX_free(ctx); +} diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h new file mode 100644 index 0000000000..9593a2825c --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.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_NIA2_H_ +#define _NR_PDCP_INTEGRITY_NIA2_H_ + +void *nr_pdcp_integrity_nia2_init(unsigned char *integrity_key); + +void nr_pdcp_integrity_nia2_integrity(void *integrity_context, + unsigned char *out, + unsigned char *buffer, int length, + int bearer, int count, int direction); + +void nr_pdcp_integrity_nia2_free_integrity(void *integrity_context); + +#endif /* _NR_PDCP_INTEGRITY_NIA2_H_ */ diff --git a/openair2/UTIL/OSA/osa_defs.h b/openair2/UTIL/OSA/osa_defs.h index 6334658683..e845315922 100644 --- a/openair2/UTIL/OSA/osa_defs.h +++ b/openair2/UTIL/OSA/osa_defs.h @@ -48,7 +48,8 @@ typedef enum { NAS_INT_ALG = 0x02, RRC_ENC_ALG = 0x03, RRC_INT_ALG = 0x04, - UP_ENC_ALG = 0x05 + UP_ENC_ALG = 0x05, + UP_INT_ALG = 0x06 } algorithm_type_dist_t; //int derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t **keNB);