From d7931ea4121d52a13d43fb4b050c5f60288660b4 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Sat, 6 Apr 2024 16:12:08 +0200 Subject: [PATCH] bugfix: speedup integrity computation The main work of this commit is to rewrite cipher_aes_128_cbc_cmac() which was too slow and created issues when running the 4G eNB on some machines (LLL when using a b210). Instead of creating the security context, use it, and delete it, all in cipher_aes_128_cbc_cmac(), we introduce the function init_aes_128_cbc_cmac() to create it, and we change cipher_aes_128_cbc_cmac() to just use it, and we also adapt free_aes_128_cbc_cmac(). The function aes_128_cbc_cmac() is removed, it does not follow the new init/use/free API. Doing so, on the primary test machine used to do this work (a powerful and fast machine where it is possible to set the CPUs' frequencies) it was possible to limit the CPUs' frequencies to 500 MHz (it was not possible to go lower) without any LLL (using a b210, quectel UE in idle, no data traffic, only periodic measurement reports). Before this commit, it was necessary to set a frequency of at least 1.8 GHz otherwise some LLL would appear. Then we need to adapt the rest of the code to take into account those changes. A notion of security container (stream_security_container_t) containing ciphering and integrity contexts is introduced. And the code is harmonized for, hopefully, better readability. The (opaque) type stream_security_context_t is introduced, used by both integrity and ciphering contexts. The idea is to use a init()/use()/free() API for all the security procedures, identical for all the ciphering and integrity procedures. --- openair2/LAYER2/PDCP_v10.1.0/pdcp.c | 11 +- openair2/LAYER2/PDCP_v10.1.0/pdcp.h | 9 +- openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c | 29 +-- openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h | 14 +- .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.c | 19 +- .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.h | 8 +- .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c | 25 +-- .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h | 8 +- .../LAYER2/nr_pdcp/nr_pdcp_security_nea2.c | 8 +- .../LAYER2/nr_pdcp/nr_pdcp_security_nea2.h | 8 +- openair3/NAS/COMMON/API/NETWORK/nas_message.c | 18 +- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 16 +- openair3/NAS/NR_UE/nr_nas_msg_sim.h | 2 + openair3/NAS/UE/EMM/SecurityModeControl.c | 7 + openair3/NAS/UE/EMM/emmData.h | 2 + openair3/SECU/aes_128_cbc_cmac.c | 169 +++++------------- openair3/SECU/aes_128_cbc_cmac.h | 6 +- openair3/SECU/nas_stream_eea1.c | 34 ++-- openair3/SECU/nas_stream_eea1.h | 3 + openair3/SECU/nas_stream_eea2.c | 17 +- openair3/SECU/nas_stream_eea2.h | 37 +--- openair3/SECU/nas_stream_eia1.c | 24 ++- openair3/SECU/nas_stream_eia1.h | 3 + openair3/SECU/nas_stream_eia2.c | 19 +- openair3/SECU/nas_stream_eia2.h | 3 + openair3/SECU/secu_defs.c | 67 +++++++ openair3/SECU/secu_defs.h | 31 +++- 27 files changed, 323 insertions(+), 274 deletions(-) diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c index 99081a5ffc..d1beaa5d4b 100644 --- a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c +++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c @@ -2124,9 +2124,12 @@ pdcp_config_set_security( pdcp_pP->cipheringAlgorithm, pdcp_pP->integrityProtAlgorithm); - kRRCenc != NULL ? memcpy(pdcp_pP->kRRCenc, kRRCenc, 32) : memset(pdcp_pP->kRRCenc, 0, 32); - kRRCint != NULL ? memcpy(pdcp_pP->kRRCint, kRRCint, 32) : memset(pdcp_pP->kRRCint, 0, 32); - kUPenc != NULL ? memcpy(pdcp_pP->kUPenc, kUPenc, 32) : memset(pdcp_pP->kUPenc, 0, 32); + kRRCenc != NULL ? memcpy(pdcp_pP->kRRCenc, kRRCenc+16, 16) : memset(pdcp_pP->kRRCenc, 0, 16); + kRRCint != NULL ? memcpy(pdcp_pP->kRRCint, kRRCint+16, 16) : memset(pdcp_pP->kRRCint, 0, 16); + kUPenc != NULL ? memcpy(pdcp_pP->kUPenc, kUPenc+16, 16) : memset(pdcp_pP->kUPenc, 0, 16); + + pdcp_pP->security_container_rrc = stream_security_container_create(pdcp_pP->cipheringAlgorithm, pdcp_pP->integrityProtAlgorithm, pdcp_pP->kRRCenc, pdcp_pP->kRRCint); + pdcp_pP->security_container_up = stream_security_container_create(pdcp_pP->cipheringAlgorithm, 0, pdcp_pP->kUPenc, NULL); /* Activate security */ pdcp_pP->security_activated = 1; @@ -2196,6 +2199,8 @@ void rrc_pdcp_config_req ( pdcp_p->last_submitted_pdcp_rx_sn = 4095; pdcp_p->seq_num_size = 0; pdcp_p->first_missing_pdu = -1; + stream_security_container_delete(pdcp_p->security_container_rrc); + stream_security_container_delete(pdcp_p->security_container_up); pdcp_p->security_activated = 0; h_rc = hashtable_remove(pdcp_coll_p, key); break; diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp.h b/openair2/LAYER2/PDCP_v10.1.0/pdcp.h index 6b0e2499fe..4a7f71b315 100644 --- a/openair2/LAYER2/PDCP_v10.1.0/pdcp.h +++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp.h @@ -181,9 +181,12 @@ typedef struct pdcp_s { * Control-Plane RRC integrity key * These keys are configured by RRC layer */ - uint8_t kUPenc[32]; - uint8_t kRRCint[32]; - uint8_t kRRCenc[32]; + uint8_t kUPenc[16]; + uint8_t kRRCint[16]; + uint8_t kRRCenc[16]; + + stream_security_container_t *security_container_rrc; + stream_security_container_t *security_container_up; uint8_t security_activated; diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c index a88d4028ae..e969467231 100644 --- a/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c +++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c @@ -112,6 +112,7 @@ pdcp_apply_security( { uint8_t *buffer_encrypted = NULL; nas_stream_cipher_t encrypt_params = {0}; + stream_security_container_t *container; DevAssert(pdcp_pP != NULL); DevAssert(pdcp_pdu_buffer != NULL); @@ -122,7 +123,11 @@ pdcp_apply_security( encrypt_params.direction = (pdcp_pP->is_ue == 1) ? SECU_DIRECTION_UPLINK : SECU_DIRECTION_DOWNLINK; encrypt_params.bearer = rb_id - 1; encrypt_params.count = pdcp_get_next_count_tx(pdcp_pP, srb_flagP, current_sn); - encrypt_params.key_length = 16; + + if (srb_flagP) + container = pdcp_pP->security_container_rrc; + else + container = pdcp_pP->security_container_up; if (srb_flagP) { /* SRBs */ @@ -133,7 +138,7 @@ pdcp_apply_security( encrypt_params.message = pdcp_pdu_buffer; encrypt_params.blength = (pdcp_header_len + sdu_buffer_size) << 3; - encrypt_params.key = pdcp_pP->kRRCint + 16; // + 128; + encrypt_params.context = container->integrity_context; mac_i = &pdcp_pdu_buffer[pdcp_header_len + sdu_buffer_size]; @@ -142,15 +147,13 @@ pdcp_apply_security( stream_compute_integrity(pdcp_pP->integrityProtAlgorithm, &encrypt_params, mac_i); - - encrypt_params.key = pdcp_pP->kRRCenc; // + 128 // bit key } else { LOG_D(PDCP, "[OSA][RB %ld] %s Applying user-plane security\n", rb_id, (pdcp_pP->is_ue != 0) ? "UE -> eNB" : "eNB -> UE"); - - encrypt_params.key = pdcp_pP->kUPenc;// + 128; } + encrypt_params.context = container->ciphering_context; + encrypt_params.message = &pdcp_pdu_buffer[pdcp_header_len]; encrypt_params.blength = sdu_buffer_size << 3; @@ -180,6 +183,7 @@ pdcp_validate_security( { uint8_t *buffer_decrypted = NULL; nas_stream_cipher_t decrypt_params = {0}; + stream_security_container_t *container; DevAssert(pdcp_pP != NULL); @@ -195,17 +199,17 @@ pdcp_validate_security( decrypt_params.count = pdcp_get_next_count_rx(pdcp_pP, srb_flagP, hfn, sn); decrypt_params.message = &pdcp_pdu_buffer[pdcp_header_len]; decrypt_params.blength = (sdu_buffer_size - pdcp_header_len) << 3; - decrypt_params.key_length = 16; if (srb_flagP) { LOG_D(PDCP, "[OSA][RB %ld] %s Validating control-plane security\n", rb_id, (pdcp_pP->is_ue != 0) ? "eNB -> UE" : "UE -> eNB"); - decrypt_params.key = pdcp_pP->kRRCenc;// + 128; + container = pdcp_pP->security_container_rrc; } else { LOG_D(PDCP, "[OSA][RB %ld] %s Validating user-plane security\n", rb_id, (pdcp_pP->is_ue != 0) ? "eNB -> UE" : "UE -> eNB"); - decrypt_params.key = pdcp_pP->kUPenc;// + 128; + container = pdcp_pP->security_container_up; } /* Uncipher the block */ + decrypt_params.context = container->ciphering_context; stream_compute_encrypt(pdcp_pP->cipheringAlgorithm, &decrypt_params, buffer_decrypted); if (!IS_SOFTMODEM_IQPLAYER) { @@ -213,7 +217,7 @@ pdcp_validate_security( /* Now check the integrity of the complete PDU */ decrypt_params.message = pdcp_pdu_buffer; decrypt_params.blength = sdu_buffer_size << 3; - decrypt_params.key = pdcp_pP->kRRCint + 16;// 128; + decrypt_params.context = container->integrity_context; if (pdcp_pP->integrityProtAlgorithm != EIA0_ALG_ID) { uint8_t result[4] = {0}; @@ -221,10 +225,9 @@ pdcp_validate_security( if (memcmp(result, &pdcp_pdu_buffer[sdu_buffer_size], 4) != 0) { LOG_E(PDCP, - "[OSA][RB %ld] %s failed to validate MAC-I (key %llx) of incoming PDU\n", + "[OSA][RB %ld] %s failed to validate MAC-I of incoming PDU\n", rb_id, - (pdcp_pP->is_ue != 0) ? "UE" : "eNB", - ((long long unsigned int *)decrypt_params.key)[0]); + (pdcp_pP->is_ue != 0) ? "UE" : "eNB"); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_VALIDATE_SECURITY, VCD_FUNCTION_OUT); return -1; } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h index 5d2fd56f70..d26ce83b82 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h @@ -28,6 +28,7 @@ #include "nr_pdcp_sdu.h" #include "openair2/RRC/NR/rrc_gNB_radio_bearers.h" +#include "openair3/SECU/secu_defs.h" /* PDCP Formats according to clause 6.2 of 3GPP TS 38.323 */ /* SN Size applicable to SRBs, UM DRBs and AM DRBs */ @@ -151,16 +152,17 @@ typedef struct nr_pdcp_entity_t { int integrity_algorithm; unsigned char ciphering_key[16]; unsigned char integrity_key[16]; - void *security_context; - void (*cipher)(void *security_context, + stream_security_context_t *security_context; + void (*cipher)(stream_security_context_t *security_context, unsigned char *buffer, int length, int bearer, int count, int direction); - void (*free_security)(void *security_context); - void *integrity_context; - void (*integrity)(void *integrity_context, unsigned char *out, + void (*free_security)(stream_security_context_t *security_context); + stream_security_context_t *integrity_context; + void (*integrity)(stream_security_context_t *integrity_context, + unsigned char *out, unsigned char *buffer, int length, int bearer, int count, int direction); - void (*free_integrity)(void *integrity_context); + void (*free_integrity)(stream_security_context_t *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_nia1.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.c index 3dbf15136b..d21c98610e 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.c @@ -28,24 +28,23 @@ #include "openair3/SECU/secu_defs.h" #include "openair3/SECU/key_nas_deriver.h" -void *nr_pdcp_integrity_nia1_init(unsigned char *integrity_key) +stream_security_context_t *nr_pdcp_integrity_nia1_init(unsigned char *integrity_key) { nas_stream_cipher_t *ret; ret = calloc(1, sizeof(*ret)); if (ret == NULL) abort(); - ret->key = malloc(16); if (ret->key == NULL) abort(); - memcpy(ret->key, integrity_key, 16); - ret->key_length = 16; /* unused */ + ret->context = malloc(16); if (ret->context == NULL) abort(); + memcpy(ret->context, integrity_key, 16); - return ret; + return (stream_security_context_t *)ret; } -void nr_pdcp_integrity_nia1_integrity(void *integrity_context, +void nr_pdcp_integrity_nia1_integrity(stream_security_context_t *integrity_context, unsigned char *out, unsigned char *buffer, int length, int bearer, int count, int direction) { - nas_stream_cipher_t *ctx = integrity_context; + nas_stream_cipher_t *ctx = (nas_stream_cipher_t *)integrity_context; ctx->message = buffer; ctx->count = count; @@ -56,9 +55,9 @@ void nr_pdcp_integrity_nia1_integrity(void *integrity_context, stream_compute_integrity(EIA1_128_ALG_ID, ctx, out); } -void nr_pdcp_integrity_nia1_free_integrity(void *integrity_context) +void nr_pdcp_integrity_nia1_free_integrity(stream_security_context_t *integrity_context) { - nas_stream_cipher_t *ctx = integrity_context; - free(ctx->key); + nas_stream_cipher_t *ctx = (nas_stream_cipher_t *)integrity_context; + free(ctx->context); free(ctx); } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.h index 5c44bad4a7..89f6d9156a 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.h @@ -22,13 +22,15 @@ #ifndef _NR_PDCP_INTEGRITY_NIA1_H_ #define _NR_PDCP_INTEGRITY_NIA1_H_ -void *nr_pdcp_integrity_nia1_init(unsigned char *integrity_key); +#include "openair3/SECU/secu_defs.h" -void nr_pdcp_integrity_nia1_integrity(void *integrity_context, +stream_security_context_t *nr_pdcp_integrity_nia1_init(unsigned char *integrity_key); + +void nr_pdcp_integrity_nia1_integrity(stream_security_context_t *integrity_context, unsigned char *out, unsigned char *buffer, int length, int bearer, int count, int direction); -void nr_pdcp_integrity_nia1_free_integrity(void *integrity_context); +void nr_pdcp_integrity_nia1_free_integrity(stream_security_context_t *integrity_context); #endif /* _NR_PDCP_INTEGRITY_NIA1_H_ */ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c index 497ed154d5..f6db1b10a6 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c @@ -30,29 +30,25 @@ #include "openair3/SECU/aes_128.h" #include "openair3/SECU/aes_128_cbc_cmac.h" -void *nr_pdcp_integrity_nia2_init(uint8_t integrity_key[16]) +stream_security_context_t *nr_pdcp_integrity_nia2_init(uint8_t integrity_key[16]) { - // This is a hack. Reduce the 3 functions to just cipher? - // No. The overhead is x8 times more. Don't change before measuring - // return integrity_key; cbc_cmac_ctx_t* ctx = calloc(1, sizeof(cbc_cmac_ctx_t)); AssertFatal(ctx, "Memory exhausted"); *ctx = init_aes_128_cbc_cmac(integrity_key); - return ctx; + return (stream_security_context_t *)ctx; } -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_integrity(stream_security_context_t *integrity_context, unsigned char *out, unsigned char *buffer, int length, int bearer, int count, int direction) { DevAssert(integrity_context != NULL); DevAssert(out != NULL); DevAssert(buffer != NULL); DevAssert(length > -1); - // Strange range: [1-32] instead of [0-31] DevAssert(bearer > 0 && bearer < 33); DevAssert(count > -1); - cbc_cmac_ctx_t* ctx = (cbc_cmac_ctx_t*)integrity_context; + cbc_cmac_ctx_t *ctx = (cbc_cmac_ctx_t *)integrity_context; aes_128_t k_iv = {0}; memcpy(&k_iv.key, ctx->key, sizeof(k_iv.key)); @@ -64,18 +60,13 @@ void nr_pdcp_integrity_nia2_integrity(void *integrity_context, unsigned char *ou uint8_t result[16] = {0}; byte_array_t msg = {.buf = buffer, .len = length}; - cipher_aes_128_cbc_cmac((cbc_cmac_ctx_t*)integrity_context, &k_iv, msg, sizeof(result), result); - //aes_128_cbc_cmac(&k_iv, msg, sizeof(result), result); + cipher_aes_128_cbc_cmac(ctx, &k_iv, msg, sizeof(result), result); - // AES CMAC (RFC 4493) outputs 128 bits but NR PDCP PDUs have a MAC-I of - // 32 bits (see 38.323 6.2). RFC 4493 2.1 says to truncate most significant - // bit first (so seems to say 33.401 B.2.3) - // Precondition: out should have enough space... memcpy(out, result, 4); } -void nr_pdcp_integrity_nia2_free_integrity(void *integrity_context) +void nr_pdcp_integrity_nia2_free_integrity(stream_security_context_t *integrity_context) { - free( ( cbc_cmac_ctx_t*) integrity_context); + free_aes_128_cbc_cmac((cbc_cmac_ctx_t *)integrity_context); + free(integrity_context); } - diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h index 53f117aafb..c9bd6cd881 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h @@ -24,13 +24,15 @@ #include -void *nr_pdcp_integrity_nia2_init(uint8_t integrity_key[16]); +#include "openair3/SECU/secu_defs.h" -void nr_pdcp_integrity_nia2_integrity(void *integrity_context, +stream_security_context_t *nr_pdcp_integrity_nia2_init(uint8_t integrity_key[16]); + +void nr_pdcp_integrity_nia2_integrity(stream_security_context_t *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); +void nr_pdcp_integrity_nia2_free_integrity(stream_security_context_t *integrity_context); #endif /* _NR_PDCP_INTEGRITY_NIA2_H_ */ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c index e54b854125..617d5c3372 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c @@ -29,14 +29,14 @@ #include #include -void *nr_pdcp_security_nea2_init(unsigned char *ciphering_key) +stream_security_context_t *nr_pdcp_security_nea2_init(unsigned char *ciphering_key) { // This is a hack, IMO init, cipher and free functions should be reduced to cipher. // Test show a ~10% more processing time - return ciphering_key; + return (stream_security_context_t *)ciphering_key; } -void nr_pdcp_security_nea2_cipher(void *security_context, unsigned char *buffer, int length, int bearer, int count, int direction) +void nr_pdcp_security_nea2_cipher(stream_security_context_t *security_context, unsigned char *buffer, int length, int bearer, int count, int direction) { DevAssert(security_context != NULL); DevAssert(buffer != NULL); @@ -62,7 +62,7 @@ void nr_pdcp_security_nea2_cipher(void *security_context, unsigned char *buffer, memcpy(buffer, out, length); } -void nr_pdcp_security_nea2_free_security(void *security_context) +void nr_pdcp_security_nea2_free_security(stream_security_context_t *security_context) { (void)security_context; } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.h index c3667c93d4..4550e7290d 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.h @@ -22,12 +22,14 @@ #ifndef _NR_PDCP_SECURITY_NEA2_H_ #define _NR_PDCP_SECURITY_NEA2_H_ -void *nr_pdcp_security_nea2_init(unsigned char *ciphering_key); +#include "openair3/SECU/secu_defs.h" -void nr_pdcp_security_nea2_cipher(void *security_context, +stream_security_context_t *nr_pdcp_security_nea2_init(unsigned char *ciphering_key); + +void nr_pdcp_security_nea2_cipher(stream_security_context_t *security_context, unsigned char *buffer, int length, int bearer, int count, int direction); -void nr_pdcp_security_nea2_free_security(void *security_context); +void nr_pdcp_security_nea2_free_security(stream_security_context_t *security_context); #endif /* _NR_PDCP_SECURITY_NEA2_H_ */ diff --git a/openair3/NAS/COMMON/API/NETWORK/nas_message.c b/openair3/NAS/COMMON/API/NETWORK/nas_message.c index 9a7dd8bd89..32454a13e3 100644 --- a/openair3/NAS/COMMON/API/NETWORK/nas_message.c +++ b/openair3/NAS/COMMON/API/NETWORK/nas_message.c @@ -1009,8 +1009,7 @@ static int _nas_message_decrypt( (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK", (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num, count); - stream_cipher.key = emm_security_context->knas_enc.value; - stream_cipher.key_length = AUTH_KNAS_ENC_SIZE; + stream_cipher.context = emm_security_context->security_container->ciphering_context; stream_cipher.count = count; stream_cipher.bearer = 0x00; //33.401 section 8.1.1 stream_cipher.direction = direction; @@ -1045,8 +1044,7 @@ static int _nas_message_decrypt( (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK", (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num, count); - stream_cipher.key = emm_security_context->knas_enc.value; - stream_cipher.key_length = AUTH_KNAS_ENC_SIZE; + stream_cipher.context = emm_security_context->security_container->ciphering_context; stream_cipher.count = count; stream_cipher.bearer = 0x00; //33.401 section 8.1.1 stream_cipher.direction = direction; @@ -1178,8 +1176,7 @@ static int _nas_message_encrypt( (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK", (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num, count); - stream_cipher.key = emm_security_context->knas_enc.value; - stream_cipher.key_length = AUTH_KNAS_ENC_SIZE; + stream_cipher.context = emm_security_context->security_container->ciphering_context; stream_cipher.count = count; stream_cipher.bearer = 0x00; //33.401 section 8.1.1 stream_cipher.direction = direction; @@ -1208,8 +1205,7 @@ static int _nas_message_encrypt( (direction == SECU_DIRECTION_UPLINK) ? "UPLINK":"DOWNLINK", (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num, count); - stream_cipher.key = emm_security_context->knas_enc.value; - stream_cipher.key_length = AUTH_KNAS_ENC_SIZE; + stream_cipher.context = emm_security_context->security_container->ciphering_context; stream_cipher.count = count; stream_cipher.bearer = 0x00; //33.401 section 8.1.1 stream_cipher.direction = direction; @@ -1347,8 +1343,7 @@ static uint32_t _nas_message_get_mac( fflush(stderr); #endif - stream_cipher.key = emm_security_context->knas_int.value; - stream_cipher.key_length = AUTH_KNAS_INT_SIZE; + stream_cipher.context = emm_security_context->security_container->integrity_context; stream_cipher.count = count; stream_cipher.bearer = 0x00; //33.401 section 8.1.1 stream_cipher.direction = direction; @@ -1391,8 +1386,7 @@ static uint32_t _nas_message_get_mac( (direction == SECU_DIRECTION_UPLINK) ? emm_security_context->ul_count.seq_num:emm_security_context->dl_count.seq_num, count); - stream_cipher.key = emm_security_context->knas_int.value; - stream_cipher.key_length = AUTH_KNAS_INT_SIZE; + stream_cipher.context = emm_security_context->security_container->integrity_context; stream_cipher.count = count; stream_cipher.bearer = 0x00; //33.401 section 8.1.1 stream_cipher.direction = direction; diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 57c3e1df58..ae6587df8d 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -515,6 +515,10 @@ 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_create() (handle ciphering in general) */ + /* todo: stream_security_container_delete() is not called anywhere, deal with that */ + nas->security_container = stream_security_container_create(0, 2 /* hardcoded: nia2 */, NULL, nas->security.knas_int); OctetString res; res.length = 16; res.value = calloc(1,16); @@ -598,8 +602,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.key = nas->security.knas_int; - stream_cipher.key_length = 16; + stream_cipher.context = nas->security_container->integrity_context; stream_cipher.count = nas->security.mm_counter++; stream_cipher.bearer = 1; stream_cipher.direction = 0; @@ -701,8 +704,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia } initialNasMsg->length = length; - stream_cipher.key = nas->security.knas_int; - stream_cipher.key_length = 16; + stream_cipher.context = nas->security_container->integrity_context; stream_cipher.count = nas->security.mm_counter++; stream_cipher.bearer = 1; stream_cipher.direction = 0; @@ -764,8 +766,7 @@ 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 = { - .key = nas->security.knas_int, - .key_length = 16, + .context = nas->security_container->integrity_context, .count = nas->security.mm_counter++, .bearer = 1, .direction = 0, @@ -857,8 +858,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.key = nas->security.knas_int; - stream_cipher.key_length = 16; + stream_cipher.context = nas->security_container->integrity_context; stream_cipher.count = nas->security.mm_counter++; stream_cipher.bearer = 1; stream_cipher.direction = 0; diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg_sim.h index a892bf1b4b..0d2d8f5cc9 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.h @@ -41,6 +41,7 @@ #include "as_message.h" #include "FGSUplinkNasTransport.h" #include +#include "secu_defs.h" #define PLAIN_5GS_MSG 0b0000 #define INTEGRITY_PROTECTED 0b0001 @@ -98,6 +99,7 @@ typedef struct { typedef struct { uicc_t *uicc; ue_sa_security_key_t security; + stream_security_container_t *security_container; Guti5GSMobileIdentity_t *guti; bool termination_procedure; } nr_ue_nas_t; diff --git a/openair3/NAS/UE/EMM/SecurityModeControl.c b/openair3/NAS/UE/EMM/SecurityModeControl.c index 3386499663..a068b65d68 100644 --- a/openair3/NAS/UE/EMM/SecurityModeControl.c +++ b/openair3/NAS/UE/EMM/SecurityModeControl.c @@ -258,6 +258,7 @@ int emm_proc_security_mode_command(nas_user_t *user, int native_ksi, int ksi, LOG_TRACE(INFO, "EMM-PROC - Update Current security context"); /* Release non-current security context */ + stream_security_container_delete(user->emm_data->security->security_container); _security_release(user->emm_data->security); user->emm_data->security = user->emm_data->non_current; /* Reset the uplink NAS COUNT counter */ @@ -265,6 +266,12 @@ int emm_proc_security_mode_command(nas_user_t *user, int native_ksi, int ksi, user->emm_data->security->ul_count.seq_num = 0; /* Set new security context indicator */ security_context_is_new = true; + /* create contexts */ + user->emm_data->security->security_container = stream_security_container_create( + seea, + seia, + user->emm_data->non_current->knas_enc.value, + user->emm_data->non_current->knas_int.value); } } diff --git a/openair3/NAS/UE/EMM/emmData.h b/openair3/NAS/UE/EMM/emmData.h index 50219b7883..d90820edf9 100644 --- a/openair3/NAS/UE/EMM/emmData.h +++ b/openair3/NAS/UE/EMM/emmData.h @@ -42,6 +42,7 @@ Description Defines internal private data handled by EPS Mobility #include "commonDef.h" #include "networkDef.h" #include "securityDef.h" +#include "openair3/SECU/secu_defs.h" #include "OctetString.h" #include "nas_timer.h" @@ -138,6 +139,7 @@ typedef struct emm_security_context_s { //OctetString ksgsn; /* SGSN security key (mapped context) */ OctetString knas_enc; /* NAS cyphering key */ OctetString knas_int; /* NAS integrity key */ + stream_security_container_t *security_container; /* container for NAS security */ struct count_s{ uint32_t spare:8; uint32_t overflow:16; diff --git a/openair3/SECU/aes_128_cbc_cmac.c b/openair3/SECU/aes_128_cbc_cmac.c index da925f6d1e..6d50cb693a 100644 --- a/openair3/SECU/aes_128_cbc_cmac.c +++ b/openair3/SECU/aes_128_cbc_cmac.c @@ -31,101 +31,56 @@ // code for version 3.0 or greater #include -static -const char *propq = NULL; - -void aes_128_cbc_cmac(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) -{ - OSSL_LIB_CTX* library_context = OSSL_LIB_CTX_new(); - DevAssert(library_context != NULL); - - /* Fetch the CMAC implementation */ - EVP_MAC* mac = EVP_MAC_fetch(library_context, "CMAC", propq); - DevAssert(mac != NULL); - - /* Create a context for the CMAC operation */ - EVP_MAC_CTX* mctx = EVP_MAC_CTX_new(mac); - DevAssert(mctx != NULL); - - // The underlying cipher to be used - char cipher_name[] = "aes128"; - - OSSL_PARAM params[2] = {0}; - params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, cipher_name, - sizeof(cipher_name)); - params[1] = OSSL_PARAM_construct_end(); - - /* Initialise the CMAC operation */ - int rc = EVP_MAC_init(mctx, k_iv->key, sizeof(k_iv->key), params); - DevAssert(rc != 0); - - size_t sz_iv = 0; - uint8_t* iv = NULL; - if(k_iv->type == AES_INITIALIZATION_VECTOR_8){ - sz_iv = 8; - iv = (uint8_t*)k_iv->iv8.iv; - } else if(k_iv->type == AES_INITIALIZATION_VECTOR_16) { - sz_iv = 16; - iv = (uint8_t*)k_iv->iv16.iv; - } else { - DevAssert(0!=0 && "Unknwon Initialization vector"); - } - - /* Make one or more calls to process the data to be authenticated */ - rc = EVP_MAC_update(mctx, iv, sz_iv); - DevAssert(rc != 0); - - /* Make one or more calls to process the data to be authenticated */ - rc = EVP_MAC_update(mctx, msg.buf, msg.len); - DevAssert(rc != 0); - - /* Make a call to the final with a NULL buffer to get the length of the MAC */ - size_t out_len = 0; - rc = EVP_MAC_final(mctx, out, &out_len, len_out); - DevAssert(rc != 0); - - EVP_MAC_CTX_free(mctx); - EVP_MAC_free(mac); - OSSL_LIB_CTX_free(library_context); -} - -cbc_cmac_ctx_t init_aes_128_cbc_cmac(uint8_t key[16]) +cbc_cmac_ctx_t init_aes_128_cbc_cmac(const uint8_t key[16]) { DevAssert(key != NULL); - OSSL_LIB_CTX* library_context = OSSL_LIB_CTX_new(); - DevAssert(library_context != NULL); - /* Fetch the CMAC implementation */ - EVP_MAC* mac = EVP_MAC_fetch(library_context, "CMAC", propq); + EVP_MAC *mac_implementation = EVP_MAC_fetch(NULL, "CMAC", NULL); + DevAssert(mac_implementation != NULL); + + /* Create a context for the CMAC operation */ + EVP_MAC_CTX* mac = EVP_MAC_CTX_new(mac_implementation); DevAssert(mac != NULL); - cbc_cmac_ctx_t ctx = {.lib_ctx = library_context, .mac = mac }; - - assert(16 == sizeof(ctx.key)); + cbc_cmac_ctx_t ctx = { .lib_ctx = mac_implementation, .mac = mac }; memcpy(ctx.key, key, 16); - return ctx; + // The underlying cipher to be used + char cipher_name[] = "aes128"; + + OSSL_PARAM params[2] = {0}; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, cipher_name, + sizeof(cipher_name)); + params[1] = OSSL_PARAM_construct_end(); + + /* Initialise the CMAC operation */ + int rc = EVP_MAC_init(mac, key, 16, params); + DevAssert(rc != 0); + + return ctx; } void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) { DevAssert(ctx != NULL); DevAssert(k_iv != NULL); - /* Create a context for the CMAC operation */ - EVP_MAC_CTX* mctx = EVP_MAC_CTX_new(ctx->mac); - DevAssert(mctx != NULL); - // The underlying cipher to be used - char cipher_name[] = "aes128"; - - OSSL_PARAM params[2] = {0}; - params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, cipher_name, - sizeof(cipher_name)); - params[1] = OSSL_PARAM_construct_end(); - - /* Initialise the CMAC operation */ - int rc = EVP_MAC_init(mctx, k_iv->key, sizeof(k_iv->key), params); + /* reset the CMAC */ + /* There is a bug with version <= 3.0.2 of OpenSSL for which we must + * pass the key to reset. (Some Ubuntu version provide this version.) + * Passing the key has some impact on performance. + * todo: drop support for old versions of openssl. + */ +#if OPENSSL_VERSION_NUMBER <= 0x30000020 + /* todo: when/if we remove this code, don't forget to check use of k_iv->key + * somewhere else in the codebase. + * If not used, remove from aes_128_t* + */ + int rc = EVP_MAC_init(ctx->mac, k_iv->key, 16, NULL); +#else + int rc = EVP_MAC_init(ctx->mac, NULL, 0, NULL); +#endif DevAssert(rc != 0); size_t sz_iv = 0; @@ -141,66 +96,33 @@ void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, b } /* Make one or more calls to process the data to be authenticated */ - rc = EVP_MAC_update(mctx, iv, sz_iv); + rc = EVP_MAC_update(ctx->mac, iv, sz_iv); DevAssert(rc != 0); /* Make one or more calls to process the data to be authenticated */ - rc = EVP_MAC_update(mctx, msg.buf, msg.len); + rc = EVP_MAC_update(ctx->mac, msg.buf, msg.len); DevAssert(rc != 0); /* Make a call to the final with a NULL buffer to get the length of the MAC */ size_t out_len = 0; - rc = EVP_MAC_final(mctx, out, &out_len, len_out); + rc = EVP_MAC_final(ctx->mac, out, &out_len, len_out); DevAssert(rc != 0); - - EVP_MAC_CTX_free(mctx); } void free_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx) { DevAssert(ctx != NULL); - EVP_MAC_free(ctx->mac); - OSSL_LIB_CTX_free(ctx->lib_ctx); + EVP_MAC_CTX_free(ctx->mac); + EVP_MAC_free(ctx->lib_ctx); } #else // code for 1.1.x or lower -void aes_128_cbc_cmac(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) -{ - DevAssert(k_iv != NULL); - - CMAC_CTX *ctx = CMAC_CTX_new(); - DevAssert(ctx != NULL); - - CMAC_Init(ctx, k_iv->key, sizeof(k_iv->key), EVP_aes_128_cbc(), NULL); - - size_t sz_iv = 0; - uint8_t* iv = NULL; - if(k_iv->type == AES_INITIALIZATION_VECTOR_8){ - sz_iv = 8; - iv = (uint8_t*)k_iv->iv8.iv; - } else if(k_iv->type == AES_INITIALIZATION_VECTOR_16) { - sz_iv = 16; - iv = (uint8_t*)k_iv->iv16.iv; - } else { - AssertFatal(0 != 0, "Unknwon Initialization vector"); - } - - CMAC_Update(ctx, iv, sz_iv); - - CMAC_Update(ctx, msg.buf, msg.len); - size_t len_res = 0; - CMAC_Final(ctx, out, &len_res); - DevAssert(len_res <= len_out); - - CMAC_CTX_free(ctx); -} - -cbc_cmac_ctx_t init_aes_128_cbc_cmac(uint8_t key[16]) +cbc_cmac_ctx_t init_aes_128_cbc_cmac(const uint8_t key[16]) { DevAssert(key != NULL); - cbc_cmac_ctx_t ctx = {.lib_ctx = NULL, .mac = NULL }; + cbc_cmac_ctx_t ctx = {.lib_ctx = NULL, .mac = NULL }; ctx.mac = CMAC_CTX_new(); DevAssert(ctx.mac != NULL); @@ -218,9 +140,9 @@ void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, b DevAssert(k_iv != NULL); //From https://man.openbsd.org/CMAC_Init.3 - //If ctx is already initialized, CMAC_Init() can be called again with key, - //cipher, and impl all set to NULL and key_len set to 0. In that case, any - //data already processed is discarded and ctx is re-initialized to start + //If ctx is already initialized, CMAC_Init() can be called again with key, + //cipher, and impl all set to NULL and key_len set to 0. In that case, any + //data already processed is discarded and ctx is re-initialized to start //reading data anew. CMAC_Init(ctx->mac, NULL, 0, NULL, NULL); @@ -251,4 +173,3 @@ void free_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx) } #endif - diff --git a/openair3/SECU/aes_128_cbc_cmac.h b/openair3/SECU/aes_128_cbc_cmac.h index b5118118c3..de3b664dd7 100644 --- a/openair3/SECU/aes_128_cbc_cmac.h +++ b/openair3/SECU/aes_128_cbc_cmac.h @@ -36,12 +36,8 @@ typedef struct { uint8_t key[16]; } cbc_cmac_ctx_t ; -void aes_128_cbc_cmac(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]); - -cbc_cmac_ctx_t init_aes_128_cbc_cmac(uint8_t key[16]); - +cbc_cmac_ctx_t init_aes_128_cbc_cmac(const uint8_t key[16]); void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]); - void free_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx); #endif diff --git a/openair3/SECU/nas_stream_eea1.c b/openair3/SECU/nas_stream_eea1.c index a4e4b84ca4..027205353b 100644 --- a/openair3/SECU/nas_stream_eea1.c +++ b/openair3/SECU/nas_stream_eea1.c @@ -38,10 +38,9 @@ void nas_stream_encrypt_eea1(nas_stream_cipher_t const *stream_cipher, uint8_t * //uint32_t byte_length; uint32_t *KS; uint32_t K[4],IV[4]; + uint8_t *key = (uint8_t *)stream_cipher->context; DevAssert(stream_cipher != NULL); - DevAssert(stream_cipher->key != NULL); - DevAssert(stream_cipher->key_length == 16); DevAssert(out != NULL); n = ( stream_cipher->blength + 31 ) / 32; @@ -50,22 +49,20 @@ void nas_stream_encrypt_eea1(nas_stream_cipher_t const *stream_cipher, uint8_t * memset(&snow_3g_context, 0, sizeof(snow_3g_context)); /*Initialisation*/ - /* Load the confidentiality key for SNOW 3G initialization as in section - 3.4. */ - memcpy(K+3,stream_cipher->key+0,4); /*K[3] = key[0]; we assume + /* Load the confidentiality key for SNOW 3G initialization as in section 3.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 * most important bit of key*/ - memcpy(K+2,stream_cipher->key+4,4); /*K[2] = key[1];*/ - memcpy(K+1,stream_cipher->key+8,4); /*K[1] = key[2];*/ - memcpy(K+0,stream_cipher->key+12,4); /*K[0] = key[3]; we assume - K[0]=key[96]||key[97]||...||key[127] , with key[127] the - * least important bit of key*/ + memcpy(K+2,key+4,4); /*K[2] = key[1];*/ + memcpy(K+1,key+8,4); /*K[1] = key[2];*/ + memcpy(K+0,key+12,4); /*K[0] = key[3]; we assume + * K[0]=key[96]||key[97]||...||key[127] , with key[127] the + * least important bit of key*/ K[3] = hton_int32(K[3]); K[2] = hton_int32(K[2]); K[1] = hton_int32(K[1]); K[0] = hton_int32(K[0]); - /* Prepare the initialization vector (IV) for SNOW 3G initialization as in - section 3.4. */ + /* Prepare the initialization vector (IV) for SNOW 3G initialization as in section 3.4. */ IV[3] = stream_cipher->count; IV[2] = ((((uint32_t)stream_cipher->bearer) << 3) | ((((uint32_t)stream_cipher->direction) & 0x1) << 2)) << 24; IV[1] = IV[3]; @@ -104,3 +101,16 @@ void nas_stream_encrypt_eea1(nas_stream_cipher_t const *stream_cipher, uint8_t * out[ceil_index - 1] = stream_cipher->message[ceil_index - 1]; } } + +stream_security_context_t *stream_ciphering_init_eea1(const uint8_t *ciphering_key) +{ + void *ret = calloc(1, 16); + AssertFatal(ret != NULL, "out of memory\n"); + memcpy(ret, ciphering_key, 16); + return (stream_security_context_t *)ret; +} + +void stream_ciphering_free_eea1(stream_security_context_t *ciphering_context) +{ + free(ciphering_context); +} diff --git a/openair3/SECU/nas_stream_eea1.h b/openair3/SECU/nas_stream_eea1.h index d713dc60d3..ebddd7af35 100644 --- a/openair3/SECU/nas_stream_eea1.h +++ b/openair3/SECU/nas_stream_eea1.h @@ -29,4 +29,7 @@ void nas_stream_encrypt_eea1(nas_stream_cipher_t const *stream_cipher, uint8_t *out); +stream_security_context_t *stream_ciphering_init_eea1(const uint8_t *ciphering_key); +void stream_ciphering_free_eea1(stream_security_context_t *ciphering_context); + #endif diff --git a/openair3/SECU/nas_stream_eea2.c b/openair3/SECU/nas_stream_eea2.c index dbdc040883..6600afaace 100644 --- a/openair3/SECU/nas_stream_eea2.c +++ b/openair3/SECU/nas_stream_eea2.c @@ -33,15 +33,13 @@ void nas_stream_encrypt_eea2(nas_stream_cipher_t const *stream_cipher, uint8_t *out) { DevAssert(stream_cipher != NULL); - DevAssert(stream_cipher->key != NULL); - DevAssert(stream_cipher->key_length == 32); DevAssert(stream_cipher->bearer < 32); DevAssert(stream_cipher->direction < 2); DevAssert(stream_cipher->message != NULL); DevAssert(stream_cipher->blength > 7); aes_128_t p = {0}; - memcpy(p.key, stream_cipher->key, stream_cipher->key_length); + memcpy(p.key, stream_cipher->context, 16); p.type = AES_INITIALIZATION_VECTOR_16; p.iv16.d.count = htonl(stream_cipher->count); p.iv16.d.bearer = stream_cipher->bearer; @@ -54,3 +52,16 @@ void nas_stream_encrypt_eea2(nas_stream_cipher_t const *stream_cipher, uint8_t * byte_array_t msg = {.buf = stream_cipher->message, .len = byte_lenght}; aes_128_ctr(&p, msg, len_out, out); } + +stream_security_context_t *stream_ciphering_init_eea2(const uint8_t *ciphering_key) +{ + void *ret = calloc(1, 16); + AssertFatal(ret != NULL, "out of memory\n"); + memcpy(ret, ciphering_key, 16); + return (stream_security_context_t *)ret; +} + +void stream_ciphering_free_eea2(stream_security_context_t *ciphering_context) +{ + free(ciphering_context); +} diff --git a/openair3/SECU/nas_stream_eea2.h b/openair3/SECU/nas_stream_eea2.h index c6133c987d..f64fff6c4d 100644 --- a/openair3/SECU/nas_stream_eea2.h +++ b/openair3/SECU/nas_stream_eea2.h @@ -26,40 +26,9 @@ #include "secu_defs.h" #include -/* Rijndael S-box OSA_SR */ - -static const uint8_t OSA_SR[256] = { - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, - 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, - 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, - 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, - 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, - 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, - 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, - 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, - 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, - 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; - -/* S-box OSA_SQ */ - -static const uint8_t OSA_SQ[256] = { - 0x25, 0x24, 0x73, 0x67, 0xD7, 0xAE, 0x5C, 0x30, 0xA4, 0xEE, 0x6E, 0xCB, 0x7D, 0xB5, 0x82, 0xDB, 0xE4, 0x8E, 0x48, 0x49, - 0x4F, 0x5D, 0x6A, 0x78, 0x70, 0x88, 0xE8, 0x5F, 0x5E, 0x84, 0x65, 0xE2, 0xD8, 0xE9, 0xCC, 0xED, 0x40, 0x2F, 0x11, 0x28, - 0x57, 0xD2, 0xAC, 0xE3, 0x4A, 0x15, 0x1B, 0xB9, 0xB2, 0x80, 0x85, 0xA6, 0x2E, 0x02, 0x47, 0x29, 0x07, 0x4B, 0x0E, 0xC1, - 0x51, 0xAA, 0x89, 0xD4, 0xCA, 0x01, 0x46, 0xB3, 0xEF, 0xDD, 0x44, 0x7B, 0xC2, 0x7F, 0xBE, 0xC3, 0x9F, 0x20, 0x4C, 0x64, - 0x83, 0xA2, 0x68, 0x42, 0x13, 0xB4, 0x41, 0xCD, 0xBA, 0xC6, 0xBB, 0x6D, 0x4D, 0x71, 0x21, 0xF4, 0x8D, 0xB0, 0xE5, 0x93, - 0xFE, 0x8F, 0xE6, 0xCF, 0x43, 0x45, 0x31, 0x22, 0x37, 0x36, 0x96, 0xFA, 0xBC, 0x0F, 0x08, 0x52, 0x1D, 0x55, 0x1A, 0xC5, - 0x4E, 0x23, 0x69, 0x7A, 0x92, 0xFF, 0x5B, 0x5A, 0xEB, 0x9A, 0x1C, 0xA9, 0xD1, 0x7E, 0x0D, 0xFC, 0x50, 0x8A, 0xB6, 0x62, - 0xF5, 0x0A, 0xF8, 0xDC, 0x03, 0x3C, 0x0C, 0x39, 0xF1, 0xB8, 0xF3, 0x3D, 0xF2, 0xD5, 0x97, 0x66, 0x81, 0x32, 0xA0, 0x00, - 0x06, 0xCE, 0xF6, 0xEA, 0xB7, 0x17, 0xF7, 0x8C, 0x79, 0xD6, 0xA7, 0xBF, 0x8B, 0x3F, 0x1F, 0x53, 0x63, 0x75, 0x35, 0x2C, - 0x60, 0xFD, 0x27, 0xD3, 0x94, 0xA5, 0x7C, 0xA1, 0x05, 0x58, 0x2D, 0xBD, 0xD9, 0xC7, 0xAF, 0x6B, 0x54, 0x0B, 0xE0, 0x38, - 0x04, 0xC8, 0x9D, 0xE7, 0x14, 0xB1, 0x87, 0x9C, 0xDF, 0x6F, 0xF9, 0xDA, 0x2A, 0xC4, 0x59, 0x16, 0x74, 0x91, 0xAB, 0x26, - 0x61, 0x76, 0x34, 0x2B, 0xAD, 0x99, 0xFB, 0x72, 0xEC, 0x33, 0x12, 0xDE, 0x98, 0x3B, 0xC0, 0x9B, 0x3E, 0x18, 0x10, 0x3A, - 0x56, 0xE1, 0x77, 0xC9, 0x1E, 0x9E, 0x95, 0xA3, 0x90, 0x19, 0xA8, 0x6C, 0x09, 0xD0, 0xF0, 0x86}; - void nas_stream_encrypt_eea2(nas_stream_cipher_t const *stream_cipher, uint8_t *out); +stream_security_context_t *stream_ciphering_init_eea2(const uint8_t *ciphering_key); +void stream_ciphering_free_eea2(stream_security_context_t *ciphering_context); + #endif diff --git a/openair3/SECU/nas_stream_eia1.c b/openair3/SECU/nas_stream_eia1.c index f080db7aac..c648ee585f 100644 --- a/openair3/SECU/nas_stream_eia1.c +++ b/openair3/SECU/nas_stream_eia1.c @@ -126,16 +126,17 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const *stream_cipher, uint8_t o uint64_t M_D_2; int rem_bits; uint32_t mask = 0; - uint32_t *message; + 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,stream_cipher->key+0,4); /*K[3] = key[0]; we assume + memcpy(K+3,key+0,4); /*K[3] = key[0]; we assume K[3]=key[0]||key[1]||...||key[31] , with key[0] the * most important bit of key*/ - memcpy(K+2,stream_cipher->key+4,4); /*K[2] = key[1];*/ - memcpy(K+1,stream_cipher->key+8,4); /*K[1] = key[2];*/ - memcpy(K+0,stream_cipher->key+12,4); /*K[0] = key[3]; we assume + memcpy(K+2,key+4,4); /*K[2] = key[1];*/ + memcpy(K+1,key+8,4); /*K[1] = key[2];*/ + memcpy(K+0,key+12,4); /*K[0] = key[3]; we assume K[0]=key[96]||key[97]||...||key[127] , with key[127] the * least important bit of key*/ K[3] = hton_int32(K[3]); @@ -213,3 +214,16 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const *stream_cipher, uint8_t o MAC_I = hton_int32(MAC_I); memcpy(out, &MAC_I, 4); } + +stream_security_context_t *stream_integrity_init_eia1(const uint8_t *integrity_key) +{ + void *ret = calloc(1, 16); + AssertFatal(ret != NULL, "out of memory\n"); + memcpy(ret, integrity_key, 16); + return (stream_security_context_t *)ret; +} + +void stream_integrity_free_eia1(stream_security_context_t *integrity_context) +{ + free(integrity_context); +} diff --git a/openair3/SECU/nas_stream_eia1.h b/openair3/SECU/nas_stream_eia1.h index 58a7f8be99..a385781035 100644 --- a/openair3/SECU/nas_stream_eia1.h +++ b/openair3/SECU/nas_stream_eia1.h @@ -26,4 +26,7 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const* stream_cipher, uint8_t out[4]); +stream_security_context_t *stream_integrity_init_eia1(const uint8_t *integrity_key); +void stream_integrity_free_eia1(stream_security_context_t *integrity_context); + #endif diff --git a/openair3/SECU/nas_stream_eia2.c b/openair3/SECU/nas_stream_eia2.c index 2655c8ad81..073592ec6e 100644 --- a/openair3/SECU/nas_stream_eia2.c +++ b/openair3/SECU/nas_stream_eia2.c @@ -45,11 +45,11 @@ void nas_stream_encrypt_eia2(nas_stream_cipher_t const *stream_cipher, uint8_t o DevAssert(stream_cipher != NULL); DevAssert(stream_cipher->message != NULL); DevAssert(stream_cipher->bearer < 32); - DevAssert(stream_cipher->key_length == 16); DevAssert((stream_cipher->blength & 0x7) == 0); + cbc_cmac_ctx_t *ctx = (cbc_cmac_ctx_t *)stream_cipher->context; aes_128_t k_iv = {0}; - memcpy(k_iv.key, stream_cipher->key, sizeof(k_iv.key)); + memcpy(&k_iv.key, ctx->key, 16); k_iv.type = AES_INITIALIZATION_VECTOR_8; k_iv.iv8.d.bearer = stream_cipher->bearer; k_iv.iv8.d.direction = stream_cipher->direction; @@ -58,8 +58,21 @@ void nas_stream_encrypt_eia2(nas_stream_cipher_t const *stream_cipher, uint8_t o size_t const m_length = stream_cipher->blength >> 3; uint8_t result[16] = {0}; byte_array_t msg = {.buf = stream_cipher->message, .len = m_length }; - aes_128_cbc_cmac(&k_iv, msg, sizeof(result), result); + cipher_aes_128_cbc_cmac((cbc_cmac_ctx_t *)stream_cipher->context, &k_iv, msg, sizeof(result), result); memcpy(out, result, 4); } +stream_security_context_t *stream_integrity_init_eia2(const uint8_t *integrity_key) +{ + cbc_cmac_ctx_t *ret = calloc(1, sizeof(cbc_cmac_ctx_t)); + AssertFatal(ret != NULL, "out of memory\n"); + *ret = init_aes_128_cbc_cmac(integrity_key); + return (stream_security_context_t *)ret; +} + +void stream_integrity_free_eia2(stream_security_context_t *integrity_context) +{ + free_aes_128_cbc_cmac((cbc_cmac_ctx_t *)integrity_context); + free(integrity_context); +} diff --git a/openair3/SECU/nas_stream_eia2.h b/openair3/SECU/nas_stream_eia2.h index 1349408fb7..b3da1d14a4 100644 --- a/openair3/SECU/nas_stream_eia2.h +++ b/openair3/SECU/nas_stream_eia2.h @@ -26,4 +26,7 @@ void nas_stream_encrypt_eia2(nas_stream_cipher_t const* stream_cipher, uint8_t out[4]); +stream_security_context_t *stream_integrity_init_eia2(const uint8_t *integrity_key); +void stream_integrity_free_eia2(stream_security_context_t *integrity_context); + #endif diff --git a/openair3/SECU/secu_defs.c b/openair3/SECU/secu_defs.c index 58a8754bad..47dc3affc4 100644 --- a/openair3/SECU/secu_defs.c +++ b/openair3/SECU/secu_defs.c @@ -64,3 +64,70 @@ void stream_compute_encrypt(eea_alg_id_e alg, nas_stream_cipher_t const* stream_ } } +stream_security_context_t *stream_integrity_init(int integrity_algorithm, const uint8_t *integrity_key) +{ + switch (integrity_algorithm) { + case EEA0_ALG_ID: return NULL; + case EEA1_128_ALG_ID: return stream_integrity_init_eia1(integrity_key); + case EEA2_128_ALG_ID: return stream_integrity_init_eia2(integrity_key); + default: AssertFatal(0, "unsupported integrity algorithm\n"); + } +} + +void stream_integrity_free(int integrity_algorithm, stream_security_context_t *integrity_context) +{ + switch (integrity_algorithm) { + case EEA0_ALG_ID: return; + case EEA1_128_ALG_ID: return stream_integrity_free_eia1(integrity_context); + case EEA2_128_ALG_ID: return stream_integrity_free_eia2(integrity_context); + default: AssertFatal(0, "unsupported integrity algorithm\n"); + } +} + +stream_security_context_t *stream_ciphering_init(int ciphering_algorithm, const uint8_t *ciphering_key) +{ + switch (ciphering_algorithm) { + case EEA0_ALG_ID: return NULL; + case EEA1_128_ALG_ID: return stream_ciphering_init_eea1(ciphering_key); + case EEA2_128_ALG_ID: return stream_ciphering_init_eea2(ciphering_key); + default: AssertFatal(0, "unsupported ciphering algorithm\n"); + } +} + +void stream_ciphering_free(int ciphering_algorithm, stream_security_context_t *ciphering_context) +{ + switch (ciphering_algorithm) { + case EEA0_ALG_ID: return; + case EEA1_128_ALG_ID: return stream_ciphering_free_eea1(ciphering_context); + case EEA2_128_ALG_ID: return stream_ciphering_free_eea2(ciphering_context); + default: AssertFatal(0, "unsupported ciphering algorithm\n"); + } +} + +stream_security_container_t *stream_security_container_create(int ciphering_algorithm, + int integrity_algorithm, + const uint8_t *ciphering_key, + const uint8_t *integrity_key) +{ + stream_security_container_t *container = calloc(1, sizeof(*container)); + AssertFatal(container != NULL, "out of memory\n"); + + container->integrity_algorithm = integrity_algorithm; + container->ciphering_algorithm = ciphering_algorithm; + + container->integrity_context = stream_integrity_init(integrity_algorithm, integrity_key); + container->ciphering_context = stream_ciphering_init(ciphering_algorithm, ciphering_key); + + return container; +} + +void stream_security_container_delete(stream_security_container_t *container) +{ + /* passing NULL is accepted */ + if (container == NULL) + return; + + stream_integrity_free(container->integrity_algorithm, container->integrity_context); + stream_ciphering_free(container->ciphering_algorithm, container->ciphering_context); + free(container); +} diff --git a/openair3/SECU/secu_defs.h b/openair3/SECU/secu_defs.h index 7b96544bd5..3dd4cb7cfd 100644 --- a/openair3/SECU/secu_defs.h +++ b/openair3/SECU/secu_defs.h @@ -28,17 +28,42 @@ #define SECU_DIRECTION_UPLINK 0 #define SECU_DIRECTION_DOWNLINK 1 +/* stream_security_context_t is an opaque structure + * it is different for each integrity and ciphering algorithm in use + */ +typedef void stream_security_context_t; + +/* stream_security_container_t contains the current configuration + * of integrity and ciphering. It is used by PDCP and NAS. + */ typedef struct { - uint8_t *key; - uint32_t key_length; + int integrity_algorithm; + int ciphering_algorithm; + stream_security_context_t *ciphering_context; + stream_security_context_t *integrity_context; +} stream_security_container_t; + +typedef struct { + stream_security_context_t *context; uint32_t count; uint8_t bearer; uint8_t direction; uint8_t *message; /* length in bits */ - uint32_t blength; + uint32_t blength; } nas_stream_cipher_t; +stream_security_context_t *stream_integrity_init(int integrity_algorithm, const uint8_t *integrity_key); +stream_security_context_t *stream_ciphering_init(int ciphering_algorithm, const uint8_t *ciphering_key); +void stream_integrity_free(int integrity_algorithm, stream_security_context_t *integrity_context); +void stream_ciphering_free(int ciphering_algorithm, stream_security_context_t *ciphering_context); + +stream_security_container_t *stream_security_container_create(int ciphering_algorithm, + int integrity_algorithm, + const uint8_t *ciphering_key, + const uint8_t *integrity_key); +void stream_security_container_delete(stream_security_container_t *container); + /*! * @brief Encrypt/Decrypt a block of data based on the provided algorithm * @param[in] algorithm Algorithm used to encrypt the data