Implement SUCI Profile Scheme A for 5G UE

Extend UICC configuration parsing to provide: routing_indicator,
protection_scheme, home_network_public_key, and home_network_public_key_id.
Use the protection_scheme value to decide what SUCI Profile Scheme to
apply during SUCI generation. Add support for Profile Scheme A which
provides ECIES-based encryption using Curve25519 and X9.63 KDF as outlined
in TS 33.501 Section C.3.4.1 Profile A.

When a configuration file specifies an unsupported Profile Scheme,
the NAS layer triggers a fatal error. This occurs either because
Profile Scheme B is unimplemented or the build uses OpenSSL < 3.0,
which lacks Curve25519 and X9.63 KDF support, ensuring users are
informed of the incompatibility.
This commit is contained in:
Aaron van Diepen
2026-03-23 15:38:22 +01:00
parent af4b0d53e9
commit 8ec7b4009b
15 changed files with 466 additions and 37 deletions

View File

@@ -674,6 +674,8 @@ target_link_libraries(UTIL PUBLIC instrumentation)
set(SECURITY_SRC
${OPENAIR3_DIR}/SECU/secu_defs.c
${OPENAIR3_DIR}/SECU/curve_25519.c
${OPENAIR3_DIR}/SECU/x963_kdf.c
${OPENAIR3_DIR}/SECU/kdf.c
${OPENAIR3_DIR}/SECU/aes_128_ctr.c
${OPENAIR3_DIR}/SECU/aes_128_cbc_cmac.c
@@ -1911,7 +1913,7 @@ target_link_libraries(nr-uesoftmodem PRIVATE
target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt nr_ue_phy_meas)
target_link_libraries(nr-uesoftmodem PRIVATE ${T_LIB})
target_link_libraries(nr-uesoftmodem PRIVATE nr_nas lib_uicc usim_lib)
target_link_libraries(nr-uesoftmodem PRIVATE nr_nas lib_uicc usim_lib SECURITY)
target_link_libraries(nr-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
# force the generation of ASN.1 so that we don't need to wait during the build

View File

@@ -80,6 +80,8 @@ add_boolean_option(NAS_BUILT_IN_UE False "UE NAS layer present in this
# SECU LIB
################################################################################
set(secu_cn_SRC
${OPENAIR3_DIR}/SECU/curve_25519.c
${OPENAIR3_DIR}/SECU/x963_kdf.c
${OPENAIR3_DIR}/SECU/aes_128_ctr.c
${OPENAIR3_DIR}/SECU/aes_128_cbc_cmac.c
${OPENAIR3_DIR}/SECU/sha_256_hmac.c

View File

@@ -94,6 +94,51 @@ char *itoa(int i) {
return strdup(buffer);
}
/****************************************************************************
** **
** Name: digit_string_to_bcd_value() **
** **
** Description: Converts a decimal ASCII coded string into its BCD encoded**
** value. **
** **
** Inputs: bcd_value: Output buffer **
** digit_string: Input digit string **
** size: Size of bcd_value in bytes **
** **
** Outputs: bcd_value: Converted BCD value **
** Return: 0 on success, -1 on error **
** **
***************************************************************************/
int digit_string_to_bcd_value(uint8_t *bcd_value, const char *digit_string, int size)
{
int i;
uint8_t len = strlen(digit_string);
/* Accept even (size*2) or odd (size*2 - 1) digit counts */
if (len < size * 2 - 1 || len > size * 2) {
fprintf(stderr, "the string '%s' should be of length %d or %d\n", digit_string, size * 2 - 1, size * 2);
return -1;
}
for (i = 0; i < size; i++) {
uint8_t low = digit_string[2 * i] - '0';
uint8_t high = (2 * i + 1 < len) ? digit_string[2 * i + 1] - '0' : 0xF;
if (low > 9 || (high > 9 && high != 0xF))
goto error;
bcd_value[i] = (high << 4) | low;
}
return 0;
error:
fprintf(stderr, "the string '%s' is not a valid digit string\n", digit_string);
for (i = 0; i < size; i++)
bcd_value[i] = 0;
return -1;
}
/**
* @brief Convert a version string x.y.z into numbers.
*

View File

@@ -123,6 +123,8 @@ static inline void *malloc_or_fail(size_t size)
int hex_char_to_hex_value (char c);
// Converts an hexadecimal ASCII coded string into its value.**
int hex_string_to_hex_value (uint8_t *hex_value, const char *hex_string, int size);
// Converts a decimal ASCII coded string into its BCD encoded value.
int digit_string_to_bcd_value(uint8_t *bcd_value, const char *digit_string, int size);
/* Map task id to printable name. */
typedef struct {

View File

@@ -78,18 +78,22 @@ The simulation reads values from a named section in the config file.
**Config options in the `uicc` section**:
| Parameter | Description | Default value |
|---------------|-----------------------------------|--------------------------------------------|
| `imsi` | User IMSI | `2089900007487` |
| `nmc_size` | Number of digits in NMC | `2` |
| `key` | Subscription key (Ki) | `fec86ba6eb707ed08905757b1bb44b8f` |
| `opc` | OPc value | `c42449363bbad02b66d16bc975d77cc1` |
| `amf` | AMF value | `8000` |
| `sqn` | Sequence number | `000000` |
| `dnn` | Default DNN (APN) | `oai` |
| `nssai_sst` | NSSAI slice/service type | `1` |
| `nssai_sd` | NSSAI slice differentiator | `0xffffff` |
| `imeisv` | IMEISV string | `6754567890123413` |
| Parameter | Description | Default value |
|------------------------------|-----------------------------------|--------------------------------------------------------------------|
| `imsi` | User IMSI | `2089900007487` |
| `nmc_size` | Number of digits in NMC | `2` |
| `key` | Subscription key (Ki) | `fec86ba6eb707ed08905757b1bb44b8f` |
| `opc` | OPc value | `c42449363bbad02b66d16bc975d77cc1` |
| `amf` | AMF value | `8000` |
| `sqn` | Sequence number | `000000` |
| `dnn` | Default DNN (APN) | `oai` |
| `nssai_sst` | NSSAI slice/service type | `1` |
| `nssai_sd` | NSSAI slice differentiator | `0xffffff` |
| `imeisv` | IMEISV string | `6754567890123413` |
| `routing_indicator` | Routing Indicator | `0000` |
| `protection_scheme` | SUCI Profile Scheme | `0` |
| `home_network_public_key_id` | Home Network Public Key ID | `1` |
| `home_network_public_key` | Home Network Public Key | `5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650` |
These are parsed and stored in the `uicc_t` structure.
@@ -116,3 +120,37 @@ The **IMEISV**, is encoded using the `fill_imeisv()` helper. This function extra
See TS 24.501 §4.4 for reference.
#### SUCI (Subscription Concealed Identifier)
The **SUCI**, is generated using the `fill_suci()` helper. This function extracts the MCC, MNC, and MSIN from the configured `imsi` with the use of `nmc_size` in the UICC context and populates the mobile identity structure.
Contains:
* MCC and MNC (public network identity)
* Routing Indicator
* Protection Scheme ID
* Home Network Public Key ID
* Concealed or clear MSIN depending on the protection scheme
If the UE is unable to generate SUCI due to configuration or crypto limitations, the UE will fail to generate a `Registration Request`.
###### 0. Null Scheme (TS 33.501 §C.2)
MSIN in cleartext.
###### 1. Profile A (TS 33.501 §C.3.4.1)
MSIN is concealed using elliptic curve cryptography.
Based on:
* Curve25519 for key agreement
* X9.63 KDF for key derivation
(requires OpenSSL ≥ 3.0)
###### 2. Profile B (TS 33.501 §C.3.4.2)
MSIN is concealed using elliptic curve cryptography.
Based on:
* P-256 for key agreement
* X9.63 KDF for key derivation
(currently not supported)

View File

@@ -1066,6 +1066,8 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NGAP/ngap_gNB_overload.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NGAP/ngap_gNB_default_values.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/secu_defs.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/curve_25519.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/x963_kdf.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/sha_256_hmac.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/nas_stream_eea0.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/aes_128_ecb.h \
@@ -1088,6 +1090,8 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/aes_128_ctr.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/nas_stream_eia2.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/snow3g.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/curve_25519.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/x963_kdf.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/sha_256_hmac.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/aes_128_cbc_cmac.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/SECU/nas_stream_eia1.c \

View File

@@ -17,7 +17,7 @@
static int decode_guti_5gs_mobile_identity(Guti5GSMobileIdentity_t *guti, const uint8_t *buffer);
static int encode_guti_5gs_mobile_identity(const Guti5GSMobileIdentity_t *guti, uint8_t *buffer);
static int encode_suci_5gs_mobile_identity(const Suci5GSMobileIdentity_t *suci, uint8_t *buffer);
static int encode_suci_5gs_mobile_identity(const Suci5GSMobileIdentity_t *suci, uint8_t *buffer, uint32_t len);
static int encode_imeisv_5gs_mobile_identity(const Imeisv5GSMobileIdentity_t *imeisv, uint8_t *buffer);
int encode_stmsi_5gs_mobile_identity(uint8_t *buffer, const Stmsi5GSMobileIdentity_t *stmsi, uint32_t len)
@@ -121,7 +121,7 @@ int encode_5gs_mobile_identity(const FGSMobileIdentity *fgsmobileidentity, uint8
}
if (fgsmobileidentity->suci.typeofidentity == FGS_MOBILE_IDENTITY_SUCI) {
encoded_rc = encode_suci_5gs_mobile_identity(&fgsmobileidentity->suci, buffer + encoded);
encoded_rc = encode_suci_5gs_mobile_identity(&fgsmobileidentity->suci, buffer + encoded, len);
}
if (fgsmobileidentity->imeisv.typeofidentity == FGS_MOBILE_IDENTITY_IMEISV) {
@@ -218,9 +218,13 @@ static int encode_guti_5gs_mobile_identity(const Guti5GSMobileIdentity_t *guti,
return encoded;
}
static int encode_suci_5gs_mobile_identity(const Suci5GSMobileIdentity_t *suci, uint8_t *buffer)
static int encode_suci_5gs_mobile_identity(const Suci5GSMobileIdentity_t *suci, uint8_t *buffer, uint32_t len)
{
uint32_t encoded = 0;
if (len < 8)
return -1;
*(buffer + encoded) = (suci->supiformat << 4) | (suci->typeofidentity);
encoded++;
*(buffer + encoded) = ((suci->mccdigit2 & 0xf) << 4) | (suci->mccdigit1 & 0xf);
@@ -242,15 +246,34 @@ static int encode_suci_5gs_mobile_identity(const Suci5GSMobileIdentity_t *suci,
*(buffer + encoded) = suci->homenetworkpki;
encoded++;
const char *ptr = suci->schemeoutput;
while (ptr < suci->schemeoutput + strlen(suci->schemeoutput)) {
buffer[encoded] = ((*(ptr + 1) - '0') << 4) | (*(ptr) - '0');
encoded++;
ptr += 2;
}
DevAssert(suci->schemeoutput);
size_t rawlen = strlen(suci->schemeoutput);
size_t outlen = (rawlen + 1) / 2;
if (strlen(suci->schemeoutput) % 2 == 1)
buffer[encoded++] = ((*(ptr - 1) - '0')) | 0xF0;
if (len - encoded < outlen)
return -1;
if (suci->protectionschemeId == 0) {
AssertFatal(suci->homenetworkpki == 0,
"Invalid SUCI: homenetworkpki=%u with null protection scheme (must be 0)\n",
suci->homenetworkpki);
int rc = digit_string_to_bcd_value(buffer + encoded, suci->schemeoutput, outlen);
AssertFatal(rc == 0,
"BCD encoding failed (rc=%d, input=\"%s\", len=%zu, out_len=%zu)\n",
rc,
suci->schemeoutput,
rawlen,
outlen);
} else {
int rc = hex_string_to_hex_value(buffer + encoded, suci->schemeoutput, outlen);
AssertFatal(rc == 0,
"Encoding SUCI schemeoutput failed (rc=%d, input=\"%s\", len=%zu, out_len=%zu)\n",
rc,
suci->schemeoutput,
rawlen,
outlen);
}
encoded += outlen;
return encoded;
}

View File

@@ -41,7 +41,7 @@ typedef struct {
uint8_t spare6: 1;
uint8_t protectionschemeId: 4;
uint8_t homenetworkpki;
char schemeoutput[32];
char schemeoutput[128];
} Suci5GSMobileIdentity_t;
typedef struct {

View File

@@ -9,6 +9,7 @@
#include "nr_nas_msg.h"
#include <netinet/in.h>
#include "NR_NAS_defs.h"
#include <openssl/opensslv.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,6 +54,18 @@
#include "key_nas_deriver.h"
#include "nr-uesoftmodem.h"
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include "curve_25519.h"
#include "aes_128_ctr.h"
#include "x963_kdf.h"
#include "sha_256_hmac.h"
static const char hex[] = "0123456789abcdef";
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
#define MY_OPENSSL_VERSION_STR OpenSSL_version(OPENSSL_VERSION)
#else
#define MY_OPENSSL_VERSION_STR SSLeay_version(SSLEAY_VERSION)
#endif
static nr_ue_nas_t nr_ue_nas[MAX_NUM_NR_UE_INST] = {0};
nr_ue_nas_t *get_nr_ue_nas_info(uint8_t ue_inst)
@@ -388,6 +401,83 @@ static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t b
return NAS_SECURITY_INTEGRITY_PASSED;
}
static void suci_profile_scheme_a_generate_output(const uint8_t home_network_public_key[32],
const char *msin,
const size_t msin_len,
char *schemeoutput)
{
/* 1> Eph. key pair generation */
uint8_t eph_priv[32] = {0};
uint8_t eph_pub[32] = {0};
x25519_generate_keypair(eph_priv, eph_pub);
/* 2> Key agreement */
uint8_t eph_shared_key[32] = {0};
x25519_shared_secret(eph_priv, home_network_public_key, eph_shared_key);
explicit_bzero(eph_priv, 32);
/* 3> Key derivation */
uint8_t kdf_output[64] = {0};
byte_array_t kdf_secret = {.buf = eph_shared_key, .len = 32};
byte_array_t kdf_info = {.buf = eph_pub, .len = 32};
x963_kdf(kdf_secret, kdf_info, 64, kdf_output);
explicit_bzero(eph_shared_key, 32);
aes_128_t aes_ctx;
aes_ctx.type = AES_INITIALIZATION_VECTOR_16;
memcpy(aes_ctx.key, kdf_output, 16);
memcpy(aes_ctx.iv16.iv, kdf_output + 16, 16);
uint8_t eph_mac_key[32] = {0};
memcpy(eph_mac_key, kdf_output + 32, 32);
explicit_bzero(kdf_output, 64);
/* 4> Symmetric encryption */
size_t msin_bcd_len = (msin_len + 1) / 2;
uint8_t msin_bcd[msin_bcd_len];
memset(msin_bcd, 0, msin_bcd_len);
int rc = digit_string_to_bcd_value(msin_bcd, msin, msin_bcd_len);
AssertFatal(rc == 0, "Encoding MSIN failed (rc=%d, input=\"%s\", len=%zu, out_len=%zu)", rc, msin, msin_len, msin_bcd_len);
byte_array_t payload = {.buf = msin_bcd, .len = msin_bcd_len};
uint8_t ciphertext[msin_bcd_len];
aes_128_ctr(&aes_ctx, payload, msin_bcd_len, ciphertext);
explicit_bzero(aes_ctx.key, 16);
explicit_bzero(aes_ctx.iv16.iv, 16);
explicit_bzero(msin_bcd, msin_bcd_len);
/* 5> MAC function */
uint8_t mac_full[32] = {0};
byte_array_t mac_input = {.buf = ciphertext, .len = msin_bcd_len};
sha_256_hmac(eph_mac_key, mac_input, 32, mac_full);
explicit_bzero(eph_mac_key, 32);
/* Build SUCI scheme output --- */
/* eph_pub (32 bytes -> 64 hex chars) */
for (int i = 0; i < 32; i++) {
*schemeoutput++ = hex[eph_pub[i] >> 4];
*schemeoutput++ = hex[eph_pub[i] & 0x0F];
}
/* ciphertext (~45 bytes -> ~90 hex chars) */
for (int i = 0; i < msin_bcd_len; i++) {
*schemeoutput++ = hex[ciphertext[i] >> 4];
*schemeoutput++ = hex[ciphertext[i] & 0x0F];
}
/* MAC (8 bytes -> 16 hex chars) */
for (int i = 0; i < 8; i++) {
*schemeoutput++ = hex[mac_full[i] >> 4];
*schemeoutput++ = hex[mac_full[i] & 0x0F];
}
}
static int fill_suci(FGSMobileIdentity *mi, const uicc_t *uicc)
{
mi->suci.typeofidentity = FGS_MOBILE_IDENTITY_SUCI;
@@ -397,7 +487,50 @@ static int fill_suci(FGSMobileIdentity *mi, const uicc_t *uicc)
mi->suci.mccdigit1 = uicc->imsiStr[0] - '0';
mi->suci.mccdigit2 = uicc->imsiStr[1] - '0';
mi->suci.mccdigit3 = uicc->imsiStr[2] - '0';
memcpy(mi->suci.schemeoutput, uicc->imsiStr + 3 + uicc->nmc_size, strlen(uicc->imsiStr) - (3 + uicc->nmc_size));
mi->suci.routingindicatordigit1 = uicc->routing_indicatorStr[0] - '0';
mi->suci.routingindicatordigit2 = uicc->routing_indicatorStr[1] - '0';
mi->suci.routingindicatordigit3 = uicc->routing_indicatorStr[2] - '0';
mi->suci.routingindicatordigit4 = uicc->routing_indicatorStr[3] - '0';
char *msin = uicc->imsiStr + 3 + uicc->nmc_size;
uint8_t msin_len = strlen(msin);
mi->suci.protectionschemeId = uicc->protection_scheme;
switch (uicc->protection_scheme) {
case 0: /* Null scheme (TS 33.501 C.2) */
{
mi->suci.homenetworkpki = 0;
memcpy(mi->suci.schemeoutput, msin, msin_len);
break;
}
case 1: /* Profile A (TS 33.501 C.3.4.1) */
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
mi->suci.homenetworkpki = uicc->home_network_public_key_id;
suci_profile_scheme_a_generate_output(uicc->home_network_public_key, msin, msin_len, mi->suci.schemeoutput);
#else
AssertFatal(false,
"Protection Scheme not supported when using a version below OpenSSL 3.0 %d (OpenSSL version: %s)\n",
uicc->protection_scheme,
MY_OPENSSL_VERSION_STR);
#endif
break;
}
case 2: /* Profile B (TS 33.501 C.3.4.2) */
{
mi->suci.homenetworkpki = uicc->home_network_public_key_id;
AssertFatal(false, "Unsupported Protection Scheme in UICC %d\n", uicc->protection_scheme);
break;
}
default: // Unknown schemes
{
AssertFatal(false, "Unknown Protection Scheme in UICC %d\n", uicc->protection_scheme);
}
}
LOG_D(NAS,
"SUCI in registration request: SUPI type: %d Type of Identity: %u MCC: %u%u%u, MNC: %u%u%u, \
Routing Indicator %d%d%d%d Protection Scheme ID: %u, Home Network PKI: %u, Scheme Output: %s\n",

View File

@@ -0,0 +1,74 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include "curve_25519.h"
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* code for version 3.0 or greater */
#include "common/utils/assertions.h"
#include <openssl/evp.h>
#include <openssl/core_names.h>
void x25519_generate_keypair(uint8_t priv[32], uint8_t pub[32])
{
DevAssert(priv != NULL);
DevAssert(pub != NULL);
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
DevAssert(pctx != NULL);
int rc = EVP_PKEY_keygen_init(pctx);
DevAssert(rc == 1);
EVP_PKEY *pkey = NULL;
rc = EVP_PKEY_keygen(pctx, &pkey);
DevAssert(rc == 1);
EVP_PKEY_CTX_free(pctx);
size_t len = 32;
rc = EVP_PKEY_get_raw_private_key(pkey, priv, &len);
DevAssert(rc == 1 && len == 32);
rc = EVP_PKEY_get_raw_public_key(pkey, pub, &len);
DevAssert(rc == 1 && len == 32);
EVP_PKEY_free(pkey);
}
void x25519_shared_secret(const uint8_t priv[32], const uint8_t peer_pub[32], uint8_t secret[32])
{
DevAssert(priv != NULL);
DevAssert(peer_pub != NULL);
DevAssert(secret != NULL);
EVP_PKEY *pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, priv, 32);
DevAssert(pkey != NULL);
EVP_PKEY *peerkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, peer_pub, 32);
DevAssert(peerkey != NULL);
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
DevAssert(ctx != NULL);
int rc = EVP_PKEY_derive_init(ctx);
DevAssert(rc == 1);
rc = EVP_PKEY_derive_set_peer(ctx, peerkey);
DevAssert(rc == 1);
size_t len = 32;
rc = EVP_PKEY_derive(ctx, secret, &len);
DevAssert(rc == 1 && len == 32);
EVP_PKEY_free(pkey);
EVP_PKEY_free(peerkey);
EVP_PKEY_CTX_free(ctx);
}
#endif

View File

@@ -0,0 +1,23 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef CURVE_25519_OAI_H
#define CURVE_25519_OAI_H
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* code for version 3.0 or greater */
#include <stdint.h>
#include <stddef.h>
void x25519_generate_keypair(uint8_t priv[32], uint8_t pub[32]);
void x25519_shared_secret(const uint8_t priv[32], const uint8_t peer_pub[32], uint8_t secret[32]);
#endif
#endif

49
openair3/SECU/x963_kdf.c Normal file
View File

@@ -0,0 +1,49 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include "x963_kdf.h"
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* code for version 3.0 or greater */
#include <string.h>
#include "common/utils/assertions.h"
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/core_names.h>
void x963_kdf(const byte_array_t sharedsecret, const byte_array_t sharedinfo, size_t len, uint8_t out[len])
{
DevAssert(sharedsecret.buf != NULL);
DevAssert(sharedsecret.len > 0);
DevAssert(out != NULL);
DevAssert(len > 0);
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "X963KDF", NULL);
DevAssert(kdf != NULL);
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
DevAssert(kctx != NULL);
EVP_KDF_free(kdf);
OSSL_PARAM params[4], *p = params;
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha256, strlen(SN_sha256));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, sharedsecret.buf, sharedsecret.len);
if (sharedinfo.buf != NULL && sharedinfo.len > 0) {
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, sharedinfo.buf, sharedinfo.len);
}
*p = OSSL_PARAM_construct_end();
int rc = EVP_KDF_derive(kctx, out, len, params);
DevAssert(rc == 1);
EVP_KDF_CTX_free(kctx);
}
#endif

23
openair3/SECU/x963_kdf.h Normal file
View File

@@ -0,0 +1,23 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef x963_KDF_OAI_H
#define x963_KDF_OAI_H
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* code for version 3.0 or greater */
#include <stdint.h>
#include <stdlib.h>
#include "byte_array.h"
void x963_kdf(const byte_array_t sharedsecret, const byte_array_t sharedinfo, size_t len, uint8_t out[len]);
#endif
#endif

View File

@@ -14,21 +14,26 @@ extern uint16_t NB_UE_INST;
" key: cyphering key\n"\
" opc: cyphering OPc\n"\
" imiesv: string with IMEISV value\n"
#define DEFAULT_HOME_NETWORK_PUBLIC_KEY "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* configuration parameters for the rfsimulator device */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define UICC_PARAMS_DESC { \
{"imsi", "USIM IMSI\n", 0, .strptr=&uicc->imsiStr, .defstrval="2089900007487", TYPE_STRING, 0 }, \
{"nmc_size" "number of digits in NMC", 0, .iptr=&uicc->nmc_size, .defintval=2, TYPE_INT, 0 }, \
{"key", "USIM Ki\n", 0, .strptr=&uicc->keyStr, .defstrval="fec86ba6eb707ed08905757b1bb44b8f", TYPE_STRING, 0 }, \
{"opc", "USIM OPc\n", 0, .strptr=&uicc->opcStr, .defstrval="c42449363bbad02b66d16bc975d77cc1", TYPE_STRING, 0 }, \
{"amf", "USIM amf\n", 0, .strptr=&uicc->amfStr, .defstrval="8000", TYPE_STRING, 0 }, \
{"sqn", "USIM sqn\n", 0, .strptr=&uicc->sqnStr, .defstrval="000000", TYPE_STRING, 0 }, \
{"dnn", "UE dnn (apn)\n", 0, .strptr=&uicc->dnnStr, .defstrval="oai", TYPE_STRING, 0 }, \
{"nssai_sst", "UE nssai\n", 0, .iptr=&uicc->nssai_sst, .defintval=1, TYPE_INT, 0 }, \
{"nssai_sd", "UE nssai\n", 0, .iptr=&uicc->nssai_sd, .defintval=0xffffff, TYPE_INT, 0 }, \
{"imeisv", "IMEISV\n", 0, .strptr=&uicc->imeisvStr, .defstrval="6754567890123413", TYPE_STRING, 0 }, \
#define UICC_PARAMS_DESC { \
{"imsi", "USIM IMSI\n", 0, .strptr=&uicc->imsiStr, .defstrval="2089900007487", TYPE_STRING, 0}, \
{"nmc_size", "number of digits in NMC", 0, .iptr=&uicc->nmc_size, .defintval=2, TYPE_INT, 0}, \
{"key", "USIM Ki\n", 0, .strptr=&uicc->keyStr, .defstrval="fec86ba6eb707ed08905757b1bb44b8f", TYPE_STRING, 0}, \
{"opc", "USIM OPc\n", 0, .strptr=&uicc->opcStr, .defstrval="c42449363bbad02b66d16bc975d77cc1", TYPE_STRING, 0}, \
{"amf", "USIM amf\n", 0, .strptr=&uicc->amfStr, .defstrval="8000", TYPE_STRING, 0}, \
{"sqn", "USIM sqn\n", 0, .strptr=&uicc->sqnStr, .defstrval="000000", TYPE_STRING, 0}, \
{"dnn", "UE dnn (apn)\n", 0, .strptr=&uicc->dnnStr, .defstrval="oai", TYPE_STRING, 0}, \
{"nssai_sst", "UE nssai\n", 0, .iptr=&uicc->nssai_sst, .defintval=1, TYPE_INT, 0}, \
{"nssai_sd", "UE nssai\n", 0, .iptr=&uicc->nssai_sd, .defintval=0xffffff, TYPE_INT, 0}, \
{"imeisv", "IMEISV\n", 0, .strptr=&uicc->imeisvStr, .defstrval="6754567890123413", TYPE_STRING, 0}, \
{"routing_indicator", "Routing Indicator\n", 0, .strptr = &uicc->routing_indicatorStr, .defstrval="0000", TYPE_STRING, 0}, \
{"protection_scheme", "SUCI Profile Scheme\n", 0, .iptr = &uicc->protection_scheme, .defintval=0, TYPE_INT, 0}, \
{"home_network_public_key_id", "Home Network Public Key ID\n", 0, .iptr=&uicc->home_network_public_key_id, .defintval=1, TYPE_INT, 0}, \
{"home_network_public_key", "Home Network Public Key\n", 0, .strptr=&uicc->home_network_public_keyStr, .defstrval=DEFAULT_HOME_NETWORK_PUBLIC_KEY, TYPE_STRING, 0}, \
};
static uicc_t** uiccArray=NULL;
@@ -62,6 +67,7 @@ uicc_t *init_uicc(char *sectionName) {
to_hex(uicc->opcStr,uicc->opc, sizeof(uicc->opc) );
to_hex(uicc->sqnStr,uicc->sqn, sizeof(uicc->sqn) );
to_hex(uicc->amfStr,uicc->amf, sizeof(uicc->amf) );
to_hex(uicc->home_network_public_keyStr, uicc->home_network_public_key, sizeof(uicc->home_network_public_key));
return uicc;
}

View File

@@ -37,6 +37,10 @@ typedef struct {
int nssai_sd;
pdu_session_config_t pdu_sessions[256];
int n_pdu_sessions;
char *routing_indicatorStr;
int protection_scheme;
char *home_network_public_keyStr;
int home_network_public_key_id;
uint8_t key[16];
uint8_t opc[16];
uint8_t amf[2];
@@ -49,6 +53,7 @@ typedef struct {
uint8_t ck[16];
uint8_t ik[16];
uint8_t milenage_res[8];
uint8_t home_network_public_key[32];
} uicc_t;
/*