package core import ( "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/binary" "encoding/hex" "fmt" "github.com/packwiz/packwiz/curseforge/murmur2" "hash" "strconv" "strings" ) // GetHashImpl gets an implementation of hash.Hash for the given hash type string func GetHashImpl(hashType string) (HashStringer, error) { switch strings.ToLower(hashType) { case "sha1": return hexStringer{sha1.New()}, nil case "sha256": return hexStringer{sha256.New()}, nil case "sha512": return hexStringer{sha512.New()}, nil case "md5": return hexStringer{md5.New()}, nil case "murmur2": // TODO: change to something indicating that this is the CF variant return number32As64Stringer{murmur2.New()}, nil case "length-bytes": // TODO: only used internally for now; should not be saved return number64Stringer{&LengthHasher{}}, nil } return nil, fmt.Errorf("hash implementation %s not found", hashType) } var preferredHashList = []string{ "murmur2", "md5", "sha1", "sha256", "sha512", } type HashStringer interface { hash.Hash HashToString([]byte) string } type hexStringer struct { hash.Hash } func (hexStringer) HashToString(data []byte) string { return hex.EncodeToString(data) } type number32As64Stringer struct { hash.Hash } func (number32As64Stringer) HashToString(data []byte) string { return strconv.FormatUint(uint64(binary.BigEndian.Uint32(data)), 10) } type number64Stringer struct { hash.Hash } func (number64Stringer) HashToString(data []byte) string { return strconv.FormatUint(binary.BigEndian.Uint64(data), 10) } type LengthHasher struct { length uint64 } func (h *LengthHasher) Write(p []byte) (n int, err error) { h.length += uint64(len(p)) return len(p), nil } func (h *LengthHasher) Sum(b []byte) []byte { ext := append(b, make([]byte, 8)...) binary.BigEndian.PutUint64(ext, h.length) return ext } func (h *LengthHasher) Size() int { return 8 } func (h *LengthHasher) BlockSize() int { return 1 } func (h *LengthHasher) Reset() { h.length = 0 }