Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: minor refactorings in package hash #3186

Merged
merged 4 commits into from May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 20 additions & 50 deletions hash/hash_comparator.go
Expand Up @@ -60,6 +60,9 @@ func CompareBcrypt(_ context.Context, password []byte, hash []byte) error {

err := bcrypt.CompareHashAndPassword(hash, password)
if err != nil {
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
return errors.WithStack(ErrMismatchedHashAndPassword)
}
return err
}

Expand All @@ -75,15 +78,9 @@ func CompareArgon2id(_ context.Context, password []byte, hash []byte) error {
}

// Derive the key from the other password using the same parameters.
otherHash := argon2.IDKey([]byte(password), salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength)
otherHash := argon2.IDKey(password, salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength)

// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(hash, otherHash)
}

func CompareArgon2i(_ context.Context, password []byte, hash []byte) error {
Expand All @@ -95,15 +92,9 @@ func CompareArgon2i(_ context.Context, password []byte, hash []byte) error {
}

// Derive the key from the other password using the same parameters.
otherHash := argon2.Key([]byte(password), salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength)
otherHash := argon2.Key(password, salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength)

// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(hash, otherHash)
}

func ComparePbkdf2(_ context.Context, password []byte, hash []byte) error {
Expand All @@ -117,13 +108,7 @@ func ComparePbkdf2(_ context.Context, password []byte, hash []byte) error {
// Derive the key from the other password using the same parameters.
otherHash := pbkdf2.Key(password, salt, int(p.Iterations), int(p.KeyLength), getPseudorandomFunctionForPbkdf2(p.Algorithm))

// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(hash, otherHash)
}

func CompareScrypt(_ context.Context, password []byte, hash []byte) error {
Expand All @@ -140,13 +125,7 @@ func CompareScrypt(_ context.Context, password []byte, hash []byte) error {
return errors.WithStack(err)
}

// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(hash, otherHash)
}

func CompareSSHA(_ context.Context, password []byte, hash []byte) error {
Expand Down Expand Up @@ -199,13 +178,7 @@ func CompareFirebaseScrypt(_ context.Context, password []byte, hash []byte) erro
stream.XORKeyStream(cipherText[aes.BlockSize:], signerKey)
otherHash := cipherText[aes.BlockSize:]

// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(hash, otherHash)
}

func CompareMD5(_ context.Context, password []byte, hash []byte) error {
Expand All @@ -223,13 +196,7 @@ func CompareMD5(_ context.Context, password []byte, hash []byte) error {
//#nosec G401 -- compatibility for imported passwords
otherHash := md5.Sum(arg)

// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash[:]) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(hash, otherHash[:])
}

var (
Expand Down Expand Up @@ -427,12 +394,7 @@ func compareSHAHelper(hasher string, raw []byte, hash []byte) error {
encodedHash := []byte(base64.StdEncoding.EncodeToString(hash))
newEncodedHash := []byte(base64.StdEncoding.EncodeToString(sha))

// Check that the contents of the hashed passwords are identical.
// subtle.ConstantTimeCompare() is used to help prevent timing attacks.
if subtle.ConstantTimeCompare(encodedHash, newEncodedHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
return comparePasswordHashConstantTime(encodedHash, newEncodedHash)
}

// decodeSSHAHash decodes SSHA[1|256|512] encoded password hash in usual {SSHA...} format.
Expand Down Expand Up @@ -558,3 +520,11 @@ func decodeMD5Hash(encodedHash string) (pf, salt, hash []byte, err error) {
return nil, nil, nil, ErrInvalidHash
}
}

func comparePasswordHashConstantTime(hash, otherHash []byte) error {
// use subtle.ConstantTimeCompare() to prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return nil
}
return errors.WithStack(ErrMismatchedHashAndPassword)
}
2 changes: 1 addition & 1 deletion hash/hasher_argon2.go
Expand Up @@ -57,7 +57,7 @@ func (h *Argon2) Generate(ctx context.Context, password []byte) ([]byte, error)
// Pass the plaintext password, salt and parameters to the argon2.IDKey
// function. This will generate a hash of the password using the Argon2id
// variant.
hash := argon2.IDKey([]byte(password), salt, p.Iterations, toKB(p.Memory), p.Parallelism, p.KeyLength)
hash := argon2.IDKey(password, salt, p.Iterations, toKB(p.Memory), p.Parallelism, p.KeyLength)

var b bytes.Buffer
if _, err := fmt.Fprintf(
Expand Down