From 6a24de7687e469ff29f63663a421fc0182b627fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nagy=20K=C3=A1roly=20G=C3=A1briel?= Date: Mon, 25 Sep 2023 14:28:03 +0300 Subject: [PATCH] htpasswd: implement sha256 and sha512 algorithms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nagy Károly Gábriel --- htpasswd/htpasswd.go | 88 ++++++++++++++++++++++++++++++++++++++ htpasswd/htpasswd_test.go | 4 ++ htpasswd/htpasswd_testdata | 8 ++-- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/htpasswd/htpasswd.go b/htpasswd/htpasswd.go index e82ac33..f0195f6 100644 --- a/htpasswd/htpasswd.go +++ b/htpasswd/htpasswd.go @@ -5,6 +5,8 @@ import ( "bytes" "crypto/rand" "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "encoding/base64" "errors" "fmt" @@ -30,6 +32,10 @@ const ( HashSHA HashAlgorithm = "sha" // HashSSHA Salted SHA HashSSHA HashAlgorithm = "ssha" + // HashSHA256 256 variant of SHA + HashSHA256 HashAlgorithm = "sha256" + // HashSHA512 512 variant of SHA + HashSHA512 HashAlgorithm = "sha512" ) // ParseHtpasswdFile parses a .htpasswd file @@ -174,6 +180,10 @@ func verifyPass(pass, hash string, alg HashAlgorithm) error { return verifySHA(pass, hash) case HashSSHA: return verifySSHA(pass, hash) + case HashSHA256: + return verifySHA256(pass, hash) + case HashSHA512: + return verifySHA512(pass, hash) } return fmt.Errorf("unsupported hash algorithm %v", alg) } @@ -235,6 +245,53 @@ func verifySSHA(password, hashedPassword string) error { return nil } + +func verifySHA256(password, hashedPassword string) error { + eppS := hashedPassword[3:] + hash, err := base64.StdEncoding.DecodeString(eppS) + + if err != nil { + return fmt.Errorf("cannot base64 decode") + } + + sha := sha256.New() + _, err = sha.Write([]byte(password)) + + if err != nil { + return err + } + + sum := sha.Sum(nil) + + if !bytes.Equal(sum, hash) { + return fmt.Errorf("wrong password") + } + return nil +} + +func verifySHA512(password, hashedPassword string) error { + eppS := hashedPassword[3:] + hash, err := base64.StdEncoding.DecodeString(eppS) + + if err != nil { + return fmt.Errorf("cannot base64 decode") + } + + sha := sha512.New() + _, err = sha.Write([]byte(password)) + + if err != nil { + return err + } + + sum := sha.Sum(nil) + + if !bytes.Equal(sum, hash) { + return fmt.Errorf("wrong password") + } + return nil +} + func (pp Passwds) toByte() []byte { pass := []byte{} for name, hash := range pp { @@ -254,6 +311,10 @@ func identifyHash(h string) (HashAlgorithm, error) { return HashSHA, nil case strings.HasPrefix(h, "{SSHA}"): return HashSSHA, nil + case strings.HasPrefix(h, "$5$"): + return HashSHA256, nil + case strings.HasPrefix(h, "$6$"): + return HashSHA512, nil } return "", fmt.Errorf("unsupported hash algorithm") @@ -274,6 +335,12 @@ func createHash(passwd string, algo HashAlgorithm) (string, error) { case HashSSHA: prefix = "{SSHA}" hash, err = hashSSha(passwd) + case HashSHA256: + prefix = "$5$" + hash, err = hashSha256(passwd) + case HashSHA512: + prefix = "$6$" + hash, err = hashSha512(passwd) } if err != nil { return "", err @@ -303,6 +370,27 @@ func hashSha(passwd string) (string, error) { passwordSum := []byte(s.Sum(nil)) return base64.StdEncoding.EncodeToString(passwordSum), nil } + +func hashSha256(passwd string) (string, error) { + s := sha256.New() + _, err := s.Write([]byte(passwd)) + if err != nil { + return "", err + } + passwordSum := []byte(s.Sum(nil)) + return base64.StdEncoding.EncodeToString(passwordSum), nil +} + +func hashSha512(passwd string) (string, error) { + s := sha512.New() + _, err := s.Write([]byte(passwd)) + if err != nil { + return "", err + } + passwordSum := []byte(s.Sum(nil)) + return base64.StdEncoding.EncodeToString(passwordSum), nil +} + func hashSSha(passwd string) (string, error) { s := sha1.New() _, err := s.Write([]byte(passwd)) diff --git a/htpasswd/htpasswd_test.go b/htpasswd/htpasswd_test.go index 1a49df2..67897c7 100644 --- a/htpasswd/htpasswd_test.go +++ b/htpasswd/htpasswd_test.go @@ -33,6 +33,8 @@ func TestCreateUser(t *testing.T) { {"bcrypt", "123456@", HashBCrypt, nil}, {"ssha", "123456@", HashSSHA, nil}, {"sha", "123456@", HashSHA, nil}, + {"sha256", "123456@", HashSHA256, nil}, + {"sha512", "123456@", HashSHA512, nil}, } for _, tc := range testCases { @@ -62,6 +64,8 @@ func TestVerifyPassword(t *testing.T) { {"bcrypt", "123456@", nil}, {"ssha", "123456@", nil}, {"sha", "123456@", nil}, + {"sha256", "123456@", nil}, + {"sha512", "123456@", nil}, } for _, tc := range testCases { diff --git a/htpasswd/htpasswd_testdata b/htpasswd/htpasswd_testdata index 0545eb6..e9207a2 100644 --- a/htpasswd/htpasswd_testdata +++ b/htpasswd/htpasswd_testdata @@ -1,4 +1,6 @@ -bcrypt:$2a$10$9NdvqvFl9Yz9FM2D.i9Na.K1CiNF1ldk9hgRR57lYJiRBUnGt2THq -ssha:{SSHA}7fPHbfKv92vC/IFhKdnEKSTBKubvta9a +apr1:$apr1$v0FnbGJM$b2P3y1ltZYHakDaHrWx3N1 +bcrypt:$2a$10$pBSUext6NDsFYrm8GviW.OFe6SczH91INRC3YmsfE3HJp/fPmRaee +ssha:{SSHA}LoTRQgCdeGIeJ3nxDQMCmQSWdnMEsLqj +sha512:$6$78RjySv19bx/knbdL6q1cpoV8WblZwc3x+wmPGQUvrSycxc4liKbksvDr9HZj76hgRuZZCyEngP+WEJmePArCQ== sha:{SHA}YEn9/RmoXLdbyB9TEDJ0OqWoPy8= -apr1:$apr1$U9.3kynN$MCKs53Oz35J0OYrSxfheW. +sha256:$5$ufJ2S16IOhB6XLTGrVLqGQBKv/odjE3rypxnUDLqaS0=