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.
This commit is contained in:
Evgenii Stratonikov 2021-03-23 10:51:53 +03:00 committed by Leonard Lyubich
parent e9d1b1884c
commit 225b24f7f4
4 changed files with 65 additions and 10 deletions

View file

@ -124,11 +124,17 @@ func hashBytes(data []byte) []byte {
// Verify verifies the signature of msg using the public key pub. It returns // Verify verifies the signature of msg using the public key pub. It returns
// nil only if signature is valid. // nil only if signature is valid.
func Verify(pub *ecdsa.PublicKey, msg, sig []byte) error { func Verify(pub *ecdsa.PublicKey, msg, sig []byte) error {
if r, s := unmarshalXY(sig); r == nil || s == nil { return VerifyHash(pub, hashBytes(msg), sig)
return ErrCannotUnmarshal }
} else if pub == nil {
// 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 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) 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. // 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. // The security of the private key depends on the entropy of rand.
func Sign(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -4,6 +4,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha512"
"crypto/x509" "crypto/x509"
"encoding/hex" "encoding/hex"
"math/big" "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) { t.Run("low level", func(t *testing.T) {
var ( var (
data = []byte("Hello world") data = []byte("Hello world")

View file

@ -34,10 +34,16 @@ func hashBytesRFC6979(data []byte) []byte {
// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated // 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. // to the byte-length of the subgroup. This function does not perform that.
func SignRFC6979(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) { 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 { if key == nil {
return nil, ErrEmptyPrivateKey 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() rBytes, sBytes := r.Bytes(), s.Bytes()
signature := make([]byte, RFC6979SignatureSize) signature := make([]byte, RFC6979SignatureSize)
@ -73,3 +79,17 @@ func VerifyRFC6979(key *ecdsa.PublicKey, msg, sig []byte) error {
return nil 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
}

View file

@ -1,6 +1,7 @@
package crypto package crypto
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"testing" "testing"
@ -63,12 +64,23 @@ func TestRFC6979(t *testing.T) {
{ // Generated by Go { // Generated by Go
sig := data[offset : offset+RFC6979SignatureSize] sig := data[offset : offset+RFC6979SignatureSize]
res, err := SignRFC6979(key, body) { // SignRFC6979
require.NoError(t, err) 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 offset += RFC6979SignatureSize
} }