From 225b24f7f42dc8ceb77e8706551d57525d880946 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 23 Mar 2021 10:51:53 +0300 Subject: [PATCH] Allow to use hash in Sign*/Verify* Sometimes having a whole buffer in memory isn't desired. New interface allows us to provide hashes instead. --- ecdsa.go | 21 ++++++++++++++++----- ecdsa_test.go | 12 ++++++++++++ rfc6979.go | 22 +++++++++++++++++++++- rfc6979_test.go | 20 ++++++++++++++++---- 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/ecdsa.go b/ecdsa.go index df66cf4..e392a8b 100644 --- a/ecdsa.go +++ b/ecdsa.go @@ -124,11 +124,17 @@ func hashBytes(data []byte) []byte { // Verify verifies the signature of msg using the public key pub. It returns // nil only if signature is valid. func Verify(pub *ecdsa.PublicKey, msg, sig []byte) error { - if r, s := unmarshalXY(sig); r == nil || s == nil { - return ErrCannotUnmarshal - } else if pub == nil { + return VerifyHash(pub, hashBytes(msg), sig) +} + +// VerifyHash verifies the signature of msg using it's hash the public key pub. +// It returns nil only if signature is valid. +func VerifyHash(pub *ecdsa.PublicKey, msgHash, sig []byte) error { + if pub == nil { return ErrEmptyPublicKey - } else if !ecdsa.Verify(pub, hashBytes(msg), r, s) { + } else if r, s := unmarshalXY(sig); r == nil || s == nil { + return ErrCannotUnmarshal + } else if !ecdsa.Verify(pub, msgHash, r, s) { return errors.Wrapf(ErrInvalidSignature, "%0x : %0x", r, s) } @@ -140,7 +146,12 @@ func Verify(pub *ecdsa.PublicKey, msg, sig []byte) error { // will be truncated to that length. It returns the signature as slice bytes. // The security of the private key depends on the entropy of rand. func Sign(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { - x, y, err := ecdsa.Sign(rand.Reader, key, hashBytes(msg)) + return SignHash(key, hashBytes(msg)) +} + +// SignHash signs message using it's hash and private key. +func SignHash(key *ecdsa.PrivateKey, msgHash []byte) ([]byte, error) { + x, y, err := ecdsa.Sign(rand.Reader, key, msgHash) if err != nil { return nil, err } diff --git a/ecdsa_test.go b/ecdsa_test.go index 3190a46..cd8d7fa 100644 --- a/ecdsa_test.go +++ b/ecdsa_test.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/sha512" "crypto/x509" "encoding/hex" "math/big" @@ -125,6 +126,17 @@ func TestSignVerify(t *testing.T) { }) }) + t.Run("using prepared hash", func(t *testing.T) { + var ( + data = []byte("Hello world") + sum = sha512.Sum512(data) + key = test.DecodeKey(0) + ) + sig, err := SignHash(key, sum[:]) + require.NoError(t, err) + require.NoError(t, VerifyHash(&key.PublicKey, sum[:], sig)) + }) + t.Run("low level", func(t *testing.T) { var ( data = []byte("Hello world") diff --git a/rfc6979.go b/rfc6979.go index 42f6469..e62284e 100644 --- a/rfc6979.go +++ b/rfc6979.go @@ -34,10 +34,16 @@ func hashBytesRFC6979(data []byte) []byte { // Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated // to the byte-length of the subgroup. This function does not perform that. func SignRFC6979(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { + return SignRFC6979Hash(key, hashBytesRFC6979(msg)) +} + +// SignRFC6979Hash signs sha256 hash of the message using the private key. +func SignRFC6979Hash(key *ecdsa.PrivateKey, msgHash []byte) ([]byte, error) { if key == nil { return nil, ErrEmptyPrivateKey } - r, s := rfc6979.SignECDSA(key, hashBytesRFC6979(msg), sha256.New) + + r, s := rfc6979.SignECDSA(key, msgHash, sha256.New) rBytes, sBytes := r.Bytes(), s.Bytes() signature := make([]byte, RFC6979SignatureSize) @@ -73,3 +79,17 @@ func VerifyRFC6979(key *ecdsa.PublicKey, msg, sig []byte) error { return nil } + +// VerifyRFC6979 verifies the signature of msg using the public key. It +// return nil only if signature is valid. +func VerifyRFC6979Hash(key *ecdsa.PublicKey, msgHash, sig []byte) error { + if key == nil { + return ErrEmptyPublicKey + } else if r, s, err := decodeSignature(sig); err != nil { + return err + } else if !ecdsa.Verify(key, msgHash, r, s) { + return ErrWrongSignature + } + + return nil +} diff --git a/rfc6979_test.go b/rfc6979_test.go index c42b037..178016f 100644 --- a/rfc6979_test.go +++ b/rfc6979_test.go @@ -1,6 +1,7 @@ package crypto import ( + "crypto/sha256" "encoding/hex" "testing" @@ -63,12 +64,23 @@ func TestRFC6979(t *testing.T) { { // Generated by Go sig := data[offset : offset+RFC6979SignatureSize] - res, err := SignRFC6979(key, body) - require.NoError(t, err) + { // SignRFC6979 + res, err := SignRFC6979(key, body) + require.NoError(t, err) - require.Equal(t, sig, res, "step: %d, %02x", i, res) + require.Equal(t, sig, res, "step: %d, %02x", i, res) - require.NoErrorf(t, VerifyRFC6979(pub, body, sig), "step: %d", i) + require.NoErrorf(t, VerifyRFC6979(pub, body, sig), "step: %d", i) + } + { // SignRFC6979Hash + sum := sha256.Sum256(body) + res, err := SignRFC6979Hash(key, sum[:]) + require.NoError(t, err) + + require.Equal(t, sig, res, "step: %d, %02x", i, res) + + require.NoErrorf(t, VerifyRFC6979Hash(pub, sum[:], sig), "step: %d", i) + } offset += RFC6979SignatureSize }