hasher256.go raw

   1  // Copyright (c) 2024 The Decred developers
   2  // Use of this source code is governed by an ISC
   3  // license that can be found in the LICENSE file.
   4  //
   5  // Main Go code originally written and optimized by Dave Collins May 2020.
   6  // Additional cleanup and comments added July 2024.
   7  
   8  package blake256
   9  
  10  import (
  11  	"hash"
  12  )
  13  
  14  // iv256 is the BLAKE-256 initialization vector.
  15  var iv256 = [8]uint32{
  16  	0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
  17  	0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
  18  }
  19  
  20  // statePrefix256 is the prefix used when serializing the intermediate state to
  21  // identify the state as belonging to a BLAKE-256 rolling hash.  It is the
  22  // second value in iv256.
  23  const statePrefix256 = 0xbb67ae85
  24  
  25  // Hasher256 provides a zero-allocation implementation to compute a rolling
  26  // BLAKE-256 checksum.
  27  //
  28  // It can safely be copied at any point to save its intermediate state for use
  29  // in additional processing later, without having to write the previously
  30  // written data again.
  31  //
  32  // In addition to the aforementioned in-process state saving capability, it also
  33  // supports serializing the intermediate state to enable sharing across process
  34  // boundaries.
  35  //
  36  // It is effectively a mix of a [hash.Hash], [encoding.BinaryMarshaler], and
  37  // [encoding.BinaryUnmarshaler] with a modified API that enables zero
  38  // allocations and also provides additional convenience funcs for writing
  39  // integers encoded with both big and little endian as well as writing
  40  // individual bytes.
  41  //
  42  // However, it also implements [hash.Hash], [encoding.BinaryMarshaler], and
  43  // [encoding.BinaryUnmarshaler] for callers that aren't as concerned about
  44  // reducing allocations and would prefer to use it with the aforementioned
  45  // standard library interfaces.
  46  //
  47  // NOTE: The zero value is NOT safe to use.  It must be initialized via
  48  // NewHasher256.
  49  type Hasher256 struct {
  50  	h hasher
  51  }
  52  
  53  // Write adds the given bytes to the rolling hash.
  54  //
  55  // NOTE: This method only returns an error in order to satisfy the [io.Writer]
  56  // and [hash.Hash] interfaces.  However, it will never error, meaning the error
  57  // will always be nil, so it is safe to ignore.
  58  //
  59  // Callers may optionally choose to call [WriteBytes] which does not return an
  60  // error to make the fact writing can never fail.
  61  func (h *Hasher256) Write(b []byte) (int, error) {
  62  	return h.h.write(b)
  63  }
  64  
  65  // WriteBytes adds the given bytes to the rolling hash.
  66  //
  67  // This method is identical to [Write] except it does not return an error in
  68  // order to make it clear that writing can never fail.
  69  func (h *Hasher256) WriteBytes(b []byte) {
  70  	h.h.write(b)
  71  }
  72  
  73  // WriteByte adds the given byte to the rolling hash.
  74  func (h *Hasher256) WriteByte(b byte) {
  75  	h.h.writeByte(b)
  76  }
  77  
  78  // WriteString adds the given string to the rolling hash.
  79  func (h *Hasher256) WriteString(s string) {
  80  	h.h.writeString(s)
  81  }
  82  
  83  // WriteUint16LE encodes the given unsigned 16-bit integer as a 2-byte
  84  // little-endian byte sequence and adds it to the rolling hash.
  85  func (h *Hasher256) WriteUint16LE(val uint16) {
  86  	h.h.writeUint16LE(val)
  87  }
  88  
  89  // WriteUint16BE encodes the given unsigned 16-bit integer as a 2-byte
  90  // big-endian byte sequence and adds it to the rolling hash.
  91  func (h *Hasher256) WriteUint16BE(val uint16) {
  92  	h.h.writeUint16BE(val)
  93  }
  94  
  95  // WriteUint32LE encodes the given unsigned 32-bit integer as a 4-byte
  96  // little-endian byte sequence and adds it to the rolling hash.
  97  func (h *Hasher256) WriteUint32LE(val uint32) {
  98  	h.h.writeUint32LE(val)
  99  }
 100  
 101  // WriteUint32BE encodes the given unsigned 32-bit integer as a 4-byte
 102  // big-endian byte sequence and adds it to the rolling hash.
 103  func (h *Hasher256) WriteUint32BE(val uint32) {
 104  	h.h.writeUint32BE(val)
 105  }
 106  
 107  // WriteUint64LE encodes the given unsigned 64-bit integer as an 8-byte
 108  // little-endian byte sequence and adds it to the rolling hash.
 109  func (h *Hasher256) WriteUint64LE(val uint64) {
 110  	h.h.writeUint64LE(val)
 111  }
 112  
 113  // WriteUint64BE encodes the given unsigned 64-bit integer as an 8-byte
 114  // big-endian byte sequence and adds it to the rolling hash.
 115  func (h *Hasher256) WriteUint64BE(val uint64) {
 116  	h.h.writeUint64BE(val)
 117  }
 118  
 119  // Reset resets the state of the rolling hash.
 120  //
 121  // This is part of the [hash.Hash] interface.
 122  func (h *Hasher256) Reset() {
 123  	h.h.reset(iv256)
 124  }
 125  
 126  // Size returns the size of a BLAKE-256 hash in bytes.
 127  //
 128  // This is part of the [hash.Hash] interface.
 129  func (h *Hasher256) Size() int {
 130  	return Size
 131  }
 132  
 133  // BlockSize returns the underlying block size of the BLAKE-256 hashing
 134  // algorithm.
 135  //
 136  // This is part of the [hash.Hash] interface.
 137  func (h *Hasher256) BlockSize() int {
 138  	return BlockSize
 139  }
 140  
 141  // Sum finalizes the rolling hash, appends the resulting checksum to the
 142  // provided slice and returns the resulting slice.  It does not change the
 143  // underlying hash state.
 144  //
 145  // Note that allocations can often be avoided by providing a slice that has
 146  // enough capacity to house the resulting checksum.  For example:
 147  //
 148  //	digest := make([]byte, blake256.Size)
 149  //	h := blake256.NewHasher256()
 150  //	h.WriteUint64LE(1)
 151  //	digest = h.Sum(digest[:0])
 152  //
 153  // This is part of the [hash.Hash] interface.
 154  func (h Hasher256) Sum(b []byte) []byte {
 155  	// Note h is a copy so that the caller can keep writing and summing.
 156  	sum := h.h.finalize256()
 157  	return append(b, sum[:]...)
 158  }
 159  
 160  // Sum256 finalizes the rolling hash and returns the resulting checksum.  It
 161  // does not change the underlying hash state.
 162  func (h Hasher256) Sum256() [Size]byte {
 163  	// Note h is a copy so that the caller can keep writing and summing.
 164  	return h.h.finalize256()
 165  }
 166  
 167  // SaveState appends the current intermediate state of the rolling hash, as
 168  // generated by [Hasher256.MarshalBinary], to the provided slice and returns the
 169  // resulting slice.  It does not change the underlying hash state.
 170  //
 171  // The resulting serialized data may be used to resume from the current
 172  // intermediate state later without having to write the previously written data
 173  // again by providing it to [Hasher256.UnmarshalBinary].
 174  //
 175  // As described by the [Hasher256] documentation, the hasher instance can simply
 176  // be copied to achieve the same result much more efficiently when the caller is
 177  // able to keep a copy.  Therefore, that approach should be preferred when
 178  // possible.
 179  //
 180  // However, the ability to serialize the state is also provided to enable
 181  // sharing it across process boundaries.
 182  //
 183  // Note that allocations can typically be avoided by providing a slice that has
 184  // enough capacity to house the resulting state as defined by the
 185  // [SavedStateSize] constant.  For example:
 186  //
 187  //	state := make([]byte, blake256.SavedStateSize)
 188  //	h := blake256.NewHasher256()
 189  //	h.WriteUint64LE(1)
 190  //	state = h.SaveState(state[:0])
 191  func (h *Hasher256) SaveState(target []byte) []byte {
 192  	return h.h.saveState(target, statePrefix256)
 193  }
 194  
 195  // MarshalBinary returns the intermediate state of the rolling hash serialized
 196  // into a binary form that may be used to resume from the current state later
 197  // without having to write the previously written data again.  It does not
 198  // change the underlying hash state.
 199  //
 200  // As described by the [Hasher256] documentation, the hasher instance can simply
 201  // be copied to achieve the same result much more efficiently when the caller is
 202  // able to keep a copy.  Therefore, that approach should be preferred when
 203  // possible.
 204  //
 205  // However, the ability to serialize the state is also provided to enable
 206  // sharing it across process boundaries.
 207  //
 208  // NOTE: This method only returns an error in order to satisfy the
 209  // [encoding.BinaryMarshaler] interface.  However, it will never error, meaning
 210  // the error will always be nil, so it is safe to ignore.
 211  //
 212  // Callers that wish to avoid allocations should prefer [Hasher256.SaveState]
 213  // instead.
 214  func (h *Hasher256) MarshalBinary() ([]byte, error) {
 215  	var state [SavedStateSize]byte
 216  	h.h.putSavedState(state[:], statePrefix256)
 217  	return state[:], nil
 218  }
 219  
 220  // UnmarshalBinary restores the rolling hash to the provided serialized
 221  // intermediate state.  See [Hasher256.MarshalBinary] for more details.
 222  //
 223  // [ErrMalformedState] will be returned when the provided serialized state is
 224  // not at least the required [SavedStateSize] number of bytes.
 225  //
 226  // [ErrMismatchedState] will be returned if the provided state is not for a
 227  // BLAKE-256 hash.  For example, it will be returned when attempting to restore
 228  // a BLAKE-224 intermediate state.
 229  //
 230  // This implements the [encoding.BinaryUnmarshaler] interface.
 231  func (h *Hasher256) UnmarshalBinary(state []byte) error {
 232  	return h.h.loadState(state, statePrefix256)
 233  }
 234  
 235  // NewHasher256 returns a zero-allocation hasher for computing a rolling
 236  // BLAKE-256 checksum.
 237  func NewHasher256() *Hasher256 {
 238  	h := Hasher256{makeHasher(iv256)}
 239  	return &h
 240  }
 241  
 242  // NewHasher256Salt returns a zero-allocation hasher for computing a rolling
 243  // BLAKE-256 checksum initialized with the given 16-byte salt slice.
 244  //
 245  // It will panic if the provided salt is not 16 bytes.
 246  func NewHasher256Salt(salt []byte) *Hasher256 {
 247  	h := Hasher256{makeHasher(iv256)}
 248  	h.h.initializeSalt(salt)
 249  	return &h
 250  }
 251  
 252  // New returns a new [hash.Hash] computing the BLAKE-256 checksum.
 253  //
 254  // Callers should prefer [NewHasher256] instead since it returns a concrete type
 255  // that has more functionality and allows avoiding additional allocations.  It
 256  // can also be used as a [hash.Hash] if desired.
 257  func New() hash.Hash {
 258  	return NewHasher256()
 259  }
 260  
 261  // NewSalt returns a new [hash.Hash] computing the BLAKE-256 checksum
 262  // initialized with the given 16-byte salt.
 263  //
 264  // It will panic if the provided salt is not 16 bytes.
 265  //
 266  // Callers should prefer [NewHasher256Salt] instead since it returns a concrete
 267  // type that has more functionality and allows avoiding additional allocations.
 268  // It can also be used as a [hash.Hash] if desired.
 269  func NewSalt(salt []byte) hash.Hash {
 270  	return NewHasher256Salt(salt)
 271  }
 272  
 273  // Sum256 returns the BLAKE-256 checksum of the data.
 274  func Sum256(data []byte) [Size]byte {
 275  	h := makeHasher(iv256)
 276  	h.write(data)
 277  	return h.finalize256()
 278  }
 279