@@ -858,15 +858,155 @@ func TestHandleRegistrationRequest_CipheredNAS_MacFailed_SkipContainer(t *testin
858858 }
859859}
860860
861+ // TestHandleRegistrationRequest_NgKsi_Increment validates that the AMF
862+ // allocates the next available ngKSI slot (current + 1) to avoid reusing
863+ // the UE's active key, per 3GPP TS 24.501 section 9.11.3.32.
864+ func TestHandleRegistrationRequest_NgKsi_Increment (t * testing.T ) {
865+ ctx := context .TODO ()
866+ amfInstance := amf .New (& FakeDBInstance {
867+ Operator : & db.Operator {
868+ Mcc : "001" ,
869+ Mnc : "01" ,
870+ Sst : 1 ,
871+ SupportedTACs : "[\" 000001\" ]" ,
872+ },
873+ }, & FakeAusf {
874+ AvKgAka : & ausf.AuthResult {
875+ Rand : hex .EncodeToString (make ([]byte , 16 )),
876+ Autn : hex .EncodeToString (make ([]byte , 16 )),
877+ },
878+ Supi : mustSUPIFromPrefixed ("imsi-001019756139935" ),
879+ Kseaf : "testkey" ,
880+ }, nil )
881+
882+ ue , _ , err := buildUeAndRadio ()
883+ if err != nil {
884+ t .Fatalf ("could not create UE and radio: %v" , err )
885+ }
886+
887+ ue .Suci = "testsuci"
888+ ue .Supi = mustSUPIFromPrefixed ("imsi-001019756139935" )
889+
890+ m , err := buildTestRegistrationRequestMessageWithNgKsi (0 , nil , 0 , 3 )
891+ if err != nil {
892+ t .Fatalf ("could not build registration request message: %v" , err )
893+ }
894+
895+ err = handleRegistrationRequest (ctx , amfInstance , ue , m )
896+ if err != nil {
897+ t .Fatalf ("registration request should be accepted, got: %v" , err )
898+ }
899+
900+ if ue .NgKsi .Ksi != 4 {
901+ t .Fatalf ("expected ngKSI=4 (next after 3), got %d" , ue .NgKsi .Ksi )
902+ }
903+ }
904+
905+ // TestHandleRegistrationRequest_NgKsi_WrapAt6 validates that when the UE sends
906+ // ngKSI=6 (the maximum valid value), the AMF wraps around to 0 rather than
907+ // using value 7 (which means "no key available").
908+ func TestHandleRegistrationRequest_NgKsi_WrapAt6 (t * testing.T ) {
909+ ctx := context .TODO ()
910+ amfInstance := amf .New (& FakeDBInstance {
911+ Operator : & db.Operator {
912+ Mcc : "001" ,
913+ Mnc : "01" ,
914+ Sst : 1 ,
915+ SupportedTACs : "[\" 000001\" ]" ,
916+ },
917+ }, & FakeAusf {
918+ AvKgAka : & ausf.AuthResult {
919+ Rand : hex .EncodeToString (make ([]byte , 16 )),
920+ Autn : hex .EncodeToString (make ([]byte , 16 )),
921+ },
922+ Supi : mustSUPIFromPrefixed ("imsi-001019756139935" ),
923+ Kseaf : "testkey" ,
924+ }, nil )
925+
926+ ue , _ , err := buildUeAndRadio ()
927+ if err != nil {
928+ t .Fatalf ("could not create UE and radio: %v" , err )
929+ }
930+
931+ ue .Suci = "testsuci"
932+ ue .Supi = mustSUPIFromPrefixed ("imsi-001019756139935" )
933+
934+ m , err := buildTestRegistrationRequestMessageWithNgKsi (0 , nil , 0 , 6 )
935+ if err != nil {
936+ t .Fatalf ("could not build registration request message: %v" , err )
937+ }
938+
939+ err = handleRegistrationRequest (ctx , amfInstance , ue , m )
940+ if err != nil {
941+ t .Fatalf ("registration request should be accepted, got: %v" , err )
942+ }
943+
944+ if ue .NgKsi .Ksi != 0 {
945+ t .Fatalf ("expected ngKSI=0 (wrapped from 6), got %d" , ue .NgKsi .Ksi )
946+ }
947+ }
948+
949+ // TestHandleRegistrationRequest_NgKsi_NoKeyAvailable validates that when the UE
950+ // sends ngKSI=7 ("no key available"), the AMF handles it gracefully by
951+ // resetting to 0 with native security context type.
952+ func TestHandleRegistrationRequest_NgKsi_NoKeyAvailable (t * testing.T ) {
953+ ctx := context .TODO ()
954+ amfInstance := amf .New (& FakeDBInstance {
955+ Operator : & db.Operator {
956+ Mcc : "001" ,
957+ Mnc : "01" ,
958+ Sst : 1 ,
959+ SupportedTACs : "[\" 000001\" ]" ,
960+ },
961+ }, & FakeAusf {
962+ AvKgAka : & ausf.AuthResult {
963+ Rand : hex .EncodeToString (make ([]byte , 16 )),
964+ Autn : hex .EncodeToString (make ([]byte , 16 )),
965+ },
966+ Supi : mustSUPIFromPrefixed ("imsi-001019756139935" ),
967+ Kseaf : "testkey" ,
968+ }, nil )
969+
970+ ue , _ , err := buildUeAndRadio ()
971+ if err != nil {
972+ t .Fatalf ("could not create UE and radio: %v" , err )
973+ }
974+
975+ ue .Suci = "testsuci"
976+ ue .Supi = mustSUPIFromPrefixed ("imsi-001019756139935" )
977+
978+ m , err := buildTestRegistrationRequestMessageWithNgKsi (0 , nil , 0 , 7 )
979+ if err != nil {
980+ t .Fatalf ("could not build registration request message: %v" , err )
981+ }
982+
983+ err = handleRegistrationRequest (ctx , amfInstance , ue , m )
984+ if err != nil {
985+ t .Fatalf ("registration request should be accepted, got: %v" , err )
986+ }
987+
988+ if ue .NgKsi .Ksi != 0 {
989+ t .Fatalf ("expected ngKSI=0 (reset from no-key-available=7), got %d" , ue .NgKsi .Ksi )
990+ }
991+
992+ if ue .NgKsi .Tsc != models .ScTypeNative {
993+ t .Fatalf ("expected TSC=NATIVE, got %v" , ue .NgKsi .Tsc )
994+ }
995+ }
996+
861997func buildTestRegistrationRequestMessage (cipherAlg uint8 , key * [16 ]uint8 , ulcount uint32 ) (* nas.GmmMessage , error ) {
998+ return buildTestRegistrationRequestMessageWithNgKsi (cipherAlg , key , ulcount , 0 )
999+ }
1000+
1001+ func buildTestRegistrationRequestMessageWithNgKsi (cipherAlg uint8 , key * [16 ]uint8 , ulcount uint32 , ngKsi uint8 ) (* nas.GmmMessage , error ) {
8621002 m := nas .NewGmmMessage ()
8631003
8641004 registrationRequest := nasMessage .NewRegistrationRequest (0 )
8651005 registrationRequest .SetExtendedProtocolDiscriminator (nasMessage .Epd5GSMobilityManagementMessage )
8661006 registrationRequest .SetSecurityHeaderType (nas .SecurityHeaderTypePlainNas )
8671007 registrationRequest .SetSpareHalfOctet (0x00 )
8681008 registrationRequest .SetMessageType (nas .MsgTypeRegistrationRequest )
869- registrationRequest .NgksiAndRegistrationType5GS .SetNasKeySetIdentifiler (uint8 ( 0 ) )
1009+ registrationRequest .NgksiAndRegistrationType5GS .SetNasKeySetIdentifiler (ngKsi )
8701010 registrationRequest .SetRegistrationType5GS (nasMessage .RegistrationType5GSInitialRegistration )
8711011 registrationRequest .SetFOR (1 )
8721012 registrationRequest .MobileIdentity5GS = nasType.MobileIdentity5GS {
0 commit comments