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  }