From bf3e2992d9e7e095ca7b38d9ad6585fdfab02a7c Mon Sep 17 00:00:00 2001 From: Allen00991 Date: Thu, 4 Jul 2024 02:56:18 +0000 Subject: [PATCH] Add sha256 integrity algo --- go.mod | 3 + pkg/ike/handler.go | 38 ++++++--- pkg/ike/message/types.go | 2 + pkg/ike/security/security.go | 123 +++++++++++++-------------- pkg/ike/security/security_test.go | 135 ++++++++++++++++++++++++++++++ pkg/ike/xfrm/xfrm.go | 25 +++++- 6 files changed, 245 insertions(+), 81 deletions(-) create mode 100644 pkg/ike/security/security_test.go diff --git a/go.mod b/go.mod index 67f57c2e..a5d6367b 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.9.0 github.com/urfave/cli v1.22.5 github.com/vishvananda/netlink v1.1.0 github.com/wmnsk/go-gtp v0.8.11-0.20240705144331-f53bfdd4233b @@ -27,6 +28,7 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/free5gc/openapi v1.0.9-0.20240503143645-eac9f06c2f6b // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -43,6 +45,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect diff --git a/pkg/ike/handler.go b/pkg/ike/handler.go index 2a2301f5..6d7a787e 100644 --- a/pkg/ike/handler.go +++ b/pkg/ike/handler.go @@ -103,7 +103,7 @@ func (s *Server) HandleIKESAINIT( if len(proposal.EncryptionAlgorithm) > 0 { for _, transform := range proposal.EncryptionAlgorithm { - if is_supported(ike_message.TypeEncryptionAlgorithm, transform.TransformID, + if isTransformSupported(ike_message.TypeEncryptionAlgorithm, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { encryptionAlgorithmTransform = transform break @@ -117,7 +117,7 @@ func (s *Server) HandleIKESAINIT( } if len(proposal.PseudorandomFunction) > 0 { for _, transform := range proposal.PseudorandomFunction { - if is_supported(ike_message.TypePseudorandomFunction, transform.TransformID, + if isTransformSupported(ike_message.TypePseudorandomFunction, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { pseudorandomFunctionTransform = transform break @@ -131,7 +131,7 @@ func (s *Server) HandleIKESAINIT( } if len(proposal.IntegrityAlgorithm) > 0 { for _, transform := range proposal.IntegrityAlgorithm { - if is_supported(ike_message.TypeIntegrityAlgorithm, transform.TransformID, + if isTransformSupported(ike_message.TypeIntegrityAlgorithm, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { integrityAlgorithmTransform = transform break @@ -145,7 +145,7 @@ func (s *Server) HandleIKESAINIT( } if len(proposal.DiffieHellmanGroup) > 0 { for _, transform := range proposal.DiffieHellmanGroup { - if is_supported(ike_message.TypeDiffieHellmanGroup, transform.TransformID, + if isTransformSupported(ike_message.TypeDiffieHellmanGroup, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { diffieHellmanGroupTransform = transform break @@ -572,7 +572,7 @@ func (s *Server) HandleIKEAUTH( if len(proposal.EncryptionAlgorithm) > 0 { for _, transform := range proposal.EncryptionAlgorithm { - if is_Kernel_Supported(ike_message.TypeEncryptionAlgorithm, transform.TransformID, + if isTransformKernelSupported(ike_message.TypeEncryptionAlgorithm, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { encryptionAlgorithmTransform = transform break @@ -589,7 +589,7 @@ func (s *Server) HandleIKEAUTH( } if len(proposal.IntegrityAlgorithm) > 0 { for _, transform := range proposal.IntegrityAlgorithm { - if is_Kernel_Supported(ike_message.TypeIntegrityAlgorithm, transform.TransformID, + if isTransformKernelSupported(ike_message.TypeIntegrityAlgorithm, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { integrityAlgorithmTransform = transform break @@ -601,7 +601,7 @@ func (s *Server) HandleIKEAUTH( } // Optional if len(proposal.DiffieHellmanGroup) > 0 { for _, transform := range proposal.DiffieHellmanGroup { - if is_Kernel_Supported(ike_message.TypeDiffieHellmanGroup, transform.TransformID, + if isTransformKernelSupported(ike_message.TypeDiffieHellmanGroup, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { diffieHellmanGroupTransform = transform break @@ -613,7 +613,7 @@ func (s *Server) HandleIKEAUTH( } // Optional if len(proposal.ExtendedSequenceNumbers) > 0 { for _, transform := range proposal.ExtendedSequenceNumbers { - if is_Kernel_Supported(ike_message.TypeExtendedSequenceNumbers, transform.TransformID, + if isTransformKernelSupported(ike_message.TypeExtendedSequenceNumbers, transform.TransformID, transform.AttributePresent, transform.AttributeValue) { extendedSequenceNumbersTransform = transform break @@ -1835,7 +1835,7 @@ func (s *Server) CreatePDUSessionChildSA( // Integrity transform if pduSession.SecurityIntegrity { proposal.IntegrityAlgorithm.BuildTransform(ike_message.TypeIntegrityAlgorithm, - ike_message.AUTH_HMAC_SHA1_96, nil, nil, nil) + ikeUe.N3IWFIKESecurityAssociation.IntegrityAlgorithm.TransformID, nil, nil, nil) } // RFC 7296 @@ -1952,7 +1952,12 @@ func (s *Server) StartDPD(ikeUe *n3iwf_context.N3IWFIkeUe) { } } -func is_supported(transformType uint8, transformID uint16, attributePresent bool, attributeValue uint16) bool { +func isTransformSupported( + transformType uint8, + transformID uint16, + attributePresent bool, + attributeValue uint16, +) bool { switch transformType { case ike_message.TypeEncryptionAlgorithm: switch transformID { @@ -2004,6 +2009,8 @@ func is_supported(transformType uint8, transformID uint16, attributePresent bool return true case ike_message.PRF_HMAC_TIGER: return false + case ike_message.PRF_HMAC_SHA2_256: + return true default: return false } @@ -2021,6 +2028,8 @@ func is_supported(transformType uint8, transformID uint16, attributePresent bool return false case ike_message.AUTH_AES_XCBC_96: return false + case ike_message.AUTH_HMAC_SHA2_256_128: + return true default: return false } @@ -2052,8 +2061,11 @@ func is_supported(transformType uint8, transformID uint16, attributePresent bool } } -func is_Kernel_Supported( - transformType uint8, transformID uint16, attributePresent bool, attributeValue uint16, +func isTransformKernelSupported( + transformType uint8, + transformID uint16, + attributePresent bool, + attributeValue uint16, ) bool { switch transformType { case ike_message.TypeEncryptionAlgorithm: @@ -2136,6 +2148,8 @@ func is_Kernel_Supported( return false case ike_message.AUTH_AES_XCBC_96: return true + case ike_message.AUTH_HMAC_SHA2_256_128: + return true default: return false } diff --git a/pkg/ike/message/types.go b/pkg/ike/message/types.go index 48002640..39e79ca6 100644 --- a/pkg/ike/message/types.go +++ b/pkg/ike/message/types.go @@ -80,6 +80,7 @@ const ( PRF_HMAC_MD5 = iota + 1 PRF_HMAC_SHA1 PRF_HMAC_TIGER + PRF_HMAC_SHA2_256 = 5 ) const ( @@ -89,6 +90,7 @@ const ( AUTH_DES_MAC AUTH_KPDK_MD5 AUTH_AES_XCBC_96 + AUTH_HMAC_SHA2_256_128 = 12 ) const ( diff --git a/pkg/ike/security/security.go b/pkg/ike/security/security.go index f1f7ea7b..188eeb6c 100644 --- a/pkg/ike/security/security.go +++ b/pkg/ike/security/security.go @@ -7,15 +7,17 @@ import ( "crypto/hmac" "crypto/md5" "crypto/rand" - "crypto/sha1" + "crypto/sha1" // #nosec G505 + "crypto/sha256" "encoding/binary" "encoding/hex" - "errors" "hash" "io" "math/big" "strings" + "github.com/pkg/errors" + "github.com/free5gc/n3iwf/internal/logger" n3iwf_context "github.com/free5gc/n3iwf/pkg/context" "github.com/free5gc/n3iwf/pkg/ike/message" @@ -141,7 +143,9 @@ func NewPseudorandomFunction(key []byte, algorithmType uint16) (hash.Hash, bool) case message.PRF_HMAC_MD5: return hmac.New(md5.New, key), true case message.PRF_HMAC_SHA1: - return hmac.New(sha1.New, key), true + return hmac.New(sha1.New, key), true // #nosec G401 + case message.PRF_HMAC_SHA2_256: + return hmac.New(sha256.New, key), true default: ikeLog.Errorf("Unsupported pseudo random function: %d", algorithmType) return nil, false @@ -149,72 +153,53 @@ func NewPseudorandomFunction(key []byte, algorithmType uint16) (hash.Hash, bool) } // Integrity Algorithm -func CalculateChecksum(key []byte, originData []byte, algorithmType uint16) ([]byte, error) { - ikeLog := logger.IKELog - switch algorithmType { +func calculateIntegrity(key []byte, originData []byte, transform *message.Transform) ([]byte, error) { + expectKeyLen, ok := getKeyLength( + transform.TransformType, transform.TransformID, + transform.AttributePresent, transform.AttributeValue) + if !ok { + return nil, errors.Errorf("calculateIntegrity[%d]: unsupported algo", transform.TransformID) + } + keyLen := len(key) + if keyLen != expectKeyLen { + return nil, errors.Errorf("calculateIntegrity[%d]: Unmatched input key length[%d:%d]", + transform.TransformID, keyLen, expectKeyLen) + } + outputLen, ok := getOutputLength( + transform.TransformType, transform.TransformID, + transform.AttributePresent, transform.AttributeValue) + if !ok { + return nil, errors.Errorf("calculateIntegrity[%d]: unsupported algo", transform.TransformID) + } + + var integrityFunction hash.Hash + switch transform.TransformID { case message.AUTH_HMAC_MD5_96: - if len(key) != 16 { - return nil, errors.New("Unmatched input key length") - } - integrityFunction := hmac.New(md5.New, key) - if _, err := integrityFunction.Write(originData); err != nil { - ikeLog.Errorf("Hash function write error when calculating checksum: %+v", err) - return nil, errors.New("Hash function write error") - } - return integrityFunction.Sum(nil), nil + integrityFunction = hmac.New(md5.New, key) case message.AUTH_HMAC_SHA1_96: - if len(key) != 20 { - return nil, errors.New("Unmatched input key length") - } - integrityFunction := hmac.New(sha1.New, key) - if _, err := integrityFunction.Write(originData); err != nil { - ikeLog.Errorf("Hash function write error when calculating checksum: %+v", err) - return nil, errors.New("Hash function write error") - } - return integrityFunction.Sum(nil)[:12], nil + integrityFunction = hmac.New(sha1.New, key) // #nosec G401 + case message.AUTH_HMAC_SHA2_256_128: + integrityFunction = hmac.New(sha256.New, key) default: - ikeLog.Errorf("Unsupported integrity function: %d", algorithmType) - return nil, errors.New("Unsupported algorithm") + return nil, errors.Errorf("calculateIntegrity[%d]: unsupported algo", transform.TransformID) + } + + if _, err := integrityFunction.Write(originData); err != nil { + return nil, errors.Wrapf(err, "calculateIntegrity[%d]", transform.TransformID) } + return integrityFunction.Sum(nil)[:outputLen], nil } -func VerifyIKEChecksum(key []byte, originData []byte, checksum []byte, algorithmType uint16) (bool, error) { +func verifyIntegrity(key []byte, originData []byte, checksum []byte, transform *message.Transform) (bool, error) { ikeLog := logger.IKELog - switch algorithmType { - case message.AUTH_HMAC_MD5_96: - if len(key) != 16 { - return false, errors.New("Unmatched input key length") - } - integrityFunction := hmac.New(md5.New, key) - if _, err := integrityFunction.Write(originData); err != nil { - ikeLog.Errorf("Hash function write error when verifying IKE checksum: %+v", err) - return false, errors.New("Hash function write error") - } - checksumOfMessage := integrityFunction.Sum(nil) - - ikeLog.Tracef("Calculated checksum:\n%s\nReceived checksum:\n%s", - hex.Dump(checksumOfMessage), hex.Dump(checksum)) - - return hmac.Equal(checksumOfMessage, checksum), nil - case message.AUTH_HMAC_SHA1_96: - if len(key) != 20 { - return false, errors.New("Unmatched input key length") - } - integrityFunction := hmac.New(sha1.New, key) - if _, err := integrityFunction.Write(originData); err != nil { - ikeLog.Errorf("Hash function write error when verifying IKE checksum: %+v", err) - return false, errors.New("Hash function write error") - } - checksumOfMessage := integrityFunction.Sum(nil)[:12] - - ikeLog.Tracef("Calculated checksum:\n%s\nReceived checksum:\n%s", - hex.Dump(checksumOfMessage), hex.Dump(checksum)) - - return hmac.Equal(checksumOfMessage, checksum), nil - default: - ikeLog.Errorf("Unsupported integrity function: %d", algorithmType) - return false, errors.New("Unsupported algorithm") + expectChecksum, err := calculateIntegrity(key, originData, transform) + if err != nil { + return false, errors.Wrapf(err, "verifyIntegrity") } + + ikeLog.Tracef("Calculated checksum:\n%s\nReceived checksum:\n%s", + hex.Dump(expectChecksum), hex.Dump(checksum)) + return hmac.Equal(expectChecksum, checksum), nil } // Encryption Algorithm @@ -605,9 +590,9 @@ func DecryptProcedure(ikeSecurityAssociation *n3iwf_context.IKESecurityAssociati return nil, errors.New("Encoding IKE message failed") } - ok, err = VerifyIKEChecksum(ikeSecurityAssociation.SK_ai, + ok, err = verifyIntegrity(ikeSecurityAssociation.SK_ai, ikeMessageData[:len(ikeMessageData)-checksumLength], checksum, - transformIntegrityAlgorithm.TransformID) + transformIntegrityAlgorithm) if err != nil { ikeLog.Errorf("Error occur when verifying checksum: %+v", err) return nil, errors.New("Error verify checksum") @@ -701,9 +686,9 @@ func EncryptProcedure(ikeSecurityAssociation *n3iwf_context.IKESecurityAssociati ikeLog.Error(err) return errors.New("Encoding IKE message error") } - checksumOfMessage, err := CalculateChecksum(ikeSecurityAssociation.SK_ar, + checksumOfMessage, err := calculateIntegrity(ikeSecurityAssociation.SK_ar, responseIKEMessageData[:len(responseIKEMessageData)-checksumLength], - transformIntegrityAlgorithm.TransformID) + transformIntegrityAlgorithm) if err != nil { ikeLog.Errorf("Calculating checksum failed: %+v", err) return errors.New("Error calculating checksum") @@ -800,6 +785,8 @@ func getKeyLength(transformType uint8, transformID uint16, attributePresent bool return 16, true case message.PRF_HMAC_SHA1: return 20, true + case message.PRF_HMAC_SHA2_256: + return 32, true case message.PRF_HMAC_TIGER: return 0, false default: @@ -819,6 +806,8 @@ func getKeyLength(transformType uint8, transformID uint16, attributePresent bool return 0, false case message.AUTH_AES_XCBC_96: return 0, false + case message.AUTH_HMAC_SHA2_256_128: + return 32, true default: return 0, false } @@ -868,6 +857,8 @@ func getOutputLength( return 20, true case message.PRF_HMAC_TIGER: return 0, false + case message.PRF_HMAC_SHA2_256: + return 32, true default: return 0, false } @@ -885,6 +876,8 @@ func getOutputLength( return 0, false case message.AUTH_AES_XCBC_96: return 0, false + case message.AUTH_HMAC_SHA2_256_128: + return 16, true default: return 0, false } diff --git a/pkg/ike/security/security_test.go b/pkg/ike/security/security_test.go new file mode 100644 index 00000000..ad6a0007 --- /dev/null +++ b/pkg/ike/security/security_test.go @@ -0,0 +1,135 @@ +package security + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/free5gc/n3iwf/pkg/ike/message" +) + +func TestVerifyIntegrity(t *testing.T) { + tests := []struct { + name string + key string + originData []byte + checksum string + transform *message.Transform + expectedValid bool + }{ + { + name: "HMAC MD5 96 - valid", + key: "0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "c30f366e411540f68221d04a", + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_MD5_96, + }, + expectedValid: true, + }, + { + name: "HMAC MD5 96 - invalid checksum", + key: "0123456789abcdef", + originData: []byte("hello world"), + checksum: "01231875aa", + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_MD5_96, + }, + expectedValid: false, + }, + { + name: "HMAC MD5 96 - invalid key length", + key: "0123", + originData: []byte("hello world"), + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_MD5_96, + }, + expectedValid: false, + }, + { + name: "HMAC SHA1 96 - valid", + key: "0123456789abcdef0123456789abcdef01234567", + originData: []byte("hello world"), + checksum: "5089f6a86e4dafb89e3fcd23", + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_SHA1_96, + }, + expectedValid: true, + }, + { + name: "HMAC SHA1 96 - invalid checksum", + key: "0123456789abcdef0123456789abcdef01234567", + originData: []byte("hello world"), + checksum: "01231875aa", + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_SHA1_96, + }, + expectedValid: false, + }, + { + name: "HMAC SHA1 96 - invalid key length", + key: "0123", + originData: []byte("hello world"), + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_SHA1_96, + }, + expectedValid: false, + }, + { + name: "HMAC SHA256 128 - valid", + key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "a64166565bc1f48eb3edd4109fcaeb72", + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_SHA2_256_128, + }, + expectedValid: true, + }, + { + name: "HMAC SHA256 128 - invalid checksum", + key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + originData: []byte("hello world"), + checksum: "01231875aa", + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_SHA1_96, + }, + expectedValid: false, + }, + { + name: "HMAC SHA256 128 - invalid key length", + key: "0123", + originData: []byte("hello world"), + transform: &message.Transform{ + TransformType: message.TypeIntegrityAlgorithm, + TransformID: message.AUTH_HMAC_SHA1_96, + }, + expectedValid: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var key, checksum []byte + var err error + checksum, err = hex.DecodeString(tt.checksum) + require.NoError(t, err, "failed to decode checksum hex string") + + key, err = hex.DecodeString(tt.key) + require.NoError(t, err, "failed to decode key hex string") + + valid, err := verifyIntegrity(key, tt.originData, checksum, tt.transform) + if tt.expectedValid { + require.NoError(t, err, "verifyIntegrity returned an error") + } + require.Equal(t, tt.expectedValid, valid) + }) + } +} diff --git a/pkg/ike/xfrm/xfrm.go b/pkg/ike/xfrm/xfrm.go index c36f45ad..c6581b15 100644 --- a/pkg/ike/xfrm/xfrm.go +++ b/pkg/ike/xfrm/xfrm.go @@ -45,6 +45,8 @@ func (xfrmIntegrityAlgorithmType XFRMIntegrityAlgorithmType) String() string { return "hmac(sha1)" case message.AUTH_AES_XCBC_96: return "xcbc(aes)" + case message.AUTH_HMAC_SHA2_256_128: + return "hmac(sha256)" default: return "" } @@ -66,8 +68,9 @@ func ApplyXFRMRule(n3iwf_is_initiator bool, xfrmiId uint32, } if childSecurityAssociation.IntegrityAlgorithm != 0 { xfrmIntegrityAlgorithm = &netlink.XfrmStateAlgo{ - Name: XFRMIntegrityAlgorithmType(childSecurityAssociation.IntegrityAlgorithm).String(), - Key: childSecurityAssociation.ResponderToInitiatorIntegrityKey, + Name: XFRMIntegrityAlgorithmType(childSecurityAssociation.IntegrityAlgorithm).String(), + Key: childSecurityAssociation.ResponderToInitiatorIntegrityKey, + TruncateLen: getTruncateLength(childSecurityAssociation.IntegrityAlgorithm), } } } else { @@ -77,8 +80,9 @@ func ApplyXFRMRule(n3iwf_is_initiator bool, xfrmiId uint32, } if childSecurityAssociation.IntegrityAlgorithm != 0 { xfrmIntegrityAlgorithm = &netlink.XfrmStateAlgo{ - Name: XFRMIntegrityAlgorithmType(childSecurityAssociation.IntegrityAlgorithm).String(), - Key: childSecurityAssociation.InitiatorToResponderIntegrityKey, + Name: XFRMIntegrityAlgorithmType(childSecurityAssociation.IntegrityAlgorithm).String(), + Key: childSecurityAssociation.InitiatorToResponderIntegrityKey, + TruncateLen: getTruncateLength(childSecurityAssociation.IntegrityAlgorithm), } } } @@ -276,3 +280,16 @@ func SetupIPsecXfrmi(xfrmIfaceName, parentIfaceName string, xfrmIfaceId uint32, return xfrmi, nil } + +func getTruncateLength(transformID uint16) int { + switch transformID { + case message.AUTH_HMAC_MD5_96: + return 96 + case message.AUTH_HMAC_SHA1_96: + return 96 + case message.AUTH_HMAC_SHA2_256_128: + return 128 + default: + return 96 + } +}