diff --git a/internal/security/ranUe.go b/internal/security/ranUe.go old mode 100644 new mode 100755 index 14daab7..3371b85 --- a/internal/security/ranUe.go +++ b/internal/security/ranUe.go @@ -1,12 +1,16 @@ package security import ( + "bytes" "encoding/hex" + "fmt" "regexp" "github.com/calee0219/fatal" "golang.org/x/net/ipv4" + "github.com/free5gc/n3iwue/pkg/factory" + "github.com/free5gc/nas/logger" "github.com/free5gc/nas/nasMessage" "github.com/free5gc/nas/nasType" "github.com/free5gc/nas/security" @@ -28,6 +32,20 @@ type RanUeContext struct { Kamf []uint8 AnType models.AccessType AuthenticationSubs models.AuthenticationSubscription + + SQNIndBitLen uint8 // Number of index bits (2-16, default: 5) + SQNWrappingDelta uint64 // Maximum allowed sequence advancement (default: 2^28) + SQNArray []uint64 // Array of highest SQN values per index +} + +type Milenage struct { + res []byte // RES (Expected Response) + ck []byte // Cipher Key + ik []byte // Integrity Key + ak []byte // Anonymity Key + ak_r []byte // Anonymity Key for re-synchronization (AK*) + mac_a []byte // Message Authentication Code A (MAC-A) + mac_s []byte // Message Authentication Code S (MAC-S for re-sync) } func CalculateIpv4HeaderChecksum(hdr *ipv4.Header) uint32 { @@ -48,27 +66,27 @@ func CalculateIpv4HeaderChecksum(hdr *ipv4.Header) uint32 { } func GetAuthSubscription(k, sqn, amf, opc, op string) models.AuthenticationSubscription { - var authSubs models.AuthenticationSubscription - authSubs.PermanentKey = &models.PermanentKey{ - PermanentKeyValue: k, - } - authSubs.Opc = &models.Opc{ - OpcValue: opc, - } - authSubs.Milenage = &models.Milenage{ - Op: &models.Op{ - OpValue: op, + AuthSubs := models.AuthenticationSubscription{ + PermanentKey: &models.PermanentKey{ + PermanentKeyValue: k, }, + Opc: &models.Opc{ + OpcValue: opc, + }, + Milenage: &models.Milenage{ + Op: &models.Op{ + OpValue: op, + }, + }, + AuthenticationManagementField: amf, + SequenceNumber: sqn, + AuthenticationMethod: models.AuthMethod__5_G_AKA, } - authSubs.AuthenticationManagementField = amf - - authSubs.SequenceNumber = sqn - authSubs.AuthenticationMethod = models.AuthMethod__5_G_AKA - return authSubs + return AuthSubs } func NewRanUeContext(supi string, ranUeNgapId int64, cipheringAlg, integrityAlg uint8, - AnType models.AccessType, + AnType models.AccessType, initialSQN string, ) *RanUeContext { ue := RanUeContext{} ue.RanUeNgapId = ranUeNgapId @@ -76,74 +94,49 @@ func NewRanUeContext(supi string, ranUeNgapId int64, cipheringAlg, integrityAlg ue.CipheringAlg = cipheringAlg ue.IntegrityAlg = integrityAlg ue.AnType = AnType + + ue.SQNIndBitLen = 5 // Default: 5 bits for index (32 indices) + ue.SQNWrappingDelta = 1 << 28 // Default: 2^28 (268M sequences) + + // Initialize SQN array + arraySize := 1 << ue.SQNIndBitLen + ue.SQNArray = make([]uint64, arraySize) + + // Initialize SQN from config if provided + if initialSQN != "" { + if sqnBytes, err := hex.DecodeString(initialSQN); err == nil && len(sqnBytes) == 6 { + initialSQNUint := sqnBytesToUint64(sqnBytes) + ue.setSqnMs(initialSQNUint) + } + } + return &ue } func (ue *RanUeContext) DeriveRESstarAndSetKey( - authSubs models.AuthenticationSubscription, rand []byte, snName string, + authSubs models.AuthenticationSubscription, rand []byte, snName string, autn []byte, ) []byte { sqn, err := hex.DecodeString(authSubs.SequenceNumber) if err != nil { fatal.Fatalf("DecodeString error: %+v", err) } - amf, err := hex.DecodeString(authSubs.AuthenticationManagementField) - if err != nil { - fatal.Fatalf("DecodeString error: %+v", err) - } - - // Run milenage - macA, macS := make([]byte, 8), make([]byte, 8) - ck, ik := make([]byte, 16), make([]byte, 16) - res := make([]byte, 8) - ak, akStar := make([]byte, 6), make([]byte, 6) + // Increment SQN as per original logic - opc := make([]byte, 16) - _ = opc - k, err := hex.DecodeString(authSubs.PermanentKey.PermanentKeyValue) + // Use optimized milenage calculation + milenageResult, err := ue.calculateMilenage(sqn, rand, false) if err != nil { - fatal.Fatalf("DecodeString error: %+v", err) - } - - if authSubs.Opc.OpcValue == "" { - opStr := authSubs.Milenage.Op.OpValue - var op []byte - op, err = hex.DecodeString(opStr) - if err != nil { - fatal.Fatalf("DecodeString error: %+v", err) - } - - opc, err = milenage.GenerateOPC(k, op) - if err != nil { - fatal.Fatalf("milenage GenerateOPC error: %+v", err) - } - } else { - opc, err = hex.DecodeString(authSubs.Opc.OpcValue) - if err != nil { - fatal.Fatalf("DecodeString error: %+v", err) - } - } - - // Generate MAC_A, MAC_S - err = milenage.F1(opc, k, rand, sqn, amf, macA, macS) - if err != nil { - fatal.Fatalf("regexp Compile error: %+v", err) - } - - // Generate RES, CK, IK, AK, AKstar - err = milenage.F2345(opc, k, rand, res, ck, ik, ak, akStar) - if err != nil { - fatal.Fatalf("regexp Compile error: %+v", err) + fatal.Fatalf("calculateMilenage error: %+v", err) } // derive RES* - key := append(ck, ik...) + key := append(milenageResult.ck, milenageResult.ik...) FC := ueauth.FC_FOR_RES_STAR_XRES_STAR_DERIVATION P0 := []byte(snName) P1 := rand - P2 := res + P2 := milenageResult.res - ue.DerivateKamf(key, snName, sqn, ak) + ue.DerivateKamf(key, snName, autn[:]) ue.DerivateAlgKey() kdfVal_for_resStar, err := ueauth.GetKDFValue( key, @@ -161,14 +154,11 @@ func (ue *RanUeContext) DeriveRESstarAndSetKey( return kdfVal_for_resStar[len(kdfVal_for_resStar)/2:] } -func (ue *RanUeContext) DerivateKamf(key []byte, snName string, SQN, AK []byte) { +func (ue *RanUeContext) DerivateKamf(key []byte, snName string, autn []byte) { FC := ueauth.FC_FOR_KAUSF_DERIVATION P0 := []byte(snName) - SQNxorAK := make([]byte, 6) - for i := 0; i < len(SQN); i++ { - SQNxorAK[i] = SQN[i] ^ AK[i] - } - P1 := SQNxorAK + SqnXorAK := autn[:6] + P1 := SqnXorAK Kausf, err := ueauth.GetKDFValue(key, FC, P0, ueauth.KDFLen(P0), P1, ueauth.KDFLen(P1)) if err != nil { fatal.Fatalf("GetKDFValue error: %+v", err) @@ -271,3 +261,282 @@ func (ue *RanUeContext) GetBearerType() uint8 { return security.OnlyOneBearer } } + +// Authentication result constants +const ( + AuthSuccess = iota // Authentication successful + SQNOutOfSync // SQN synchronization needed + MACFailure // MAC-A verification failed + ReplayAttack // Potential replay attack (old SQN) + SQNTooFarAhead // SQN too far ahead (>window) + AuthParameterError // Invalid AUTN/RAND parameters +) + +// SQN utility functions + +// sqnBytesToUint64 converts a 6-byte SQN to uint64 for arithmetic operations +func sqnBytesToUint64(sqn []byte) uint64 { + if len(sqn) != 6 { + return 0 + } + + var result uint64 + for i := 0; i < 6; i++ { + result = (result << 8) | uint64(sqn[i]) + } + return result +} + +// uint64ToSqnBytes converts a uint64 SQN to 6-byte array +func uint64ToSqnBytes(sqn uint64) []byte { + result := make([]byte, 6) + for i := 5; i >= 0; i-- { + result[i] = byte(sqn & 0xFF) + sqn >>= 8 + } + return result +} + +// getSeqFromSqn extracts the sequence number from a 48-bit SQN +func (ue *RanUeContext) getSeqFromSqn(sqn uint64) uint64 { + // Clear index bits and shift right + sqn &= ^((1 << ue.SQNIndBitLen) - 1) + sqn >>= ue.SQNIndBitLen + // Mask to 48-bit range + sqn &= (1 << 48) - 1 + return sqn +} + +// getIndFromSqn extracts the index from a 48-bit SQN +func (ue *RanUeContext) getIndFromSqn(sqn uint64) uint64 { + return sqn & ((1 << ue.SQNIndBitLen) - 1) +} + +// getSeqMs returns the highest sequence number among all indices +func (ue *RanUeContext) getSeqMs() uint64 { + return ue.getSeqFromSqn(ue.getSqnMs()) +} + +// getSqnMs returns the highest SQN value among all indices +func (ue *RanUeContext) getSqnMs() uint64 { + var maxSqn uint64 + for _, sqn := range ue.SQNArray { + if sqn > maxSqn { + maxSqn = sqn + } + } + return maxSqn +} + +func (ue *RanUeContext) setSqnMs(sqn uint64) { + ind := ue.getIndFromSqn(sqn) + logger.NasLog.Infof("setSqnMs: sqn=0x%012x, ind=%d", sqn, ind) + ue.SQNArray[ind] = sqn +} + +// getCurrentSqn returns the current SQN as 6-byte array (compatible with existing code) +func (ue *RanUeContext) getCurrentSqn() []byte { + return uint64ToSqnBytes(ue.getSqnMs()) +} + +// calculateMilenage performs milenage computation +// dummyAmf: if true, uses AMF=0x0000 for re-synchronization; if false, uses configured AMF +func (ue *RanUeContext) calculateMilenage(sqn, rand []byte, dummyAmf bool) (*Milenage, error) { + // Get cryptographic keys + k, opc, configAmf, err := ue.getCryptographicKeys() + if err != nil { + return nil, fmt.Errorf("failed to get cryptographic keys: %v", err) + } + + // Choose AMF value based on dummyAmf flag + var amf []byte + if dummyAmf { + amf = []byte{0x00, 0x00} // For re-synchronization (TS 33.102) + } else { + amf = configAmf // Use configured AMF + } + + // Allocate result buffers + result := &Milenage{ + res: make([]byte, 8), + ck: make([]byte, 16), + ik: make([]byte, 16), + ak: make([]byte, 6), + ak_r: make([]byte, 6), + mac_a: make([]byte, 8), + mac_s: make([]byte, 8), + } + + // Generate MAC_A and MAC_S using F1 + err = milenage.F1(opc, k, rand, sqn, amf, result.mac_a, result.mac_s) + if err != nil { + return nil, fmt.Errorf("F1 computation failed: %v", err) + } + + // Generate RES, CK, IK, AK, AK* using F2345 + err = milenage.F2345(opc, k, rand, result.res, result.ck, result.ik, + result.ak, result.ak_r) + if err != nil { + return nil, fmt.Errorf("F2345 computation failed: %v", err) + } + + return result, nil +} + +// getCryptographicKeys extracts K, OPc, and AMF from the authentication subscription +func (ue *RanUeContext) getCryptographicKeys() (k, opc, amf []byte, err error) { + // Decode permanent key (K) + k, err = hex.DecodeString(ue.AuthenticationSubs.PermanentKey.PermanentKeyValue) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to decode permanent key: %v", err) + } + + // Decode AMF + amf, err = hex.DecodeString(ue.AuthenticationSubs.AuthenticationManagementField) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to decode AMF: %v", err) + } + + // Decode or generate OPC + if ue.AuthenticationSubs.Opc.OpcValue != "" { + opc, err = hex.DecodeString(ue.AuthenticationSubs.Opc.OpcValue) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to decode OPC: %v", err) + } + } else { + // Generate OPC from OP + op, err := hex.DecodeString(ue.AuthenticationSubs.Milenage.Op.OpValue) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to decode OP: %v", err) + } + + opc, err = milenage.GenerateOPC(k, op) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to generate OPC: %v", err) + } + } + + return k, opc, amf, nil +} + +// VerifyAUTN performs comprehensive AUTN verification and SQN synchronization check +// Returns: (authResult, error) +func (ue *RanUeContext) VerifyAUTN(autn, rand []byte) (int, error) { + nasLog := logger.NasLog + // Input validation + if len(autn) != 16 || len(rand) != 16 { + return AuthParameterError, + fmt.Errorf("invalid parameter length: AUTN=%d, RAND=%d", len(autn), len(rand)) + } + + // Extract AUTN components: (SQN⊕AK) || AMF || MAC-A + receivedSQNxorAK := autn[:6] // First 6 bytes + receivedMAC := autn[8:16] // Last 8 bytes + + // Use direct F2345 to calculate AK for SQN recovery + currentSQN := ue.getCurrentSqn() + milenageResult, err := ue.calculateMilenage(currentSQN, rand, false) + if err != nil { + return AuthParameterError, + fmt.Errorf("failed to calculate milenage for AUTS: %v", err) + } + + // Recover actual SQN = (SQN⊕AK) ⊕ AK + receivedSQN := make([]byte, 6) + for i := 0; i < 6; i++ { + receivedSQN[i] = receivedSQNxorAK[i] ^ milenageResult.ak[i] + } + + sqnOk, err := ue.checkSqn(sqnBytesToUint64(receivedSQN)) + if err != nil { + return AuthParameterError, + fmt.Errorf("failed to check SQN: %v", err) + } + + // Now verify MAC-A with the recovered SQN + milenageResult, err = ue.calculateMilenage(receivedSQN, rand, false) + if err != nil { + return AuthParameterError, + fmt.Errorf("failed to calculate milenage for AUTS: %v", err) + } + + if !bytes.Equal(milenageResult.mac_a, receivedMAC) { + return MACFailure, + fmt.Errorf("MAC-A verification failed") + } + + if !sqnOk { + return SQNOutOfSync, err + } + nasLog.Infof("Extracted SQN from AUTN: %x", receivedSQN) + + return AuthSuccess, nil +} + +// checkSqn implements validation SQN algorithm +// SQN = SEQ || IND (TS 33.102 C.1.1) +func (ue *RanUeContext) checkSqn(sqn uint64) (bool, error) { + seq := ue.getSeqFromSqn(sqn) + ind := ue.getIndFromSqn(sqn) + + // Check 1: SQN too far ahead (wrapping delta check) + seqMs := ue.getSeqMs() + if seq > seqMs && (seq-seqMs) > ue.SQNWrappingDelta { + return false, + fmt.Errorf("SQN too far ahead: seq=%d, seqMs=%d, delta=%d", + seq, seqMs, ue.SQNWrappingDelta) + } + + // Check 2: Replay attack prevention (SQN must be greater than stored for this index) + if len(ue.SQNArray) <= int(ind) { + return false, + fmt.Errorf("invalid SQN index: %d (array size: %d)", ind, len(ue.SQNArray)) + } + + storedSeqForInd := ue.getSeqFromSqn(ue.SQNArray[ind]) + if seq <= storedSeqForInd { + return false, + fmt.Errorf("SQN replay or too old: seq=%d <= stored_seq=%d for index=%d", + seq, storedSeqForInd, ind) + } + + // SQN is acceptable - update the array + ue.SQNArray[ind] = sqn + + // TS 33.102 + // Sync the SQN for security in config + if err := factory.SyncConfigSQN(sqn); err != nil { + return false, fmt.Errorf("failed to sync config SQN: %v", err) + } + + return true, nil +} + +// GenerateAUTS generates Authentication Token for re-Synchronization +// AUTS = (SQN_MS ⊕ AK*) || MAC-S +func (ue *RanUeContext) GenerateAUTS(rand []byte) ([]byte, error) { + if len(rand) != 16 { + return nil, fmt.Errorf("invalid RAND length: %d", len(rand)) + } + + currentSQN := ue.getCurrentSqn() + + // Calculate milenage with dummy AMF for re-synchronization + milenageResult, err := ue.calculateMilenage(currentSQN, rand, true) + if err != nil { + return nil, fmt.Errorf("failed to calculate milenage for AUTS: %v", err) + } + + // Construct AUTS = (SQN_MS ⊕ AK*) || MAC-S + auts := make([]byte, 14) // 6 + 8 bytes + + // First 6 bytes: SQN_MS ⊕ AK* + for i := 0; i < 6; i++ { + auts[i] = currentSQN[i] ^ milenageResult.ak_r[i] + } + + // Last 8 bytes: MAC-S + copy(auts[6:], milenageResult.mac_s) + + return auts, nil +} diff --git a/internal/util/initContext.go b/internal/util/initContext.go index 22a42b4..835f079 100644 --- a/internal/util/initContext.go +++ b/internal/util/initContext.go @@ -38,6 +38,7 @@ func InitN3UEContext() { security.AlgCiphering128NEA0, security.AlgIntegrity128NIA2, models.AccessType_NON_3_GPP_ACCESS, + factory.N3ueInfo.Security.SQN, ) n3ueContext.RanUeContext.AmfUeNgapId = 1 n3ueContext.RanUeContext.AuthenticationSubs = getAuthSubscription() diff --git a/pkg/factory/factory.go b/pkg/factory/factory.go index dcaccfc..8c660da 100644 --- a/pkg/factory/factory.go +++ b/pkg/factory/factory.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "strconv" "gopkg.in/yaml.v3" @@ -68,6 +67,10 @@ func WriteConfigWithKey(key, value string) error { if ptr := findNodePtrWithKey(&root, key); ptr != nil { ptr.Value = value + if key == "SQN" { + ptr.Style = 0 + ptr.Tag = "!!str" + } } else { return errors.New("There's no value with the key") } @@ -110,16 +113,10 @@ func checkConfigVersion() error { return nil } -func SyncConfigSQN(offset uint8) error { - if sqn, err := strconv.ParseInt(N3ueInfo.Security.SQN, 16, 0); err == nil { - sqn = sqn + int64(offset) - logger.CfgLog.Infof("Write SQN=%12x into config file", sqn) - if err = WriteConfigWithKey("SQN", fmt.Sprintf("%12x", sqn)); err != nil { - return fmt.Errorf("Write config file: %+v", err) - } - } else { - return fmt.Errorf("Parse SQN fail: %+v", err) +func SyncConfigSQN(sqn uint64) error { + logger.CfgLog.Infof("Write SQN=%012x into config file", sqn) + if err := WriteConfigWithKey("SQN", fmt.Sprintf("%012x", sqn)); err != nil { + return fmt.Errorf("Write config file: %+v", err) } - return nil } diff --git a/pkg/ike/handler.go b/pkg/ike/handler.go index 6743c79..ed334f7 100755 --- a/pkg/ike/handler.go +++ b/pkg/ike/handler.go @@ -348,14 +348,7 @@ func (s *Server) handleIKEAUTH( return } - // TS 33.102 - // Sync the SQN for security in config - if err = factory.SyncConfigSQN(1); err != nil { - ikeLog.Errorf("syncConfigSQN: %+v", err) - return - } ikeSecurityAssociation.State++ - case EAP_RegistrationRequest: var eapExpanded *ike_eap.EapExpanded eapExpanded, ok = eapReq.EapTypeData.(*ike_eap.EapExpanded) @@ -364,45 +357,14 @@ func (s *Server) handleIKEAUTH( return } - var decodedNAS *nas.Message - // Decode NAS - Authentication Request nasData := eapExpanded.VendorData[4:] - decodedNAS = new(nas.Message) - if err = decodedNAS.PlainNasDecode(&nasData); err != nil { - ikeLog.Errorf("Decode plain NAS fail: %+v", err) + pdu, keepAuth := s.HandleNas(nasData) + if pdu == nil { + ikeLog.Error("HandleNas() failed") return } - // Calculate for RES* - if decodedNAS == nil || decodedNAS.GmmMessage == nil { - nasLog.Error("decodedNAS is nil") - return - } - - switch decodedNAS.GmmMessage.GetMessageType() { - case nas.MsgTypeAuthenticationRequest: - nasLog.Info("Received Authentication Request") - default: - nasLog.Errorf("Received unexpected message type: %d", - decodedNAS.GmmMessage.GetMessageType()) - } - - rand := decodedNAS.AuthenticationRequest.GetRANDValue() - - snn := n3ueSelf.N3ueInfo.GetSNN() - nasLog.Infof("SNN: %+v", snn) - resStat := ue.DeriveRESstarAndSetKey(ue.AuthenticationSubs, rand[:], snn) - - nasLog.Infof("KnasEnc: %0x", ue.KnasEnc) - nasLog.Infof("KnasInt: %0x", ue.KnasInt) - nasLog.Infof("Kamf: %0x", ue.Kamf) - nasLog.Infof("AnType: %s", ue.AnType) - nasLog.Infof("SUPI: %s", ue.Supi) - - // send NAS Authentication Response - pdu := nasPacket.GetAuthenticationResponse(resStat, "") - // IKE_AUTH - EAP exchange ikeSecurityAssociation.InitiatorMessageID++ @@ -442,7 +404,10 @@ func (s *Server) handleIKEAUTH( return } - ikeSecurityAssociation.State++ + if !keepAuth { + // keep authentication, and keep the ike state until the authentication is successful + ikeSecurityAssociation.State++ + } case EAP_Authentication: _, ok = eapReq.EapTypeData.(*ike_eap.EapExpanded) if !ok { @@ -1348,3 +1313,96 @@ func (s *Server) handleIkeReconnect() { // Trigger procedure restart via RestartRegistration event s.SendProcedureEvt(context.NewRestartRegistrationEvt()) } + +// return (nasPdu, keepAuthentication) +func (s *Server) HandleNas(nasData []byte) ([]byte, bool) { + nasLog := logger.NASLog + + n3ueSelf := s.Context() + ue := n3ueSelf.RanUeContext + decodedNAS := new(nas.Message) + if err := decodedNAS.PlainNasDecode(&nasData); err != nil { + nasLog.Errorf("HandleNas(): Decode plain NAS fail: %+v", err) + return nil, false + } + + // Calculate for RES* + if decodedNAS == nil || decodedNAS.GmmMessage == nil { + nasLog.Error("HandleNas(): decodedNAS is nil") + return nil, false + } + + var pdu []byte + switch decodedNAS.GmmMessage.GetMessageType() { + case nas.MsgTypeAuthenticationRequest: + nasLog.Info("Received Authentication Request") + + // Extract RAND and AUTN parameters + rand := decodedNAS.AuthenticationRequest.AuthenticationParameterRAND.GetRANDValue() + + // Check if AUTN is present + if decodedNAS.AuthenticationRequest.AuthenticationParameterAUTN == nil { + nasLog.Error("AUTN parameter missing in Authentication Request") + return nil, false + } + autn := decodedNAS.AuthenticationRequest.AuthenticationParameterAUTN.GetAUTN() + + nasLog.Infof("RAND: %x", rand) + nasLog.Infof("AUTN: %x", autn) + + // Perform AUTN verification and SQN synchronization + authResult, err := ue.VerifyAUTN(autn[:], rand[:]) + if err != nil { + nasLog.Errorf("AUTN verification failed: %v", err) + } + + var resStat []byte + + switch authResult { + case 0: // AUTH_SUCCESS + nasLog.Info("AUTN verification successful, SQN is fresh") + // Update SQN and derive RES* + snn := n3ueSelf.N3ueInfo.GetSNN() + resStat = ue.DeriveRESstarAndSetKey(ue.AuthenticationSubs, rand[:], snn, autn[:]) + + case 1: // AUTH_SQN_FAILURE + nasLog.Warn("SQN failure detected, generating AUTS for re-synchronization") + auts, err := ue.GenerateAUTS(rand[:]) + if err != nil { + nasLog.Errorf("AUTS generation failed: %v", err) + return nil, false + } + + nasLog.Infof("Generated AUTS: %x", auts) + // Send Authentication Failure with AUTS + pdu = nasPacket.GetAuthenticationFailure(0x15, auts) // EMM cause 0x15 = Synch failure + return pdu, true + + case 2: // AUTH_MAC_FAILURE + nasLog.Error("MAC verification failed - possible attack or corruption") + // Send Authentication Failure without AUTS + pdu = nasPacket.GetAuthenticationFailure(0x14, nil) // EMM cause 0x14 = MAC failure + return pdu, false + + default: + nasLog.Errorf("Unknown authentication result: %d", authResult) + return nil, false + } + + nasLog.Infof("KnasEnc: %0x", ue.KnasEnc) + nasLog.Infof("KnasInt: %0x", ue.KnasInt) + nasLog.Infof("Kamf: %0x", ue.Kamf) + nasLog.Infof("AnType: %s", ue.AnType) + nasLog.Infof("SUPI: %s", ue.Supi) + + // send NAS Authentication Response + pdu = nasPacket.GetAuthenticationResponse(resStat, "") + + default: + nasLog.Errorf("Received unexpected message type: %d", + decodedNAS.GmmMessage.GetMessageType()) + return nil, false + } + + return pdu, false +}