Skip to content

Commit 1b29aed

Browse files
committed
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.
1 parent 9422eec commit 1b29aed

File tree

2 files changed

+48
-79
lines changed

2 files changed

+48
-79
lines changed

crypto/secp256k1/secp256.go

Lines changed: 37 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ package secp256k1
3939
import "C"
4040

4141
import (
42-
"bytes"
4342
"errors"
4443
"unsafe"
4544

@@ -64,6 +63,12 @@ func init() {
6463
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
6564
}
6665

66+
var (
67+
ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
68+
ErrInvalidSignatureLen = errors.New("invalid signature length")
69+
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
70+
)
71+
6772
func GenerateKeyPair() ([]byte, []byte) {
6873
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
6974
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
@@ -177,69 +182,20 @@ func VerifySeckeyValidity(seckey []byte) error {
177182
return nil
178183
}
179184

180-
func VerifySignatureValidity(sig []byte) bool {
181-
//64+1
182-
if len(sig) != 65 {
183-
return false
184-
}
185-
//malleability check, highest bit must be 1
186-
if (sig[32] & 0x80) == 0x80 {
187-
return false
188-
}
189-
//recovery id check
190-
if sig[64] >= 4 {
191-
return false
192-
}
193-
194-
return true
195-
}
196-
197-
//for compressed signatures, does not need pubkey
198-
func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
199-
if msg == nil || sig == nil || pubkey1 == nil {
200-
return errors.New("inputs must be non-nil")
201-
}
202-
if len(sig) != 65 {
203-
return errors.New("invalid signature length")
204-
}
205-
if len(pubkey1) != 65 {
206-
return errors.New("Invalid public key length")
207-
}
208-
209-
//to enforce malleability, highest bit of S must be 0
210-
//S starts at 32nd byte
211-
if (sig[32] & 0x80) == 0x80 { //highest bit must be 1
212-
return errors.New("Signature not malleable")
213-
}
214-
215-
if sig[64] >= 4 {
216-
return errors.New("Recover byte invalid")
217-
}
218-
219-
// if pubkey recovered, signature valid
220-
pubkey2, err := RecoverPubkey(msg, sig)
221-
if err != nil {
222-
return err
223-
}
224-
if len(pubkey2) != 65 {
225-
return errors.New("Invalid recovered public key length")
226-
}
227-
if !bytes.Equal(pubkey1, pubkey2) {
228-
return errors.New("Public key does not match recovered public key")
229-
}
230-
231-
return nil
232-
}
233-
234-
// recovers a public key from the signature
185+
// RecoverPubkey returns the the public key of the signer.
186+
// msg must be the 32-byte hash of the message to be signed.
187+
// sig must be a 65-byte compact ECDSA signature containing the
188+
// recovery id as the last element.
235189
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
236-
if len(sig) != 65 {
237-
return nil, errors.New("Invalid signature length")
190+
if len(msg) != 32 {
191+
return nil, ErrInvalidMsgLen
192+
}
193+
if err := checkSignature(sig); err != nil {
194+
return nil, err
238195
}
239196

240197
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
241198
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
242-
243199
pubkey := make([]byte, 64)
244200
/*
245201
this slice is used for both the recoverable signature and the
@@ -248,17 +204,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
248204
pubkey recovery is one bottleneck during load in Ethereum
249205
*/
250206
bytes65 := make([]byte, 65)
251-
252207
pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
253208
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
254-
255209
recid := C.int(sig[64])
210+
256211
ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
257212
context,
258213
recoverable_sig_ptr,
259214
sig_ptr,
260215
recid)
261-
262216
if ret == C.int(0) {
263217
return nil, errors.New("Failed to parse signature")
264218
}
@@ -269,20 +223,28 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
269223
recoverable_sig_ptr,
270224
msg_ptr,
271225
)
272-
273226
if ret == C.int(0) {
274227
return nil, errors.New("Failed to recover public key")
275-
} else {
276-
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
277-
278-
var output_len C.size_t
279-
C.secp256k1_ec_pubkey_serialize( // always returns 1
280-
context,
281-
serialized_pubkey_ptr,
282-
&output_len,
283-
pubkey_ptr,
284-
0, // SECP256K1_EC_COMPRESSED
285-
)
286-
return bytes65, nil
287228
}
229+
230+
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
231+
var output_len C.size_t
232+
C.secp256k1_ec_pubkey_serialize( // always returns 1
233+
context,
234+
serialized_pubkey_ptr,
235+
&output_len,
236+
pubkey_ptr,
237+
0, // SECP256K1_EC_COMPRESSED
238+
)
239+
return bytes65, nil
240+
}
241+
242+
func checkSignature(sig []byte) error {
243+
if len(sig) != 65 {
244+
return ErrInvalidSignatureLen
245+
}
246+
if sig[64] >= 4 {
247+
return ErrInvalidRecoveryID
248+
}
249+
return nil
288250
}

crypto/secp256k1/secp256_test.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ func TestSignatureValidity(t *testing.T) {
5656
}
5757
}
5858

59+
func TestInvalidRecoveryID(t *testing.T) {
60+
_, seckey := GenerateKeyPair()
61+
msg := randentropy.GetEntropyCSPRNG(32)
62+
sig, _ := Sign(msg, seckey)
63+
sig[64] = 99
64+
_, err := RecoverPubkey(msg, sig)
65+
if err != ErrInvalidRecoveryID {
66+
t.Fatalf("got %q, want %q", err, ErrInvalidRecoveryID)
67+
}
68+
}
69+
5970
func TestSignAndRecover(t *testing.T) {
6071
pubkey1, seckey := GenerateKeyPair()
6172
msg := randentropy.GetEntropyCSPRNG(32)
@@ -70,10 +81,6 @@ func TestSignAndRecover(t *testing.T) {
7081
if !bytes.Equal(pubkey1, pubkey2) {
7182
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
7283
}
73-
err = VerifySignature(msg, sig, pubkey1)
74-
if err != nil {
75-
t.Errorf("signature verification error: %s", err)
76-
}
7784
}
7885

7986
func TestRandomMessagesWithSameKey(t *testing.T) {

0 commit comments

Comments
 (0)