-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
issues/133: Add password hashing capabilities. (#137)
What: - Add password hashing capabilities. Why: - Fixes: #133
- Loading branch information
Showing
8 changed files
with
169 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package enc | ||
package cry | ||
|
||
import ( | ||
"sync" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package cry | ||
|
||
import ( | ||
"crypto/subtle" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
|
||
"golang.org/x/crypto/scrypt" | ||
) | ||
|
||
// Most of the code here is insipired by(or taken from): | ||
// (a) https://github.com/elithrar/simple-scrypt whose license(MIT) can be found here: https://github.com/elithrar/simple-scrypt/blob/v1.3.0/LICENSE | ||
|
||
const ( | ||
// this should be increased every time the parameters passed to [scrypt.Key] are changed. | ||
version = 1 | ||
separator = "$" | ||
) | ||
|
||
func deriveKey(password, salt []byte) (derivedKey []byte, err error) { | ||
derivedKey, err = scrypt.Key(password, salt, n, r, p, keyLen) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return derivedKey, nil | ||
} | ||
|
||
// Hash returns the scrypt hash of the password. | ||
// It is safe to persist the result in your database instead of storing the actual password. | ||
func Hash(password string) (string, error) { | ||
salt := random(saltLen, saltLen) | ||
derivedKey, err := deriveKey([]byte(password), salt) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// Add version, salt to the derived key. | ||
// The salt and the derived key are hex encoded. | ||
return fmt.Sprintf( | ||
`%d%s%x%s%x`, | ||
version, | ||
separator, | ||
salt, | ||
separator, | ||
derivedKey, | ||
), nil | ||
} | ||
|
||
// Eql performs a constant-time comparison between the password and the hash. | ||
// The hash ought to have been produced by [Hash] | ||
func Eql(password, hash string) error { | ||
params := strings.Split(hash, "$") | ||
|
||
if len(params) != 3 { | ||
return errors.New("unable to parse") | ||
} | ||
|
||
pVer, err := strconv.Atoi(params[0]) | ||
if err != nil { | ||
return err | ||
} | ||
if pVer != version { | ||
return errors.New("version mismatch") | ||
} | ||
|
||
pSalt, err := hex.DecodeString(params[1]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
pDerivedKey, err := hex.DecodeString(params[2]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
dk, err := deriveKey([]byte(password), pSalt) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if subtle.ConstantTimeCompare(dk, pDerivedKey) == 1 { | ||
return nil | ||
} | ||
|
||
return errors.New("password mismatch") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package cry | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/akshayjshah/attest" | ||
) | ||
|
||
func TestHash(t *testing.T) { | ||
t.Parallel() | ||
|
||
t.Run("hash success", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
password := "hey ok" | ||
hash, err := Hash(password) | ||
attest.Ok(t, err) | ||
attest.NotZero(t, hash) | ||
}) | ||
|
||
t.Run("eql success", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
password := "hey ok" | ||
hash, err := Hash(password) | ||
attest.Ok(t, err) | ||
attest.NotZero(t, hash) | ||
|
||
err = Eql(password, hash) | ||
attest.Ok(t, err) | ||
}) | ||
|
||
t.Run("eql error", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
password := "hey ok" | ||
hash, err := Hash(password) | ||
attest.Ok(t, err) | ||
attest.NotZero(t, hash) | ||
|
||
hash = strings.ReplaceAll(hash, separator, "-") | ||
err = Eql(password, hash) | ||
attest.Error(t, err) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters