github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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  	"github.com/NebulousLabs/Sia/encoding"
    17  
    18  	"github.com/dchest/blake2b"
    19  )
    20  
    21  const (
    22  	HashSize = 32
    23  )
    24  
    25  type (
    26  	Hash [HashSize]byte
    27  
    28  	// HashSlice is used for sorting
    29  	HashSlice []Hash
    30  )
    31  
    32  var (
    33  	ErrHashWrongLen = errors.New("encoded value has the wrong length to be a hash")
    34  )
    35  
    36  // NewHash returns a blake2b 256bit hasher.
    37  func NewHash() hash.Hash {
    38  	return blake2b.New256()
    39  }
    40  
    41  // HashAll takes a set of objects as input, encodes them all using the encoding
    42  // package, and then hashes the result.
    43  func HashAll(objs ...interface{}) Hash {
    44  	// Ideally we would just write HashBytes(encoding.MarshalAll(objs)).
    45  	// Unfortunately, you can't pass 'objs' to MarshalAll without losing its
    46  	// type information; MarshalAll would just see interface{}s.
    47  	var b []byte
    48  	for _, obj := range objs {
    49  		b = append(b, encoding.Marshal(obj)...)
    50  	}
    51  	return HashBytes(b)
    52  }
    53  
    54  // HashBytes takes a byte slice and returns the result.
    55  func HashBytes(data []byte) Hash {
    56  	return Hash(blake2b.Sum256(data))
    57  }
    58  
    59  // HashObject takes an object as input, encodes it using the encoding package,
    60  // and then hashes the result.
    61  func HashObject(obj interface{}) Hash {
    62  	return HashBytes(encoding.Marshal(obj))
    63  }
    64  
    65  // These functions implement sort.Interface, allowing hashes to be sorted.
    66  func (hs HashSlice) Len() int           { return len(hs) }
    67  func (hs HashSlice) Less(i, j int) bool { return bytes.Compare(hs[i][:], hs[j][:]) < 0 }
    68  func (hs HashSlice) Swap(i, j int)      { hs[i], hs[j] = hs[j], hs[i] }
    69  
    70  // MarshalJSON marshales a hash as a hex string.
    71  func (h Hash) MarshalJSON() ([]byte, error) {
    72  	return json.Marshal(h.String())
    73  }
    74  
    75  // String prints the hash in hex.
    76  func (h Hash) String() string {
    77  	return hex.EncodeToString(h[:])
    78  }
    79  
    80  // UnmarshalJSON decodes the json hex string of the hash.
    81  func (h *Hash) UnmarshalJSON(b []byte) error {
    82  	// *2 because there are 2 hex characters per byte.
    83  	// +2 because the encoded JSON string has a `"` added at the beginning and end.
    84  	if len(b) != HashSize*2+2 {
    85  		return ErrHashWrongLen
    86  	}
    87  
    88  	// b[1 : len(b)-1] cuts off the leading and trailing `"` in the JSON string.
    89  	hBytes, err := hex.DecodeString(string(b[1 : len(b)-1]))
    90  	if err != nil {
    91  		return errors.New("could not unmarshal crypto.Hash: " + err.Error())
    92  	}
    93  	copy(h[:], hBytes)
    94  	return nil
    95  }