diff --git a/cng/hash.go b/cng/hash.go index b1b42f9..a674496 100644 --- a/cng/hash.go +++ b/cng/hash.go @@ -148,6 +148,19 @@ func hashToID(h hash.Hash) string { return hx.alg.id } +// cloneHash is an interface that defines a Clone method. +// +// hash.CloneHash will probably be added in Go 1.25, see https://golang.org/issue/69521, +// but we need it now. +type cloneHash interface { + hash.Hash + // Clone returns a separate Hash instance with the same state as h. + Clone() hash.Hash +} + +var _ hash.Hash = (*hashX)(nil) +var _ cloneHash = (*hashX)(nil) + // hashX implements [hash.Hash]. type hashX struct { alg *hashAlgorithm @@ -187,17 +200,14 @@ func (h *hashX) init() { runtime.SetFinalizer(h, (*hashX).finalize) } -func (h *hashX) Clone() (hash.Hash, error) { +func (h *hashX) Clone() hash.Hash { defer runtime.KeepAlive(h) h2 := &hashX{alg: h.alg, key: bytes.Clone(h.key)} if h.ctx != 0 { - err := bcrypt.DuplicateHash(h.ctx, &h2.ctx, nil, 0) - if err != nil { - return nil, err - } + hashClone(h.ctx, &h2.ctx) runtime.SetFinalizer(h2, (*hashX).finalize) } - return h2, nil + return h2 } func (h *hashX) Reset() { @@ -292,3 +302,11 @@ func hashReset(ctx bcrypt.HASH_HANDLE, size int) { panic(err) } } + +// hashClone clones ctx into ctx2. It panics on error. +func hashClone(ctx bcrypt.HASH_HANDLE, ctx2 *bcrypt.HASH_HANDLE) { + err := bcrypt.DuplicateHash(ctx, ctx2, nil, 0) + if err != nil { + panic(err) + } +} diff --git a/cng/hash_test.go b/cng/hash_test.go index 33f6549..67f88e4 100644 --- a/cng/hash_test.go +++ b/cng/hash_test.go @@ -79,10 +79,7 @@ func TestHash(t *testing.T) { t.Error("Write didn't change internal hash state") } - h2, err := h.(interface{ Clone() (hash.Hash, error) }).Clone() - if err != nil { - t.Fatal(err) - } + h2 := h.(interface{ Clone() hash.Hash }).Clone() h.Write(msg) h2.Write(msg) if actual, actual2 := h.Sum(nil), h2.Sum(nil); !bytes.Equal(actual, actual2) { diff --git a/cng/sha3.go b/cng/sha3.go index 1504b9b..d7aa193 100644 --- a/cng/sha3.go +++ b/cng/sha3.go @@ -73,6 +73,7 @@ func SupportsSHAKE256() bool { } var _ hash.Hash = (*DigestSHA3)(nil) +var _ cloneHash = (*DigestSHA3)(nil) // DigestSHA3 is the [sha3.SHA3] implementation using the CNG API. type DigestSHA3 struct { @@ -111,17 +112,14 @@ func (h *DigestSHA3) init() { runtime.SetFinalizer(h, (*DigestSHA3).finalize) } -func (h *DigestSHA3) Clone() (hash.Hash, error) { +func (h *DigestSHA3) Clone() hash.Hash { defer runtime.KeepAlive(h) h2 := &DigestSHA3{alg: h.alg} if h.ctx != 0 { - err := bcrypt.DuplicateHash(h.ctx, &h2.ctx, nil, 0) - if err != nil { - return nil, err - } + hashClone(h.ctx, &h2.ctx) runtime.SetFinalizer(h2, (*DigestSHA3).finalize) } - return h2, nil + return h2 } func (h *DigestSHA3) Reset() {