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