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
auto kAusf = keys::CalculateKAusfForEapAkaPrime(mk);
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->ngKsi = msg.ngKSI.ksi;
m_storage.m_nonCurrentNsCtx->keys.rand = std::move(receivedRand);
......@@ -277,7 +277,7 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &
if (IGNORE_CONTROLS_FAILURES || autnCheck == EAutnValidationRes::OK)
{
// 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->ngKsi = msg.ngKSI.ksi;
m_storage.m_nonCurrentNsCtx->keys.rand = rand.copy();
......
......@@ -192,7 +192,7 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol
// 5GMM-DEREGISTERED for any other state except 5GMM-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");
......
......@@ -30,7 +30,7 @@ void NasMm::sendDeregistration(nas::ESwitchOff switchOff, bool dueToDisable5g)
request->deRegistrationType.reRegistrationRequired = nas::EReRegistrationRequired::NOT_REQUIRED;
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.ksi = m_storage.m_currentNsCtx->ngKsi;
......
......@@ -29,9 +29,9 @@ void NasMm::sendRegistration(nas::ERegistrationType registrationType, nas::EFoll
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.ksi = m_storage.m_currentNsCtx->ngKsi;
......
......@@ -13,8 +13,26 @@
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)
{
m_logger->debug("Security Mode Command received");
auto reject = [this](nas::EMmCause cause) {
nas::SecurityModeReject resp;
resp.mmCause.value = cause;
......@@ -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));
};
if (!m_storage.m_nonCurrentNsCtx.has_value())
// ============================== Check the received ngKSI ==============================
if (!IsValidKsi(msg.ngKsi))
{
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;
}
if (msg.ngKsi.ksi == 0 &&
msg.selectedNasSecurityAlgorithms.integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 &&
msg.selectedNasSecurityAlgorithms.ciphering == nas::ETypeOfCipheringAlgorithm::EA0)
{
reject(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
// 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;
}
// TODO: check the integrity with new security context
auto &nsCtx = whichCtx == 0 ? m_storage.m_currentNsCtx : m_storage.m_nonCurrentNsCtx;
// ======================== Check the integrity with new security context ========================
{
// TODO:
octet4 mac = msg._macForNewSC;
(void)mac;
}
// Check replayed UE security capabilities
{
auto &replayed = msg.replayedUeSecurityCapabilities;
auto real = createSecurityCapabilityIe();
// ======================== Check replayed UE security capabilities ========================
if (!nas::utils::DeepEqualsIe(replayed, real))
if (!nas::utils::DeepEqualsIe(msg.replayedUeSecurityCapabilities, createSecurityCapabilityIe()))
{
m_logger->err("Replayed UE security capability mismatch");
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return;
}
}
// Handle EAP-Success message if any.
if (msg.eapMessage.has_value())
// ======================== Check selected NAS security algorithms ========================
{
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.");
auto integrity = msg.selectedNasSecurityAlgorithms.integrity;
auto ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
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);
return;
}
}
// ============================ Process the security context. ============================
// Assign ABBA (if any)
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
// 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
m_storage.m_nonCurrentNsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity;
m_storage.m_nonCurrentNsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
keys::DeriveNasKeys(*m_storage.m_nonCurrentNsCtx);
// Set the new NAS Security Context as current one. (If it is not already the current one)
if (whichCtx == 1)
m_storage.m_currentNsCtx = std::make_unique<NasSecurityContext>(nsCtx->deepCopy());
// ============================ Handle EAP-Success message if any. ============================
m_logger->debug("Derived kNasEnc[%s] kNasInt[%s]", m_storage.m_nonCurrentNsCtx->keys.kNasEnc.toHexString().c_str(),
m_storage.m_nonCurrentNsCtx->keys.kNasInt.toHexString().c_str());
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)m_storage.m_nonCurrentNsCtx->integrity,
(int)m_storage.m_nonCurrentNsCtx->ciphering);
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.");
}
// Set non-current NAS Security Context as current one.
m_storage.m_currentNsCtx = m_storage.m_nonCurrentNsCtx->deepCopy();
// ============================ Send the Security Mode Complete. ============================
// Prepare response
nas::SecurityModeComplete resp{};
// Append IMEISV if requested
......@@ -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{};
nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data);
......
......@@ -21,7 +21,7 @@ void NasMm::sendNasMessage(const nas::PlainMmMessage &msg)
// TODO trigger on send
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->ciphering != nas::ETypeOfCipheringAlgorithm::EA0))
{
......@@ -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
// 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(
"Not integrity protected NAS message received after security establishment. Ignoring received "
......@@ -101,7 +101,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
return;
}
if (!m_storage.m_currentNsCtx.has_value())
if (!m_storage.m_currentNsCtx)
{
m_logger->warn("Secured NAS message received while no security context");
sendMmStatus(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
......
......@@ -35,8 +35,8 @@ class MobileStorage
nas::IEPlmnList m_forbiddenPlmnList{};
// Security related
std::optional<NasSecurityContext> m_currentNsCtx{};
std::optional<NasSecurityContext> m_nonCurrentNsCtx{};
std::unique_ptr<NasSecurityContext> m_currentNsCtx{};
std::unique_ptr<NasSecurityContext> m_nonCurrentNsCtx{};
OctetString m_sqn{};
public:
......@@ -75,6 +75,7 @@ class MobileStorage
discardSecurity();
}
// todo metodları kaldır geri
void invalidateSim__()
{
// 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