gob/secret/hash.go

143 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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))
}