From 8bf58a16554fd2f7fdd937d7506c937f25cf4fc8 Mon Sep 17 00:00:00 2001 From: aldy505 Date: Fri, 28 May 2021 20:04:38 +0700 Subject: [PATCH] feat(pbkdf2): added verify function --- pbkdf2/pbkdf2.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++ phc-crypto.go | 3 +++ phc-crypto_test.go | 47 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/pbkdf2/pbkdf2.go b/pbkdf2/pbkdf2.go index f918e47..1112355 100644 --- a/pbkdf2/pbkdf2.go +++ b/pbkdf2/pbkdf2.go @@ -6,9 +6,12 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/sha512" + "crypto/subtle" "encoding/hex" "errors" "io" + "strconv" + "strings" "github.com/aldy505/phc-crypto/format" "golang.org/x/crypto/pbkdf2" @@ -63,5 +66,53 @@ func Hash(plain string, config Config) (string, error) { }) return hashString, nil +} + +func Verify(hash string, plain string) (bool, error) { + deserialize := format.Deserialize(hash) + + if !strings.HasPrefix(deserialize.ID, "pbkdf2") { + return false, errors.New("hashed string is not pbkdf2 instance") + } + + decodedHash, err := hex.DecodeString(deserialize.Hash) + if err != nil { + return false, err + } + keyLen := int(len(decodedHash)) + + rounds, err := strconv.ParseInt(deserialize.Params["i"].(string), 10, 32) + if err != nil { + return false, err + } + salt, err := hex.DecodeString(deserialize.Salt) + if err != nil { + return false, err + } + + hashFunc := strings.Replace(deserialize.ID, "pbkdf2", "", 1) + + var verifyHash []byte + + if hashFunc == "sha1" { + verifyHash = pbkdf2.Key([]byte(plain), salt, int(rounds), keyLen, sha1.New) + } else if hashFunc == "sha256" { + verifyHash = pbkdf2.Key([]byte(plain), salt, int(rounds), keyLen, sha256.New) + } else if hashFunc == "sha224" { + verifyHash = pbkdf2.Key([]byte(plain), salt, int(rounds), keyLen, sha256.New224) + } else if hashFunc == "sha512" { + verifyHash = pbkdf2.Key([]byte(plain), salt, int(rounds), keyLen, sha512.New) + } else if hashFunc == "sha384" { + verifyHash = pbkdf2.Key([]byte(plain), salt, int(rounds), keyLen, sha512.New384) + } else if hashFunc == "md5" { + verifyHash = pbkdf2.Key([]byte(plain), salt, int(rounds), keyLen, md5.New) + } else { + return false, errors.New("we don't support " + hashFunc + " for a hash function.") + } + + if subtle.ConstantTimeCompare(decodedHash, verifyHash) == 1 { + return true, nil + } + return false, nil } diff --git a/phc-crypto.go b/phc-crypto.go index 47e9304..1dbcbf5 100644 --- a/phc-crypto.go +++ b/phc-crypto.go @@ -95,6 +95,9 @@ func (a *Algo) Verify(hash, plain string) (verify bool, err error) { } else if a.Name == "argon2" { verify, err = argon2.Verify(hash, plain) return + } else if a.Name == "pbkdf2" { + verify, err = pbkdf2.Verify(hash, plain) + return } verify = false err = errors.New("the algorithm provided is not (yet) supported") diff --git a/phc-crypto_test.go b/phc-crypto_test.go index 0e69cb3..77dd5e7 100644 --- a/phc-crypto_test.go +++ b/phc-crypto_test.go @@ -38,7 +38,6 @@ func TestPHCCrypto(t *testing.T) { if err != nil { t.Error(err) } - t.Log(hash) typeof := reflect.TypeOf(hash).Kind() if typeof != reflect.String { t.Error("returned type is not string") @@ -97,7 +96,6 @@ func TestPHCCrypto(t *testing.T) { if err != nil { t.Error(err) } - t.Log(hash) typeof := reflect.TypeOf(hash).Kind() if typeof != reflect.String { t.Error("returned type is not string") @@ -109,7 +107,6 @@ func TestPHCCrypto(t *testing.T) { t.Error(err) } hash, err := crypto.Hash("password123") - t.Log(hash) if err != nil { t.Error(err) } @@ -157,7 +154,6 @@ func TestPHCCrypto(t *testing.T) { if err != nil { t.Error(err) } - t.Log(hash) typeof := reflect.TypeOf(hash).Kind() if typeof != reflect.String { t.Error("returned type is not string") @@ -216,11 +212,52 @@ func TestPHCCrypto(t *testing.T) { if err != nil { t.Error(err) } - t.Log(hash) typeof := reflect.TypeOf(hash).Kind() if typeof != reflect.String { t.Error("returned type is not string") } }) + t.Run("verify should return true", func(t *testing.T) { + crypto, err := phccrypto.Use("pbkdf2", phccrypto.Config{}) + if err != nil { + t.Error(err) + } + hash, err := crypto.Hash("password123") + if err != nil { + t.Error(err) + } + verify, err := crypto.Verify(hash, "password123") + if err != nil { + t.Error(err) + } + typeof := reflect.TypeOf(verify).Kind() + if typeof != reflect.Bool { + t.Error("returned type is not boolean") + } + if !verify { + t.Error("verify function returned false") + } + }) + t.Run("verify should return false", func(t *testing.T) { + crypto, err := phccrypto.Use("pbkdf2", phccrypto.Config{}) + if err != nil { + t.Error(err) + } + hash, err := crypto.Hash("password123") + if err != nil { + t.Error(err) + } + verify, err := crypto.Verify(hash, "password321") + if err != nil { + t.Error(err) + } + typeof := reflect.TypeOf(verify).Kind() + if typeof != reflect.Bool { + t.Error("returned type is not boolean") + } + if verify { + t.Error("verify function returned false") + } + }) }) }