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);