143 lines
3.3 KiB
Go
143 lines
3.3 KiB
Go
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 qu’une 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 d’un 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 d’entrée en utilisant l’algorithme demandé.
|
||
// Si une longueur de sel est fournie, le hachage est créé à partir
|
||
// d’un 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 d’une donnée d’entré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 d’origine 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))
|
||
}
|