Merge remote-tracking branch 'origin/nr-ue-nas-auth-reject' into integration_2025_w26 (!3371)

Add NAS Authentication Reject enc/dec library and unit test

This MR introduces the library for encoding/decoding of NAS Authentication
Reject (8.2.5.1 of 3GPP TS 24.501) and relevant unit test.

The message is sent by the AMF to the UE to indicate that the authentication
procedure has failed. A NAS handler was also introduced.
This commit is contained in:
Jaroslava Fiedlerova
2025-06-23 15:36:23 +02:00
13 changed files with 265 additions and 9 deletions
@@ -14,6 +14,7 @@ add_library(fgs_5gmm_lib OBJECT
FGSUplinkNasTransport.c
FGSDeregistrationRequestUEOriginating.c
fgmm_authentication_failure.c
fgmm_authentication_reject.c
)
target_link_libraries(fgs_5gmm_lib PUBLIC fgs_5gmm_ies_lib)
@@ -0,0 +1,69 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "fgmm_authentication_reject.h"
#include <string.h>
#include <arpa/inet.h> // For htons and ntohs
#include <stdlib.h> // For malloc and free
#include "fgmm_lib.h"
#define MIN_AUTH_REJECT_LEN 3
/** @brief Encode Authentication Reject (8.2.5 of 3GPP TS 24.501) */
int encode_fgmm_auth_reject(byte_array_t *buffer, const fgmm_auth_reject_msg_t *msg)
{
uint32_t encoded = 0;
const byte_array_t *eap_msg = &msg->eap_msg;
if (eap_msg->len > 0) {
return encode_eap_msg_ie(buffer, eap_msg);
}
return encoded;
}
/** @brief Decode Authentication Reject (8.2.5 of 3GPP TS 24.501)
* the message contains only optional IEIs (EAP MSG) */
int decode_fgmm_auth_reject(fgmm_auth_reject_msg_t *msg, const byte_array_t *buffer)
{
if (buffer->len < MIN_AUTH_REJECT_LEN) {
// No optional IEIs present
return 0;
}
byte_array_t ba = *buffer; // Local copy of buffer
uint32_t decoded = 0;
// Decode the IEI
uint8_t iei = buffer->buf[decoded++];
UPDATE_BYTE_ARRAY(ba, decoded);
if (iei == IEI_EAPMSG) {
return (decoded + decode_eap_msg_ie(&msg->eap_msg, &ba));
}
PRINT_NAS_ERROR("Expected EAP MSG but it is not present");
return -1;
}
bool eq_auth_reject(fgmm_auth_reject_msg_t *a, fgmm_auth_reject_msg_t *b)
{
_NAS_EQ_CHECK_LONG(a->eap_msg.len, b->eap_msg.len);
if (a->eap_msg.len > 0) {
return !memcmp(a->eap_msg.buf, b->eap_msg.buf, a->eap_msg.len);
}
return true;
}
@@ -0,0 +1,38 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef FGMM_AUTH_REJECT_H
#define FGMM_AUTH_REJECT_H
#include <stdint.h>
#include "fgmm_lib.h"
#include "common/utils/ds/byte_array.h"
typedef struct {
// EAP Message (Optional)
byte_array_t eap_msg;
} fgmm_auth_reject_msg_t;
int decode_fgmm_auth_reject(fgmm_auth_reject_msg_t *msg, const byte_array_t *buffer);
int encode_fgmm_auth_reject(byte_array_t *buffer, const fgmm_auth_reject_msg_t *msg);
bool eq_auth_reject(fgmm_auth_reject_msg_t *a, fgmm_auth_reject_msg_t *b);
#endif /* FGMM_AUTH_REJECT_H */
+68 -5
View File
@@ -25,8 +25,11 @@
#include <stdlib.h> // For malloc and free
#include "common/utils/ds/byte_array.h"
#define IEI_LEN 1
#define GPRS_TIMER_LENGTH 3 // octets
#define FGS_NAS_CAUSE_LENGTH 1
#define MIN_EAP_MSG_LEN 7
#define MIN_EAP_CONTENTS_LEN 4
/**
* Encode PDU Session Status and PDU session reactivation result (optional IEs)
@@ -152,12 +155,72 @@ bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b)
return true;
}
/* Decode EAP Message (todo) */
int decode_eap_msg_ie(const byte_array_t *buffer)
/** @brief Encode EAP message IE (3GPP TS 24.501 9.11.2.2) */
int encode_eap_msg_ie(byte_array_t *buffer, const byte_array_t *msg)
{
int eap_msg_len = (buffer->buf[0] << 8) | buffer->buf[1];
PRINT_NAS_ERROR("EAP message IE in NAS Service Reject is not handled\n");
return eap_msg_len + 2; // content length + 2 octets for the length IE
int encoded = 0;
// Sanity check on buffer length
if (buffer->len < MIN_EAP_MSG_LEN) {
PRINT_NAS_ERROR("Invalid buffer length %ld", buffer->len);
return -1;
}
// Sanity check on input length
if (msg->len < MIN_EAP_CONTENTS_LEN || msg->len > MAX_EAP_CONTENTS_LEN) {
PRINT_NAS_ERROR("Invalid EAP message length %ld", msg->len);
return -1;
}
// Encode IEI
buffer->buf[encoded++] = IEI_EAPMSG;
// Encode length of EAP message content
uint16_t len = htons(msg->len);
memcpy(&buffer->buf[encoded], &len, sizeof(len));
encoded += sizeof(len);
// Encode EAP message content
memcpy(&buffer->buf[encoded], msg->buf, msg->len);
encoded += msg->len;
return encoded;
}
/** @brief Decode EAP message IE (3GPP TS 24.501 9.11.2.2):
length of contents and message payload */
int decode_eap_msg_ie(byte_array_t *eap_msg, const byte_array_t *buffer)
{
int decoded = 0;
int min_len_to_decode = MIN_EAP_MSG_LEN - IEI_LEN;
// Check buffer length validity
if (buffer->len < min_len_to_decode) {
PRINT_NAS_ERROR("Invalid buffer for decoding EAP message IE");
return -1;
}
// Decode length IE
eap_msg->len = (buffer->buf[decoded] << 8) | buffer->buf[decoded + 1];
decoded += 2;
// Check payload length validity
if (eap_msg->len < MIN_EAP_CONTENTS_LEN || eap_msg->len > MAX_EAP_CONTENTS_LEN) {
PRINT_NAS_ERROR("Invalid EAP message length %ld", eap_msg->len);
return -1;
}
// Check for buffer overflow
if (buffer->len < decoded + eap_msg->len) {
PRINT_NAS_ERROR("Buffer too short for EAP message: %ld < %ld", buffer->len, decoded + eap_msg->len);
return -1;
}
// Copy EAP payload
memcpy(eap_msg->buf, &buffer->buf[decoded], eap_msg->len);
decoded += eap_msg->len;
return decoded;
}
int encode_fgs_nas_cause(byte_array_t *buffer, const cause_id_t *cause)
+3 -1
View File
@@ -39,6 +39,7 @@ both PDU Session Status and PDU session Reactivation Result IEs */
#define MAX_PDU_SESSION_CONTENTS_LEN 32
#define MAX_NUM_PSI 16
#define MAX_NUM_PDU_ERRORS 256
#define MAX_EAP_CONTENTS_LEN 1500
/* This macro updates the local copy of
a byte_array_t pointer used for enc/dec */
@@ -164,7 +165,8 @@ int decode_pdu_session_ie(uint8_t *psi, const byte_array_t *buffer);
int encode_gprs_timer_ie(byte_array_t *buffer, nas_service_IEI_t iei, const gprs_timer_t *timer);
int decode_gprs_timer_ie(gprs_timer_t *timer, const byte_array_t *buffer);
bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b);
int decode_eap_msg_ie(const byte_array_t *buffer);
int decode_eap_msg_ie(byte_array_t *eap_msg, const byte_array_t *buffer);
int encode_eap_msg_ie(byte_array_t *buffer, const byte_array_t *msg);
int encode_fgs_nas_cause(byte_array_t *buffer, const cause_id_t *cause);
int decode_fgs_nas_cause(cause_id_t *cause, const byte_array_t *buffer);
@@ -133,7 +133,7 @@ int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t
break;
case IEI_EAPMSG:
DECODE_NAS_IE(ba, decode_eap_msg_ie(&ba), decoded);
DECODE_NAS_IE(ba, decode_eap_msg_ie(&msg->eap_msg, &ba), decoded);
break;
default:
@@ -47,6 +47,9 @@ typedef struct {
// T3448 Value (Optional)
gprs_timer_t *t3448;
// EAP Message (Optional)
byte_array_t eap_msg;
} fgs_service_accept_msg_t;
int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t *buffer);
@@ -59,6 +59,11 @@ int encode_fgs_service_reject(byte_array_t *buffer, const fgs_service_reject_msg
ENCODE_NAS_IE(ba, encode_gprs_timer_ie(&ba, IEI_T3448_VALUE, msg->t3448), encoded);
}
// EAP message (Optional)
if (msg->eap_msg) {
ENCODE_NAS_IE(ba, encode_eap_msg_ie(&ba, msg->eap_msg), encoded);
}
return encoded;
}
@@ -99,7 +104,7 @@ int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t
break;
case IEI_EAPMSG:
DECODE_NAS_IE(ba, decode_eap_msg_ie((const byte_array_t *)&ba), decoded);
DECODE_NAS_IE(ba, decode_eap_msg_ie(msg->eap_msg, &ba), decoded);
break;
case IEI_CAG_INFO_LIST: {
@@ -35,6 +35,8 @@ typedef struct {
bool has_psi_status;
// T3448 Value (Optional)
gprs_timer_t *t3448;
// EAP message (optional)
byte_array_t *eap_msg;
} fgs_service_reject_msg_t;
int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t *buffer);
@@ -9,6 +9,7 @@
#include "fgmm_service_reject.h"
#include "fgmm_authentication_failure.h"
#include "FGSNASSecurityModeReject.h"
#include "fgmm_authentication_reject.h"
#include "nr_nas_msg.h"
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
@@ -298,6 +299,44 @@ static void test_security_mode_reject(void)
AssertFatal(eq_sec_mode_reject(&orig, &dec), "test_sec_mode_reject() failed: original and decoded messages do not match\n");
}
/**
* @brief Test NAS Authentication Reject enc/dec
*/
static void test_auth_reject(void)
{
// Dummy NAS Authentication Reject message
uint8_t dummy_eap_msg[] = {0x12, 0x34, 0x56, 0x78, 0x91, 0x01, 0x23}; // Example EAP message
fgmm_auth_reject_msg_t orig = {
.eap_msg.len = sizeof(dummy_eap_msg),
};
orig.eap_msg.buf = dummy_eap_msg;
// Expected encoded data
uint8_t expected_enc[] = {0x78, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78, 0x91, 0x01, 0x23};
// Buffer
uint8_t buf[10] = {0};
byte_array_t buffer = {.buf = buf, .len = sizeof(buf)};
// Encode
int encoded_length = encode_fgmm_auth_reject(&buffer, &orig);
AssertFatal(encoded_length >= 0, "encode_fgmm_auth_reject() failed\n");
// Compare the raw encoded buffer with expected encoded data
AssertFatal(encoded_length == sizeof(expected_enc), "Encoded length mismatch %d != %ld \n", encoded_length, sizeof(expected_enc));
AssertFatal(memcmp(buffer.buf, expected_enc, encoded_length) == 0, "Encoding mismatch!\n");
// Decode
fgmm_auth_reject_msg_t dec = {0};
uint8_t eap_msg[1500] = {0};
dec.eap_msg.buf = eap_msg;
int decoded_length = decode_fgmm_auth_reject(&dec, &buffer);
AssertFatal(decoded_length >= 0, "decode_fgmm_auth_reject() failed\n");
// Compare original and decoded messages
AssertFatal(eq_auth_reject(&orig, &dec), "test_auth_reject() failed: original and decoded messages do not match\n");
}
int main()
{
test_regression_registration_accept();
@@ -305,6 +344,7 @@ int main()
test_service_accept();
test_service_reject();
test_auth_failure();
test_auth_reject();
test_security_mode_reject();
return 0;
}
+31
View File
@@ -71,6 +71,7 @@
#include "fgs_nas_utils.h"
#include "fgmm_service_accept.h"
#include "fgmm_service_reject.h"
#include "fgmm_authentication_reject.h"
#include "ds/byte_array.h"
#include "key_nas_deriver.h"
@@ -1063,6 +1064,33 @@ static void handle_fgmm_authentication_request(nr_ue_nas_t *nas, as_nas_info_t *
generateAuthenticationResp(nas, initialNasMsg, buffer->buf);
}
/** @brief Handle authentication not accepted by the network
* This function assumes the message is not integrity protected, processes the received
* NAS message and logs whether a EAP-failure is enclosed. The UE enters state: 5GMM-DEREGISTERED.
* @todo The UE shall performs actions as per 5.4.1.3.5 of 3GPP TS 24.501, including
* (1) Abort any ongoing 5GMM procedure (2) Stop all active timers: T3510, T3516, T3517,
* T3519, T3520, T3521 (3) Delete stored SUCI. (4) handle EAP-failure message. */
static void handle_authentication_reject(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *pdu, int pdu_length)
{
LOG_E(NAS, "Received Authentication Reject message from the network\n");
uint8_t eap_msg[MAX_EAP_CONTENTS_LEN] = {0};
fgmm_auth_reject_msg_t msg = {.eap_msg.buf = eap_msg};
byte_array_t ba = {.buf = pdu + 3 /* skip header */, .len = pdu_length};
if (decode_fgmm_auth_reject(&msg, &ba) < 0) {
LOG_E(NAS, "Could not decode Authentication Reject\n");
return;
}
if (msg.eap_msg.len > 0) {
/** @todo UE handling EAP-failure message (5.4.1.2.2.11 of 3GPP TS 24.501) */
LOG_W(NAS, "NAS Authentication Reject contains an EAP message: handling is not implemented\n");
log_hex_buffer("EAP-Failure", msg.eap_msg.buf, msg.eap_msg.len);
}
nas->fiveGMM_state = FGS_DEREGISTERED;
}
int nas_itti_kgnb_refresh_req(instance_t instance, const uint8_t kgnb[32])
{
MessageDef *message_p;
@@ -2064,6 +2092,9 @@ void *nas_nrue(void *args_p)
case FGS_AUTHENTICATION_REQUEST:
handle_fgmm_authentication_request(nas, &initialNasMsg, &buffer);
break;
case FGS_AUTHENTICATION_REJECT:
handle_authentication_reject(nas, &initialNasMsg, pdu_buffer, pdu_length);
break;
case FGS_SECURITY_MODE_COMMAND:
handle_security_mode_command(nas, &initialNasMsg, pdu_buffer, pdu_length);
break;
+1 -1
View File
@@ -57,7 +57,7 @@
#define NR_FC_ALG_KEY_DER (0x69)
#define NR_FC_ALG_KEY_NG_RAN_STAR_DER (0x70)
static void log_hex_buffer(const char *label, const uint8_t *buf, const int len)
void log_hex_buffer(const char *label, const uint8_t *buf, const int len)
{
char hex_str[len * 2 + 1];
for (int i = 0; i < len; i++) {
+2
View File
@@ -36,4 +36,6 @@ void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t
void nr_derive_nh(const uint8_t k_amf[SECURITY_KEY_LEN], const uint8_t *sync_input, uint8_t *nh);
void log_hex_buffer(const char *label, const uint8_t *buf, const int len);
#endif