"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
+ "errors"
"fmt"
"hash"
"testing"
}
}
+func TestNoClone(t *testing.T) {
+ h := New(func() hash.Hash { return justHash{sha256.New()} }, []byte("key"))
+ if _, ok := h.(hash.Cloner); !ok {
+ t.Skip("no Cloner support")
+ }
+ h.Write([]byte("test"))
+ _, err := h.(hash.Cloner).Clone()
+ if !errors.Is(err, errors.ErrUnsupported) {
+ t.Errorf("Clone() = %v, want ErrUnsupported", err)
+ }
+}
+
func TestNonUniqueHash(t *testing.T) {
if boring.Enabled {
t.Skip("hash.Hash provided by boringcrypto are not comparable")
h.marshaled = true
}
+type errCloneUnsupported struct{}
+
+func (e errCloneUnsupported) Error() string {
+ return "crypto/hmac: hash does not support hash.Cloner"
+}
+
+func (e errCloneUnsupported) Unwrap() error {
+ return errors.ErrUnsupported
+}
+
// Clone implements [hash.Cloner] if the underlying hash does.
-// Otherwise, it returns [errors.ErrUnsupported].
+// Otherwise, it returns an error wrapping [errors.ErrUnsupported].
func (h *HMAC) Clone() (hash.Cloner, error) {
r := *h
ic, ok := h.inner.(hash.Cloner)
if !ok {
- return nil, errors.ErrUnsupported
+ return nil, errCloneUnsupported{}
}
oc, ok := h.outer.(hash.Cloner)
if !ok {
- return nil, errors.ErrUnsupported
+ return nil, errCloneUnsupported{}
}
var err error
r.inner, err = ic.Clone()
if err != nil {
- return nil, errors.ErrUnsupported
+ return nil, errCloneUnsupported{}
}
r.outer, err = oc.Clone()
if err != nil {
- return nil, errors.ErrUnsupported
+ return nil, errCloneUnsupported{}
}
return &r, nil
}
Sum64() uint64
}
-// A Cloner is a hash function whose state can be cloned.
+// A Cloner is a hash function whose state can be cloned, returning a value with
+// equivalent and independent state.
//
// All [Hash] implementations in the standard library implement this interface,
// unless GOFIPS140=v1.0.0 is set.
//
-// If a hash can only determine at runtime if it can be cloned,
-// (e.g., if it wraps another hash), it may return [errors.ErrUnsupported].
+// If a hash can only determine at runtime if it can be cloned (e.g. if it wraps
+// another hash), it may return an error wrapping [errors.ErrUnsupported].
type Cloner interface {
Hash
Clone() (Cloner, error)