crypto/secp256k1: verify recovery ID before calling libsecp256k1
The C library treats the recovery ID as trusted input and crashes the process for invalid values, so it needs to be verified before calling into C. This will inhibit the crash in #1983. Also remove VerifySignature because we don't use it.
This commit is contained in:
parent
9422eec554
commit
1b29aed128
@ -39,7 +39,6 @@ package secp256k1
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
@ -64,6 +63,12 @@ func init() {
|
||||
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
|
||||
ErrInvalidSignatureLen = errors.New("invalid signature length")
|
||||
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
|
||||
)
|
||||
|
||||
func GenerateKeyPair() ([]byte, []byte) {
|
||||
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
|
||||
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||
@ -177,69 +182,20 @@ func VerifySeckeyValidity(seckey []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifySignatureValidity(sig []byte) bool {
|
||||
//64+1
|
||||
if len(sig) != 65 {
|
||||
return false
|
||||
}
|
||||
//malleability check, highest bit must be 1
|
||||
if (sig[32] & 0x80) == 0x80 {
|
||||
return false
|
||||
}
|
||||
//recovery id check
|
||||
if sig[64] >= 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//for compressed signatures, does not need pubkey
|
||||
func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
|
||||
if msg == nil || sig == nil || pubkey1 == nil {
|
||||
return errors.New("inputs must be non-nil")
|
||||
}
|
||||
if len(sig) != 65 {
|
||||
return errors.New("invalid signature length")
|
||||
}
|
||||
if len(pubkey1) != 65 {
|
||||
return errors.New("Invalid public key length")
|
||||
}
|
||||
|
||||
//to enforce malleability, highest bit of S must be 0
|
||||
//S starts at 32nd byte
|
||||
if (sig[32] & 0x80) == 0x80 { //highest bit must be 1
|
||||
return errors.New("Signature not malleable")
|
||||
}
|
||||
|
||||
if sig[64] >= 4 {
|
||||
return errors.New("Recover byte invalid")
|
||||
}
|
||||
|
||||
// if pubkey recovered, signature valid
|
||||
pubkey2, err := RecoverPubkey(msg, sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pubkey2) != 65 {
|
||||
return errors.New("Invalid recovered public key length")
|
||||
}
|
||||
if !bytes.Equal(pubkey1, pubkey2) {
|
||||
return errors.New("Public key does not match recovered public key")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// recovers a public key from the signature
|
||||
// RecoverPubkey returns the the public key of the signer.
|
||||
// msg must be the 32-byte hash of the message to be signed.
|
||||
// sig must be a 65-byte compact ECDSA signature containing the
|
||||
// recovery id as the last element.
|
||||
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
||||
if len(sig) != 65 {
|
||||
return nil, errors.New("Invalid signature length")
|
||||
if len(msg) != 32 {
|
||||
return nil, ErrInvalidMsgLen
|
||||
}
|
||||
if err := checkSignature(sig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
|
||||
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
|
||||
|
||||
pubkey := make([]byte, 64)
|
||||
/*
|
||||
this slice is used for both the recoverable signature and the
|
||||
@ -248,17 +204,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
||||
pubkey recovery is one bottleneck during load in Ethereum
|
||||
*/
|
||||
bytes65 := make([]byte, 65)
|
||||
|
||||
pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
|
||||
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
|
||||
|
||||
recid := C.int(sig[64])
|
||||
|
||||
ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
|
||||
context,
|
||||
recoverable_sig_ptr,
|
||||
sig_ptr,
|
||||
recid)
|
||||
|
||||
if ret == C.int(0) {
|
||||
return nil, errors.New("Failed to parse signature")
|
||||
}
|
||||
@ -269,20 +223,28 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
||||
recoverable_sig_ptr,
|
||||
msg_ptr,
|
||||
)
|
||||
|
||||
if ret == C.int(0) {
|
||||
return nil, errors.New("Failed to recover public key")
|
||||
} else {
|
||||
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
|
||||
|
||||
var output_len C.size_t
|
||||
C.secp256k1_ec_pubkey_serialize( // always returns 1
|
||||
context,
|
||||
serialized_pubkey_ptr,
|
||||
&output_len,
|
||||
pubkey_ptr,
|
||||
0, // SECP256K1_EC_COMPRESSED
|
||||
)
|
||||
return bytes65, nil
|
||||
}
|
||||
|
||||
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
|
||||
var output_len C.size_t
|
||||
C.secp256k1_ec_pubkey_serialize( // always returns 1
|
||||
context,
|
||||
serialized_pubkey_ptr,
|
||||
&output_len,
|
||||
pubkey_ptr,
|
||||
0, // SECP256K1_EC_COMPRESSED
|
||||
)
|
||||
return bytes65, nil
|
||||
}
|
||||
|
||||
func checkSignature(sig []byte) error {
|
||||
if len(sig) != 65 {
|
||||
return ErrInvalidSignatureLen
|
||||
}
|
||||
if sig[64] >= 4 {
|
||||
return ErrInvalidRecoveryID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -56,6 +56,17 @@ func TestSignatureValidity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidRecoveryID(t *testing.T) {
|
||||
_, seckey := GenerateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
sig, _ := Sign(msg, seckey)
|
||||
sig[64] = 99
|
||||
_, err := RecoverPubkey(msg, sig)
|
||||
if err != ErrInvalidRecoveryID {
|
||||
t.Fatalf("got %q, want %q", err, ErrInvalidRecoveryID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignAndRecover(t *testing.T) {
|
||||
pubkey1, seckey := GenerateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
@ -70,10 +81,6 @@ func TestSignAndRecover(t *testing.T) {
|
||||
if !bytes.Equal(pubkey1, pubkey2) {
|
||||
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
|
||||
}
|
||||
err = VerifySignature(msg, sig, pubkey1)
|
||||
if err != nil {
|
||||
t.Errorf("signature verification error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomMessagesWithSameKey(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user