Skip to content

Commit

Permalink
Read signed message through io.Reader in HashSign/HashVerify
Browse files Browse the repository at this point in the history
When signed message is large, passing it through a byte array would
cost memory; this switches to using io.Reader to avoid that.

Using `io.Reader` in the function signatures could also help diversify
those HashSign/HashVerify from Sign/Verify, through a separate
interface, alongside the `crypto.Signer` interface.

Signed-off-by: Daiki Ueno <[email protected]>
  • Loading branch information
ueno committed Sep 20, 2023
1 parent 9783f40 commit d6c123d
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 15 deletions.
5 changes: 3 additions & 2 deletions ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "C"
import (
"crypto"
"errors"
"io"
"runtime"
)

Expand Down Expand Up @@ -109,15 +110,15 @@ func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
return evpSign(priv.withKey, 0, 0, 0, hash)
}

func HashSignECDSA(priv *PrivateKeyECDSA, h crypto.Hash, msg []byte) ([]byte, error) {
func HashSignECDSA(priv *PrivateKeyECDSA, h crypto.Hash, msg io.Reader) ([]byte, error) {
return evpHashSign(priv.withKey, h, msg)
}

func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
return evpVerify(pub.withKey, 0, 0, 0, sig, hash) == nil
}

func HashVerifyECDSA(pub *PublicKeyECDSA, h crypto.Hash, msg, sig []byte) bool {
func HashVerifyECDSA(pub *PublicKeyECDSA, h crypto.Hash, msg io.Reader, sig []byte) bool {
return evpHashVerify(pub.withKey, h, msg, sig) == nil
}

Expand Down
7 changes: 4 additions & 3 deletions ecdsa_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package openssl_test

import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
Expand Down Expand Up @@ -74,15 +75,15 @@ func testECDSASignAndVerify(t *testing.T, c elliptic.Curve) {
if openssl.VerifyECDSA(pub, hashed[:], signed) {
t.Errorf("Verify succeeded despite intentionally invalid hash!")
}
signed, err = openssl.HashSignECDSA(priv, crypto.SHA256, msg)
signed, err = openssl.HashSignECDSA(priv, crypto.SHA256, bytes.NewReader(msg))
if err != nil {
t.Fatal(err)
}
if !openssl.HashVerifyECDSA(pub, crypto.SHA256, msg, signed) {
if !openssl.HashVerifyECDSA(pub, crypto.SHA256, bytes.NewReader(msg), signed) {
t.Errorf("Verify failed")
}
signed[0] ^= 0xff
if openssl.HashVerifyECDSA(pub, crypto.SHA256, msg, signed) {
if openssl.HashVerifyECDSA(pub, crypto.SHA256, bytes.NewReader(msg), signed) {
t.Errorf("Verify failed")
}
}
Expand Down
35 changes: 29 additions & 6 deletions evp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto"
"errors"
"hash"
"io"
"strconv"
"sync"
"unsafe"
Expand Down Expand Up @@ -371,7 +372,7 @@ func evpVerify(withKey withKeyFunc, padding C.int, saltLen C.int, h crypto.Hash,
return verifyEVP(withKey, padding, nil, nil, saltLen, h, verifyInit, verify, sig, hashed)
}

func evpHashSign(withKey withKeyFunc, h crypto.Hash, msg []byte) ([]byte, error) {
func evpHashSign(withKey withKeyFunc, h crypto.Hash, msg io.Reader) ([]byte, error) {
md := cryptoHashToMD(h)
if md == nil {
return nil, errors.New("unsupported hash function: " + strconv.Itoa(int(h)))
Expand All @@ -388,8 +389,19 @@ func evpHashSign(withKey withKeyFunc, h crypto.Hash, msg []byte) ([]byte, error)
}) != 1 {
return nil, newOpenSSLError("EVP_DigestSignInit failed")
}
if C.go_openssl_EVP_DigestUpdate(ctx, unsafe.Pointer(base(msg)), C.size_t(len(msg))) != 1 {
return nil, newOpenSSLError("EVP_DigestUpdate failed")
var blockLen = C.go_openssl_EVP_MD_get_block_size(md)
var block = make([]byte, blockLen)
for {
n, err := msg.Read(block)
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if C.go_openssl_EVP_DigestUpdate(ctx, unsafe.Pointer(base(block)), C.size_t(n)) != 1 {
return nil, newOpenSSLError("EVP_DigestUpdate failed")
}
}
// Obtain the signature length
if C.go_openssl_EVP_DigestSignFinal(ctx, nil, &outLen) != 1 {
Expand All @@ -403,7 +415,7 @@ func evpHashSign(withKey withKeyFunc, h crypto.Hash, msg []byte) ([]byte, error)
return out[:outLen], nil
}

func evpHashVerify(withKey withKeyFunc, h crypto.Hash, msg, sig []byte) error {
func evpHashVerify(withKey withKeyFunc, h crypto.Hash, msg io.Reader, sig []byte) error {
md := cryptoHashToMD(h)
if md == nil {
return errors.New("unsupported hash function: " + strconv.Itoa(int(h)))
Expand All @@ -418,8 +430,19 @@ func evpHashVerify(withKey withKeyFunc, h crypto.Hash, msg, sig []byte) error {
}) != 1 {
return newOpenSSLError("EVP_DigestVerifyInit failed")
}
if C.go_openssl_EVP_DigestUpdate(ctx, unsafe.Pointer(base(msg)), C.size_t(len(msg))) != 1 {
return newOpenSSLError("EVP_DigestUpdate failed")
var blockLen = C.go_openssl_EVP_MD_get_block_size(md)
var block = make([]byte, blockLen)
for {
n, err := msg.Read(block)
if err == io.EOF {
break
}
if err != nil {
return err
}
if C.go_openssl_EVP_DigestUpdate(ctx, unsafe.Pointer(base(block)), C.size_t(n)) != 1 {
return newOpenSSLError("EVP_DigestUpdate failed")
}
}
if C.go_openssl_EVP_DigestVerifyFinal(ctx, base(sig), C.size_t(len(sig))) != 1 {
return newOpenSSLError("EVP_DigestVerifyFinal failed")
Expand Down
5 changes: 3 additions & 2 deletions rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/subtle"
"errors"
"hash"
"io"
"runtime"
"unsafe"
)
Expand Down Expand Up @@ -289,7 +290,7 @@ func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte,
return evpSign(priv.withKey, C.GO_RSA_PKCS1_PADDING, 0, h, hashed)
}

func HashSignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, msg []byte) ([]byte, error) {
func HashSignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, msg io.Reader) ([]byte, error) {
return evpHashSign(priv.withKey, h, msg)
}

Expand All @@ -306,7 +307,7 @@ func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) err
return evpVerify(pub.withKey, C.GO_RSA_PKCS1_PADDING, 0, h, sig, hashed)
}

func HashVerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, msg, sig []byte) error {
func HashVerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, msg io.Reader, sig []byte) error {
return evpHashVerify(pub.withKey, h, msg, sig)
}

Expand Down
4 changes: 2 additions & 2 deletions rsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func TestSignVerifyPKCS1v15(t *testing.T) {
if err != nil {
t.Fatal(err)
}
signed2, err := openssl.HashSignRSAPKCS1v15(priv, crypto.SHA256, msg)
signed2, err := openssl.HashSignRSAPKCS1v15(priv, crypto.SHA256, bytes.NewReader(msg))
if err != nil {
t.Fatal(err)
}
Expand All @@ -141,7 +141,7 @@ func TestSignVerifyPKCS1v15(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = openssl.HashVerifyRSAPKCS1v15(pub, crypto.SHA256, msg, signed2)
err = openssl.HashVerifyRSAPKCS1v15(pub, crypto.SHA256, bytes.NewReader(msg), signed2)
if err != nil {
t.Fatal(err)
}
Expand Down
1 change: 1 addition & 0 deletions shims.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,5 @@ DEFINEFUNC(int, PKCS5_PBKDF2_HMAC, (const char *pass, int passlen, const unsigne
DEFINEFUNC_3_0(int, EVP_PKEY_CTX_set_tls1_prf_md, (GO_EVP_PKEY_CTX_PTR arg0, const GO_EVP_MD_PTR arg1), (arg0, arg1)) \
DEFINEFUNC_3_0(int, EVP_PKEY_CTX_set1_tls1_prf_secret, (GO_EVP_PKEY_CTX_PTR arg0, const unsigned char *arg1, int arg2), (arg0, arg1, arg2)) \
DEFINEFUNC_3_0(int, EVP_PKEY_CTX_add1_tls1_prf_seed, (GO_EVP_PKEY_CTX_PTR arg0, const unsigned char *arg1, int arg2), (arg0, arg1, arg2)) \
DEFINEFUNC_RENAMED_3_0(int, EVP_MD_get_block_size, EVP_MD_block_size, (const GO_EVP_MD_PTR md), (md)) \

0 comments on commit d6c123d

Please sign in to comment.