gitlab.com/SiaPrime/SiaPrime@v1.4.1/crypto/hash.go (about) 1 package crypto 2 3 // hash.go supplies a few geneeral hashing functions, using the hashing 4 // algorithm blake2b. Because changing the hashing algorithm for Sia has much 5 // stronger implications than changing any of the other algorithms, blake2b is 6 // the only supported algorithm. Sia is not really flexible enough to support 7 // multiple. 8 9 import ( 10 "bytes" 11 "encoding/hex" 12 "encoding/json" 13 "errors" 14 "hash" 15 16 "gitlab.com/SiaPrime/SiaPrime/encoding" 17 18 "golang.org/x/crypto/blake2b" 19 ) 20 21 const ( 22 // HashSize is the length of a Hash in bytes. 23 HashSize = 32 24 ) 25 26 type ( 27 // Hash is a BLAKE2b 256-bit digest. 28 Hash [HashSize]byte 29 30 // HashSlice is used for sorting 31 HashSlice []Hash 32 ) 33 34 var ( 35 // ErrHashWrongLen is the error when encoded value has the wrong 36 // length to be a hash. 37 ErrHashWrongLen = errors.New("encoded value has the wrong length to be a hash") 38 ) 39 40 // NewHash returns a blake2b 256bit hasher. 41 func NewHash() hash.Hash { 42 h, _ := blake2b.New256(nil) // cannot fail with nil argument 43 return h 44 } 45 46 // HashAll takes a set of objects as input, encodes them all using the encoding 47 // package, and then hashes the result. 48 func HashAll(objs ...interface{}) (hash Hash) { 49 h := NewHash() 50 enc := encoding.NewEncoder(h) 51 for _, obj := range objs { 52 enc.Encode(obj) 53 } 54 h.Sum(hash[:0]) 55 return 56 } 57 58 // HashBytes takes a byte slice and returns the result. 59 func HashBytes(data []byte) Hash { 60 return Hash(blake2b.Sum256(data)) 61 } 62 63 // HashObject takes an object as input, encodes it using the encoding package, 64 // and then hashes the result. 65 func HashObject(obj interface{}) (hash Hash) { 66 h := NewHash() 67 encoding.NewEncoder(h).Encode(obj) 68 h.Sum(hash[:0]) 69 return 70 } 71 72 // These functions implement sort.Interface, allowing hashes to be sorted. 73 func (hs HashSlice) Len() int { return len(hs) } 74 func (hs HashSlice) Less(i, j int) bool { return bytes.Compare(hs[i][:], hs[j][:]) < 0 } 75 func (hs HashSlice) Swap(i, j int) { hs[i], hs[j] = hs[j], hs[i] } 76 77 // LoadString takes a string, parses the hash value of the string, and sets the 78 // value of the hash equal to the hash value of the string. 79 func (h *Hash) LoadString(s string) error { 80 // *2 because there are 2 hex characters per byte. 81 if len(s) != HashSize*2 { 82 return ErrHashWrongLen 83 } 84 hBytes, err := hex.DecodeString(s) 85 if err != nil { 86 return errors.New("could not unmarshal hash: " + err.Error()) 87 } 88 copy(h[:], hBytes) 89 return nil 90 } 91 92 // MarshalJSON marshales a hash as a hex string. 93 func (h Hash) MarshalJSON() ([]byte, error) { 94 return json.Marshal(h.String()) 95 } 96 97 // String prints the hash in hex. 98 func (h Hash) String() string { 99 return hex.EncodeToString(h[:]) 100 } 101 102 // UnmarshalJSON decodes the json hex string of the hash. 103 func (h *Hash) UnmarshalJSON(b []byte) error { 104 // *2 because there are 2 hex characters per byte. 105 // +2 because the encoded JSON string has a `"` added at the beginning and end. 106 if len(b) != HashSize*2+2 { 107 return ErrHashWrongLen 108 } 109 110 // b[1 : len(b)-1] cuts off the leading and trailing `"` in the JSON string. 111 hBytes, err := hex.DecodeString(string(b[1 : len(b)-1])) 112 if err != nil { 113 return errors.New("could not unmarshal hash: " + err.Error()) 114 } 115 copy(h[:], hBytes) 116 return nil 117 }