Skip to content

Commit

Permalink
Implement Clone for all hashers (#167)
Browse files Browse the repository at this point in the history
* implement Clone for all hashers

* document clone methods

* add TestHash_Clone comment
  • Loading branch information
qmuntal authored Sep 9, 2024
1 parent c50e935 commit 950c5fd
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 29 deletions.
149 changes: 149 additions & 0 deletions hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,34 @@ func (h *evpHash) sum(out []byte) {
runtime.KeepAlive(h)
}

// clone returns a new evpHash object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *evpHash) clone() (*evpHash, error) {
ctx := C.go_openssl_EVP_MD_CTX_new()
if ctx == nil {
return nil, newOpenSSLError("EVP_MD_CTX_new")
}
if C.go_openssl_EVP_MD_CTX_copy_ex(ctx, h.ctx) != 1 {
C.go_openssl_EVP_MD_CTX_free(ctx)
return nil, newOpenSSLError("EVP_MD_CTX_copy")
}
ctx2 := C.go_openssl_EVP_MD_CTX_new()
if ctx2 == nil {
C.go_openssl_EVP_MD_CTX_free(ctx)
return nil, newOpenSSLError("EVP_MD_CTX_new")
}
cloned := &evpHash{
ctx: ctx,
ctx2: ctx2,
size: h.size,
blockSize: h.blockSize,
marshallable: h.marshallable,
}
runtime.SetFinalizer(cloned, (*evpHash).finalize)
return cloned, nil
}

// hashState returns a pointer to the internal hash structure.
//
// The EVP_MD_CTX memory layout has changed in OpenSSL 3
Expand Down Expand Up @@ -280,6 +308,17 @@ func (h *md4Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *md4Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &md4Hash{evpHash: c}, nil
}

// NewMD5 returns a new MD5 hash.
func NewMD5() hash.Hash {
h := md5Hash{evpHash: newEvpHash(crypto.MD5)}
Expand Down Expand Up @@ -308,6 +347,17 @@ func (h *md5Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *md5Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &md5Hash{evpHash: c}, nil
}

const (
md5Magic = "md5\x01"
md5MarshaledSize = len(md5Magic) + 4*4 + 64 + 8
Expand Down Expand Up @@ -377,6 +427,17 @@ func (h *sha1Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha1Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha1Hash{evpHash: c}, nil
}

// sha1State layout is taken from
// https://github.com/openssl/openssl/blob/0418e993c717a6863f206feaa40673a261de7395/include/openssl/sha.h#L34.
type sha1State struct {
Expand Down Expand Up @@ -457,6 +518,17 @@ func (h *sha224Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha224Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha224Hash{evpHash: c}, nil
}

// NewSHA256 returns a new SHA256 hash.
func NewSHA256() hash.Hash {
h := sha256Hash{evpHash: newEvpHash(crypto.SHA256)}
Expand All @@ -476,6 +548,17 @@ func (h *sha256Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha256Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha256Hash{evpHash: c}, nil
}

const (
magic224 = "sha\x02"
magic256 = "sha\x03"
Expand Down Expand Up @@ -616,6 +699,17 @@ func (h *sha384Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha384Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha384Hash{evpHash: c}, nil
}

// NewSHA512 returns a new SHA512 hash.
func NewSHA512() hash.Hash {
h := sha512Hash{evpHash: newEvpHash(crypto.SHA512)}
Expand All @@ -635,6 +729,17 @@ func (h *sha512Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha512Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha512Hash{evpHash: c}, nil
}

// sha512State layout is taken from
// https://github.com/openssl/openssl/blob/0418e993c717a6863f206feaa40673a261de7395/include/openssl/sha.h#L95.
type sha512State struct {
Expand Down Expand Up @@ -781,6 +886,17 @@ func (h *sha3_224Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha3_224Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha3_224Hash{evpHash: c}, nil
}

// NewSHA3_256 returns a new SHA3-256 hash.
func NewSHA3_256() hash.Hash {
return &sha3_256Hash{
Expand All @@ -798,6 +914,17 @@ func (h *sha3_256Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha3_256Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha3_256Hash{evpHash: c}, nil
}

// NewSHA3_384 returns a new SHA3-384 hash.
func NewSHA3_384() hash.Hash {
return &sha3_384Hash{
Expand All @@ -815,6 +942,17 @@ func (h *sha3_384Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha3_384Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha3_384Hash{evpHash: c}, nil
}

// NewSHA3_512 returns a new SHA3-512 hash.
func NewSHA3_512() hash.Hash {
return &sha3_512Hash{
Expand All @@ -832,6 +970,17 @@ func (h *sha3_512Hash) Sum(in []byte) []byte {
return append(in, h.out[:]...)
}

// Clone returns a new [hash.Hash] object that is a deep clone of itself.
// The duplicate object contains all state and data contained in the
// original object at the point of duplication.
func (h *sha3_512Hash) Clone() (hash.Hash, error) {
c, err := h.clone()
if err != nil {
return nil, err
}
return &sha3_512Hash{evpHash: c}, nil
}

// appendUint64 appends x into b as a big endian byte sequence.
func appendUint64(b []byte, x uint64) []byte {
return append(b,
Expand Down
Loading

0 comments on commit 950c5fd

Please sign in to comment.