package crypto // hash.go supplies a few geneeral hashing functions, using the hashing // algorithm blake2b. Because changing the hashing algorithm for Sia has much // stronger implications than changing any of the other algorithms, blake2b is // the only supported algorithm. Sia is not really flexible enough to support // multiple. import ( "bytes" "encoding/hex" "encoding/json" "errors" "hash" "github.com/NebulousLabs/Sia/encoding" "golang.org/x/crypto/blake2b" ) const ( // HashSize is the length of a Hash in bytes. HashSize = 32 ) type ( // Hash is a BLAKE2b 256-bit digest. Hash [HashSize]byte // HashSlice is used for sorting HashSlice []Hash ) var ( // ErrHashWrongLen is the error when encoded value has the wrong // length to be a hash. ErrHashWrongLen = errors.New("encoded value has the wrong length to be a hash") ) // NewHash returns a blake2b 256bit hasher. func NewHash() hash.Hash { h, _ := blake2b.New256(nil) // cannot fail with nil argument return h } // HashAll takes a set of objects as input, encodes them all using the encoding // package, and then hashes the result. func HashAll(objs ...interface{}) (hash Hash) { h := NewHash() enc := encoding.NewEncoder(h) for _, obj := range objs { enc.Encode(obj) } h.Sum(hash[:0]) return } // HashBytes takes a byte slice and returns the result. func HashBytes(data []byte) Hash { return Hash(blake2b.Sum256(data)) } // HashObject takes an object as input, encodes it using the encoding package, // and then hashes the result. func HashObject(obj interface{}) (hash Hash) { h := NewHash() encoding.NewEncoder(h).Encode(obj) h.Sum(hash[:0]) return } // These functions implement sort.Interface, allowing hashes to be sorted. func (hs HashSlice) Len() int { return len(hs) } func (hs HashSlice) Less(i, j int) bool { return bytes.Compare(hs[i][:], hs[j][:]) < 0 } func (hs HashSlice) Swap(i, j int) { hs[i], hs[j] = hs[j], hs[i] } // LoadString takes a string, parses the hash value of the string, and sets the // value of the hash equal to the hash value of the string. func (h *Hash) LoadString(s string) error { // *2 because there are 2 hex characters per byte. if len(s) != HashSize*2 { return ErrHashWrongLen } hBytes, err := hex.DecodeString(s) if err != nil { return errors.New("could not unmarshal hash: " + err.Error()) } copy(h[:], hBytes) return nil } // MarshalJSON marshales a hash as a hex string. func (h Hash) MarshalJSON() ([]byte, error) { return json.Marshal(h.String()) } // String prints the hash in hex. func (h Hash) String() string { return hex.EncodeToString(h[:]) } // UnmarshalJSON decodes the json hex string of the hash. func (h *Hash) UnmarshalJSON(b []byte) error { // *2 because there are 2 hex characters per byte. // +2 because the encoded JSON string has a `"` added at the beginning and end. if len(b) != HashSize*2+2 { return ErrHashWrongLen } // b[1 : len(b)-1] cuts off the leading and trailing `"` in the JSON string. hBytes, err := hex.DecodeString(string(b[1 : len(b)-1])) if err != nil { return errors.New("could not unmarshal hash: " + err.Error()) } copy(h[:], hBytes) return nil }