From 6480eb86e247860330c5c83a286154f7eb948585 Mon Sep 17 00:00:00 2001 From: Jesper Falk Date: Tue, 27 Feb 2024 16:05:51 +0100 Subject: [PATCH 1/2] Updated PBKDF2 hasher with more complex format handling --- go.mod | 2 +- go.sum | 7 ++ hashing/hashing_test.go | 43 +++++++------ hashing/pbkdf2.go | 137 +++++++++++++++++++++++++++++----------- 4 files changed, 133 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index 9a2b976..4487794 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/iegomez/mosquitto-go-auth -go 1.18 +go 1.21 require ( github.com/go-redis/redis/v8 v8.11.5 diff --git a/go.sum b/go.sum index f6c6f4c..661cf36 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= @@ -43,6 +44,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f h1:4Gslotqbs16iAg+1KR/XdabIfq8TlAWHdwS5QJFksLc= github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -73,8 +75,11 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -231,9 +236,11 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/hashing/hashing_test.go b/hashing/hashing_test.go index e4a1cb1..63f3c9f 100644 --- a/hashing/hashing_test.go +++ b/hashing/hashing_test.go @@ -126,22 +126,29 @@ func TestArgon2ID(t *testing.T) { func TestPBKDF2(t *testing.T) { password := "test-password" - - // Test base64. - hasher := NewPBKDF2Hasher(defaultPBKDF2SaltSize, defaultPBKDF2Iterations, defaultPBKDF2Algorithm, Base64, defaultPBKDF2KeyLen) - - passwordHash, err := hasher.Hash(password) - - assert.Nil(t, err) - assert.True(t, hasher.Compare(password, passwordHash)) - assert.False(t, hasher.Compare("other", passwordHash)) - - // Test UTF8. - hasher = NewPBKDF2Hasher(defaultPBKDF2SaltSize, defaultPBKDF2Iterations, defaultPBKDF2Algorithm, UTF8, defaultPBKDF2KeyLen) - - passwordHash, err = hasher.Hash(password) - - assert.Nil(t, err) - assert.True(t, hasher.Compare(password, passwordHash)) - assert.False(t, hasher.Compare("other", passwordHash)) + b64Hasher := NewPBKDF2Hasher(defaultPBKDF2SaltSize, defaultPBKDF2Iterations, defaultPBKDF2Algorithm, Base64, defaultPBKDF2KeyLen) + utf8Hasher := NewPBKDF2Hasher(defaultPBKDF2SaltSize, defaultPBKDF2Iterations, defaultPBKDF2Algorithm, UTF8, defaultPBKDF2KeyLen) + + t.Run("OlderFormat", func(t *testing.T) { + t.Run("Base64", func(t *testing.T) { + passwordHash, err := b64Hasher.Hash(password) + + assert.Nil(t, err) + assert.True(t, b64Hasher.Compare(password, passwordHash)) + assert.False(t, b64Hasher.Compare("other", passwordHash)) + }) + t.Run("UTF8", func(t *testing.T) { + passwordHash, err := utf8Hasher.Hash(password) + + assert.Nil(t, err) + assert.True(t, utf8Hasher.Compare(password, passwordHash)) + assert.False(t, utf8Hasher.Compare("other", passwordHash)) + }) + }) + + t.Run("PHC-SF-Spec", func(t *testing.T) { + passwordHash := "$pbkdf2-sha512$i=10000,l=32$/DsNR8DBuoF/MxzLY+QVaw$YNfYNfT+6yT2blLrXKKR8Ll+aesgHYqSOtFTBsyscRM" + assert.True(t, b64Hasher.Compare(password, passwordHash)) + assert.False(t, b64Hasher.Compare("other", passwordHash)) + }) } diff --git a/hashing/pbkdf2.go b/hashing/pbkdf2.go index bc35253..c824e9f 100644 --- a/hashing/pbkdf2.go +++ b/hashing/pbkdf2.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "fmt" "math/big" + "slices" "strconv" "strings" @@ -23,13 +24,13 @@ type pbkdf2Hasher struct { keyLen int } -func NewPBKDF2Hasher(saltSize int, iterations int, algorithm string, saltEncoding string, keylen int) HashComparer { +func NewPBKDF2Hasher(saltSize int, iterations int, algorithm string, saltEncoding string, keyLen int) HashComparer { return pbkdf2Hasher{ saltSize: saltSize, iterations: iterations, algorithm: algorithm, saltEncoding: preferredEncoding(saltEncoding), - keyLen: keylen, + keyLen: keyLen, } } @@ -37,20 +38,18 @@ func NewPBKDF2Hasher(saltSize int, iterations int, algorithm string, saltEncodin * PBKDF2 methods are adapted from github.com/brocaar/chirpstack-application-server, some comments included. */ -// Hash function reference may be found at https://github.com/brocaar/chirpstack-application-server/blob/master/internal/storage/user.go#L421. - -// Generate the hash of a password for storage in the database. -// NOTE: We store the details of the hashing algorithm with the hash itself, -// making it easy to recreate the hash for password checking, even if we change -// the default criteria here. +// Hash function generates a hash of the supplied password. The hash +// can then be stored directly in the database. The return hash will +// contain options according to the PHC String format found at +// https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md func (h pbkdf2Hasher) Hash(password string) (string, error) { // Generate a random salt value with the given salt size. salt := make([]byte, h.saltSize) _, err := rand.Read(salt) - // We need to ensure that salt doesn contain $, which is 36 in decimal. - // So we check if there'sbyte that represents $ and change it with a random number in the range 0-35 - //// This is far from ideal, but should be good enough with a reasonable salt size. + // We need to ensure that salt doesn't contain $, which is 36 in decimal. + // So we check if there's byte that represents $ and change it with a random number in the range 0-35 + // // This is far from ideal, but should be good enough with a reasonable salt size. for i := 0; i < len(salt); i++ { if salt[i] == 36 { n, err := rand.Int(rand.Reader, big.NewInt(35)) @@ -69,52 +68,116 @@ func (h pbkdf2Hasher) Hash(password string) (string, error) { return h.hashWithSalt(password, salt, h.iterations, h.algorithm, h.keyLen), nil } -// HashCompare verifies that passed password hashes to the same value as the +// Compare verifies that passed password hashes to the same value as the // passed passwordHash. // Reference: https://github.com/brocaar/chirpstack-application-server/blob/master/internal/storage/user.go#L458. +// Parsing reference: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md func (h pbkdf2Hasher) Compare(password string, passwordHash string) bool { - hashSplit := strings.Split(passwordHash, "$") - - if len(hashSplit) != 5 { - log.Errorf("invalid PBKDF2 hash supplied, expected length 5, got: %d", len(hashSplit)) - return false - } - - algorithm := hashSplit[1] + hashSplit := h.getFields(passwordHash) + + var ( + err error + algorithm string + paramString string + hashedPassword []byte + salt []byte + iterations int + keyLen int + ) + if hashSplit[0] == "PBKDF2" { + algorithm = hashSplit[1] + iterations, err = strconv.Atoi(hashSplit[2]) + if err != nil { + log.Errorf("iterations error: %s", err) + return false + } - iterations, err := strconv.Atoi(hashSplit[2]) - if err != nil { - log.Errorf("iterations error: %s", err) - return false - } + switch h.saltEncoding { + case UTF8: + salt = []byte(hashSplit[3]) + default: + var err error + salt, err = base64.StdEncoding.DecodeString(hashSplit[3]) + if err != nil { + log.Errorf("base64 salt error: %s", err) + return false + } + } - var salt []byte - switch h.saltEncoding { - case UTF8: - salt = []byte(hashSplit[3]) - default: - salt, err = base64.StdEncoding.DecodeString(hashSplit[3]) + hashedPassword, err = base64.StdEncoding.DecodeString(hashSplit[4]) if err != nil { - log.Errorf("base64 salt error: %s", err) + log.Errorf("base64 hash decoding error: %s", err) return false } + keyLen = len(hashedPassword) + + } else if hashSplit[0] == "pbkdf2-sha512" { + algorithm = "sha512" + paramString = hashSplit[1] + + opts := strings.Split(paramString, ",") + for _, opt := range opts { + parts := strings.Split(opt, "=") + for i := 0; i < len(parts); i += 2 { + key := parts[i] + val := parts[i+1] + switch key { + case "i": + iterations, _ = strconv.Atoi(val) + case "l": + keyLen, _ = strconv.Atoi(val) + default: + log.Errorf("unknown options key (\"%s\")", key) + return false + } + } + } + + switch h.saltEncoding { + case UTF8: + salt = []byte(hashSplit[2]) + default: + var err error + salt, err = base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(hashSplit[2]) + if err != nil { + log.Errorf("base64 salt error: %s", err) + return false + } + } + + hashedPassword, err = base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(hashSplit[3]) + } else { + log.Errorf("invalid PBKDF2 hash supplied, unrecognized format \"%s\"", hashSplit[0]) + return false } - hashedPassword, err := base64.StdEncoding.DecodeString(hashSplit[4]) + newHash := h.hashWithSalt(password, salt, iterations, algorithm, keyLen) + hashSplit = h.getFields(newHash) + newHashedPassword, err := base64.StdEncoding.DecodeString(hashSplit[4]) if err != nil { - log.Errorf("base64 hash decoding error: %s", err) + log.Errorf("base64 salt error: %s", err) return false } - keylen := len(hashedPassword) + return slices.Compare(hashedPassword, newHashedPassword) == 0 +} - return passwordHash == h.hashWithSalt(password, salt, iterations, algorithm, keylen) +func (h pbkdf2Hasher) getFields(passwordHash string) []string { + hashSplit := strings.FieldsFunc(passwordHash, func(r rune) bool { + switch r { + case '$': + return true + default: + return false + } + }) + return hashSplit } // Reference: https://github.com/brocaar/chirpstack-application-server/blob/master/internal/storage/user.go#L432. func (h pbkdf2Hasher) hashWithSalt(password string, salt []byte, iterations int, algorithm string, keylen int) string { // Generate the hashed password. This should be a little painful, adjust ITERATIONS - // if it needs performance tweeking. Greatly depends on the hardware. + // if it needs performance tweaking. Greatly depends on the hardware. // NOTE: We store these details with the returned hashed, so changes will not // affect our ability to do password compares. shaHash := sha512.New From 05bbc603802b222975e748e814f3191ef26431d6 Mon Sep 17 00:00:00 2001 From: Jesper Falk Date: Sat, 2 Mar 2024 09:49:04 +0100 Subject: [PATCH 2/2] Lowered go version --- go.mod | 2 +- go.sum | 7 ------- hashing/pbkdf2.go | 12 ++++++++++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 4487794..9a2b976 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/iegomez/mosquitto-go-auth -go 1.21 +go 1.18 require ( github.com/go-redis/redis/v8 v8.11.5 diff --git a/go.sum b/go.sum index 661cf36..f6c6f4c 100644 --- a/go.sum +++ b/go.sum @@ -16,7 +16,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= @@ -44,7 +43,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f h1:4Gslotqbs16iAg+1KR/XdabIfq8TlAWHdwS5QJFksLc= github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -75,11 +73,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -236,11 +231,9 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/hashing/pbkdf2.go b/hashing/pbkdf2.go index c824e9f..81fd7cf 100644 --- a/hashing/pbkdf2.go +++ b/hashing/pbkdf2.go @@ -8,7 +8,6 @@ import ( "encoding/base64" "fmt" "math/big" - "slices" "strconv" "strings" @@ -159,7 +158,16 @@ func (h pbkdf2Hasher) Compare(password string, passwordHash string) bool { return false } - return slices.Compare(hashedPassword, newHashedPassword) == 0 + return h.compareBytes(hashedPassword, newHashedPassword) +} + +func (h pbkdf2Hasher) compareBytes(a, b []byte) bool { + for i, x := range a { + if b[i] != x { + return false + } + } + return true } func (h pbkdf2Hasher) getFields(passwordHash string) []string {