mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
fix (NAS UE): add Service Request NAS container with PDU status and align security and KgNB sync
Extend the UE 5GS Service Request path to carry `PDU session status` inside a NAS message container and cipher only that container value when valid NAS contexts exist. This aligns the initial Service Request handling with the TS 24.501 rules for non-cleartext IEs. Treat the generated Service Request as integrity-protected whenever an integrity context is available, and keep the post-request KgNB refresh aligned with the NAS UL count used for that protected message. Changes: - update `generateServiceRequest()` in `nr_nas_msg.c` to derive a `PDU session status` bitmap from configured UE PDU sessions and treat it as the non-cleartext trigger for the initial Service Request - build an inner plain Service Request carrying `PDU session status`, place it in the `NAS message container`, cipher only the container value with the NAS ciphering context, and keep the outer Service Request integrity protected - extend `fgs_service_request` lib to support optional `PDU session status` and `NAS message container` fields - decode known optional Service Request TLV IEs with a `switch` and skip unsupported ones - add `free_fgs_service_request()` and `eq_fgs_service_request()` - extend `nas_lib_test` to cover Service Request encoding and decoding with `PDU session status`, `NAS message container`, and skipped optional IEs, and initialize the test logging/config stubs - set the outgoing ngKSI with `set_fgs_ksi(nas)` instead of hardcoding `NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE` - move `initialNasMsg->nas_data` allocation into the protected and plain branches so each path allocates after its final size accounting - increment `nas->security.nas_count_ul` after integrity MAC computation - derive a refreshed KgNB with `derive_kgnb()` and send it through `nas_itti_kgnb_refresh_req()` after the protected Service Request is built Refs: - TS 24.501 §4.4.6 (protection of initial NAS signalling messages) - TS 24.501 §4.4.4.1 and §4.4.6 allow an initial Service Request to be integrity protected and unciphered when a valid 5G NAS security context exists and no NAS message container is included. - TS 24.501 §8.2.16.1 (Service Request message content) - TS 24.501 §8.2.16.3 (PDU session status) - TS 24.501 §9.11.3.33 (NAS message container) - TS 33.501 §6.4.3.1 (NAS integrity inputs) - TS 33.501 §6.4.4.1 (NAS confidentiality inputs) - TS 33.501 §6.8.1.2.2 derives KgNB from the UL NAS COUNT of the NAS message that moves the UE from CM-IDLE to CM-CONNECTED, unless a subsequent NAS Security Mode Complete exists, in which case that newer UL NAS COUNT becomes the freshness input. Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
This commit is contained in:
@@ -76,15 +76,20 @@ typedef enum { PDU_SESSION_INACTIVE = 0, PDU_SESSION_ACTIVE } PSI_status_t;
|
||||
typedef enum { REACTIVATION_SUCCESS = 0, REACTIVATION_FAILED } PSI_reactivation_t;
|
||||
|
||||
#define FOREACH_SA_IEI(IEI_DEF) \
|
||||
IEI_DEF(IEI_PDU_SESSION_STATUS, 0x50) \
|
||||
IEI_DEF(IEI_ALLOWED_PDU_SESSION_STATUS, 0x25) \
|
||||
IEI_DEF(IEI_PDU_SESSION_REACT_RESULT, 0x26) \
|
||||
IEI_DEF(IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE, 0x72) \
|
||||
IEI_DEF(IEI_EAPMSG, 0x78) \
|
||||
IEI_DEF(IEI_CAG_INFO_LIST, 0x75) \
|
||||
IEI_DEF(IEI_PAGING_RESTRICTION, 0x28) \
|
||||
IEI_DEF(IEI_UE_REQUEST_TYPE, 0x29) \
|
||||
IEI_DEF(IEI_UPLINK_DATA_STATUS, 0x40) \
|
||||
IEI_DEF(IEI_PDU_SESSION_STATUS, 0x50) \
|
||||
IEI_DEF(IEI_T3446_VALUE, 0x5F) \
|
||||
IEI_DEF(IEI_T3448_VALUE, 0x6B) \
|
||||
IEI_DEF(IEI_T3446_VALUE, 0x5F)
|
||||
IEI_DEF(IEI_FGS_NAS_MESSAGE_CONTAINER, 0x71) \
|
||||
IEI_DEF(IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE, 0x72) \
|
||||
IEI_DEF(IEI_CAG_INFO_LIST, 0x75) \
|
||||
IEI_DEF(IEI_EAPMSG, 0x78)
|
||||
|
||||
// Enum for Service Accept IEIs
|
||||
// Enum for Service Request procedure IEIs
|
||||
typedef enum { FOREACH_SA_IEI(TO_ENUM) } nas_service_IEI_t;
|
||||
|
||||
static const text_info_t sa_iei_s[] = {FOREACH_SA_IEI(TO_TEXT)};
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "TLVEncoder.h"
|
||||
#include "TLVDecoder.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "fgs_service_request.h"
|
||||
#include "FGSMobileIdentity.h"
|
||||
#include "NasKeySetIdentifier.h"
|
||||
@@ -16,13 +18,29 @@
|
||||
|
||||
#define LEN_FGS_MOBILE_ID_CONTENTS_SIZE 2 // octets
|
||||
#define MIN_LEN_FGS_SERVICE_REQUEST 10 // octets
|
||||
#define TLV_IE_HEADER_LENGTH 2 // IEI + length
|
||||
#define IEI_NULL 0x00
|
||||
|
||||
/** @brief Get the total encoded size of a (optional) TLV IE (IEI + length + value) */
|
||||
static int get_tlv_ie_len(const uint8_t *buffer, uint32_t len)
|
||||
{
|
||||
if (len < TLV_IE_HEADER_LENGTH)
|
||||
return -1;
|
||||
|
||||
uint8_t ie_len = buffer[1];
|
||||
uint32_t total_ie_len = TLV_IE_HEADER_LENGTH + ie_len;
|
||||
if (len < total_ie_len)
|
||||
return -1;
|
||||
|
||||
return total_ie_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode 5GMM NAS Service Request message (8.2.16 of 3GPP TS 24.501)
|
||||
*/
|
||||
int encode_fgs_service_request(uint8_t *buffer, const fgs_service_request_msg_t *service_request, uint32_t len)
|
||||
{
|
||||
const uint32_t total_len = len;
|
||||
// Return if buffer is shorter than min length
|
||||
if (len < MIN_LEN_FGS_SERVICE_REQUEST)
|
||||
return -1;
|
||||
@@ -43,6 +61,24 @@ int encode_fgs_service_request(uint8_t *buffer, const fgs_service_request_msg_t
|
||||
uint16_t tmp = htons(encoded - LEN_FGS_MOBILE_ID_CONTENTS_SIZE - 1);
|
||||
memcpy(buffer + 1, &tmp, sizeof(tmp));
|
||||
|
||||
if (service_request->has_pdu_session_status) {
|
||||
byte_array_t ba = {.buf = buffer + encoded, .len = total_len - encoded};
|
||||
int encoded_rc = encode_pdu_session_ie(&ba, IEI_PDU_SESSION_STATUS, service_request->pdu_session_status);
|
||||
if (encoded_rc < 0)
|
||||
return -1;
|
||||
encoded += encoded_rc;
|
||||
}
|
||||
|
||||
if (service_request->fgsnasmessagecontainer != NULL) {
|
||||
int encoded_rc = encode_fgc_nas_message_container(service_request->fgsnasmessagecontainer,
|
||||
IEI_FGS_NAS_MESSAGE_CONTAINER,
|
||||
buffer + encoded,
|
||||
total_len - encoded);
|
||||
if (encoded_rc < 0)
|
||||
return -1;
|
||||
encoded += encoded_rc;
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
@@ -75,5 +111,93 @@ int decode_fgs_service_request(fgs_service_request_msg_t *sr, const uint8_t *buf
|
||||
}
|
||||
decoded += decoded_rc;
|
||||
|
||||
while (decoded < len) {
|
||||
uint8_t iei = buffer[decoded];
|
||||
|
||||
switch (iei) {
|
||||
case IEI_PDU_SESSION_STATUS: {
|
||||
decoded++;
|
||||
byte_array_t ba = create_byte_array(len - decoded, buffer + decoded);
|
||||
decoded_rc = decode_pdu_session_ie(sr->pdu_session_status, &ba);
|
||||
free_byte_array(ba);
|
||||
if (decoded_rc < 0) {
|
||||
LOG_E(NAS, "Failed to decode PDU Session Status\n");
|
||||
return -1;
|
||||
}
|
||||
decoded += decoded_rc;
|
||||
sr->has_pdu_session_status = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case IEI_FGS_NAS_MESSAGE_CONTAINER: {
|
||||
if (sr->fgsnasmessagecontainer != NULL)
|
||||
return -1;
|
||||
sr->fgsnasmessagecontainer = calloc_or_fail(1, sizeof(*sr->fgsnasmessagecontainer));
|
||||
byte_array_t ba = create_byte_array(len - decoded, buffer + decoded);
|
||||
decoded_rc = decode_fgc_nas_message_container(sr->fgsnasmessagecontainer, IEI_FGS_NAS_MESSAGE_CONTAINER, ba.buf, ba.len);
|
||||
free_byte_array(ba);
|
||||
if (decoded_rc < 0) {
|
||||
LOG_E(NAS, "Failed to decode FGS NAS Message Container\n");
|
||||
free(sr->fgsnasmessagecontainer);
|
||||
return -1;
|
||||
}
|
||||
decoded += decoded_rc;
|
||||
break;
|
||||
}
|
||||
|
||||
case IEI_UPLINK_DATA_STATUS:
|
||||
case IEI_ALLOWED_PDU_SESSION_STATUS:
|
||||
case IEI_UE_REQUEST_TYPE:
|
||||
case IEI_PAGING_RESTRICTION:
|
||||
// Skip the TLV IE (IEI + length + value)
|
||||
LOG_D(NAS, "Skipping TLV IE 0x%02x\n", iei);
|
||||
decoded_rc = get_tlv_ie_len(buffer + decoded, len - decoded);
|
||||
if (decoded_rc < 0)
|
||||
return -1;
|
||||
decoded += decoded_rc;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
void free_fgs_service_request(fgs_service_request_msg_t *msg)
|
||||
{
|
||||
DevAssert(msg);
|
||||
if (msg->fgsnasmessagecontainer) {
|
||||
free(msg->fgsnasmessagecontainer->nasmessagecontainercontents.value);
|
||||
msg->fgsnasmessagecontainer->nasmessagecontainercontents.length = 0;
|
||||
free(msg->fgsnasmessagecontainer);
|
||||
}
|
||||
}
|
||||
|
||||
bool eq_fgs_service_request(const fgs_service_request_msg_t *a, const fgs_service_request_msg_t *b)
|
||||
{
|
||||
_EQ_CHECK_INT(a->naskeysetidentifier.tsc, b->naskeysetidentifier.tsc);
|
||||
_EQ_CHECK_INT(a->naskeysetidentifier.naskeysetidentifier, b->naskeysetidentifier.naskeysetidentifier);
|
||||
_EQ_CHECK_INT(a->serviceType, b->serviceType);
|
||||
_EQ_CHECK_INT(a->fiveg_s_tmsi.digit1, b->fiveg_s_tmsi.digit1);
|
||||
_EQ_CHECK_INT(a->fiveg_s_tmsi.spare, b->fiveg_s_tmsi.spare);
|
||||
_EQ_CHECK_INT(a->fiveg_s_tmsi.typeofidentity, b->fiveg_s_tmsi.typeofidentity);
|
||||
_EQ_CHECK_INT(a->fiveg_s_tmsi.amfsetid, b->fiveg_s_tmsi.amfsetid);
|
||||
_EQ_CHECK_INT(a->fiveg_s_tmsi.amfpointer, b->fiveg_s_tmsi.amfpointer);
|
||||
_EQ_CHECK_UINT32(a->fiveg_s_tmsi.tmsi, b->fiveg_s_tmsi.tmsi);
|
||||
_EQ_CHECK_INT(a->has_pdu_session_status, b->has_pdu_session_status);
|
||||
if (a->has_pdu_session_status && b->has_pdu_session_status) {
|
||||
for (int i = 0; i < MAX_NUM_PSI; i++)
|
||||
_EQ_CHECK_INT(a->pdu_session_status[i], b->pdu_session_status[i]);
|
||||
}
|
||||
_EQ_CHECK_OPTIONAL_PTR(a, b, fgsnasmessagecontainer);
|
||||
if (a->fgsnasmessagecontainer != NULL) {
|
||||
const OctetString *a_contents = &a->fgsnasmessagecontainer->nasmessagecontainercontents;
|
||||
const OctetString *b_contents = &b->fgsnasmessagecontainer->nasmessagecontainercontents;
|
||||
_EQ_CHECK_UINT32(a_contents->length, b_contents->length);
|
||||
if (a_contents->length > 0)
|
||||
EQ_CHECK_GENERIC(memcmp(a_contents->value, b_contents->value, a_contents->length) == 0, "%d", 1, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "FGCNasMessageContainer.h"
|
||||
#include "MessageType.h"
|
||||
#include "FGSMobileIdentity.h"
|
||||
#include "fgmm_lib.h"
|
||||
|
||||
#ifndef FGS_SERVICE_REQUEST_H_
|
||||
#define FGS_SERVICE_REQUEST_H_
|
||||
@@ -33,10 +34,14 @@ typedef struct {
|
||||
ServiceType serviceType: 4;
|
||||
Stmsi5GSMobileIdentity_t fiveg_s_tmsi;
|
||||
/* Optional fields */
|
||||
bool has_pdu_session_status;
|
||||
uint8_t pdu_session_status[MAX_NUM_PSI];
|
||||
FGCNasMessageContainer *fgsnasmessagecontainer;
|
||||
} fgs_service_request_msg_t;
|
||||
|
||||
int encode_fgs_service_request(uint8_t *buffer, const fgs_service_request_msg_t *servicerequest, uint32_t len);
|
||||
int decode_fgs_service_request(fgs_service_request_msg_t *sr, const uint8_t *buffer, uint32_t len);
|
||||
void free_fgs_service_request(fgs_service_request_msg_t *msg);
|
||||
bool eq_fgs_service_request(const fgs_service_request_msg_t *a, const fgs_service_request_msg_t *b);
|
||||
|
||||
#endif /* ! defined(FGS_SERVICE_REQUEST_H_) */
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "RegistrationAccept.h"
|
||||
#include "fgs_service_request.h"
|
||||
@@ -16,6 +17,8 @@
|
||||
#include "fgmm_authentication_reject.h"
|
||||
#include "nr_nas_msg.h"
|
||||
|
||||
configmodule_interface_t *uniqCfg = NULL;
|
||||
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
||||
{
|
||||
printf("detected error at %s:%d:%s: %s\n", file, line, function, s);
|
||||
@@ -25,15 +28,6 @@ void exit_function(const char *file, const char *function, const int line, const
|
||||
/**
|
||||
* @brief Equality check for NAS Service Request enc/dec
|
||||
*/
|
||||
static bool eq_service_request(const fgs_service_request_msg_t *a, const fgs_service_request_msg_t *b)
|
||||
{
|
||||
bool result = true;
|
||||
result &= memcmp(&a->naskeysetidentifier, &b->naskeysetidentifier, sizeof(NasKeySetIdentifier)) == 0;
|
||||
result &= a->serviceType == b->serviceType;
|
||||
result &= memcmp(&a->fiveg_s_tmsi, &b->fiveg_s_tmsi, sizeof(Stmsi5GSMobileIdentity_t)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @brief Test regression of NAS Registration Accept decoding
|
||||
* This test verifies that the NAS Registration Accept message
|
||||
* is correctly decoded. It decodes a message as it received from
|
||||
@@ -102,27 +96,50 @@ static void test_service_request(void)
|
||||
uint16_t amf_set_id = 0x48;
|
||||
uint16_t amf_pointer = 0x34;
|
||||
uint32_t tmsi = 0x56789ABC;
|
||||
uint8_t container_data[] = {0xde, 0xad, 0xbe};
|
||||
fgs_service_request_msg_t original_msg = {
|
||||
.naskeysetidentifier = {.tsc = NAS_KEY_SET_IDENTIFIER_NATIVE, .naskeysetidentifier = NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE},
|
||||
.serviceType = SERVICE_TYPE_DATA,
|
||||
.fiveg_s_tmsi = {.spare = 0,
|
||||
.fiveg_s_tmsi = {.digit1 = DIGIT1,
|
||||
.typeofidentity = FGS_MOBILE_IDENTITY_5GS_TMSI,
|
||||
.amfsetid = amf_set_id,
|
||||
.amfpointer = amf_pointer,
|
||||
.tmsi = tmsi}};
|
||||
.tmsi = tmsi},
|
||||
.has_pdu_session_status = true,
|
||||
.pdu_session_status = {[2] = PDU_SESSION_ACTIVE, [4] = PDU_SESSION_ACTIVE},
|
||||
.fgsnasmessagecontainer = NULL};
|
||||
|
||||
original_msg.fgsnasmessagecontainer = calloc_or_fail(1, sizeof(*original_msg.fgsnasmessagecontainer));
|
||||
FGCNasMessageContainer *nas_container = original_msg.fgsnasmessagecontainer;
|
||||
OctetString *nas_container_contents = &nas_container->nasmessagecontainercontents;
|
||||
nas_container_contents->value = calloc_or_fail(sizeof(container_data), sizeof(*nas_container_contents->value));
|
||||
memcpy(nas_container_contents->value, container_data, sizeof(container_data));
|
||||
nas_container_contents->length = sizeof(container_data);
|
||||
|
||||
uint8_t expected_encoded_data[] = {0x71,
|
||||
0x00,
|
||||
0x00,
|
||||
0x07,
|
||||
0xF4,
|
||||
(amf_set_id >> 2) & 0xFF,
|
||||
(amf_set_id << 6) | (amf_pointer & 0x3F),
|
||||
(tmsi >> 24) & 0xFF,
|
||||
(tmsi >> 16) & 0xFF,
|
||||
(tmsi >> 8) & 0xFF,
|
||||
tmsi & 0xFF};
|
||||
uint16_t tmp = htons(7); // length of 5GS mobile identity IE for 5G-S-TMSI (bytes)
|
||||
memcpy(expected_encoded_data + 1, &tmp, sizeof(tmp));
|
||||
tmsi & 0xFF,
|
||||
IEI_PDU_SESSION_STATUS,
|
||||
0x02,
|
||||
0x14,
|
||||
0x00,
|
||||
IEI_FGS_NAS_MESSAGE_CONTAINER,
|
||||
0x00,
|
||||
0x03,
|
||||
0xde,
|
||||
0xad,
|
||||
0xbe,
|
||||
IEI_UE_REQUEST_TYPE,
|
||||
0x01,
|
||||
0x00};
|
||||
int encoded_service_request_len = sizeof(expected_encoded_data) - 3;
|
||||
|
||||
// Buffer
|
||||
uint8_t buffer[64];
|
||||
@@ -131,18 +148,21 @@ static void test_service_request(void)
|
||||
// Encode NAS Service Request
|
||||
int encoded_length = encode_fgs_service_request(buffer, &original_msg, sizeof(buffer));
|
||||
AssertFatal(encoded_length >= 0, "encode_fgs_service_request() failed\n");
|
||||
AssertFatal(encoded_length == encoded_service_request_len, "Unexpected encoded SERVICE REQUEST length\n");
|
||||
|
||||
// Compare the raw encoded buffer with expected encoded data
|
||||
// Compare the raw encoded buffer with expected encoded data, excluding the skipped optional IE.
|
||||
AssertFatal(memcmp(buffer, expected_encoded_data, encoded_length) == 0, "Encoding mismatch!\n");
|
||||
|
||||
// Decode NAS Service Request
|
||||
fgs_service_request_msg_t decoded_service_request = {0};
|
||||
int decoded_length = decode_fgs_service_request(&decoded_service_request, buffer, sizeof(buffer));
|
||||
AssertFatal(decoded_length >= 0, "decode_fgs_service_request() failed\n");
|
||||
int decoded_length = decode_fgs_service_request(&decoded_service_request, expected_encoded_data, sizeof(expected_encoded_data));
|
||||
AssertFatal(decoded_length == sizeof(expected_encoded_data), "decode_fgs_service_request() failed\n");
|
||||
|
||||
// Compare original and decoded messages
|
||||
AssertFatal(eq_service_request(&original_msg, &decoded_service_request) == 0,
|
||||
AssertFatal(eq_fgs_service_request(&original_msg, &decoded_service_request),
|
||||
"test_service_request() failed: original and decoded messages do not match\n");
|
||||
free_fgs_service_request(&decoded_service_request);
|
||||
free_fgs_service_request(&original_msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,6 +363,11 @@ static void test_auth_reject(void)
|
||||
|
||||
int main()
|
||||
{
|
||||
// Initialize logging system
|
||||
logInit();
|
||||
// Set logging level for NAS to show INFO messages
|
||||
set_log(NAS, OAILOG_DEBUG);
|
||||
// Tests list
|
||||
test_regression_registration_accept();
|
||||
test_service_request();
|
||||
test_service_accept();
|
||||
|
||||
@@ -999,7 +999,7 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
|
||||
int size = 0;
|
||||
|
||||
// NAS is security protected if has valid security contexts
|
||||
bool security_protected = nas->security_container->ciphering_context && nas->security_container->integrity_context;
|
||||
bool security_protected = nas->security_container && nas->security_container->integrity_context;
|
||||
|
||||
// Set 5GMM plain header
|
||||
fgmm_nas_message_plain_t plain = {0};
|
||||
@@ -1010,23 +1010,72 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
|
||||
// Service Type
|
||||
mm_msg->serviceType = SERVICE_TYPE_DATA;
|
||||
// NAS key set identifier
|
||||
mm_msg->naskeysetidentifier.naskeysetidentifier = NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE;
|
||||
mm_msg->naskeysetidentifier.tsc = NAS_KEY_SET_IDENTIFIER_NATIVE;
|
||||
mm_msg->naskeysetidentifier.naskeysetidentifier = set_fgs_ksi(nas);
|
||||
size += 1;
|
||||
// 5G-S-TMSI
|
||||
size += fill_fgstmsi(&mm_msg->fiveg_s_tmsi, nas->guti);
|
||||
|
||||
// PDU session status is a non-cleartext Service Request IE (TS 24.501 8.2.16.3).
|
||||
// Here configured UE PDU sessions are marked active to trigger the NAS container path.
|
||||
uint8_t pdu_session_status[MAX_NUM_PSI] = {0};
|
||||
bool has_non_cleartext_ies = false;
|
||||
for (int i = 0; i < nas->uicc->n_pdu_sessions; ++i) {
|
||||
const int pdu_id = nas->uicc->pdu_sessions[i].id;
|
||||
if (pdu_id > 0 && pdu_id < MAX_NUM_PSI) {
|
||||
pdu_session_status[pdu_id] = PDU_SESSION_ACTIVE;
|
||||
has_non_cleartext_ies = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* message encoding */
|
||||
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
|
||||
if (security_protected) {
|
||||
fgmm_nas_msg_security_protected_t sp = {0};
|
||||
FGCNasMessageContainer nas_container = {0};
|
||||
|
||||
// Set security protected 5GS NAS message header (see 9.1.1 of 3GPP TS 24.501)
|
||||
sp.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE;
|
||||
sp.header.security_header_type = INTEGRITY_PROTECTED;
|
||||
sp.header.sequence_number = nas->security.nas_count_ul & 0xff;
|
||||
const int plain_sr_size = size;
|
||||
size += sizeof(sp.header);
|
||||
|
||||
// TS 24.501 4.4.6.b.1: when non-cleartext IEs are present, place the full Service Request
|
||||
// in the NAS message container and cipher only that container value before outer integrity.
|
||||
if (has_non_cleartext_ies) {
|
||||
fgmm_nas_message_plain_t full_sr = plain;
|
||||
fgs_service_request_msg_t *full_mm_msg = &full_sr.mm_msg.service_request;
|
||||
full_mm_msg->has_pdu_session_status = true;
|
||||
memcpy(full_mm_msg->pdu_session_status, pdu_session_status, sizeof(full_mm_msg->pdu_session_status));
|
||||
|
||||
const int full_sr_size = plain_sr_size + MIN_PDU_SESSION_CONTENTS_LEN + 2;
|
||||
uint8_t *inner_sr = calloc_or_fail(full_sr_size, sizeof(*inner_sr));
|
||||
const int inner_sr_len = mm_msg_encode(&full_sr, inner_sr, full_sr_size);
|
||||
if (inner_sr_len <= 0) {
|
||||
free(inner_sr);
|
||||
AssertFatal(false, "Failed to encode Service Request NAS container payload\n");
|
||||
}
|
||||
|
||||
nas_container.nasmessagecontainercontents.value = inner_sr;
|
||||
nas_container.nasmessagecontainercontents.length = inner_sr_len;
|
||||
|
||||
uint8_t ciphered_container[inner_sr_len];
|
||||
nas_stream_cipher_t container_cipher = {0};
|
||||
AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n");
|
||||
container_cipher.context = nas->security_container->ciphering_context;
|
||||
container_cipher.count = nas->security.nas_count_ul;
|
||||
container_cipher.bearer = 1;
|
||||
container_cipher.message = inner_sr;
|
||||
container_cipher.blength = inner_sr_len << 3;
|
||||
stream_compute_encrypt(nas->security_container->ciphering_algorithm, &container_cipher, ciphered_container);
|
||||
memcpy(inner_sr, ciphered_container, inner_sr_len);
|
||||
|
||||
mm_msg->fgsnasmessagecontainer = &nas_container;
|
||||
size += inner_sr_len + 3;
|
||||
}
|
||||
|
||||
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
|
||||
|
||||
// Payload: plain message
|
||||
sp.plain = plain;
|
||||
|
||||
@@ -1035,23 +1084,13 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
|
||||
initialNasMsg->length =
|
||||
security_header_len
|
||||
+ mm_msg_encode(&sp.plain, (uint8_t *)(initialNasMsg->nas_data + security_header_len), size - security_header_len);
|
||||
/* ciphering */
|
||||
uint8_t buf[initialNasMsg->length - 7];
|
||||
nas_stream_cipher_t stream_cipher;
|
||||
stream_cipher.context = nas->security_container->ciphering_context;
|
||||
AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n");
|
||||
stream_cipher.count = nas->security.nas_count_ul;
|
||||
stream_cipher.bearer = 1;
|
||||
stream_cipher.direction = 0;
|
||||
stream_cipher.message = (unsigned char *)(initialNasMsg->nas_data + 7);
|
||||
/* length in bits */
|
||||
stream_cipher.blength = (initialNasMsg->length - 7) << 3;
|
||||
stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf);
|
||||
memcpy(stream_cipher.message, buf, initialNasMsg->length - 7);
|
||||
/* integrity protection */
|
||||
uint8_t mac[4];
|
||||
nas_stream_cipher_t stream_cipher = {0};
|
||||
stream_cipher.context = nas->security_container->integrity_context;
|
||||
stream_cipher.count = nas->security.nas_count_ul++;
|
||||
const uint32_t sr_ul_count = nas->security.nas_count_ul;
|
||||
stream_cipher.count = sr_ul_count;
|
||||
nas->security.nas_count_ul++;
|
||||
stream_cipher.bearer = 1;
|
||||
stream_cipher.direction = 0;
|
||||
stream_cipher.message = (unsigned char *)(initialNasMsg->nas_data + 6);
|
||||
@@ -1061,7 +1100,12 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
|
||||
LOG_D(NAS, "Integrity protected initial NAS message: mac = %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]);
|
||||
for (int i = 0; i < 4; i++)
|
||||
initialNasMsg->nas_data[2 + i] = mac[i];
|
||||
|
||||
/* Keep AS security in sync with updated NAS UL count for post-paging reconnect */
|
||||
derive_kgnb(nas->security.kamf, sr_ul_count, nas->security.kgnb);
|
||||
nas_itti_kgnb_refresh_req(nas->UE_id, nas->security.kgnb);
|
||||
} else {
|
||||
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
|
||||
// plain encoding
|
||||
initialNasMsg->length = mm_msg_encode(&plain, initialNasMsg->nas_data, size);
|
||||
LOG_I(NAS, "PLAIN_5GS_MSG initial NAS message: Service Request with length %d \n", initialNasMsg->length);
|
||||
|
||||
Reference in New Issue
Block a user