rano/util/crypto/hash.go
2024-11-18 20:53:13 +05:30

80 lines
1.7 KiB
Go

// Copyright 2024 Patial Tech. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package crypto
import (
"bytes"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"log/slog"
"math/big"
"gitserver.in/patialtech/rano/util/logger"
"golang.org/x/crypto/argon2"
)
func MD5(b []byte) string {
hash := md5.Sum(b)
return hex.EncodeToString(hash[:])
}
func MD5Int(b []byte) uint64 {
n := new(big.Int)
n.SetString(MD5(b), 16)
return n.Uint64()
}
// Password using Argon2id
//
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
func PasswordHash(pwd string) (hash, salt string, err error) {
var sl []byte
sl, err = randomSecret(32)
if err != nil {
return
}
// Generate hash
h := argon2.IDKey([]byte(pwd), sl, 3, 12288, 1, 32)
hash = base64.StdEncoding.EncodeToString(h)
salt = base64.StdEncoding.EncodeToString(sl)
return
}
// ComparePassword
func ComparePasswordHash(pwd, hash, salt string) bool {
var h, s []byte
var err error
if h, err = base64.StdEncoding.DecodeString(hash); err != nil {
logger.Error(err, slog.String("ref", "util/crypto.ComparePasswordHash decode hash"))
}
if s, err = base64.StdEncoding.DecodeString(salt); err != nil {
logger.Error(err, slog.String("ref", "util/crypto.ComparePasswordHash decode salt"))
}
// Generate hash for comparison.
ph := argon2.IDKey([]byte(pwd), s, 3, 12288, 1, 32)
// Compare the generated hash with the stored hash.
// If they don't match return error.
return bytes.Equal(h, ph)
}
func randomSecret(length uint32) ([]byte, error) {
secret := make([]byte, length)
_, err := rand.Read(secret)
if err != nil {
return nil, err
}
return secret, nil
}