diff --git a/internal/ngap/handler.go b/internal/ngap/handler.go index 6174efaa..49ab52a7 100644 --- a/internal/ngap/handler.go +++ b/internal/ngap/handler.go @@ -1300,13 +1300,15 @@ func handlePathSwitchRequestMain(ran *context.AmfRan, } if uESecurityCapabilities != nil { - amfUe.UESecurityCapability.SetEA1_128_5G(uESecurityCapabilities.NRencryptionAlgorithms.Value.Bytes[0] & 0x80) - amfUe.UESecurityCapability.SetEA2_128_5G(uESecurityCapabilities.NRencryptionAlgorithms.Value.Bytes[0] & 0x40) - amfUe.UESecurityCapability.SetEA3_128_5G(uESecurityCapabilities.NRencryptionAlgorithms.Value.Bytes[0] & 0x20) - amfUe.UESecurityCapability.SetIA1_128_5G(uESecurityCapabilities.NRintegrityProtectionAlgorithms.Value.Bytes[0] & 0x80) - amfUe.UESecurityCapability.SetIA2_128_5G(uESecurityCapabilities.NRintegrityProtectionAlgorithms.Value.Bytes[0] & 0x40) - amfUe.UESecurityCapability.SetIA3_128_5G(uESecurityCapabilities.NRintegrityProtectionAlgorithms.Value.Bytes[0] & 0x20) - // not support any E-UTRA algorithms + storedEA, storedIA := storedNRUESecurityCapability(amfUe) + receivedEA, receivedIA := receivedNRUESecurityCapability(uESecurityCapabilities) + if storedEA != receivedEA || storedIA != receivedIA { + // TODO: Include E-UTRA capability once AmfUe stores it; current NGAP builders only preserve NR bits. + ranUe.Log.Warnf("UESecurityCapabilities mismatch in PathSwitchRequest: "+ + "stored NR(EA=0x%02x, IA=0x%02x), received NR(EA=0x%02x, IA=0x%02x); "+ + "keep stored UE security capability", + storedEA, storedIA, receivedEA, receivedIA) + } } if rANUENGAPID != nil { @@ -1410,6 +1412,29 @@ func handlePathSwitchRequestMain(ran *context.AmfRan, } } +func storedNRUESecurityCapability(amfUe *context.AmfUe) (nrEncryptionAlgorithm, nrIntegrityAlgorithm byte) { + nrEncryptionAlgorithm |= amfUe.UESecurityCapability.GetEA1_128_5G() << 7 + nrEncryptionAlgorithm |= amfUe.UESecurityCapability.GetEA2_128_5G() << 6 + nrEncryptionAlgorithm |= amfUe.UESecurityCapability.GetEA3_128_5G() << 5 + + nrIntegrityAlgorithm |= amfUe.UESecurityCapability.GetIA1_128_5G() << 7 + nrIntegrityAlgorithm |= amfUe.UESecurityCapability.GetIA2_128_5G() << 6 + nrIntegrityAlgorithm |= amfUe.UESecurityCapability.GetIA3_128_5G() << 5 + return +} + +func receivedNRUESecurityCapability( + uESecurityCapabilities *ngapType.UESecurityCapabilities, +) (nrEncryptionAlgorithm, nrIntegrityAlgorithm byte) { + if len(uESecurityCapabilities.NRencryptionAlgorithms.Value.Bytes) > 0 { + nrEncryptionAlgorithm = uESecurityCapabilities.NRencryptionAlgorithms.Value.Bytes[0] & 0xe0 + } + if len(uESecurityCapabilities.NRintegrityProtectionAlgorithms.Value.Bytes) > 0 { + nrIntegrityAlgorithm = uESecurityCapabilities.NRintegrityProtectionAlgorithms.Value.Bytes[0] & 0xe0 + } + return +} + func handleHandoverRequestAcknowledgeMain(ran *context.AmfRan, targetUe *context.RanUe, rANUENGAPID *ngapType.RANUENGAPID, diff --git a/internal/ngap/handler_test.go b/internal/ngap/handler_test.go index 52bfbb35..77b8bb12 100644 --- a/internal/ngap/handler_test.go +++ b/internal/ngap/handler_test.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "fmt" "net" + "strings" "testing" "github.com/google/uuid" @@ -156,6 +157,51 @@ func NewAmfContext(amfCtx *amf_context.AMFContext) { } } +func TestHandlePathSwitchRequestKeepsStoredUESecurityCapabilityOnMismatch(t *testing.T) { + amfSelf := amf_context.GetSelf() + NewAmfContext(amfSelf) + + sourceRan := NewAmfRan(nil) + sourceRan.AnType = "3GPP_ACCESS" + ranUe := &amf_context.RanUe{ + Ran: sourceRan, + RanUeNgapId: 1, + AmfUeNgapId: 1, + Log: sourceRan.Log, + } + sourceRan.RanUeList.Store(ranUe.RanUeNgapId, ranUe) + amfSelf.RanUePool.Store(ranUe.AmfUeNgapId, ranUe) + + amfUe := amfSelf.NewAmfUe("imsi-208930000007487") + amfUe.SecurityContextAvailable = true + amfUe.NgKsi.Ksi = 1 + amfUe.Kamf = strings.Repeat("11", 32) + amfUe.NH = []byte(strings.Repeat("\x22", 32)) + amfUe.UESecurityCapability = nasType.UESecurityCapability{ + Iei: nasMessage.RegistrationRequestUESecurityCapabilityType, + Len: 2, + Buffer: []uint8{0x00, 0x00}, + } + amfUe.UESecurityCapability.SetIA2_128_5G(1) + + ranUe.AmfUe = amfUe + amfUe.RanUe["3GPP_ACCESS"] = ranUe + + targetRan := NewAmfRan(nil) + targetRan.AnType = "3GPP_ACCESS" + sourceAMFUeNgapID := ngapType.AMFUENGAPID{Value: ranUe.AmfUeNgapId} + ranUeNgapID := ngapType.RANUENGAPID{Value: 2} + received := &ngapType.UESecurityCapabilities{} + received.NRencryptionAlgorithms.Value = aper.BitString{Bytes: []byte{0x00, 0x00}, BitLength: 16} + received.NRintegrityProtectionAlgorithms.Value = aper.BitString{Bytes: []byte{0x00, 0x00}, BitLength: 16} + + handlePathSwitchRequestMain(targetRan, &ranUeNgapID, &sourceAMFUeNgapID, nil, received, nil, nil) + + require.Equal(t, uint8(1), amfUe.UESecurityCapability.GetIA2_128_5G()) + require.Equal(t, uint8(0), amfUe.UESecurityCapability.GetIA1_128_5G()) + require.Equal(t, uint8(0), amfUe.UESecurityCapability.GetIA3_128_5G()) +} + func BuildInitialUEMessage(ranUeNgapID int64, nasPdu []byte, fiveGSTmsi string) ngapType.NGAPPDU { var TestPlmn ngapType.PLMNIdentity var tmsi aper.OctetString