Commit ebb486fb authored by aligungr's avatar aligungr

Security mode control improvements

parent faff949c
...@@ -188,7 +188,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms ...@@ -188,7 +188,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
auto kAusf = keys::CalculateKAusfForEapAkaPrime(mk); auto kAusf = keys::CalculateKAusfForEapAkaPrime(mk);
m_logger->debug("kAusf: %s", kAusf.toHexString().c_str()); m_logger->debug("kAusf: %s", kAusf.toHexString().c_str());
m_storage.m_nonCurrentNsCtx = NasSecurityContext{}; m_storage.m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>();
m_storage.m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc; m_storage.m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
m_storage.m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi; m_storage.m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
m_storage.m_nonCurrentNsCtx->keys.rand = std::move(receivedRand); m_storage.m_nonCurrentNsCtx->keys.rand = std::move(receivedRand);
...@@ -277,7 +277,7 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest & ...@@ -277,7 +277,7 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &
if (IGNORE_CONTROLS_FAILURES || autnCheck == EAutnValidationRes::OK) 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_storage.m_nonCurrentNsCtx = NasSecurityContext{}; m_storage.m_nonCurrentNsCtx = std::make_unique<NasSecurityContext>();
m_storage.m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc; m_storage.m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
m_storage.m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi; m_storage.m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
m_storage.m_nonCurrentNsCtx->keys.rand = rand.copy(); m_storage.m_nonCurrentNsCtx->keys.rand = rand.copy();
......
...@@ -192,7 +192,7 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol ...@@ -192,7 +192,7 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol
// 5GMM-DEREGISTERED for any other state except 5GMM-NULL. // 5GMM-DEREGISTERED for any other state except 5GMM-NULL.
if (oldState == EMmState::MM_DEREGISTERED && newState != EMmState::MM_DEREGISTERED && newState != EMmState::MM_NULL) if (oldState == EMmState::MM_DEREGISTERED && newState != EMmState::MM_DEREGISTERED && newState != EMmState::MM_NULL)
{ {
if (m_storage.m_currentNsCtx.has_value() || m_storage.m_nonCurrentNsCtx.has_value()) if (m_storage.m_currentNsCtx || m_storage.m_nonCurrentNsCtx)
{ {
m_logger->debug("Deleting NAS security context"); m_logger->debug("Deleting NAS security context");
......
...@@ -30,7 +30,7 @@ void NasMm::sendDeregistration(nas::ESwitchOff switchOff, bool dueToDisable5g) ...@@ -30,7 +30,7 @@ void NasMm::sendDeregistration(nas::ESwitchOff switchOff, bool dueToDisable5g)
request->deRegistrationType.reRegistrationRequired = nas::EReRegistrationRequired::NOT_REQUIRED; request->deRegistrationType.reRegistrationRequired = nas::EReRegistrationRequired::NOT_REQUIRED;
request->deRegistrationType.switchOff = switchOff; request->deRegistrationType.switchOff = switchOff;
if (m_storage.m_currentNsCtx.has_value()) if (m_storage.m_currentNsCtx)
{ {
request->ngKSI.tsc = m_storage.m_currentNsCtx->tsc; request->ngKSI.tsc = m_storage.m_currentNsCtx->tsc;
request->ngKSI.ksi = m_storage.m_currentNsCtx->ngKsi; request->ngKSI.ksi = m_storage.m_currentNsCtx->ngKsi;
......
...@@ -29,9 +29,9 @@ void NasMm::sendRegistration(nas::ERegistrationType registrationType, nas::EFoll ...@@ -29,9 +29,9 @@ void NasMm::sendRegistration(nas::ERegistrationType registrationType, nas::EFoll
switchMmState(EMmState::MM_REGISTERED_INITIATED, EMmSubState::MM_REGISTERED_INITIATED_NA); switchMmState(EMmState::MM_REGISTERED_INITIATED, EMmSubState::MM_REGISTERED_INITIATED_NA);
nas::IENasKeySetIdentifier ngKsi; nas::IENasKeySetIdentifier ngKsi{};
if (m_storage.m_currentNsCtx.has_value()) if (m_storage.m_currentNsCtx)
{ {
ngKsi.tsc = m_storage.m_currentNsCtx->tsc; ngKsi.tsc = m_storage.m_currentNsCtx->tsc;
ngKsi.ksi = m_storage.m_currentNsCtx->ngKsi; ngKsi.ksi = m_storage.m_currentNsCtx->ngKsi;
......
...@@ -13,8 +13,26 @@ ...@@ -13,8 +13,26 @@
namespace nr::ue namespace nr::ue
{ {
static bool IsValidKsi(const nas::IENasKeySetIdentifier &ngKsi)
{
return ngKsi.tsc == nas::ETypeOfSecurityContext::NATIVE_SECURITY_CONTEXT &&
ngKsi.ksi != nas::IENasKeySetIdentifier::NOT_AVAILABLE_OR_RESERVED;
}
static int FindSecurityContext(int ksi, const std::unique_ptr<NasSecurityContext> &current,
const std::unique_ptr<NasSecurityContext> &nonCurrent)
{
if (current != nullptr && current->ngKsi == ksi)
return 0;
if (nonCurrent != nullptr && nonCurrent->ngKsi == ksi)
return 1;
return -1;
}
void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg) void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
{ {
m_logger->debug("Security Mode Command received");
auto reject = [this](nas::EMmCause cause) { auto reject = [this](nas::EMmCause cause) {
nas::SecurityModeReject resp; nas::SecurityModeReject resp;
resp.mmCause.value = cause; resp.mmCause.value = cause;
...@@ -22,65 +40,110 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg) ...@@ -22,65 +40,110 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
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));
}; };
if (!m_storage.m_nonCurrentNsCtx.has_value()) // ============================== Check the received ngKSI ==============================
if (!IsValidKsi(msg.ngKsi))
{ {
reject(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE); m_logger->err("Invalid ngKSI received, tsc[%d], ksi[%d]", (int)msg.ngKsi.tsc, msg.ngKsi.ksi);
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return; return;
} }
// TODO: check the integrity with new security context if (msg.ngKsi.ksi == 0 &&
msg.selectedNasSecurityAlgorithms.integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 &&
msg.selectedNasSecurityAlgorithms.ciphering == nas::ETypeOfCipheringAlgorithm::EA0)
{
// TODO
}
int whichCtx = FindSecurityContext(msg.ngKsi.ksi, m_storage.m_currentNsCtx, m_storage.m_nonCurrentNsCtx);
if (whichCtx == -1)
{ {
m_logger->err("Security context with ngKSI[%d] not found", msg.ngKsi.ksi);
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return;
}
auto &nsCtx = whichCtx == 0 ? m_storage.m_currentNsCtx : m_storage.m_nonCurrentNsCtx;
// ======================== Check the integrity with new security context ========================
{
// TODO:
octet4 mac = msg._macForNewSC; octet4 mac = msg._macForNewSC;
(void)mac; (void)mac;
} }
// Check replayed UE security capabilities // ======================== Check replayed UE security capabilities ========================
if (!nas::utils::DeepEqualsIe(msg.replayedUeSecurityCapabilities, createSecurityCapabilityIe()))
{
m_logger->err("Replayed UE security capability mismatch");
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return;
}
// ======================== Check selected NAS security algorithms ========================
{ {
auto &replayed = msg.replayedUeSecurityCapabilities; auto integrity = msg.selectedNasSecurityAlgorithms.integrity;
auto real = createSecurityCapabilityIe(); auto ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
if (!nas::utils::DeepEqualsIe(replayed, real)) if (integrity > nas::ETypeOfIntegrityProtectionAlgorithm::IA3_128 ||
ciphering > nas::ETypeOfCipheringAlgorithm::EA3_128)
{ {
m_logger->err("Selected NAS security algorithms are invalid");
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH); reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return; return;
} }
} }
// Handle EAP-Success message if any. // ============================ Process the security context. ============================
if (msg.eapMessage.has_value())
{
if (msg.eapMessage->eap->code == eap::ECode::SUCCESS)
receiveEapSuccessMessage(*msg.eapMessage->eap);
else
m_logger->warn(
"EAP message with inconvenient code received in Security Mode Command. Ignoring EAP message.");
}
// Assign ABBA (if any) // Assign ABBA (if any)
if (msg.abba.has_value()) if (msg.abba.has_value())
m_storage.m_nonCurrentNsCtx->keys.abba = msg.abba->rawData.copy(); nsCtx->keys.abba = msg.abba->rawData.copy();
// Check selected algorithms // 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 kNasEnc[%s] kNasInt[%s]", nsCtx->keys.kNasEnc.toHexString().c_str(),
nsCtx->keys.kNasInt.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.
{
nsCtx->uplinkCount.sqn = 0;
nsCtx->uplinkCount.overflow = octet2{0};
}
if (msg.selectedNasSecurityAlgorithms.integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0)
{ {
// TODO // TODO
// if (msg.selectedNasSecurityAlgorithms.integrity is supported according to config file)
// if (msg.selectedNasSecurityAlgorithms.ciphering is supported according to config file)
} }
// Assign selected algorithms to security context, and derive NAS keys // Set the new NAS Security Context as current one. (If it is not already the current one)
m_storage.m_nonCurrentNsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity; if (whichCtx == 1)
m_storage.m_nonCurrentNsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering; m_storage.m_currentNsCtx = std::make_unique<NasSecurityContext>(nsCtx->deepCopy());
keys::DeriveNasKeys(*m_storage.m_nonCurrentNsCtx);
// ============================ Handle EAP-Success message if any. ============================
m_logger->debug("Derived kNasEnc[%s] kNasInt[%s]", m_storage.m_nonCurrentNsCtx->keys.kNasEnc.toHexString().c_str(), if (msg.eapMessage.has_value())
m_storage.m_nonCurrentNsCtx->keys.kNasInt.toHexString().c_str()); {
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)m_storage.m_nonCurrentNsCtx->integrity, if (msg.eapMessage->eap->code == eap::ECode::SUCCESS)
(int)m_storage.m_nonCurrentNsCtx->ciphering); receiveEapSuccessMessage(*msg.eapMessage->eap);
else
m_logger->warn(
"EAP message with inconvenient code received in Security Mode Command. Ignoring EAP message.");
}
// Set non-current NAS Security Context as current one. // ============================ Send the Security Mode Complete. ============================
m_storage.m_currentNsCtx = m_storage.m_nonCurrentNsCtx->deepCopy();
// Prepare response
nas::SecurityModeComplete resp{}; nas::SecurityModeComplete resp{};
// Append IMEISV if requested // Append IMEISV if requested
...@@ -94,6 +157,8 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg) ...@@ -94,6 +157,8 @@ 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
resp.nasMessageContainer = nas::IENasMessageContainer{}; resp.nasMessageContainer = nas::IENasMessageContainer{};
nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data); nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data);
......
...@@ -21,7 +21,7 @@ void NasMm::sendNasMessage(const nas::PlainMmMessage &msg) ...@@ -21,7 +21,7 @@ void NasMm::sendNasMessage(const nas::PlainMmMessage &msg)
// TODO trigger on send // TODO trigger on send
OctetString pdu{}; OctetString pdu{};
if (m_storage.m_currentNsCtx.has_value() && if (m_storage.m_currentNsCtx &&
(m_storage.m_currentNsCtx->integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0 || (m_storage.m_currentNsCtx->integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0 ||
m_storage.m_currentNsCtx->ciphering != nas::ETypeOfCipheringAlgorithm::EA0)) m_storage.m_currentNsCtx->ciphering != nas::ETypeOfCipheringAlgorithm::EA0))
{ {
...@@ -63,7 +63,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg) ...@@ -63,7 +63,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
{ {
// If any NAS signalling message is received as not integrity protected even though the secure exchange of NAS // If any NAS signalling message is received as not integrity protected even though the secure exchange of NAS
// messages has been established by the network, then the NAS shall discard this message // messages has been established by the network, then the NAS shall discard this message
if (m_storage.m_currentNsCtx.has_value()) if (m_storage.m_currentNsCtx)
{ {
m_logger->err( m_logger->err(
"Not integrity protected NAS message received after security establishment. Ignoring received " "Not integrity protected NAS message received after security establishment. Ignoring received "
...@@ -101,7 +101,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg) ...@@ -101,7 +101,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
return; return;
} }
if (!m_storage.m_currentNsCtx.has_value()) if (!m_storage.m_currentNsCtx)
{ {
m_logger->warn("Secured NAS message received while no security context"); m_logger->warn("Secured NAS message received while no security context");
sendMmStatus(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE); sendMmStatus(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
......
...@@ -35,8 +35,8 @@ class MobileStorage ...@@ -35,8 +35,8 @@ class MobileStorage
nas::IEPlmnList m_forbiddenPlmnList{}; nas::IEPlmnList m_forbiddenPlmnList{};
// Security related // Security related
std::optional<NasSecurityContext> m_currentNsCtx{}; std::unique_ptr<NasSecurityContext> m_currentNsCtx{};
std::optional<NasSecurityContext> m_nonCurrentNsCtx{}; std::unique_ptr<NasSecurityContext> m_nonCurrentNsCtx{};
OctetString m_sqn{}; OctetString m_sqn{};
public: public:
...@@ -75,6 +75,7 @@ class MobileStorage ...@@ -75,6 +75,7 @@ class MobileStorage
discardSecurity(); discardSecurity();
} }
// todo metodları kaldır geri
void invalidateSim__() void invalidateSim__()
{ {
// TODO: log // TODO: log
......
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