Unverified Commit 7d5fe2d5 authored by Ali Güngör's avatar Ali Güngör Committed by GitHub

Merge pull request #309 from aligungr/dev

Release v3.1.8
parents fa981052 1d85281c
......@@ -2,7 +2,7 @@
<a href="https://github.com/aligungr/UERANSIM"><img src="/.github/logo.png" width="75" title="UERANSIM"></a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/UERANSIM-v3.1.7-blue" />
<img src="https://img.shields.io/badge/UERANSIM-v3.1.8-blue" />
<img src="https://img.shields.io/badge/3GPP-R15-orange" />
<img src="https://img.shields.io/badge/License-GPL--3.0-green"/>
</p>
......
......@@ -27,10 +27,10 @@ Milenage Calculate(const OctetString &opc, const OctetString &key, const OctetSt
r.ak_r = OctetString::FromSpare(6);
if (milenage_f1(opc.data(), key.data(), rand.data(), sqn.data(), amf.data(), r.mac_a.data(), r.mac_s.data()))
throw std::runtime_error("OPC calculation failed");
throw std::runtime_error("Milenage calculation failed");
if (milenage_f2345(opc.data(), key.data(), rand.data(), r.res.data(), r.ck.data(), r.ik.data(), r.ak.data(),
r.ak_r.data()))
throw std::runtime_error("OPC calculation failed");
throw std::runtime_error("Milenage calculation failed");
return r;
}
......
......@@ -556,6 +556,7 @@ struct SecurityModeCommand : PlainMmMessage
std::optional<IES1UeNetworkCapability> replayedS1UeNetworkCapability{};
octet4 _macForNewSC{};
OctetString _originalPlainNasPdu{};
SecurityModeCommand();
void onBuild(NasMessageBuilder &b);
......
......@@ -81,7 +81,6 @@ std::pair<OctetString, OctetString> CalculateCkPrimeIkPrime(const OctetString &c
s[1] = sqnXorAk.copy();
auto res = crypto::CalculateKdfKey(key, 0x20, s, 2);
;
std::pair<OctetString, OctetString> ckIk;
ckIk.first = res.subCopy(0, ck.length());
......@@ -139,4 +138,20 @@ OctetString CalculateResStar(const OctetString &key, const std::string &snn, con
return output.subCopy(output.length() - 16);
}
OctetString DeriveAmfPrimeInMobility(bool isUplink, const NasCount &count, const OctetString &kAmf)
{
OctetString params[2];
params[0] = OctetString::FromOctet(isUplink ? 0x00 : 0x01);
params[1] = OctetString::FromOctet4(count.toOctet4());
return crypto::CalculateKdfKey(kAmf, 0x72, params, 2);
}
OctetString CalculateAuts(const OctetString &sqn, const OctetString &ak, const OctetString &macS)
{
OctetString auts = OctetString::Xor(sqn, ak);
auts.append(macS);
return auts;
}
} // namespace nr::ue::keys
\ No newline at end of file
......@@ -28,6 +28,11 @@ void DeriveNasKeys(NasSecurityContext &securityContext);
*/
std::string ConstructServingNetworkName(const Plmn &plmn);
/**
* Derives kAMF to kAMF' in mobility 33.501/A.13
*/
OctetString DeriveAmfPrimeInMobility(bool isUplink, const NasCount &count, const OctetString &kAmf);
/**
* Calculates K_AUSF for 5G-AKA according to given parameters as specified in 3GPP TS 33.501 Annex A.2
*/
......@@ -66,4 +71,9 @@ OctetString CalculateKAusfForEapAkaPrime(const OctetString &mk);
OctetString CalculateResStar(const OctetString &key, const std::string &snn, const OctetString &rand,
const OctetString &res);
/*
* Calculates AUTS according to the given parameters
*/
OctetString CalculateAuts(const OctetString &sqn, const OctetString &ak, const OctetString &macS);
} // namespace nr::ue::keys
......@@ -7,9 +7,10 @@
//
#include "mm.hpp"
#include <lib/nas/utils.hpp>
#include <ue/nas/keys.hpp>
static const bool IGNORE_CONTROLS_FAILURES = false;
static const bool USE_SQN_HACK = true; // TODO
namespace nr::ue
......@@ -23,6 +24,8 @@ void NasMm::receiveAuthenticationRequest(const nas::AuthenticationRequest &msg)
return;
}
m_timers->t3520.start();
if (msg.eapMessage.has_value())
receiveAuthenticationRequestEap(msg);
else
......@@ -43,8 +46,6 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
m_timers->t3521.stop();
};
m_timers->t3520.stop();
// Read EAP-AKA' request
auto &receivedEap = (const eap::EapAkaPrime &)*msg.eapMessage->eap;
auto receivedRand = receivedEap.attributes.getRand();
......@@ -62,23 +63,19 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
// Log.warning(Tag.CONFIG, "USE_SQN_HACK: %s", USE_SQN_HACK);
}
if (IGNORE_CONTROLS_FAILURES)
m_logger->warn("IGNORE_CONTROLS_FAILURES enabled");
if (USE_SQN_HACK)
/*if (USE_SQN_HACK)
{
auto ak = calculateMilenage(OctetString::FromSpare(6), receivedRand).ak;
auto ak = calculateMilenage(OctetString::FromSpare(6), receivedRand, false).ak;
m_usim->m_sqn = OctetString::Xor(receivedAutn.subCopy(0, 6), ak);
}
}*/
auto milenage = calculateMilenage(m_usim->m_sqn, receivedRand);
auto milenage = calculateMilenage(m_usim->m_sqnMng->getSqn(), receivedRand, false);
auto &res = milenage.res;
auto &ck = milenage.ck;
auto &ik = milenage.ik;
auto &milenageAk = milenage.ak;
auto &milenageMac = milenage.mac_a;
auto sqnXorAk = OctetString::Xor(m_usim->m_sqn, milenageAk);
auto sqnXorAk = OctetString::Xor(m_usim->m_sqnMng->getSqn(), milenageAk);
auto ckPrimeIkPrime =
keys::CalculateCkPrimeIkPrime(ck, ik, keys::ConstructServingNetworkName(*m_usim->m_currentPlmn), sqnXorAk);
auto &ckPrime = ckPrimeIkPrime.first;
......@@ -106,7 +103,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
// m_logger->debug("calculated kaut: %s", kaut.toHexString().c_str());
// Control received KDF
if (!IGNORE_CONTROLS_FAILURES && receivedKdf != 1)
if (receivedKdf != 1)
{
ueRejectionTimers();
......@@ -125,7 +122,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
}
// Control received AUTN
auto autnCheck = validateAutn(milenageAk, milenageMac, receivedAutn);
auto autnCheck = validateAutn(receivedRand, receivedAutn);
if (autnCheck != EAutnValidationRes::OK)
{
eap::EapAkaPrime *eapResponse = nullptr;
......@@ -148,7 +145,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
eapResponse->attributes.putClientErrorCode(0);
}
if (!IGNORE_CONTROLS_FAILURES && eapResponse != nullptr)
if (eapResponse != nullptr)
{
ueRejectionTimers();
......@@ -168,12 +165,10 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
m_logger->err("AT_MAC failure in EAP AKA'. expected: %s received: %s", expectedMac.toHexString().c_str(),
receivedMac.toHexString().c_str());
if (!IGNORE_CONTROLS_FAILURES)
{
ueRejectionTimers();
auto eapResponse = std::make_unique<eap::EapAkaPrime>(eap::ECode::RESPONSE, receivedEap.id,
eap::ESubType::AKA_CLIENT_ERROR);
auto eapResponse =
std::make_unique<eap::EapAkaPrime>(eap::ECode::RESPONSE, receivedEap.id, eap::ESubType::AKA_CLIENT_ERROR);
eapResponse->attributes.putClientErrorCode(0);
nas::AuthenticationReject response;
......@@ -183,7 +178,6 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
sendNasMessage(response);
return;
}
}
// Create new partial native NAS security context and continue key derivation
auto kAusf = keys::CalculateKAusfForEapAkaPrime(mk);
......@@ -192,9 +186,8 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
m_usim->m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>();
m_usim->m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
m_usim->m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
m_usim->m_nonCurrentNsCtx->keys.rand = std::move(receivedRand);
m_usim->m_nonCurrentNsCtx->keys.res = std::move(res);
m_usim->m_nonCurrentNsCtx->keys.resStar = {};
m_usim->m_rand = std::move(receivedRand);
m_usim->m_resStar = {};
m_usim->m_nonCurrentNsCtx->keys.kAusf = std::move(kAusf);
m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
......@@ -207,7 +200,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
{
auto *akaPrimeResponse =
new eap::EapAkaPrime(eap::ECode::RESPONSE, receivedEap.id, eap::ESubType::AKA_CHALLENGE);
akaPrimeResponse->attributes.putRes(m_usim->m_nonCurrentNsCtx->keys.res);
akaPrimeResponse->attributes.putRes(res);
akaPrimeResponse->attributes.putMac(OctetString::FromSpare(16)); // Dummy mac for now
akaPrimeResponse->attributes.putKdf(1);
......@@ -223,23 +216,39 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
sendNasMessage(response);
}
// TODO (dont forget: m_nwConsecutiveAuthFailure = 0;)
}
void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &msg)
{
auto sendFailure = [this](nas::EMmCause cause) {
auto sendFailure = [this](nas::EMmCause cause, std::optional<OctetString> &&auts = std::nullopt) {
if (cause != nas::EMmCause::SYNCH_FAILURE)
m_logger->err("Sending Authentication Failure with cause [%s]", nas::utils::EnumToString(cause));
else
m_logger->debug("Sending Authentication Failure due to SQN out of range");
// Clear RAND and RES* stored in volatile memory
m_usim->m_rand = {};
m_usim->m_resStar = {};
// Stop T3516 if running
m_timers->t3516.stop();
// Send Authentication Failure
nas::AuthenticationFailure resp{};
resp.mmCause.value = cause;
sendNasMessage(resp);
};
if (USE_SQN_HACK)
if (auts.has_value())
{
// Log.warning(Tag.CONFIG, "USE_SQN_HACK: %s", USE_SQN_HACK);
resp.authenticationFailureParameter = nas::IEAuthenticationFailureParameter{};
resp.authenticationFailureParameter->rawData = std::move(*auts);
}
if (IGNORE_CONTROLS_FAILURES)
m_logger->warn("IGNORE_CONTROLS_FAILURES enabled");
sendNasMessage(resp);
};
// ========================== Check the received parameters syntactically ==========================
if (!msg.authParamRAND.has_value() || !msg.authParamAUTN.has_value())
{
......@@ -247,72 +256,110 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &
return;
}
if (msg.authParamRAND->value.length() != 16 || msg.authParamAUTN->value.length() != 16)
{
sendFailure(nas::EMmCause::SEMANTICALLY_INCORRECT_MESSAGE);
return;
}
// =================================== Check the received ngKSI ===================================
if (msg.ngKSI.tsc == nas::ETypeOfSecurityContext::MAPPED_SECURITY_CONTEXT)
{
m_logger->err("Mapped security context not supported");
sendFailure(nas::EMmCause::UNSPECIFIED_PROTOCOL_ERROR);
return;
}
if (msg.ngKSI.ksi == nas::IENasKeySetIdentifier::NOT_AVAILABLE_OR_RESERVED)
{
m_logger->err("Invalid ngKSI value received");
sendFailure(nas::EMmCause::UNSPECIFIED_PROTOCOL_ERROR);
return;
}
if ((m_usim->m_currentNsCtx && m_usim->m_currentNsCtx->ngKsi == msg.ngKSI.ksi) ||
(m_usim->m_nonCurrentNsCtx && m_usim->m_nonCurrentNsCtx->ngKsi == msg.ngKSI.ksi))
{
if (networkFailingTheAuthCheck(true))
return;
m_timers->t3520.start();
sendFailure(nas::EMmCause::NGKSI_ALREADY_IN_USE);
return;
}
// ============================================ Others ============================================
auto &rand = msg.authParamRAND->value;
auto &autn = msg.authParamAUTN->value;
// m_logger->debug("Received rand[%s] autn[%s]", rand.toHexString().c_str(), autn.toHexString().c_str());
EAutnValidationRes autnCheck = EAutnValidationRes::OK;
if (USE_SQN_HACK)
// If the received RAND is same with store stored RAND, bypass AUTN validation
// NOTE: Not completely sure if this is correct and the spec meant this. But in worst case, synchronisation failure
// happens, and hopefully that can be restored with the normal resynchronization procedure.
if (m_usim->m_rand != rand)
{
auto ak = calculateMilenage(OctetString::FromSpare(6), rand).ak;
m_usim->m_sqn = OctetString::Xor(autn.subCopy(0, 6), ak);
autnCheck = validateAutn(rand, autn);
m_timers->t3516.start();
}
auto milenage = calculateMilenage(m_usim->m_sqn, rand);
auto &res = milenage.res;
auto &ck = milenage.ck;
auto &ik = milenage.ik;
auto ckIk = OctetString::Concat(ck, ik);
auto &milenageAk = milenage.ak;
auto &milenageMac = milenage.mac_a;
auto sqnXorAk = OctetString::Xor(m_usim->m_sqn, milenageAk);
if (autnCheck == EAutnValidationRes::OK)
{
// Calculate milenage
auto milenage = calculateMilenage(m_usim->m_sqnMng->getSqn(), rand, false);
auto ckIk = OctetString::Concat(milenage.ck, milenage.ik);
auto sqnXorAk = OctetString::Xor(m_usim->m_sqnMng->getSqn(), milenage.ak);
auto snn = keys::ConstructServingNetworkName(*m_usim->m_currentPlmn);
// m_logger->debug("Calculated res[%s] ck[%s] ik[%s] ak[%s] mac_a[%s]", res.toHexString().c_str(),
// ck.toHexString().c_str(), ik.toHexString().c_str(), milenageAk.toHexString().c_str(),
// milenageMac.toHexString().c_str());
// m_logger->debug("Used snn[%s] sqn[%s]", snn.c_str(), m_usim->m_sqn.toHexString().c_str());
auto autnCheck = validateAutn(milenageAk, milenageMac, autn);
// Store the relevant parameters
m_usim->m_rand = rand.copy();
m_usim->m_resStar = keys::CalculateResStar(ckIk, snn, rand, milenage.res);
if (IGNORE_CONTROLS_FAILURES || autnCheck == EAutnValidationRes::OK)
{
// Create new partial native NAS security context and continue with key derivation
m_usim->m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>();
m_usim->m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
m_usim->m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
m_usim->m_nonCurrentNsCtx->keys.rand = rand.copy();
m_usim->m_nonCurrentNsCtx->keys.resStar = keys::CalculateResStar(ckIk, snn, rand, res);
m_usim->m_nonCurrentNsCtx->keys.res = std::move(res);
m_usim->m_nonCurrentNsCtx->keys.kAusf = keys::CalculateKAusfFor5gAka(ck, ik, snn, sqnXorAk);
m_usim->m_nonCurrentNsCtx->keys.kAusf = keys::CalculateKAusfFor5gAka(milenage.ck, milenage.ik, snn, sqnXorAk);
m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
keys::DeriveKeysSeafAmf(*m_base->config, *m_usim->m_currentPlmn, *m_usim->m_nonCurrentNsCtx);
// m_logger->debug("Derived kSeaf[%s] kAusf[%s] kAmf[%s]",
// m_usim->m_nonCurrentNsCtx->keys.kSeaf.toHexString().c_str(),
// m_usim->m_nonCurrentNsCtx->keys.kAusf.toHexString().c_str(),
// m_usim->m_nonCurrentNsCtx->keys.kAmf.toHexString().c_str());
// Send response
m_nwConsecutiveAuthFailure = 0;
m_timers->t3520.stop();
nas::AuthenticationResponse resp;
resp.authenticationResponseParameter = nas::IEAuthenticationResponseParameter{};
resp.authenticationResponseParameter->rawData = m_usim->m_nonCurrentNsCtx->keys.resStar.copy();
resp.authenticationResponseParameter->rawData = m_usim->m_resStar.copy();
sendNasMessage(resp);
}
else if (autnCheck == EAutnValidationRes::MAC_FAILURE)
{
if (networkFailingTheAuthCheck(true))
return;
m_timers->t3520.start();
sendFailure(nas::EMmCause::MAC_FAILURE);
}
else if (autnCheck == EAutnValidationRes::SYNCHRONISATION_FAILURE)
{
// TODO
m_logger->err("SYNCHRONISATION_FAILURE case not implemented yet in AUTN validation");
sendFailure(nas::EMmCause::UNSPECIFIED_PROTOCOL_ERROR);
if (networkFailingTheAuthCheck(true))
return;
m_timers->t3520.start();
auto milenage = calculateMilenage(m_usim->m_sqnMng->getSqn(), rand, true);
auto auts = keys::CalculateAuts(m_usim->m_sqnMng->getSqn(), milenage.ak_r, milenage.mac_s);
sendFailure(nas::EMmCause::SYNCH_FAILURE, std::move(auts));
}
else
else // the other case, separation bit mismatched
{
sendFailure(nas::EMmCause::UNSPECIFIED_PROTOCOL_ERROR);
if (networkFailingTheAuthCheck(true))
return;
m_timers->t3520.start();
sendFailure(nas::EMmCause::NON_5G_AUTHENTICATION_UNACCEPTABLE);
}
}
......@@ -344,6 +391,11 @@ void NasMm::receiveAuthenticationReject(const nas::AuthenticationReject &msg)
{
m_logger->err("Authentication Reject received.");
// The RAND and RES* values stored in the ME shall be deleted and timer T3516, if running, shall be stopped
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
if (msg.eapMessage.has_value() && msg.eapMessage->eap->code != eap::ECode::FAILURE)
{
m_logger->warn("Network sent EAP with inconvenient type in AuthenticationReject, ignoring EAP IE.");
......@@ -394,54 +446,69 @@ void NasMm::receiveEapResponseMessage(const eap::Eap &eap)
}
}
EAutnValidationRes NasMm::validateAutn(const OctetString &ak, const OctetString &mac, const OctetString &autn)
EAutnValidationRes NasMm::validateAutn(const OctetString &rand, const OctetString &autn)
{
// Decode AUTN
OctetString receivedSQNxorAK = autn.subCopy(0, 6);
OctetString receivedSQN = OctetString::Xor(receivedSQNxorAK, ak);
OctetString receivedAMF = autn.subCopy(6, 2);
OctetString receivedMAC = autn.subCopy(8, 8);
// Check MAC
if (receivedMAC != mac)
{
m_logger->err("AUTN validation MAC mismatch. expected: %s received: %s", mac.toHexString().c_str(),
receivedMAC.toHexString().c_str());
return EAutnValidationRes::MAC_FAILURE;
}
// TS 33.501: An ME accessing 5G shall check during authentication that the "separation bit" in the AMF field
// of AUTN is set to 1. The "separation bit" is bit 0 of the AMF field of AUTN.
// Check the separation bit
if (receivedAMF.get(0).bit(7) != 1)
{
m_logger->err("AUTN validation SEP-BIT failure. expected: 1, received: 0");
return EAutnValidationRes::AMF_SEPARATION_BIT_FAILURE;
}
// Derive AK and MAC
auto milenage = calculateMilenage(m_usim->m_sqnMng->getSqn(), rand, false);
OctetString receivedSQN = OctetString::Xor(receivedSQNxorAK, milenage.ak);
// Verify that the received sequence number SQN is in the correct range
if (!checkSqn(receivedSQN))
{
m_logger->err("AUTN validation SQN not acceptable");
if (!m_usim->m_sqnMng->checkSqn(receivedSQN))
return EAutnValidationRes::SYNCHRONISATION_FAILURE;
// Re-execute the milenage calculation (if case of sqn is changed with the received value)
milenage = calculateMilenage(m_usim->m_sqnMng->getSqn(), rand, false);
// Check MAC
if (receivedMAC != milenage.mac_a)
{
m_logger->err("AUTN validation MAC mismatch. expected [%s] received [%s]", milenage.mac_a.toHexString().c_str(),
receivedMAC.toHexString().c_str());
return EAutnValidationRes::MAC_FAILURE;
}
return EAutnValidationRes::OK;
}
bool NasMm::checkSqn(const OctetString &)
crypto::milenage::Milenage NasMm::calculateMilenage(const OctetString &sqn, const OctetString &rand, bool dummyAmf)
{
// TODO:
// Verify the freshness of sequence numbers to determine whether the specified sequence number is
// in the correct range and acceptable by the USIM. See 3GPP TS 33.102, Annex C.2.
return true;
}
OctetString amf = dummyAmf ? OctetString::FromSpare(2) : m_base->config->amf.copy();
crypto::milenage::Milenage NasMm::calculateMilenage(const OctetString &sqn, const OctetString &rand)
{
if (m_base->config->opType == OpType::OPC)
return crypto::milenage::Calculate(m_base->config->opC, m_base->config->key, rand, sqn, m_base->config->amf);
return crypto::milenage::Calculate(m_base->config->opC, m_base->config->key, rand, sqn, amf);
OctetString opc = crypto::milenage::CalculateOpC(m_base->config->opC, m_base->config->key);
return crypto::milenage::Calculate(opc, m_base->config->key, rand, sqn, m_base->config->amf);
return crypto::milenage::Calculate(opc, m_base->config->key, rand, sqn, amf);
}
bool NasMm::networkFailingTheAuthCheck(bool hasChance)
{
if (hasChance && m_nwConsecutiveAuthFailure++ < 3)
return false;
// NOTE: Normally if we should check if the UE has an emergency. If it has, it should consider as network passed the
// auth check, instead of performing the actions in the following lines. But it's difficult to maintain and
// implement this behaviour. Therefore we would expect other solutions for an emergency case. Such as
// - Network initiates a Security Mode Command with IA0 and EA0
// - UE performs emergency registration after releasing the connection
// END
m_logger->err("Network failing the authentication check");
localReleaseConnection();
// TODO: treat the active cell as barred
return true;
}
} // namespace nr::ue
\ No newline at end of file
......@@ -11,7 +11,7 @@
#include <lib/nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/nas/task.hpp>
#include <ue/nas/usim.hpp>
#include <ue/nas/usim/usim.hpp>
#include <ue/rrc/task.hpp>
#include <utils/common.hpp>
......@@ -232,6 +232,15 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol
m_usim->m_nonCurrentNsCtx = {};
}
}
// If the UE enters the 5GMM state 5GMM-DEREGISTERED or 5GMM-NULL,
// The RAND and RES* values stored in the ME shall be deleted and timer T3516, if running, shall be stopped
if (newState == EMmState::MM_DEREGISTERED || newState == EMmState::MM_NULL)
{
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
}
}
void NasMm::onSwitchRmState(ERmState oldState, ERmState newState)
......@@ -278,6 +287,12 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState)
nas::ESwitchOff::NORMAL_DE_REGISTRATION)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
}
// If the UE enters the 5GMM-IDLE, the RAND and RES* values stored
// in the ME shall be deleted and timer T3516, if running, shall be stopped
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
}
}
......
......@@ -9,12 +9,25 @@
#include "mm.hpp"
#include <lib/nas/utils.hpp>
#include <ue/nas/sm/sm.hpp>
namespace nr::ue
{
void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &msg)
{
// Abnormal case: 5.4.4.5, c) Generic UE configuration update and de-registration procedure collision
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED)
{
// "If the UE receives CONFIGURATION UPDATE COMMAND message after sending a DEREGISTRATION REQUEST message and
// the access type included in the DEREGISTRATION REQUEST message is same as the access in which the
// CONFIGURATION UPDATE COMMAND message is received, then the UE shall ignore the CONFIGURATION UPDATE COMMAND
// message and proceed with the de-registration procedure. Otherwise, the UE shall proceed with both the
// procedures."
m_logger->warn("Configuration Update Command ignored because of the De-registration procedure collusion");
return;
}
// Indicates there exists at least one configuration to be updated
bool hasNewConfig = false;
......@@ -127,6 +140,14 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms
}
}
// "If acknowledgement requested is indicated in the Configuration update indication IE in the CONFIGURATION UPDATE
// COMMAND message, the UE shall send a CONFIGURATION UPDATE COMPLETE message."
if (msg.configurationUpdateIndication.has_value() &&
msg.configurationUpdateIndication->ack == nas::EAcknowledgement::REQUESTED)
{
sendNasMessage(nas::ConfigurationUpdateComplete{});
}
// "If the CONFIGURATION UPDATE COMMAND message indicates "registration requested" in the Configuration update
// indication IE and:"
if (msg.configurationUpdateIndication.has_value() &&
......@@ -137,7 +158,7 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms
if (!hasNewConfig || (msg.allowedNssai.has_value() || msg.configuredNssai.has_value() ||
msg.networkSlicingIndication.has_value()))
{
if (hasEmergency()) // "an emergency PDU session exists,"
if (m_sm->anyEmergencySession()) // "an emergency PDU session exists,"
{
// "the UE shall, after the completion of the generic UE configuration
// update procedure and after the emergency PDU session is released, release the existing N1 NAS
......@@ -163,14 +184,6 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms
// TODO
}
}
// "If acknowledgement requested is indicated in the Configuration update indication IE in the CONFIGURATION UPDATE
// COMMAND message, the UE shall send a CONFIGURATION UPDATE COMPLETE message."
if (msg.configurationUpdateIndication.has_value() &&
msg.configurationUpdateIndication->ack == nas::EAcknowledgement::REQUESTED)
{
sendNasMessage(nas::ConfigurationUpdateComplete{});
}
}
} // namespace nr::ue
......@@ -236,13 +236,13 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
if (cause == nas::EMmCause::PLMN_NOT_ALLOWED)
{
// todo: add to forbidden plmn
nas::utils::AddToPlmnList(m_usim->m_forbiddenPlmnList, nas::utils::PlmnFrom(*m_usim->m_currentPlmn));
}
if (cause == nas::EMmCause::TA_NOT_ALLOWED || cause == nas::EMmCause::ROAMING_NOT_ALLOWED_IN_TA ||
cause == nas::EMmCause::NO_SUITIBLE_CELLS_IN_TA)
{
// todo: add to forbidden tai
nas::utils::AddToTaiList(m_usim->m_forbiddenTaiListRoaming, *m_usim->m_currentTai);
}
if (cause == nas::EMmCause::ILLEGAL_UE || cause == nas::EMmCause::FIVEG_SERVICES_NOT_ALLOWED)
......
......@@ -11,7 +11,7 @@
#include <lib/crypt/milenage.hpp>
#include <lib/nas/nas.hpp>
#include <lib/nas/timer.hpp>
#include <ue/nas/usim.hpp>
#include <ue/nas/usim/usim.hpp>
#include <ue/nts.hpp>
#include <ue/types.hpp>
#include <utils/nts.hpp>
......@@ -38,10 +38,12 @@ class NasMm
// Most recent registration request
std::unique_ptr<nas::RegistrationRequest> m_lastRegistrationRequest{};
// Most recent de-registration request
std::unique_ptr<nas::DeRegistrationRequestUeOriginating> m_lastDeregistrationRequest{};
// Most recent service request
std::unique_ptr<nas::ServiceRequest> m_lastServiceRequest{};
// Most recent de-registration request
std::unique_ptr<nas::DeRegistrationRequestUeOriginating> m_lastDeregistrationRequest{};
// Indicates that last registration request was sent without a NAS security context
bool m_lastRegWithoutNsc{};
// Indicates the last de-registration cause
EDeregCause m_lastDeregCause{};
// Indicates the last service request cause
......@@ -58,6 +60,8 @@ class NasMm
nas::IE5gsNetworkFeatureSupport m_nwFeatureSupport{};
// Last time Service Request needed indication for Data
long m_lastTimeServiceReqNeededIndForData{};
// Number of times the network failing the authentication check
int m_nwConsecutiveAuthFailure{};
friend class UeCmdHandler;
......@@ -112,9 +116,9 @@ class NasMm
void receiveEapSuccessMessage(const eap::Eap &eap);
void receiveEapFailureMessage(const eap::Eap &eap);
void receiveEapResponseMessage(const eap::Eap &eap);
EAutnValidationRes validateAutn(const OctetString &ak, const OctetString &mac, const OctetString &autn);
bool checkSqn(const OctetString &sqn);
crypto::milenage::Milenage calculateMilenage(const OctetString &sqn, const OctetString &rand);
EAutnValidationRes validateAutn(const OctetString &rand, const OctetString &autn);
crypto::milenage::Milenage calculateMilenage(const OctetString &sqn, const OctetString &rand, bool dummyAmf);
bool networkFailingTheAuthCheck(bool hasChance);
private: /* Security */
void receiveSecurityModeCommand(const nas::SecurityModeCommand &msg);
......
......@@ -18,11 +18,6 @@ namespace nr::ue
void NasMm::handlePlmnSearchResponse(const std::vector<UeCellMeasurement> &measures)
{
// TODO
// if (m_base->nodeListener)
// m_base->nodeListener->onConnected(app::NodeType::UE, m_base->config->getNodeName(), app::NodeType::GNB,
// gnbName);
if (m_mmSubState != EMmSubState::MM_REGISTERED_PLMN_SEARCH &&
m_mmSubState != EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE &&
m_mmSubState != EMmSubState::MM_DEREGISTERED_PLMN_SEARCH &&
......
......@@ -97,6 +97,7 @@ void NasMm::sendInitialRegistration(EInitialRegCause regCause)
// Send the message
sendNasMessage(*request);
m_lastRegistrationRequest = std::move(request);
m_lastRegWithoutNsc = m_usim->m_currentNsCtx == nullptr;
// Process timers
m_timers->t3510.start();
......@@ -187,6 +188,7 @@ void NasMm::sendMobilityRegistration(ERegUpdateCause updateCause)
// Send the message
sendNasMessage(*request);
m_lastRegistrationRequest = std::move(request);
m_lastRegWithoutNsc = m_usim->m_currentNsCtx == nullptr;
// Process timers
m_timers->t3510.start();
......@@ -218,6 +220,11 @@ void NasMm::receiveRegistrationAccept(const nas::RegistrationAccept &msg)
receiveInitialRegistrationAccept(msg);
else
receiveMobilityRegistrationAccept(msg);
// The RAND and RES* values stored in the ME shall be deleted and timer T3516, if running, shall be stopped
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
}
void NasMm::receiveInitialRegistrationAccept(const nas::RegistrationAccept &msg)
......@@ -449,6 +456,11 @@ void NasMm::receiveRegistrationReject(const nas::RegistrationReject &msg)
receiveInitialRegistrationReject(msg);
else
receiveMobilityRegistrationReject(msg);
// The RAND and RES* values stored in the ME shall be deleted and timer T3516, if running, shall be stopped
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
}
void NasMm::receiveInitialRegistrationReject(const nas::RegistrationReject &msg)
......
......@@ -8,6 +8,7 @@
#include "mm.hpp"
#include <lib/nas/utils.hpp>
#include <ue/nas/enc.hpp>
#include <ue/nas/keys.hpp>
namespace nr::ue
......@@ -29,6 +30,24 @@ static int FindSecurityContext(int ksi, const std::unique_ptr<NasSecurityContext
return -1;
}
static std::unique_ptr<NasSecurityContext> LocallyDeriveNsc()
{
auto nsc = std::make_unique<NasSecurityContext>();
nsc->tsc = nas::ETypeOfSecurityContext::NATIVE_SECURITY_CONTEXT;
nsc->ngKsi = 0;
nsc->downlinkCount = {};
nsc->uplinkCount = {};
nsc->integrity = nas::ETypeOfIntegrityProtectionAlgorithm::IA0;
nsc->ciphering = nas::ETypeOfCipheringAlgorithm::EA0;
nsc->keys.abba = OctetString::FromSpare(2);
nsc->keys.kAusf = OctetString::FromSpare(32);
nsc->keys.kSeaf = OctetString::FromSpare(32);
nsc->keys.kAmf = OctetString::FromSpare(32);
nsc->keys.kNasInt = OctetString::FromSpare(16);
nsc->keys.kNasEnc = OctetString::FromSpare(16);
return nsc;
}
void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
{
m_logger->debug("Security Mode Command received");
......@@ -37,11 +56,18 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
nas::SecurityModeReject resp;
resp.mmCause.value = cause;
sendNasMessage(resp);
m_logger->err("Rejecting Security Mode Command with cause: %s", nas::utils::EnumToString(cause));
m_logger->err("Rejecting Security Mode Command with cause [%s]", nas::utils::EnumToString(cause));
};
// The RAND and RES* values stored in the ME shall be deleted and timer T3516, if running, shall be stopped
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
// ============================== Check the received ngKSI ==============================
bool locallyDerived = false;
if (!IsValidKsi(msg.ngKsi))
{
m_logger->err("Invalid ngKSI received, tsc[%d], ksi[%d]", (int)msg.ngKsi.tsc, msg.ngKsi.ksi);
......@@ -53,7 +79,18 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
msg.selectedNasSecurityAlgorithms.integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 &&
msg.selectedNasSecurityAlgorithms.ciphering == nas::ETypeOfCipheringAlgorithm::EA0)
{
// TODO
if (hasEmergency())
{
m_logger->debug("Locally deriving a current NAS security context");
m_usim->m_currentNsCtx = LocallyDeriveNsc();
locallyDerived = true;
}
else
{
m_logger->err("[IA0, EA0] cannot be accepted as the UE does not have an emergency");
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return;
}
}
int whichCtx = FindSecurityContext(msg.ngKsi.ksi, m_usim->m_currentNsCtx, m_usim->m_nonCurrentNsCtx);
......@@ -66,14 +103,6 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
auto &nsCtx = whichCtx == 0 ? m_usim->m_currentNsCtx : m_usim->m_nonCurrentNsCtx;
// ======================== Check the integrity with new security context ========================
{
// TODO:
octet4 mac = msg._macForNewSC;
(void)mac;
}
// ======================== Check replayed UE security capabilities ========================
if (!nas::utils::DeepEqualsIe(msg.replayedUeSecurityCapabilities, createSecurityCapabilityIe()))
......@@ -96,37 +125,102 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return;
}
if (integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 && !(hasEmergency() || locallyDerived))
{
m_logger->err("[IA0] cannot be accepted as the UE does not have an emergency");
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return;
}
}
// ======================== Check the integrity with new security context ========================
bool clearNasCount = false;
bool horizontalDeriveNeeded =
msg.additional5gSecurityInformation.has_value() &&
msg.additional5gSecurityInformation->hdp == nas::EHorizontalDerivationParameter::REQUIRED;
if (msg.selectedNasSecurityAlgorithms.integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0)
{
NasSecurityContext tmpCtx = nsCtx->deepCopy();
tmpCtx.integrity = msg.selectedNasSecurityAlgorithms.integrity;
tmpCtx.ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
// Before deriving the keys for temporary NAS security context, concern the horizontal derivation case
// Because 33.501/6.9.3 says integrity check should be performed with the new key
if (horizontalDeriveNeeded)
tmpCtx.keys.kAmf = keys::DeriveAmfPrimeInMobility(true, tmpCtx.uplinkCount, tmpCtx.keys.kAmf);
keys::DeriveNasKeys(tmpCtx);
uint32_t calculatedMac = nas_enc::ComputeMac(tmpCtx.integrity, tmpCtx.downlinkCount, tmpCtx.is3gppAccess, false,
tmpCtx.keys.kNasInt, msg._originalPlainNasPdu);
// First check with the last estimated NAS COUNT
if (calculatedMac != static_cast<uint32_t>(msg._macForNewSC))
{
// Integrity check failed with the last NAS COUNT
// Now check with the NAS COUNT=0
tmpCtx.downlinkCount = {}; // assign NAS COUNT=0
calculatedMac = nas_enc::ComputeMac(tmpCtx.integrity, tmpCtx.downlinkCount, tmpCtx.is3gppAccess, false,
tmpCtx.keys.kNasInt, msg._originalPlainNasPdu);
if (calculatedMac != static_cast<uint32_t>(msg._macForNewSC))
{
// If it still mismatched, reject the security mode command
m_logger->err("Security Mode Command integrity check failed");
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return;
}
else
{
// Otherwise since the integrity passed with NAS COUNT=0, we should clear the NAS COUNT
// as specified in 5.4.2.3
m_logger->debug("Setting downlink NAS COUNT to 0");
clearNasCount = true;
}
}
}
// ============================ Process the security context. ============================
// ============================ Process the security context ============================
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)nsCtx->integrity, (int)nsCtx->ciphering);
// Clear the NAS count if necessary
if (clearNasCount)
nsCtx->downlinkCount = {};
// Assign ABBA (if any)
if (msg.abba.has_value())
nsCtx->keys.abba = msg.abba->rawData.copy();
// Handle horizontal derivation
if (horizontalDeriveNeeded)
{
m_logger->debug("Performing kAMF' derivation from kAMF in mobility");
nsCtx->keys.kAmf = keys::DeriveAmfPrimeInMobility(true, nsCtx->uplinkCount, nsCtx->keys.kAmf);
nsCtx->uplinkCount = {};
nsCtx->downlinkCount = {};
}
// Assign selected algorithms to security context, and derive NAS keys
nsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity;
nsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
keys::DeriveNasKeys(*nsCtx);
// m_logger->debug("Derived NAS keys integrity[%s] ciphering[%s]", nsCtx->keys.kNasInt.toHexString().c_str(),
// nsCtx->keys.kNasEnc.toHexString().c_str());
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)nsCtx->integrity, (int)nsCtx->ciphering);
// The UE shall in addition reset the uplink NAS COUNT counter if a) the SECURITY MODE COMMAND message is received
// in order to take a 5G NAS security context into use created after a successful execution of the 5G AKA based
// primary authentication and key agreement procedure or the EAP based ...
if (whichCtx == 1) // It is unclear how we can detect this, but checking if it is 'non-current' one.
if (whichCtx == 1) // NOTE: It is unclear how we can detect this, but checking if it is 'non-current' one.
{
nsCtx->uplinkCount.sqn = 0;
nsCtx->uplinkCount.overflow = octet2{0};
}
if (msg.selectedNasSecurityAlgorithms.integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0)
{
// TODO
}
// Set the new NAS Security Context as current one. (If it is not already the current one)
if (whichCtx == 1)
m_usim->m_currentNsCtx = std::make_unique<NasSecurityContext>(nsCtx->deepCopy());
......@@ -157,10 +251,21 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
}
}
// TODO: Bu service request de olabilir en son hangisiyse, ayrıca son mesaj yerine son unciphered mesaj da olabilir
// See 4.4.6
// Handle NAS message container
if (msg.additional5gSecurityInformation.has_value() &&
msg.additional5gSecurityInformation->rinmr == nas::ERetransmissionOfInitialNasMessageRequest::REQUESTED)
{
resp.nasMessageContainer = nas::IENasMessageContainer{};
if (m_mmState == EMmState::MM_REGISTERED_INITIATED && m_lastRegistrationRequest)
nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data);
else if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED && m_lastServiceRequest)
nas::EncodeNasMessage(*m_lastServiceRequest, resp.nasMessageContainer->data);
}
if (m_lastRegWithoutNsc && m_lastRegistrationRequest)
{
resp.nasMessageContainer = nas::IENasMessageContainer{};
nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data);
}
// Send response
sendNasMessage(resp);
......
......@@ -7,6 +7,7 @@
//
#include "mm.hpp"
#include <lib/nas/utils.hpp>
#include <ue/nas/sm/sm.hpp>
......@@ -211,6 +212,11 @@ void NasMm::receiveServiceReject(const nas::ServiceReject &msg)
m_serCounter = 0;
m_timers->t3517.stop();
// The RAND and RES* values stored in the ME shall be deleted and timer T3516, if running, shall be stopped
m_usim->m_rand = {};
m_usim->m_resStar = {};
m_timers->t3516.stop();
auto cause = msg.mmCause.value;
m_logger->err("Service Reject received with cause [%s]", nas::utils::EnumToString(cause));
......
......@@ -85,6 +85,11 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
}
break;
}
case 3516: {
m_usim->m_rand = {};
m_usim->m_resStar = {};
break;
}
case 3517: {
if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED)
{
......@@ -109,6 +114,11 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
m_usim->m_storedSuci = {};
break;
}
case 3520: {
logExpired();
networkFailingTheAuthCheck(false);
break;
}
case 3521: {
if (timer.getExpiryCount() == 5)
{
......
......@@ -30,7 +30,7 @@ static bool IsAcceptedWithoutIntegrity(const nas::PlainMmMessage &msg)
return msgType == nas::EMessageType::IDENTITY_REQUEST || msgType == nas::EMessageType::AUTHENTICATION_REQUEST ||
msgType == nas::EMessageType::AUTHENTICATION_RESULT || msgType == nas::EMessageType::AUTHENTICATION_REJECT ||
msgType == nas::EMessageType::REGISTRATION_REJECT ||
msgType == nas::EMessageType::DEREGISTRATION_ACCEPT_UE_TERMINATED ||
msgType == nas::EMessageType::DEREGISTRATION_ACCEPT_UE_ORIGINATING ||
msgType == nas::EMessageType::SERVICE_REJECT;
}
......@@ -118,6 +118,9 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
return;
}
((nas::SecurityModeCommand &)(*smcMsg))._macForNewSC = securedMm.messageAuthenticationCode;
((nas::SecurityModeCommand &)(*smcMsg))._originalPlainNasPdu = securedMm.plainNasMessage.copy();
receiveMmMessage((const nas::PlainMmMessage &)(*smcMsg));
return;
}
......@@ -169,8 +172,6 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
void NasMm::receiveMmMessage(const nas::PlainMmMessage &msg)
{
// TODO: trigger on receive
switch (msg.messageType)
{
case nas::EMessageType::REGISTRATION_ACCEPT:
......
......@@ -40,8 +40,6 @@ void NasSm::sendSmMessage(int psi, const nas::SmMessage &msg)
void NasSm::receiveSmMessage(const nas::SmMessage &msg)
{
// TODO: trigger on receive
switch (msg.messageType)
{
case nas::EMessageType::PDU_SESSION_ESTABLISHMENT_ACCEPT:
......
......@@ -8,7 +8,7 @@
#pragma once
#include "usim.hpp"
#include <ue/nas/usim/usim.hpp>
#include <lib/crypt/milenage.hpp>
#include <lib/nas/nas.hpp>
#include <lib/nas/timer.hpp>
......
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "sqn_mng.hpp"
#include <stdexcept>
namespace nr::ue
{
SqnManager::SqnManager(uint64_t indBitLen, uint64_t wrappingDelta)
: m_indBitLen{indBitLen}, m_wrappingDelta{wrappingDelta}, m_sqnArr(1ull << m_indBitLen)
{
if (m_indBitLen < 2 || m_indBitLen > 16)
throw std::runtime_error("bad indBitLen");
}
uint64_t SqnManager::getSeqFromSqn(uint64_t sqn) const
{
sqn &= ~((1ull << m_indBitLen) - 1ull);
sqn >>= m_indBitLen;
sqn &= (1ull << 48ull) - 1ull;
return sqn;
}
uint64_t SqnManager::getIndFromSqn(uint64_t sqn) const
{
return sqn & ((1ull << m_indBitLen) - 1ull);
}
uint64_t SqnManager::getSeqMs() const
{
return getSeqFromSqn(getSqnMs());
}
uint64_t SqnManager::getSqnMs() const
{
return *std::max_element(m_sqnArr.begin(), m_sqnArr.end());
}
bool SqnManager::checkSqn(uint64_t sqn)
{
uint64_t seq = getSeqFromSqn(sqn);
uint64_t ind = getIndFromSqn(sqn);
if (seq - getSeqMs() > m_wrappingDelta)
return false;
if (seq <= getSeqFromSqn(m_sqnArr[ind]))
return false;
m_sqnArr[ind] = sqn;
return true;
}
bool SqnManager::checkSqn(const OctetString &sqn)
{
OctetString str;
str.appendOctet2(0);
str.append(sqn);
return checkSqn(str.get8UL(0));
}
OctetString SqnManager::getSqn() const
{
return OctetString::FromOctet8(getSqnMs()).subCopy(2);
}
} // namespace nr::ue
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#pragma once
#include <algorithm>
#include <cstdint>
#include <vector>
#include <utils/octet_string.hpp>
namespace nr::ue
{
class SqnManager
{
private:
uint64_t m_indBitLen;
uint64_t m_wrappingDelta;
std::vector<uint64_t> m_sqnArr;
public:
SqnManager(uint64_t indBitLen, uint64_t wrappingDelta);
private:
[[nodiscard]] uint64_t getSeqFromSqn(uint64_t sqn) const;
[[nodiscard]] uint64_t getIndFromSqn(uint64_t sqn) const;
[[nodiscard]] uint64_t getSeqMs() const;
[[nodiscard]] uint64_t getSqnMs() const;
bool checkSqn(uint64_t sqn);
public:
[[nodiscard]] OctetString getSqn() const;
bool checkSqn(const OctetString &sqn);
};
} // namespace nr::ue
......@@ -19,6 +19,8 @@ void ue::Usim::initialize(bool hasSupi, const UeConfig::Initials &initials)
m_defConfiguredNssai = initials.defaultConfiguredNssai;
m_configuredNssai = initials.configuredNssai;
m_sqnMng = std::make_unique<SqnManager>(5ull, 1ull << 28ull);
}
bool Usim::isValid()
......
......@@ -8,9 +8,12 @@
#pragma once
#include <lib/nas/msg.hpp>
#include "sqn_mng.hpp"
#include <memory>
#include <optional>
#include <lib/nas/msg.hpp>
#include <ue/types.hpp>
#include <utils/common_types.hpp>
#include <utils/octet_string.hpp>
......@@ -46,7 +49,9 @@ class Usim
// Security related
std::unique_ptr<NasSecurityContext> m_currentNsCtx{};
std::unique_ptr<NasSecurityContext> m_nonCurrentNsCtx{};
OctetString m_sqn{};
OctetString m_rand{};
OctetString m_resStar{};
std::unique_ptr<SqnManager> m_sqnMng{};
// NSSAI related
NetworkSlice m_defConfiguredNssai{};
......@@ -67,6 +72,7 @@ class Usim
public:
void initialize(bool hasSupi, const UeConfig::Initials &initials);
bool isValid();
void invalidate();
};
......
......@@ -303,10 +303,6 @@ struct UeKeys
{
OctetString abba{};
OctetString rand{};
OctetString res{};
OctetString resStar{}; // used in 5G-AKA
OctetString kAusf{};
OctetString kSeaf{};
OctetString kAmf{};
......@@ -316,9 +312,6 @@ struct UeKeys
[[nodiscard]] UeKeys deepCopy() const
{
UeKeys keys;
keys.rand = rand.subCopy(0);
keys.res = res.subCopy(0);
keys.resStar = resStar.subCopy(0);
keys.kAusf = kAusf.subCopy(0);
keys.kSeaf = kSeaf.subCopy(0);
keys.kAmf = kAmf.subCopy(0);
......
......@@ -15,10 +15,10 @@ struct cons
// Version information
static constexpr const uint8_t Major = 3;
static constexpr const uint8_t Minor = 1;
static constexpr const uint8_t Patch = 7;
static constexpr const uint8_t Patch = 8;
static constexpr const char *Project = "UERANSIM";
static constexpr const char *Tag = "v3.1.7";
static constexpr const char *Name = "UERANSIM v3.1.7";
static constexpr const char *Tag = "v3.1.8";
static constexpr const char *Name = "UERANSIM v3.1.8";
static constexpr const char *Owner = "ALİ GÜNGÖR";
// Some port values
......
--[[
-- Dissector for Radio Link Simulation Protocol
-- (used by UERANSIM <https://github.com/aligungr/UERANSIM>).
-- When this dissector was written, UERANSIM was in version 3.1.7.
--
-- CC0-1.0 2021 - Louis Royer (<https://github.com/louisroyer/RLS-wireshark-dissector>)
--]]
......@@ -30,14 +29,14 @@ local sim_pos_y = ProtoField.uint32("rls.sim_pos_y", "RLS Position Y", base.DEC)
local sim_pos_z = ProtoField.uint32("rls.sim_pos_z", "RLS Position Z", base.DEC)
-- For cell Info Response
local mcc = ProtoField.uint16("rls.mmc", "RLS MMC", base.DEC)
local mnc = ProtoField.uint16("rls.mnc", "RLS MNC", base.DEC)
local long_mnc = ProtoField.bool("rls.long_mnc", "RLS MNC is long", base.BOOL)
local nci = ProtoField.uint64("rls.nci", "RLS New Radio Cell Identity", base.HEX)
local tac = ProtoField.uint32("rls.tac", "RLS Tracking Area Code", base.DEC)
local mcc = ProtoField.uint16("rls.mcc", "MCC", base.DEC)
local mnc = ProtoField.uint16("rls.mnc", "MNC", base.DEC)
local long_mnc = ProtoField.bool("rls.long_mnc", "MNC is 3-digit", base.BOOL)
local nci = ProtoField.uint64("rls.nci", "NR Cell Identity", base.HEX)
local tac = ProtoField.uint32("rls.tac", "Tracking Area Code", base.DEC)
local dbm = ProtoField.int32("rls.dbm", "RLS Signal Strength (dBm)", base.DEC)
local gnb_name = ProtoField.string("rls.gnb_name", "RLS gNb name")
local link_ip = ProtoField.string("rls.link_ip", "RLS gNb Link IP")
local gnb_name = ProtoField.string("rls.gnb_name", "gNB Name")
local link_ip = ProtoField.string("rls.link_ip", "gNB Link IP")
-- For PDU Delivery
local pdu_type_name = {
......@@ -49,14 +48,14 @@ local pdu_type_name = {
local pdu_type = ProtoField.uint8("rls.pdu_type", "RLS PDU Type", base.DEC, pdu_type_name)
local rrc_channel_name = {
[0] = "BCCH_BCH",
[1] = "BCCH_DL_SCH",
[2] = "DL_CCCH",
[3] = "DL_DCCH",
[0] = "BCCH-BCH",
[1] = "BCCH-DL-SCH",
[2] = "DL-CCCH",
[3] = "DL-DCCH",
[4] = "PCCH",
[5] = "UL_CCCH",
[6] = "UL_CCCH1",
[7] = "UL_DCCH",
[5] = "UL-CCCH",
[6] = "UL-CCCH1",
[7] = "UL-DCCH",
}
local rrc_channel_dissector = {
......@@ -71,7 +70,7 @@ local rrc_channel_dissector = {
}
local rrc_channel = ProtoField.uint32("rls.rrc_channel", "RRC Channel", base.DEC, rrc_channel_name)
local session_id = ProtoField.uint32("rls.session_id", "RLS Session ID", base.DEC)
local session_id = ProtoField.uint32("rls.session_id", "PDU Session ID", base.DEC)
--[[
-- Dissector definition
......@@ -109,7 +108,7 @@ function rls_protocol.dissector(buffer, pinfo, tree)
subtree:add(sim_pos_z, buffer(21,4))
elseif msg_type == 2 then -- Cell Info Response
subtree:add(mcc, buffer(13,2))
local mnc_tree = subtree:add(rls_protocol, buffer(15,3), "RLS MNC: "..tostring(buffer(15,2):uint()))
local mnc_tree = subtree:add(rls_protocol, buffer(15,3), "MNC: "..tostring(buffer(15,2):uint()))
mnc_tree:add(mnc, buffer(15,2))
mnc_tree:add(long_mnc, buffer(17,1))
subtree:add(nci, buffer(18,8))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment