From: Filippo Valsorda Date: Thu, 22 May 2025 16:00:02 +0000 (+0200) Subject: crypto/hmac: wrap ErrUnsupported returned by Clone X-Git-Tag: go1.25rc2~2^2~13 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4731832342f6430d6eb4cb13a00b97c3db5da993;p=gostls13.git crypto/hmac: wrap ErrUnsupported returned by Clone Updates #69521 Change-Id: I6a6a4656403b9d35d5e4641b5c5c4975f3fa0e43 Reviewed-on: https://go-review.googlesource.com/c/go/+/675555 Reviewed-by: Austin Clements Auto-Submit: Filippo Valsorda LUCI-TryBot-Result: Go LUCI Reviewed-by: Roland Shoemaker --- diff --git a/src/crypto/hmac/hmac_test.go b/src/crypto/hmac/hmac_test.go index 9b7eee7bf7..4046a9555a 100644 --- a/src/crypto/hmac/hmac_test.go +++ b/src/crypto/hmac/hmac_test.go @@ -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") diff --git a/src/crypto/internal/fips140/hmac/hmac.go b/src/crypto/internal/fips140/hmac/hmac.go index 9b28017662..a18b22650d 100644 --- a/src/crypto/internal/fips140/hmac/hmac.go +++ b/src/crypto/internal/fips140/hmac/hmac.go @@ -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 } diff --git a/src/hash/hash.go b/src/hash/hash.go index af84e7796b..d4b9a91663 100644 --- a/src/hash/hash.go +++ b/src/hash/hash.go @@ -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)