Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions cng/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
}
}
5 changes: 1 addition & 4 deletions cng/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 4 additions & 6 deletions cng/sha3.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down