goutils/ahash/ahash.go

264 lines
5.8 KiB
Go

// Package ahash provides support for hashing data with a selectable
//
// hash function.
package ahash
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"hash"
"hash/adler32"
"hash/crc32"
"hash/crc64"
"hash/fnv"
"io"
"sort"
"git.wntrmute.dev/kyle/goutils/assert"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/md4"
"golang.org/x/crypto/ripemd160"
"golang.org/x/crypto/sha3"
)
func sha224Slicer(bs []byte) []byte {
sum := sha256.Sum224(bs)
return sum[:]
}
func sha256Slicer(bs []byte) []byte {
sum := sha256.Sum256(bs)
return sum[:]
}
func sha384Slicer(bs []byte) []byte {
sum := sha512.Sum384(bs)
return sum[:]
}
func sha512Slicer(bs []byte) []byte {
sum := sha512.Sum512(bs)
return sum[:]
}
// Hash represents a generic hash function that may or may not be secure. It
// satisfies the hash.Hash interface.
type Hash struct {
hash.Hash
secure bool
algo string
}
// HashAlgo returns the name of the underlying hash algorithm.
func (h *Hash) HashAlgo() string {
return h.algo
}
// IsSecure returns true if the Hash is a cryptographic hash.
func (h *Hash) IsSecure() bool {
return h.secure
}
// Sum32 returns true if the underlying hash is a 32-bit hash; if is, the
// uint32 parameter will contain the hash.
func (h *Hash) Sum32() (uint32, bool) {
h32, ok := h.Hash.(hash.Hash32)
if !ok {
return 0, false
}
return h32.Sum32(), true
}
// IsHash32 returns true if the underlying hash is a 32-bit hash function.
func (h *Hash) IsHash32() bool {
_, ok := h.Hash.(hash.Hash32)
return ok
}
// Sum64 returns true if the underlying hash is a 64-bit hash; if is, the
// uint64 parameter will contain the hash.
func (h *Hash) Sum64() (uint64, bool) {
h64, ok := h.Hash.(hash.Hash64)
if !ok {
return 0, false
}
return h64.Sum64(), true
}
// IsHash64 returns true if the underlying hash is a 64-bit hash function.
func (h *Hash) IsHash64() bool {
_, ok := h.Hash.(hash.Hash64)
return ok
}
func blakeFunc(bf func(key []byte) (hash.Hash, error)) func() hash.Hash {
return func() hash.Hash {
h, err := bf(nil)
assert.NoError(err, "while constructing a BLAKE2 hash function")
return h
}
}
var secureHashes = map[string]func() hash.Hash{
"ripemd160": ripemd160.New,
"sha224": sha256.New224,
"sha256": sha256.New,
"sha384": sha512.New384,
"sha512": sha512.New,
"sha3-224": sha3.New224,
"sha3-256": sha3.New256,
"sha3-384": sha3.New384,
"sha3-512": sha3.New512,
"blake2s-256": blakeFunc(blake2s.New256),
"blake2b-256": blakeFunc(blake2b.New256),
"blake2b-384": blakeFunc(blake2b.New384),
"blake2b-512": blakeFunc(blake2b.New512),
}
func newHash32(f func() hash.Hash32) func() hash.Hash {
return func() hash.Hash {
return f()
}
}
func newHash64(f func() hash.Hash64) func() hash.Hash {
return func() hash.Hash {
return f()
}
}
func newCRC64(tab uint64) func() hash.Hash {
return newHash64(
func() hash.Hash64 {
return crc64.New(crc64.MakeTable(tab))
})
}
var insecureHashes = map[string]func() hash.Hash{
"md4": md4.New,
"md5": md5.New,
"sha1": sha1.New,
"adler32": newHash32(adler32.New),
"crc32-ieee": newHash32(crc32.NewIEEE),
"crc64": newCRC64(crc64.ISO),
"crc64-ecma": newCRC64(crc64.ECMA),
"fnv1-32a": newHash32(fnv.New32a),
"fnv1-32": newHash32(fnv.New32),
"fnv1-64a": newHash64(fnv.New64a),
"fnv1-64": newHash64(fnv.New64),
}
// New returns a new Hash for the specified algorithm.
func New(algo string) (*Hash, error) {
h := &Hash{algo: algo}
hf, ok := secureHashes[algo]
if ok {
h.Hash = hf()
h.secure = true
return h, nil
}
hf, ok = insecureHashes[algo]
if ok {
h.Hash = hf()
h.secure = false
return h, nil
}
return nil, errors.New("chash: unsupport hash algorithm " + algo)
}
// Sum returns the digest (not the hex digest) of the data using the given
// algorithm.
func Sum(algo string, data []byte) ([]byte, error) {
h, err := New(algo)
if err != nil {
return nil, err
}
_, err = h.Write(data)
if err != nil {
return nil, err
}
return h.Sum(nil), nil
}
// SumReader reads all the data from the given io.Reader and returns the
// digest (not the hex digest) from the specified algorithm.
func SumReader(algo string, r io.Reader) ([]byte, error) {
h, err := New(algo)
if err != nil {
return nil, err
}
_, err = io.Copy(h, r)
if err != nil {
return nil, err
}
return h.Sum(nil), nil
}
// SumLimitedReader reads n bytes of data from the io.reader and returns the
// digest (not the hex digest) from the specified algorithm.
func SumLimitedReader(algo string, r io.Reader, n int64) ([]byte, error) {
limit := &io.LimitedReader{
R: r,
N: n,
}
return SumReader(algo, limit)
}
var insecureHashList, secureHashList, hashList []string
func init() {
shl := len(secureHashes) // secure hash list length
ihl := len(insecureHashes) // insecure hash list length
ahl := shl + ihl // all hash list length
insecureHashList = make([]string, 0, ihl)
secureHashList = make([]string, 0, shl)
hashList = make([]string, 0, ahl)
for algo := range insecureHashes {
insecureHashList = append(insecureHashList, algo)
}
sort.Strings(insecureHashList)
for algo := range secureHashes {
secureHashList = append(secureHashList, algo)
}
sort.Strings(secureHashList)
hashList = append(hashList, insecureHashList...)
hashList = append(hashList, secureHashList...)
sort.Strings(hashList)
}
// HashList returns a sorted list of all the hash algorithms supported by the
// package.
func HashList() []string {
return hashList[:]
}
// SecureHashList returns a sorted list of all the secure (cryptographic) hash
// algorithms supported by the package.
func SecureHashList() []string {
return secureHashList[:]
}
// InsecureHashList returns a sorted list of all the insecure hash algorithms
// supported by the package.
func InsecureHashList() []string {
return insecureHashList[:]
}