From 1c4e1029a88e1c229fa358f2b89d92ad55e67359 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 24 Sep 2024 14:33:58 +0200 Subject: [PATCH] Add NAS Service Request enc/dec functions * FGMM enc/dec library files * Plain message encoding + logic for security protected message * added fill_fgstmsi function * add checks on length in 5G-S-TMSI encoder function * encoding of NAS Service Request and relevant mandatory IEs --- CMakeLists.txt | 6 ++ .../NAS/COMMON/EMM/MSG/fgs_service_request.c | 90 ++++++++++++++++++ .../NAS/COMMON/EMM/MSG/fgs_service_request.h | 62 +++++++++++++ openair3/NAS/COMMON/IES/FGSMobileIdentity.c | 55 +++++++++++ openair3/NAS/COMMON/IES/FGSMobileIdentity.h | 5 + openair3/NAS/NR_UE/nr_nas_msg.c | 93 +++++++++++++++++++ openair3/NAS/NR_UE/nr_nas_msg.h | 6 ++ 7 files changed, 317 insertions(+) create mode 100644 openair3/NAS/COMMON/EMM/MSG/fgs_service_request.c create mode 100644 openair3/NAS/COMMON/EMM/MSG/fgs_service_request.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef1045726c..05c3609052 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1561,6 +1561,10 @@ set(libnas_emm_msg_OBJS ${NAS_SRC}COMMON/EMM/MSG/UplinkNasTransport.c ) +set(libnas_fgs_msg_OBJS + ${NAS_SRC}COMMON/EMM/MSG/fgs_service_request.c +) + set(libnas_esm_msg_OBJS ${NAS_SRC}COMMON/ESM/MSG/ActivateDedicatedEpsBearerContextAccept.c ${NAS_SRC}COMMON/ESM/MSG/ActivateDedicatedEpsBearerContextReject.c @@ -1785,6 +1789,7 @@ add_library(LIB_NAS_SIMUE ${libnas_api_OBJS} ${libnas_ue_api_OBJS} ${libnas_emm_msg_OBJS} + ${libnas_fgs_msg_OBJS} ${libnas_esm_msg_OBJS} ${libnas_ies_OBJS} ${libnas_utils_OBJS} @@ -1831,6 +1836,7 @@ include_directories(${NAS_SRC}UE) include_directories(${NAS_SRC}UE/API/USER) include_directories(${NAS_SRC}UE/API/USIM) include_directories(${NAS_SRC}UE/EMM) +include_directories(${NAS_SRC}UE/EMM/MSG) include_directories(${NAS_SRC}UE/EMM/SAP) include_directories(${NAS_SRC}UE/ESM) include_directories(${NAS_SRC}UE/ESM/SAP) diff --git a/openair3/NAS/COMMON/EMM/MSG/fgs_service_request.c b/openair3/NAS/COMMON/EMM/MSG/fgs_service_request.c new file mode 100644 index 0000000000..85e67498a5 --- /dev/null +++ b/openair3/NAS/COMMON/EMM/MSG/fgs_service_request.c @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include +#include + +#include "TLVEncoder.h" +#include "TLVDecoder.h" +#include "fgs_service_request.h" +#include "FGSMobileIdentity.h" +#include "NasKeySetIdentifier.h" +#include "ServiceType.h" +#include "NR_NAS_defs.h" + +#define LEN_FGS_MOBILE_ID_CONTENTS_SIZE 2 // octets +#define MIN_LEN_FGS_SERVICE_REQUEST 10 // octets + +/** + * @brief Encode 5GMM NAS Service Request message (8.2.16 of 3GPP TS 24.501) + */ +int encode_fgs_service_request(uint8_t *buffer, fgs_service_request_msg_t *service_request, uint32_t len) +{ + // Return if buffer is shorter than min length + if (len < MIN_LEN_FGS_SERVICE_REQUEST) + return -1; + + int encoded = 0; + + // ngKSI + Service type (1 octet) (M) + *buffer = ((encode_nas_key_set_identifier(&service_request->naskeysetidentifier, IEI_NULL) & 0x0f) << 4) + | (service_request->serviceType & 0x0f); + encoded++; + len -= encoded; + + // 5GS Mobile Identity (M) type 5G-S-TMSI (9 octets) + encoded += LEN_FGS_MOBILE_ID_CONTENTS_SIZE; // skip "Length of 5GS mobile identity contents" IE + len -= encoded; + encoded += encode_stmsi_5gs_mobile_identity(buffer + encoded, &service_request->fiveg_s_tmsi, len); + // encode length of 5GS mobile identity contents + uint16_t tmp = htons(encoded - LEN_FGS_MOBILE_ID_CONTENTS_SIZE - 1); + memcpy(buffer + 1, &tmp, sizeof(tmp)); + + return encoded; +} + +/** + * @brief Decode 5GMM NAS Service Request message (8.2.16 of 3GPP TS 24.501) + */ +int decode_fgs_service_request(fgs_service_request_msg_t *sr, const uint8_t *buffer, uint32_t len) +{ + uint32_t decoded = 0; + int decoded_rc = 0; + + // Service type (1/2 octet) (M) + sr->serviceType = *buffer & 0x0f; + // KSI (1/2 octet) (M) + if ((decoded_rc = decode_nas_key_set_identifier(&sr->naskeysetidentifier, IEI_NULL, *buffer >> 4)) < 0) { + return decoded_rc; + } + decoded++; + + // 5GS Mobile Identity (M) type 5G-S-TMSI (9 octets) (M) + decoded += LEN_FGS_MOBILE_ID_CONTENTS_SIZE; // skip "Length of 5GS mobile identity contents" IE + if ((decoded_rc = decode_stmsi_5gs_mobile_identity(&sr->fiveg_s_tmsi, buffer + decoded, len - decoded)) < 0) { + return decoded_rc; + } + decoded += decoded_rc; + + return decoded; +} diff --git a/openair3/NAS/COMMON/EMM/MSG/fgs_service_request.h b/openair3/NAS/COMMON/EMM/MSG/fgs_service_request.h new file mode 100644 index 0000000000..eecd1ee34a --- /dev/null +++ b/openair3/NAS/COMMON/EMM/MSG/fgs_service_request.h @@ -0,0 +1,62 @@ +/* + * 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 +#include +#include + +#include "ProtocolDiscriminator.h" +#include "SecurityHeaderType.h" +#include "NasKeySetIdentifier.h" +#include "ServiceType.h" +#include "ExtendedProtocolDiscriminator.h" +#include "FGCNasMessageContainer.h" +//#include "PDUSessionStatus.h" +#include "MessageType.h" +#include "SpareHalfOctet.h" +#include "FGSMobileIdentity.h" + +#ifndef FGS_SERVICE_REQUEST_H_ +#define FGS_SERVICE_REQUEST_H_ + +/** + * Message name: Service request (8.2.16 of 3GPP TS 24.501) + * Description: The SERVICE REQUEST message is sent by the UE to the AMF + * in order to request the establishment of an N1 NAS signalling + * connection and/or to request the establishment of user-plane + * resources for PDU sessions which are established without + * user-plane resources + * Direction: UE to network + */ + +typedef struct { + /* Mandatory fields */ + NasKeySetIdentifier naskeysetidentifier; + ServiceType serviceType: 4; + Stmsi5GSMobileIdentity_t fiveg_s_tmsi; + /* Optional fields */ + FGCNasMessageContainer *fgsnasmessagecontainer; +} fgs_service_request_msg_t; + +int encode_fgs_service_request(uint8_t *buffer, 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); + +#endif /* ! defined(FGS_SERVICE_REQUEST_H_) */ diff --git a/openair3/NAS/COMMON/IES/FGSMobileIdentity.c b/openair3/NAS/COMMON/IES/FGSMobileIdentity.c index 98f0b5017a..f70dfc66ed 100644 --- a/openair3/NAS/COMMON/IES/FGSMobileIdentity.c +++ b/openair3/NAS/COMMON/IES/FGSMobileIdentity.c @@ -38,6 +38,7 @@ #include "FGSMobileIdentity.h" #define MIN_LEN_FGS_MOBILE_ID_IE 3 // type of identity "No identity" +#define LEN_5G_FGS_TMSI_IE 9 static int decode_guti_5gs_mobile_identity(Guti5GSMobileIdentity_t *guti, const uint8_t *buffer); @@ -45,6 +46,56 @@ static int encode_guti_5gs_mobile_identity(Guti5GSMobileIdentity_t *guti, uint8_ static int encode_suci_5gs_mobile_identity(Suci5GSMobileIdentity_t *suci, uint8_t *buffer); static int encode_imeisv_5gs_mobile_identity(Imeisv5GSMobileIdentity_t *imeisv, uint8_t *buffer); +int encode_stmsi_5gs_mobile_identity(uint8_t *buffer, Stmsi5GSMobileIdentity_t *stmsi, uint32_t len) +{ + // Return if buffer is shorter than min length + if (len < LEN_5G_FGS_TMSI_IE) + return -1; + + uint32_t encoded = 0; + // octet 4 + *(buffer + encoded) = (DIGIT1 << 4) | ((stmsi->spare & 0x1) << 3) | (stmsi->typeofidentity & 0x7); + encoded++; + // octet 5-6 + uint16_t temp = 0x00 | ((stmsi->amfsetid) << 6) | (stmsi->amfpointer & 0x3f); + IES_ENCODE_U16(buffer, encoded, temp); + // octet 7 + IES_ENCODE_U32(buffer, encoded, stmsi->tmsi); + return encoded; +} + +int decode_stmsi_5gs_mobile_identity(Stmsi5GSMobileIdentity_t *stmsi, const uint8_t *buffer, uint32_t len) +{ + int decoded = 0; + uint16_t temp; + + // Return if buffer is shorter than min length + if (len < LEN_5G_FGS_TMSI_IE) + return -1; + + // Octet 4: spare (bit 3) and type of identity (bits 0-2) + stmsi->digit1 = DIGIT1; + stmsi->spare = (*(buffer + decoded) >> 3) & 0x1; + stmsi->typeofidentity = *(buffer + decoded) & 0x7; + + // Validate type of identity + if (stmsi->typeofidentity != FGS_MOBILE_IDENTITY_5GS_TMSI) { + return -1; + } + + decoded++; + + // Octet 5-6: AMF Set ID (bits 6-15) and AMF Pointer (bits 0-5) + IES_DECODE_U16(buffer, decoded, temp); + stmsi->amfsetid = (temp >> 6) & 0x03ff; + stmsi->amfpointer = temp & 0x3f; + + // Octet 7-10: 32-bit TMSI + IES_DECODE_U32(buffer, decoded, stmsi->tmsi); + + return decoded; +} + int decode_5gs_mobile_identity(FGSMobileIdentity *fgsmobileidentity, uint8_t iei, const uint8_t *buffer, uint32_t len) { int decoded_rc = TLV_DECODE_VALUE_DOESNT_MATCH; @@ -109,6 +160,10 @@ int encode_5gs_mobile_identity(FGSMobileIdentity *fgsmobileidentity, uint8_t iei buffer + encoded); } + if (fgsmobileidentity->stmsi.typeofidentity == FGS_MOBILE_IDENTITY_5GS_TMSI) { + encoded_rc = encode_stmsi_5gs_mobile_identity(buffer + encoded, &fgsmobileidentity->stmsi, len); + } + if (encoded_rc < 0) { return encoded_rc; } diff --git a/openair3/NAS/COMMON/IES/FGSMobileIdentity.h b/openair3/NAS/COMMON/IES/FGSMobileIdentity.h index 9356a46eff..e4747461e9 100644 --- a/openair3/NAS/COMMON/IES/FGSMobileIdentity.h +++ b/openair3/NAS/COMMON/IES/FGSMobileIdentity.h @@ -37,6 +37,8 @@ #ifndef FGS_MOBILE_IDENTITY_H_ #define FGS_MOBILE_IDENTITY_H_ +// 4x 1bit according to 9.11.3.4 5GS mobile identity (type 5G-S-TMSI) +#define DIGIT1 0xF typedef struct { uint8_t spare:5; @@ -151,5 +153,8 @@ int encode_5gs_mobile_identity(FGSMobileIdentity *fgsmobileidentity, uint8_t iei int decode_5gs_mobile_identity(FGSMobileIdentity *fgsmobileidentity, uint8_t iei, const uint8_t *buffer, uint32_t len); +int encode_stmsi_5gs_mobile_identity(uint8_t *buffer, Stmsi5GSMobileIdentity_t *stmsi, uint32_t len); +int decode_stmsi_5gs_mobile_identity(Stmsi5GSMobileIdentity_t *stmsi, const uint8_t *buffer, uint32_t len); + #endif /* FGS MOBILE IDENTITY_H_ */ diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 1199aad94b..5e9ce65194 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -194,6 +194,18 @@ static int fill_guti(FGSMobileIdentity *mi, const Guti5GSMobileIdentity_t *guti) return 13; } +static int fill_fgstmsi(Stmsi5GSMobileIdentity_t *stmsi, const Guti5GSMobileIdentity_t *guti) +{ + AssertFatal(guti != NULL, "UE has no GUTI\n"); + stmsi->amfpointer = guti->amfpointer; + stmsi->amfsetid = guti->amfsetid; + stmsi->tmsi = guti->tmsi; + stmsi->digit1 = DIGIT1; + stmsi->spare = 0; + stmsi->typeofidentity = FGS_MOBILE_IDENTITY_5GS_TMSI; + return 10; +} + static int fill_imeisv(FGSMobileIdentity *mi, const uicc_t *uicc) { int i = 0; @@ -260,6 +272,9 @@ int mm_msg_encode(MM_msg *mm_msg, uint8_t *buffer, uint32_t len) encode_result = encode_fgs_deregistration_request_ue_originating(&mm_msg->fgs_deregistration_request_ue_originating, buffer, len); break; + case FGS_SERVICE_REQUEST: + encode_result = encode_fgs_service_request(buffer, &mm_msg->service_request, len); + break; default: LOG_TRACE(ERROR, "EMM-MSG - Unexpected message type: 0x%x", mm_msg->header.message_type); encode_result = TLV_ENCODE_WRONG_MESSAGE_TYPE; @@ -550,6 +565,84 @@ void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) nas->registration_request_len = initialNasMsg->length; } +void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) +{ + LOG_I(NAS, "Generate initial NAS message: Service Request\n"); + int size = 0; + fgs_nas_message_t nas_msg = {0}; + memset(&nas_msg, 0, sizeof(nas_msg)); + // NAS is security protected if has valid security contexts + bool security_protected = nas->security_container->ciphering_context && nas->security_container->integrity_context; + MM_msg *mm_msg; + + if (security_protected) { + /* Set security protected 5GS NAS message header (see 9.1.1 of 3GPP TS 24.501) */ + nas_msg.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; + nas_msg.header.security_header_type = INTEGRITY_PROTECTED; + nas_msg.header.sequence_number = nas->security.nas_count_ul & 0xff; + size += sizeof(fgs_nas_message_security_header_t); + mm_msg = &nas_msg.security_protected.plain.mm_msg; + } else { + // Set Mobility Management plain message header + mm_msg = &nas_msg.plain.mm_msg; + } + // Fill MM plain message header + mm_msg->header.ex_protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; + mm_msg->header.security_header_type = PLAIN_5GS_MSG; + mm_msg->header.message_type = FGS_SERVICE_REQUEST; + size += sizeof(mm_msg_header_t); + + // Fill Service Request + // Service Type + mm_msg->service_request.serviceType = SERVICE_TYPE_DATA; + // NAS key set identifier + mm_msg->service_request.naskeysetidentifier.naskeysetidentifier = NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE; + mm_msg->service_request.naskeysetidentifier.tsc = NAS_KEY_SET_IDENTIFIER_NATIVE; + size += 1; + // 5G-S-TMSI + size += fill_fgstmsi(&mm_msg->service_request.fiveg_s_tmsi, nas->guti); + + /* message encoding */ + initialNasMsg->nas_data = (Byte_t *)malloc(size * sizeof(Byte_t)); + if (security_protected) { + // security protected encoding + int security_header_len = nas_protected_security_header_encode((char *)(initialNasMsg->nas_data), &(nas_msg.header), size); + initialNasMsg->length = + security_header_len + + mm_msg_encode(mm_msg, (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]; + stream_cipher.context = nas->security_container->integrity_context; + stream_cipher.count = nas->security.nas_count_ul++; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->nas_data + 6); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 6) << 3; + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); + printf("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]; + } else { + // plain encoding + initialNasMsg->length = mm_msg_encode(mm_msg, (uint8_t *)(initialNasMsg->nas_data), size); + LOG_I(NAS, "PLAIN_5GS_MSG initial NAS message: Service Request with length %d \n", initialNasMsg->length); + } +} + void generateIdentityResponse(as_nas_info_t *initialNasMsg, uint8_t identitytype, uicc_t *uicc) { int size = sizeof(mm_msg_header_t); diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index e10ac54b0f..0e324052ad 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -36,6 +36,7 @@ #include "FGSNASSecurityModeComplete.h" #include "FGSDeregistrationRequestUEOriginating.h" #include "RegistrationComplete.h" +#include "EMM/MSG/fgs_service_request.h" #include "as_message.h" #include "FGSUplinkNasTransport.h" #include @@ -48,6 +49,9 @@ #define PAYLOAD_CONTAINER_LENGTH_MIN 3 #define PAYLOAD_CONTAINER_LENGTH_MAX 65537 +/* 3GPP TS 24.501: 9.11.3.50 Service type */ +#define SERVICE_TYPE_DATA 0x1 + /* List of allowed NSSAI from NAS messaging. */ typedef struct { int sst; @@ -106,6 +110,7 @@ typedef struct { typedef union { mm_msg_header_t header; registration_request_msg registration_request; + fgs_service_request_msg_t service_request; fgs_identiy_response_msg fgs_identity_response; fgs_authentication_response_msg fgs_auth_response; fgs_deregistration_request_ue_originating_msg fgs_deregistration_request_ue_originating; @@ -158,6 +163,7 @@ typedef struct { nr_ue_nas_t *get_ue_nas_info(module_id_t module_id); void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas); +void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas); void *nas_nrue_task(void *args_p); void *nas_nrue(void *args_p); void nas_init_nrue(int num_ues);