]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/hmac: wrap ErrUnsupported returned by Clone
authorFilippo Valsorda <filippo@golang.org>
Thu, 22 May 2025 16:00:02 +0000 (18:00 +0200)
committerGopher Robot <gobot@golang.org>
Mon, 30 Jun 2025 16:31:32 +0000 (09:31 -0700)
Updates #69521

Change-Id: I6a6a4656403b9d35d5e4641b5c5c4975f3fa0e43
Reviewed-on: https://go-review.googlesource.com/c/go/+/675555
Reviewed-by: Austin Clements <austin@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
src/crypto/hmac/hmac_test.go
src/crypto/internal/fips140/hmac/hmac.go
src/hash/hash.go

index 9b7eee7bf7873e70e88bfa2e88fc2ba1e0ad2cd5..4046a9555a8e35fc9e7f16561d86a8d5430a2547 100644 (file)
@@ -11,6 +11,7 @@ import (
        "crypto/sha1"
        "crypto/sha256"
        "crypto/sha512"
+       "errors"
        "fmt"
        "hash"
        "testing"
@@ -583,6 +584,18 @@ func TestHMAC(t *testing.T) {
        }
 }
 
+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")
index 9b28017662564b126f31c2028510151341b3a7a5..a18b22650d105b763cd0f8ed4ae877f50edc23ff 100644 (file)
@@ -130,26 +130,36 @@ func (h *HMAC) Reset() {
        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
 }
index af84e7796bdf47306b43dab2daf2fcaf4a20c31a..d4b9a91663c0de15d826467dff2ba4a81d7984e3 100644 (file)
@@ -57,13 +57,14 @@ type Hash64 interface {
        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)