gob/secret/hash.go

143 lines
3.3 KiB
Go
Raw Permalink Normal View History

2023-09-23 20:15:37 +00:00
package secret
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
. "gitea.zaclys.com/bvaudour/gob/option"
"gitea.zaclys.com/bvaudour/gob/random"
)
func errAlgorithm(algorithm string) error {
return fmt.Errorf("unknown algorithm: %s", algorithm)
}
// Hasher est un utilitaire permettant de hasher une chaîne et de vérifier quune chaîne correspond bien à un hashage.
type Hasher interface {
Hash(data []byte) (hashed []byte)
Compare(data []byte, hashed []byte) bool
}
type hasher struct {
h hash.Hash
}
func (h hasher) Hash(data []byte) []byte {
h.h.Write(data)
return h.h.Sum(nil)
}
func (h hasher) Compare(data []byte, hashed []byte) bool {
return string(h.Hash(data)) == string(hashed)
}
// HashResult est le résultat dun hash.
type HashResult[T ~[]byte | ~string] struct {
Hash T
Salt T
}
func hashOf(data []byte, saltLen int, h Hasher) (result HashResult[[]byte]) {
l := len(data)
in := make([]byte, l+saltLen)
result.Salt = random.BytesKey(saltLen, random.All)
copy(in[:l], data)
copy(in[l:], result.Salt)
result.Hash = h.Hash(in)
return
}
// HashOf génère le hash et un sel aléatoire à une chaîne donnée.
func HashOf[T ~[]byte | ~string](data T, saltLen int, h Hasher) (result HashResult[T]) {
rb := hashOf([]byte(data), saltLen, h)
result.Hash, result.Salt = T(rb.Hash), T(rb.Salt)
return
}
const (
MD5 = "md5"
SHA1 = "sha1"
SHA256 = "sha256"
SHA512 = "sha512"
BCRYPT = "bcrypt"
)
var (
hashers = map[string]Hasher{
MD5: hasher{md5.New()},
SHA1: hasher{sha1.New()},
SHA256: hasher{sha256.New()},
SHA512: hasher{sha512.New()},
BCRYPT: NewBcrypt(14),
}
)
// AvailableHashAlgorithms retourne la liste des noms des algorithmes de hachage supportés.
func AvailableHashAlgorithms() (out []string) {
for h := range hashers {
out = append(out, h)
}
return
}
// IsHashAlgorithmAvailable vérifie si le nom fournit en entrée fait partie des algorithmes de hachage supportés.
func IsHashAlgorithmAvailable(name string) bool {
_, exists := hashers[name]
return exists
}
// AddHashAlgorithm ajoute un algorithme de hashage à disposition.
func AddHashAlgorithm(name string, hasher Hasher) {
hashers[name] = hasher
}
// Hash chiffre la donnée dentrée en utilisant lalgorithme demandé.
// Si une longueur de sel est fournie, le hachage est créé à partir
// dun sel défini aléatoirement.
// Les algorithmes supportés par défaut sont :
// - md5
// - sha1
// - sha256
// - sha512
// - bcrypt
func Hash[T ~[]byte | ~string](data T, algorithm string, saltLen ...int) (result Result[HashResult[T]]) {
h, ok := hashers[algorithm]
if !ok {
return Err[HashResult[T]](errAlgorithm(algorithm))
}
sl := 0
if len(saltLen) > 0 && saltLen[0] > 0 {
sl = saltLen[0]
}
r := HashOf(data, sl, h)
return Ok(r)
}
// CheckHash vérifie le hash dune donnée dentrée avec un algorithme de hashage.
// Si le hash a été généré avec du sel, la chaîne doit être la concaténation
// de la chaîne dorigine et du sel.
func CheckHash[T ~[]byte | ~string](data T, hash HashResult[T], algorithm string) bool {
h, ok := hashers[algorithm]
if !ok {
return false
}
ld, ls := len(data), len(hash.Salt)
in := make([]byte, ld+ls)
copy(in[:ld], []byte(data))
copy(in[ld:], []byte(hash.Salt))
return h.Compare(in, []byte(hash.Hash))
}