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 @@ ...@@ -2,7 +2,7 @@
<a href="https://github.com/aligungr/UERANSIM"><img src="/.github/logo.png" width="75" title="UERANSIM"></a> <a href="https://github.com/aligungr/UERANSIM"><img src="/.github/logo.png" width="75" title="UERANSIM"></a>
</p> </p>
<p align="center"> <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/3GPP-R15-orange" />
<img src="https://img.shields.io/badge/License-GPL--3.0-green"/> <img src="https://img.shields.io/badge/License-GPL--3.0-green"/>
</p> </p>
......
...@@ -27,10 +27,10 @@ Milenage Calculate(const OctetString &opc, const OctetString &key, const OctetSt ...@@ -27,10 +27,10 @@ Milenage Calculate(const OctetString &opc, const OctetString &key, const OctetSt
r.ak_r = OctetString::FromSpare(6); 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())) 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(), 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())) r.ak_r.data()))
throw std::runtime_error("OPC calculation failed"); throw std::runtime_error("Milenage calculation failed");
return r; return r;
} }
......
...@@ -556,6 +556,7 @@ struct SecurityModeCommand : PlainMmMessage ...@@ -556,6 +556,7 @@ struct SecurityModeCommand : PlainMmMessage
std::optional<IES1UeNetworkCapability> replayedS1UeNetworkCapability{}; std::optional<IES1UeNetworkCapability> replayedS1UeNetworkCapability{};
octet4 _macForNewSC{}; octet4 _macForNewSC{};
OctetString _originalPlainNasPdu{};
SecurityModeCommand(); SecurityModeCommand();
void onBuild(NasMessageBuilder &b); void onBuild(NasMessageBuilder &b);
......
...@@ -81,7 +81,6 @@ std::pair<OctetString, OctetString> CalculateCkPrimeIkPrime(const OctetString &c ...@@ -81,7 +81,6 @@ std::pair<OctetString, OctetString> CalculateCkPrimeIkPrime(const OctetString &c
s[1] = sqnXorAk.copy(); s[1] = sqnXorAk.copy();
auto res = crypto::CalculateKdfKey(key, 0x20, s, 2); auto res = crypto::CalculateKdfKey(key, 0x20, s, 2);
;
std::pair<OctetString, OctetString> ckIk; std::pair<OctetString, OctetString> ckIk;
ckIk.first = res.subCopy(0, ck.length()); ckIk.first = res.subCopy(0, ck.length());
...@@ -139,4 +138,20 @@ OctetString CalculateResStar(const OctetString &key, const std::string &snn, con ...@@ -139,4 +138,20 @@ OctetString CalculateResStar(const OctetString &key, const std::string &snn, con
return output.subCopy(output.length() - 16); 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 } // namespace nr::ue::keys
\ No newline at end of file
...@@ -28,6 +28,11 @@ void DeriveNasKeys(NasSecurityContext &securityContext); ...@@ -28,6 +28,11 @@ void DeriveNasKeys(NasSecurityContext &securityContext);
*/ */
std::string ConstructServingNetworkName(const Plmn &plmn); 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 * 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); ...@@ -66,4 +71,9 @@ OctetString CalculateKAusfForEapAkaPrime(const OctetString &mk);
OctetString CalculateResStar(const OctetString &key, const std::string &snn, const OctetString &rand, OctetString CalculateResStar(const OctetString &key, const std::string &snn, const OctetString &rand,
const OctetString &res); 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 } // namespace nr::ue::keys
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
// //
#include "mm.hpp" #include "mm.hpp"
#include <lib/nas/utils.hpp>
#include <ue/nas/keys.hpp> #include <ue/nas/keys.hpp>
static const bool IGNORE_CONTROLS_FAILURES = false;
static const bool USE_SQN_HACK = true; // TODO static const bool USE_SQN_HACK = true; // TODO
namespace nr::ue namespace nr::ue
...@@ -23,6 +24,8 @@ void NasMm::receiveAuthenticationRequest(const nas::AuthenticationRequest &msg) ...@@ -23,6 +24,8 @@ void NasMm::receiveAuthenticationRequest(const nas::AuthenticationRequest &msg)
return; return;
} }
m_timers->t3520.start();
if (msg.eapMessage.has_value()) if (msg.eapMessage.has_value())
receiveAuthenticationRequestEap(msg); receiveAuthenticationRequestEap(msg);
else else
...@@ -43,8 +46,6 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -43,8 +46,6 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
m_timers->t3521.stop(); m_timers->t3521.stop();
}; };
m_timers->t3520.stop();
// Read EAP-AKA' request // Read EAP-AKA' request
auto &receivedEap = (const eap::EapAkaPrime &)*msg.eapMessage->eap; auto &receivedEap = (const eap::EapAkaPrime &)*msg.eapMessage->eap;
auto receivedRand = receivedEap.attributes.getRand(); auto receivedRand = receivedEap.attributes.getRand();
...@@ -62,23 +63,19 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -62,23 +63,19 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
// Log.warning(Tag.CONFIG, "USE_SQN_HACK: %s", USE_SQN_HACK); // Log.warning(Tag.CONFIG, "USE_SQN_HACK: %s", USE_SQN_HACK);
} }
if (IGNORE_CONTROLS_FAILURES) /*if (USE_SQN_HACK)
m_logger->warn("IGNORE_CONTROLS_FAILURES enabled");
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); 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 &res = milenage.res;
auto &ck = milenage.ck; auto &ck = milenage.ck;
auto &ik = milenage.ik; auto &ik = milenage.ik;
auto &milenageAk = milenage.ak; 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 = auto ckPrimeIkPrime =
keys::CalculateCkPrimeIkPrime(ck, ik, keys::ConstructServingNetworkName(*m_usim->m_currentPlmn), sqnXorAk); keys::CalculateCkPrimeIkPrime(ck, ik, keys::ConstructServingNetworkName(*m_usim->m_currentPlmn), sqnXorAk);
auto &ckPrime = ckPrimeIkPrime.first; auto &ckPrime = ckPrimeIkPrime.first;
...@@ -106,7 +103,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -106,7 +103,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
// m_logger->debug("calculated kaut: %s", kaut.toHexString().c_str()); // m_logger->debug("calculated kaut: %s", kaut.toHexString().c_str());
// Control received KDF // Control received KDF
if (!IGNORE_CONTROLS_FAILURES && receivedKdf != 1) if (receivedKdf != 1)
{ {
ueRejectionTimers(); ueRejectionTimers();
...@@ -125,7 +122,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -125,7 +122,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
} }
// Control received AUTN // Control received AUTN
auto autnCheck = validateAutn(milenageAk, milenageMac, receivedAutn); auto autnCheck = validateAutn(receivedRand, receivedAutn);
if (autnCheck != EAutnValidationRes::OK) if (autnCheck != EAutnValidationRes::OK)
{ {
eap::EapAkaPrime *eapResponse = nullptr; eap::EapAkaPrime *eapResponse = nullptr;
...@@ -148,7 +145,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -148,7 +145,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
eapResponse->attributes.putClientErrorCode(0); eapResponse->attributes.putClientErrorCode(0);
} }
if (!IGNORE_CONTROLS_FAILURES && eapResponse != nullptr) if (eapResponse != nullptr)
{ {
ueRejectionTimers(); ueRejectionTimers();
...@@ -168,12 +165,10 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -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(), m_logger->err("AT_MAC failure in EAP AKA'. expected: %s received: %s", expectedMac.toHexString().c_str(),
receivedMac.toHexString().c_str()); receivedMac.toHexString().c_str());
if (!IGNORE_CONTROLS_FAILURES)
{
ueRejectionTimers(); ueRejectionTimers();
auto eapResponse = std::make_unique<eap::EapAkaPrime>(eap::ECode::RESPONSE, receivedEap.id, auto eapResponse =
eap::ESubType::AKA_CLIENT_ERROR); std::make_unique<eap::EapAkaPrime>(eap::ECode::RESPONSE, receivedEap.id, eap::ESubType::AKA_CLIENT_ERROR);
eapResponse->attributes.putClientErrorCode(0); eapResponse->attributes.putClientErrorCode(0);
nas::AuthenticationReject response; nas::AuthenticationReject response;
...@@ -183,7 +178,6 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -183,7 +178,6 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
sendNasMessage(response); sendNasMessage(response);
return; return;
} }
}
// Create new partial native NAS security context and continue key derivation // Create new partial native NAS security context and continue key derivation
auto kAusf = keys::CalculateKAusfForEapAkaPrime(mk); auto kAusf = keys::CalculateKAusfForEapAkaPrime(mk);
...@@ -192,9 +186,8 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -192,9 +186,8 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
m_usim->m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>(); m_usim->m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>();
m_usim->m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc; m_usim->m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
m_usim->m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi; m_usim->m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
m_usim->m_nonCurrentNsCtx->keys.rand = std::move(receivedRand); m_usim->m_rand = std::move(receivedRand);
m_usim->m_nonCurrentNsCtx->keys.res = std::move(res); m_usim->m_resStar = {};
m_usim->m_nonCurrentNsCtx->keys.resStar = {};
m_usim->m_nonCurrentNsCtx->keys.kAusf = std::move(kAusf); m_usim->m_nonCurrentNsCtx->keys.kAusf = std::move(kAusf);
m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy(); m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
...@@ -207,7 +200,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -207,7 +200,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
{ {
auto *akaPrimeResponse = auto *akaPrimeResponse =
new eap::EapAkaPrime(eap::ECode::RESPONSE, receivedEap.id, eap::ESubType::AKA_CHALLENGE); 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.putMac(OctetString::FromSpare(16)); // Dummy mac for now
akaPrimeResponse->attributes.putKdf(1); akaPrimeResponse->attributes.putKdf(1);
...@@ -223,23 +216,39 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -223,23 +216,39 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
sendNasMessage(response); sendNasMessage(response);
} }
// TODO (dont forget: m_nwConsecutiveAuthFailure = 0;)
} }
void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &msg) 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{}; nas::AuthenticationFailure resp{};
resp.mmCause.value = cause; 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) sendNasMessage(resp);
m_logger->warn("IGNORE_CONTROLS_FAILURES enabled"); };
// ========================== Check the received parameters syntactically ==========================
if (!msg.authParamRAND.has_value() || !msg.authParamAUTN.has_value()) if (!msg.authParamRAND.has_value() || !msg.authParamAUTN.has_value())
{ {
...@@ -247,72 +256,110 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest & ...@@ -247,72 +256,110 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &
return; 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 &rand = msg.authParamRAND->value;
auto &autn = msg.authParamAUTN->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; autnCheck = validateAutn(rand, autn);
m_usim->m_sqn = OctetString::Xor(autn.subCopy(0, 6), ak); m_timers->t3516.start();
} }
auto milenage = calculateMilenage(m_usim->m_sqn, rand); if (autnCheck == EAutnValidationRes::OK)
auto &res = milenage.res; {
auto &ck = milenage.ck; // Calculate milenage
auto &ik = milenage.ik; auto milenage = calculateMilenage(m_usim->m_sqnMng->getSqn(), rand, false);
auto ckIk = OctetString::Concat(ck, ik); auto ckIk = OctetString::Concat(milenage.ck, milenage.ik);
auto &milenageAk = milenage.ak; auto sqnXorAk = OctetString::Xor(m_usim->m_sqnMng->getSqn(), milenage.ak);
auto &milenageMac = milenage.mac_a;
auto sqnXorAk = OctetString::Xor(m_usim->m_sqn, milenageAk);
auto snn = keys::ConstructServingNetworkName(*m_usim->m_currentPlmn); 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(), // Store the relevant parameters
// ck.toHexString().c_str(), ik.toHexString().c_str(), milenageAk.toHexString().c_str(), m_usim->m_rand = rand.copy();
// milenageMac.toHexString().c_str()); m_usim->m_resStar = keys::CalculateResStar(ckIk, snn, rand, milenage.res);
// m_logger->debug("Used snn[%s] sqn[%s]", snn.c_str(), m_usim->m_sqn.toHexString().c_str());
auto autnCheck = validateAutn(milenageAk, milenageMac, autn);
if (IGNORE_CONTROLS_FAILURES || autnCheck == EAutnValidationRes::OK)
{
// Create new partial native NAS security context and continue with key derivation // Create new partial native NAS security context and continue with key derivation
m_usim->m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>(); m_usim->m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>();
m_usim->m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc; m_usim->m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
m_usim->m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi; m_usim->m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
m_usim->m_nonCurrentNsCtx->keys.rand = rand.copy(); m_usim->m_nonCurrentNsCtx->keys.kAusf = keys::CalculateKAusfFor5gAka(milenage.ck, milenage.ik, snn, sqnXorAk);
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.abba = msg.abba.rawData.copy(); m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
keys::DeriveKeysSeafAmf(*m_base->config, *m_usim->m_currentPlmn, *m_usim->m_nonCurrentNsCtx); 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 // Send response
m_nwConsecutiveAuthFailure = 0;
m_timers->t3520.stop();
nas::AuthenticationResponse resp; nas::AuthenticationResponse resp;
resp.authenticationResponseParameter = nas::IEAuthenticationResponseParameter{}; resp.authenticationResponseParameter = nas::IEAuthenticationResponseParameter{};
resp.authenticationResponseParameter->rawData = m_usim->m_nonCurrentNsCtx->keys.resStar.copy(); resp.authenticationResponseParameter->rawData = m_usim->m_resStar.copy();
sendNasMessage(resp); sendNasMessage(resp);
} }
else if (autnCheck == EAutnValidationRes::MAC_FAILURE) else if (autnCheck == EAutnValidationRes::MAC_FAILURE)
{ {
if (networkFailingTheAuthCheck(true))
return;
m_timers->t3520.start();
sendFailure(nas::EMmCause::MAC_FAILURE); sendFailure(nas::EMmCause::MAC_FAILURE);
} }
else if (autnCheck == EAutnValidationRes::SYNCHRONISATION_FAILURE) else if (autnCheck == EAutnValidationRes::SYNCHRONISATION_FAILURE)
{ {
// TODO if (networkFailingTheAuthCheck(true))
m_logger->err("SYNCHRONISATION_FAILURE case not implemented yet in AUTN validation"); return;
sendFailure(nas::EMmCause::UNSPECIFIED_PROTOCOL_ERROR);
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) ...@@ -344,6 +391,11 @@ void NasMm::receiveAuthenticationReject(const nas::AuthenticationReject &msg)
{ {
m_logger->err("Authentication Reject received."); 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) 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."); 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) ...@@ -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 // Decode AUTN
OctetString receivedSQNxorAK = autn.subCopy(0, 6); OctetString receivedSQNxorAK = autn.subCopy(0, 6);
OctetString receivedSQN = OctetString::Xor(receivedSQNxorAK, ak);
OctetString receivedAMF = autn.subCopy(6, 2); OctetString receivedAMF = autn.subCopy(6, 2);
OctetString receivedMAC = autn.subCopy(8, 8); OctetString receivedMAC = autn.subCopy(8, 8);
// Check MAC // Check the separation bit
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.
if (receivedAMF.get(0).bit(7) != 1) if (receivedAMF.get(0).bit(7) != 1)
{ {
m_logger->err("AUTN validation SEP-BIT failure. expected: 1, received: 0"); m_logger->err("AUTN validation SEP-BIT failure. expected: 1, received: 0");
return EAutnValidationRes::AMF_SEPARATION_BIT_FAILURE; 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 // Verify that the received sequence number SQN is in the correct range
if (!checkSqn(receivedSQN)) if (!m_usim->m_sqnMng->checkSqn(receivedSQN))
{
m_logger->err("AUTN validation SQN not acceptable");
return EAutnValidationRes::SYNCHRONISATION_FAILURE; 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; return EAutnValidationRes::OK;
} }
bool NasMm::checkSqn(const OctetString &) crypto::milenage::Milenage NasMm::calculateMilenage(const OctetString &sqn, const OctetString &rand, bool dummyAmf)
{ {
// TODO: OctetString amf = dummyAmf ? OctetString::FromSpare(2) : m_base->config->amf.copy();
// 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;
}
crypto::milenage::Milenage NasMm::calculateMilenage(const OctetString &sqn, const OctetString &rand)
{
if (m_base->config->opType == OpType::OPC) 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); 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 } // namespace nr::ue
\ No newline at end of file
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <lib/nas/utils.hpp> #include <lib/nas/utils.hpp>
#include <ue/app/task.hpp> #include <ue/app/task.hpp>
#include <ue/nas/task.hpp> #include <ue/nas/task.hpp>
#include <ue/nas/usim.hpp> #include <ue/nas/usim/usim.hpp>
#include <ue/rrc/task.hpp> #include <ue/rrc/task.hpp>
#include <utils/common.hpp> #include <utils/common.hpp>
...@@ -232,6 +232,15 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol ...@@ -232,6 +232,15 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol
m_usim->m_nonCurrentNsCtx = {}; 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) void NasMm::onSwitchRmState(ERmState oldState, ERmState newState)
...@@ -278,6 +287,12 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState) ...@@ -278,6 +287,12 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState)
nas::ESwitchOff::NORMAL_DE_REGISTRATION) nas::ESwitchOff::NORMAL_DE_REGISTRATION)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA); 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 @@ ...@@ -9,12 +9,25 @@
#include "mm.hpp" #include "mm.hpp"
#include <lib/nas/utils.hpp> #include <lib/nas/utils.hpp>
#include <ue/nas/sm/sm.hpp>
namespace nr::ue namespace nr::ue
{ {
void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &msg) 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 // Indicates there exists at least one configuration to be updated
bool hasNewConfig = false; bool hasNewConfig = false;
...@@ -127,6 +140,14 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms ...@@ -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 // "If the CONFIGURATION UPDATE COMMAND message indicates "registration requested" in the Configuration update
// indication IE and:" // indication IE and:"
if (msg.configurationUpdateIndication.has_value() && if (msg.configurationUpdateIndication.has_value() &&
...@@ -137,7 +158,7 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms ...@@ -137,7 +158,7 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms
if (!hasNewConfig || (msg.allowedNssai.has_value() || msg.configuredNssai.has_value() || if (!hasNewConfig || (msg.allowedNssai.has_value() || msg.configuredNssai.has_value() ||
msg.networkSlicingIndication.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 // "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 // 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 ...@@ -163,14 +184,6 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms
// TODO // 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 } // namespace nr::ue
...@@ -236,13 +236,13 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi ...@@ -236,13 +236,13 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
if (cause == nas::EMmCause::PLMN_NOT_ALLOWED) 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 || if (cause == nas::EMmCause::TA_NOT_ALLOWED || cause == nas::EMmCause::ROAMING_NOT_ALLOWED_IN_TA ||
cause == nas::EMmCause::NO_SUITIBLE_CELLS_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) if (cause == nas::EMmCause::ILLEGAL_UE || cause == nas::EMmCause::FIVEG_SERVICES_NOT_ALLOWED)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <lib/crypt/milenage.hpp> #include <lib/crypt/milenage.hpp>
#include <lib/nas/nas.hpp> #include <lib/nas/nas.hpp>
#include <lib/nas/timer.hpp> #include <lib/nas/timer.hpp>
#include <ue/nas/usim.hpp> #include <ue/nas/usim/usim.hpp>
#include <ue/nts.hpp> #include <ue/nts.hpp>
#include <ue/types.hpp> #include <ue/types.hpp>
#include <utils/nts.hpp> #include <utils/nts.hpp>
...@@ -38,10 +38,12 @@ class NasMm ...@@ -38,10 +38,12 @@ class NasMm
// Most recent registration request // Most recent registration request
std::unique_ptr<nas::RegistrationRequest> m_lastRegistrationRequest{}; std::unique_ptr<nas::RegistrationRequest> m_lastRegistrationRequest{};
// Most recent de-registration request
std::unique_ptr<nas::DeRegistrationRequestUeOriginating> m_lastDeregistrationRequest{};
// Most recent service request // Most recent service request
std::unique_ptr<nas::ServiceRequest> m_lastServiceRequest{}; 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 // Indicates the last de-registration cause
EDeregCause m_lastDeregCause{}; EDeregCause m_lastDeregCause{};
// Indicates the last service request cause // Indicates the last service request cause
...@@ -58,6 +60,8 @@ class NasMm ...@@ -58,6 +60,8 @@ class NasMm
nas::IE5gsNetworkFeatureSupport m_nwFeatureSupport{}; nas::IE5gsNetworkFeatureSupport m_nwFeatureSupport{};
// Last time Service Request needed indication for Data // Last time Service Request needed indication for Data
long m_lastTimeServiceReqNeededIndForData{}; long m_lastTimeServiceReqNeededIndForData{};
// Number of times the network failing the authentication check
int m_nwConsecutiveAuthFailure{};
friend class UeCmdHandler; friend class UeCmdHandler;
...@@ -112,9 +116,9 @@ class NasMm ...@@ -112,9 +116,9 @@ class NasMm
void receiveEapSuccessMessage(const eap::Eap &eap); void receiveEapSuccessMessage(const eap::Eap &eap);
void receiveEapFailureMessage(const eap::Eap &eap); void receiveEapFailureMessage(const eap::Eap &eap);
void receiveEapResponseMessage(const eap::Eap &eap); void receiveEapResponseMessage(const eap::Eap &eap);
EAutnValidationRes validateAutn(const OctetString &ak, const OctetString &mac, const OctetString &autn); EAutnValidationRes validateAutn(const OctetString &rand, const OctetString &autn);
bool checkSqn(const OctetString &sqn); crypto::milenage::Milenage calculateMilenage(const OctetString &sqn, const OctetString &rand, bool dummyAmf);
crypto::milenage::Milenage calculateMilenage(const OctetString &sqn, const OctetString &rand); bool networkFailingTheAuthCheck(bool hasChance);
private: /* Security */ private: /* Security */
void receiveSecurityModeCommand(const nas::SecurityModeCommand &msg); void receiveSecurityModeCommand(const nas::SecurityModeCommand &msg);
......
...@@ -18,11 +18,6 @@ namespace nr::ue ...@@ -18,11 +18,6 @@ namespace nr::ue
void NasMm::handlePlmnSearchResponse(const std::vector<UeCellMeasurement> &measures) 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 && if (m_mmSubState != EMmSubState::MM_REGISTERED_PLMN_SEARCH &&
m_mmSubState != EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE && m_mmSubState != EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE &&
m_mmSubState != EMmSubState::MM_DEREGISTERED_PLMN_SEARCH && m_mmSubState != EMmSubState::MM_DEREGISTERED_PLMN_SEARCH &&
......
...@@ -97,6 +97,7 @@ void NasMm::sendInitialRegistration(EInitialRegCause regCause) ...@@ -97,6 +97,7 @@ void NasMm::sendInitialRegistration(EInitialRegCause regCause)
// Send the message // Send the message
sendNasMessage(*request); sendNasMessage(*request);
m_lastRegistrationRequest = std::move(request); m_lastRegistrationRequest = std::move(request);
m_lastRegWithoutNsc = m_usim->m_currentNsCtx == nullptr;
// Process timers // Process timers
m_timers->t3510.start(); m_timers->t3510.start();
...@@ -187,6 +188,7 @@ void NasMm::sendMobilityRegistration(ERegUpdateCause updateCause) ...@@ -187,6 +188,7 @@ void NasMm::sendMobilityRegistration(ERegUpdateCause updateCause)
// Send the message // Send the message
sendNasMessage(*request); sendNasMessage(*request);
m_lastRegistrationRequest = std::move(request); m_lastRegistrationRequest = std::move(request);
m_lastRegWithoutNsc = m_usim->m_currentNsCtx == nullptr;
// Process timers // Process timers
m_timers->t3510.start(); m_timers->t3510.start();
...@@ -218,6 +220,11 @@ void NasMm::receiveRegistrationAccept(const nas::RegistrationAccept &msg) ...@@ -218,6 +220,11 @@ void NasMm::receiveRegistrationAccept(const nas::RegistrationAccept &msg)
receiveInitialRegistrationAccept(msg); receiveInitialRegistrationAccept(msg);
else else
receiveMobilityRegistrationAccept(msg); 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) void NasMm::receiveInitialRegistrationAccept(const nas::RegistrationAccept &msg)
...@@ -449,6 +456,11 @@ void NasMm::receiveRegistrationReject(const nas::RegistrationReject &msg) ...@@ -449,6 +456,11 @@ void NasMm::receiveRegistrationReject(const nas::RegistrationReject &msg)
receiveInitialRegistrationReject(msg); receiveInitialRegistrationReject(msg);
else else
receiveMobilityRegistrationReject(msg); 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) void NasMm::receiveInitialRegistrationReject(const nas::RegistrationReject &msg)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "mm.hpp" #include "mm.hpp"
#include <lib/nas/utils.hpp> #include <lib/nas/utils.hpp>
#include <ue/nas/enc.hpp>
#include <ue/nas/keys.hpp> #include <ue/nas/keys.hpp>
namespace nr::ue namespace nr::ue
...@@ -29,6 +30,24 @@ static int FindSecurityContext(int ksi, const std::unique_ptr<NasSecurityContext ...@@ -29,6 +30,24 @@ static int FindSecurityContext(int ksi, const std::unique_ptr<NasSecurityContext
return -1; 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) void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
{ {
m_logger->debug("Security Mode Command received"); m_logger->debug("Security Mode Command received");
...@@ -37,11 +56,18 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg) ...@@ -37,11 +56,18 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
nas::SecurityModeReject resp; nas::SecurityModeReject resp;
resp.mmCause.value = cause; resp.mmCause.value = cause;
sendNasMessage(resp); 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 ============================== // ============================== Check the received ngKSI ==============================
bool locallyDerived = false;
if (!IsValidKsi(msg.ngKsi)) if (!IsValidKsi(msg.ngKsi))
{ {
m_logger->err("Invalid ngKSI received, tsc[%d], ksi[%d]", (int)msg.ngKsi.tsc, msg.ngKsi.ksi); 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) ...@@ -53,7 +79,18 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
msg.selectedNasSecurityAlgorithms.integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 && msg.selectedNasSecurityAlgorithms.integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 &&
msg.selectedNasSecurityAlgorithms.ciphering == nas::ETypeOfCipheringAlgorithm::EA0) 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); 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) ...@@ -66,14 +103,6 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
auto &nsCtx = whichCtx == 0 ? m_usim->m_currentNsCtx : m_usim->m_nonCurrentNsCtx; 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 ======================== // ======================== Check replayed UE security capabilities ========================
if (!nas::utils::DeepEqualsIe(msg.replayedUeSecurityCapabilities, createSecurityCapabilityIe())) if (!nas::utils::DeepEqualsIe(msg.replayedUeSecurityCapabilities, createSecurityCapabilityIe()))
...@@ -96,37 +125,102 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg) ...@@ -96,37 +125,102 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH); reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return; 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) // Assign ABBA (if any)
if (msg.abba.has_value()) if (msg.abba.has_value())
nsCtx->keys.abba = msg.abba->rawData.copy(); 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 // Assign selected algorithms to security context, and derive NAS keys
nsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity; nsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity;
nsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering; nsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
keys::DeriveNasKeys(*nsCtx); 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 // 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 // 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 ... // 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.sqn = 0;
nsCtx->uplinkCount.overflow = octet2{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) // Set the new NAS Security Context as current one. (If it is not already the current one)
if (whichCtx == 1) if (whichCtx == 1)
m_usim->m_currentNsCtx = std::make_unique<NasSecurityContext>(nsCtx->deepCopy()); m_usim->m_currentNsCtx = std::make_unique<NasSecurityContext>(nsCtx->deepCopy());
...@@ -157,10 +251,21 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg) ...@@ -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 // Handle NAS message container
// See 4.4.6 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{}; resp.nasMessageContainer = nas::IENasMessageContainer{};
nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data); nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data);
}
// Send response // Send response
sendNasMessage(resp); sendNasMessage(resp);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// //
#include "mm.hpp" #include "mm.hpp"
#include <lib/nas/utils.hpp> #include <lib/nas/utils.hpp>
#include <ue/nas/sm/sm.hpp> #include <ue/nas/sm/sm.hpp>
...@@ -211,6 +212,11 @@ void NasMm::receiveServiceReject(const nas::ServiceReject &msg) ...@@ -211,6 +212,11 @@ void NasMm::receiveServiceReject(const nas::ServiceReject &msg)
m_serCounter = 0; m_serCounter = 0;
m_timers->t3517.stop(); 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; auto cause = msg.mmCause.value;
m_logger->err("Service Reject received with cause [%s]", nas::utils::EnumToString(cause)); m_logger->err("Service Reject received with cause [%s]", nas::utils::EnumToString(cause));
......
...@@ -85,6 +85,11 @@ void NasMm::onTimerExpire(nas::NasTimer &timer) ...@@ -85,6 +85,11 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
} }
break; break;
} }
case 3516: {
m_usim->m_rand = {};
m_usim->m_resStar = {};
break;
}
case 3517: { case 3517: {
if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED) if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED)
{ {
...@@ -109,6 +114,11 @@ void NasMm::onTimerExpire(nas::NasTimer &timer) ...@@ -109,6 +114,11 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
m_usim->m_storedSuci = {}; m_usim->m_storedSuci = {};
break; break;
} }
case 3520: {
logExpired();
networkFailingTheAuthCheck(false);
break;
}
case 3521: { case 3521: {
if (timer.getExpiryCount() == 5) if (timer.getExpiryCount() == 5)
{ {
......
...@@ -30,7 +30,7 @@ static bool IsAcceptedWithoutIntegrity(const nas::PlainMmMessage &msg) ...@@ -30,7 +30,7 @@ static bool IsAcceptedWithoutIntegrity(const nas::PlainMmMessage &msg)
return msgType == nas::EMessageType::IDENTITY_REQUEST || msgType == nas::EMessageType::AUTHENTICATION_REQUEST || return msgType == nas::EMessageType::IDENTITY_REQUEST || msgType == nas::EMessageType::AUTHENTICATION_REQUEST ||
msgType == nas::EMessageType::AUTHENTICATION_RESULT || msgType == nas::EMessageType::AUTHENTICATION_REJECT || msgType == nas::EMessageType::AUTHENTICATION_RESULT || msgType == nas::EMessageType::AUTHENTICATION_REJECT ||
msgType == nas::EMessageType::REGISTRATION_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; msgType == nas::EMessageType::SERVICE_REJECT;
} }
...@@ -118,6 +118,9 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg) ...@@ -118,6 +118,9 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
return; return;
} }
((nas::SecurityModeCommand &)(*smcMsg))._macForNewSC = securedMm.messageAuthenticationCode;
((nas::SecurityModeCommand &)(*smcMsg))._originalPlainNasPdu = securedMm.plainNasMessage.copy();
receiveMmMessage((const nas::PlainMmMessage &)(*smcMsg)); receiveMmMessage((const nas::PlainMmMessage &)(*smcMsg));
return; return;
} }
...@@ -169,8 +172,6 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg) ...@@ -169,8 +172,6 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
void NasMm::receiveMmMessage(const nas::PlainMmMessage &msg) void NasMm::receiveMmMessage(const nas::PlainMmMessage &msg)
{ {
// TODO: trigger on receive
switch (msg.messageType) switch (msg.messageType)
{ {
case nas::EMessageType::REGISTRATION_ACCEPT: case nas::EMessageType::REGISTRATION_ACCEPT:
......
...@@ -40,8 +40,6 @@ void NasSm::sendSmMessage(int psi, const nas::SmMessage &msg) ...@@ -40,8 +40,6 @@ void NasSm::sendSmMessage(int psi, const nas::SmMessage &msg)
void NasSm::receiveSmMessage(const nas::SmMessage &msg) void NasSm::receiveSmMessage(const nas::SmMessage &msg)
{ {
// TODO: trigger on receive
switch (msg.messageType) switch (msg.messageType)
{ {
case nas::EMessageType::PDU_SESSION_ESTABLISHMENT_ACCEPT: case nas::EMessageType::PDU_SESSION_ESTABLISHMENT_ACCEPT:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#pragma once #pragma once
#include "usim.hpp" #include <ue/nas/usim/usim.hpp>
#include <lib/crypt/milenage.hpp> #include <lib/crypt/milenage.hpp>
#include <lib/nas/nas.hpp> #include <lib/nas/nas.hpp>
#include <lib/nas/timer.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) ...@@ -19,6 +19,8 @@ void ue::Usim::initialize(bool hasSupi, const UeConfig::Initials &initials)
m_defConfiguredNssai = initials.defaultConfiguredNssai; m_defConfiguredNssai = initials.defaultConfiguredNssai;
m_configuredNssai = initials.configuredNssai; m_configuredNssai = initials.configuredNssai;
m_sqnMng = std::make_unique<SqnManager>(5ull, 1ull << 28ull);
} }
bool Usim::isValid() bool Usim::isValid()
......
...@@ -8,9 +8,12 @@ ...@@ -8,9 +8,12 @@
#pragma once #pragma once
#include <lib/nas/msg.hpp> #include "sqn_mng.hpp"
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <lib/nas/msg.hpp>
#include <ue/types.hpp> #include <ue/types.hpp>
#include <utils/common_types.hpp> #include <utils/common_types.hpp>
#include <utils/octet_string.hpp> #include <utils/octet_string.hpp>
...@@ -46,7 +49,9 @@ class Usim ...@@ -46,7 +49,9 @@ class Usim
// Security related // Security related
std::unique_ptr<NasSecurityContext> m_currentNsCtx{}; std::unique_ptr<NasSecurityContext> m_currentNsCtx{};
std::unique_ptr<NasSecurityContext> m_nonCurrentNsCtx{}; std::unique_ptr<NasSecurityContext> m_nonCurrentNsCtx{};
OctetString m_sqn{}; OctetString m_rand{};
OctetString m_resStar{};
std::unique_ptr<SqnManager> m_sqnMng{};
// NSSAI related // NSSAI related
NetworkSlice m_defConfiguredNssai{}; NetworkSlice m_defConfiguredNssai{};
...@@ -67,6 +72,7 @@ class Usim ...@@ -67,6 +72,7 @@ class Usim
public: public:
void initialize(bool hasSupi, const UeConfig::Initials &initials); void initialize(bool hasSupi, const UeConfig::Initials &initials);
bool isValid(); bool isValid();
void invalidate(); void invalidate();
}; };
......
...@@ -303,10 +303,6 @@ struct UeKeys ...@@ -303,10 +303,6 @@ struct UeKeys
{ {
OctetString abba{}; OctetString abba{};
OctetString rand{};
OctetString res{};
OctetString resStar{}; // used in 5G-AKA
OctetString kAusf{}; OctetString kAusf{};
OctetString kSeaf{}; OctetString kSeaf{};
OctetString kAmf{}; OctetString kAmf{};
...@@ -316,9 +312,6 @@ struct UeKeys ...@@ -316,9 +312,6 @@ struct UeKeys
[[nodiscard]] UeKeys deepCopy() const [[nodiscard]] UeKeys deepCopy() const
{ {
UeKeys keys; UeKeys keys;
keys.rand = rand.subCopy(0);
keys.res = res.subCopy(0);
keys.resStar = resStar.subCopy(0);
keys.kAusf = kAusf.subCopy(0); keys.kAusf = kAusf.subCopy(0);
keys.kSeaf = kSeaf.subCopy(0); keys.kSeaf = kSeaf.subCopy(0);
keys.kAmf = kAmf.subCopy(0); keys.kAmf = kAmf.subCopy(0);
......
...@@ -15,10 +15,10 @@ struct cons ...@@ -15,10 +15,10 @@ struct cons
// Version information // Version information
static constexpr const uint8_t Major = 3; static constexpr const uint8_t Major = 3;
static constexpr const uint8_t Minor = 1; 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 *Project = "UERANSIM";
static constexpr const char *Tag = "v3.1.7"; static constexpr const char *Tag = "v3.1.8";
static constexpr const char *Name = "UERANSIM v3.1.7"; static constexpr const char *Name = "UERANSIM v3.1.8";
static constexpr const char *Owner = "ALİ GÜNGÖR"; static constexpr const char *Owner = "ALİ GÜNGÖR";
// Some port values // Some port values
......
--[[ --[[
-- Dissector for Radio Link Simulation Protocol -- Dissector for Radio Link Simulation Protocol
-- (used by UERANSIM <https://github.com/aligungr/UERANSIM>). -- (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>) -- 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) ...@@ -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) local sim_pos_z = ProtoField.uint32("rls.sim_pos_z", "RLS Position Z", base.DEC)
-- For cell Info Response -- For cell Info Response
local mcc = ProtoField.uint16("rls.mmc", "RLS MMC", base.DEC) local mcc = ProtoField.uint16("rls.mcc", "MCC", base.DEC)
local mnc = ProtoField.uint16("rls.mnc", "RLS MNC", base.DEC) local mnc = ProtoField.uint16("rls.mnc", "MNC", base.DEC)
local long_mnc = ProtoField.bool("rls.long_mnc", "RLS MNC is long", base.BOOL) local long_mnc = ProtoField.bool("rls.long_mnc", "MNC is 3-digit", base.BOOL)
local nci = ProtoField.uint64("rls.nci", "RLS New Radio Cell Identity", base.HEX) local nci = ProtoField.uint64("rls.nci", "NR Cell Identity", base.HEX)
local tac = ProtoField.uint32("rls.tac", "RLS Tracking Area Code", base.DEC) local tac = ProtoField.uint32("rls.tac", "Tracking Area Code", base.DEC)
local dbm = ProtoField.int32("rls.dbm", "RLS Signal Strength (dBm)", 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 gnb_name = ProtoField.string("rls.gnb_name", "gNB Name")
local link_ip = ProtoField.string("rls.link_ip", "RLS gNb Link IP") local link_ip = ProtoField.string("rls.link_ip", "gNB Link IP")
-- For PDU Delivery -- For PDU Delivery
local pdu_type_name = { local pdu_type_name = {
...@@ -49,14 +48,14 @@ 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 pdu_type = ProtoField.uint8("rls.pdu_type", "RLS PDU Type", base.DEC, pdu_type_name)
local rrc_channel_name = { local rrc_channel_name = {
[0] = "BCCH_BCH", [0] = "BCCH-BCH",
[1] = "BCCH_DL_SCH", [1] = "BCCH-DL-SCH",
[2] = "DL_CCCH", [2] = "DL-CCCH",
[3] = "DL_DCCH", [3] = "DL-DCCH",
[4] = "PCCH", [4] = "PCCH",
[5] = "UL_CCCH", [5] = "UL-CCCH",
[6] = "UL_CCCH1", [6] = "UL-CCCH1",
[7] = "UL_DCCH", [7] = "UL-DCCH",
} }
local rrc_channel_dissector = { local rrc_channel_dissector = {
...@@ -71,7 +70,7 @@ 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 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 -- Dissector definition
...@@ -109,7 +108,7 @@ function rls_protocol.dissector(buffer, pinfo, tree) ...@@ -109,7 +108,7 @@ function rls_protocol.dissector(buffer, pinfo, tree)
subtree:add(sim_pos_z, buffer(21,4)) subtree:add(sim_pos_z, buffer(21,4))
elseif msg_type == 2 then -- Cell Info Response elseif msg_type == 2 then -- Cell Info Response
subtree:add(mcc, buffer(13,2)) 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(mnc, buffer(15,2))
mnc_tree:add(long_mnc, buffer(17,1)) mnc_tree:add(long_mnc, buffer(17,1))
subtree:add(nci, buffer(18,8)) 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