shake.go raw

   1  // Copyright 2014 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package sha3
   6  
   7  import (
   8  	"crypto/sha3"
   9  	"hash"
  10  	"io"
  11  )
  12  
  13  // ShakeHash defines the interface to hash functions that support
  14  // arbitrary-length output. When used as a plain [hash.Hash], it
  15  // produces minimum-length outputs that provide full-strength generic
  16  // security.
  17  type ShakeHash interface {
  18  	hash.Hash
  19  
  20  	// Read reads more output from the hash; reading affects the hash's
  21  	// state. (ShakeHash.Read is thus very different from Hash.Sum.)
  22  	// It never returns an error, but subsequent calls to Write or Sum
  23  	// will panic.
  24  	io.Reader
  25  
  26  	// Clone returns a copy of the ShakeHash in its current state.
  27  	Clone() ShakeHash
  28  }
  29  
  30  // NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
  31  // Its generic security strength is 128 bits against all attacks if at
  32  // least 32 bytes of its output are used.
  33  func NewShake128() ShakeHash {
  34  	return &shakeWrapper{sha3.NewSHAKE128(), 32, false, sha3.NewSHAKE128}
  35  }
  36  
  37  // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
  38  // Its generic security strength is 256 bits against all attacks if
  39  // at least 64 bytes of its output are used.
  40  func NewShake256() ShakeHash {
  41  	return &shakeWrapper{sha3.NewSHAKE256(), 64, false, sha3.NewSHAKE256}
  42  }
  43  
  44  // NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
  45  // a customizable variant of SHAKE128.
  46  // N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
  47  // desired. S is a customization byte string used for domain separation - two cSHAKE
  48  // computations on same input with different S yield unrelated outputs.
  49  // When N and S are both empty, this is equivalent to NewShake128.
  50  func NewCShake128(N, S []byte) ShakeHash {
  51  	return &shakeWrapper{sha3.NewCSHAKE128(N, S), 32, false, func() *sha3.SHAKE {
  52  		return sha3.NewCSHAKE128(N, S)
  53  	}}
  54  }
  55  
  56  // NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
  57  // a customizable variant of SHAKE256.
  58  // N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
  59  // desired. S is a customization byte string used for domain separation - two cSHAKE
  60  // computations on same input with different S yield unrelated outputs.
  61  // When N and S are both empty, this is equivalent to NewShake256.
  62  func NewCShake256(N, S []byte) ShakeHash {
  63  	return &shakeWrapper{sha3.NewCSHAKE256(N, S), 64, false, func() *sha3.SHAKE {
  64  		return sha3.NewCSHAKE256(N, S)
  65  	}}
  66  }
  67  
  68  // ShakeSum128 writes an arbitrary-length digest of data into hash.
  69  func ShakeSum128(hash, data []byte) {
  70  	h := NewShake128()
  71  	h.Write(data)
  72  	h.Read(hash)
  73  }
  74  
  75  // ShakeSum256 writes an arbitrary-length digest of data into hash.
  76  func ShakeSum256(hash, data []byte) {
  77  	h := NewShake256()
  78  	h.Write(data)
  79  	h.Read(hash)
  80  }
  81  
  82  // shakeWrapper adds the Size, Sum, and Clone methods to a sha3.SHAKE
  83  // to implement the ShakeHash interface.
  84  type shakeWrapper struct {
  85  	*sha3.SHAKE
  86  	outputLen int
  87  	squeezing bool
  88  	newSHAKE  func() *sha3.SHAKE
  89  }
  90  
  91  func (w *shakeWrapper) Read(p []byte) (n int, err error) {
  92  	w.squeezing = true
  93  	return w.SHAKE.Read(p)
  94  }
  95  
  96  func (w *shakeWrapper) Clone() ShakeHash {
  97  	s := w.newSHAKE()
  98  	b, err := w.MarshalBinary()
  99  	if err != nil {
 100  		panic(err) // unreachable
 101  	}
 102  	if err := s.UnmarshalBinary(b); err != nil {
 103  		panic(err) // unreachable
 104  	}
 105  	return &shakeWrapper{s, w.outputLen, w.squeezing, w.newSHAKE}
 106  }
 107  
 108  func (w *shakeWrapper) Size() int { return w.outputLen }
 109  
 110  func (w *shakeWrapper) Sum(b []byte) []byte {
 111  	if w.squeezing {
 112  		panic("sha3: Sum after Read")
 113  	}
 114  	out := make([]byte, w.outputLen)
 115  	// Clone the state so that we don't affect future Write calls.
 116  	s := w.Clone()
 117  	s.Read(out)
 118  	return append(b, out...)
 119  }
 120