diff --git a/btcec/signature.go b/btcec/signature.go index deedd172..cdd7cedf 100644 --- a/btcec/signature.go +++ b/btcec/signature.go @@ -284,6 +284,25 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int { // format and thus we match bitcoind's behaviour here. func recoverKeyFromSignature(curve *KoblitzCurve, sig *Signature, msg []byte, iter int, doChecks bool) (*PublicKey, error) { + // Parse and validate the R and S signature components. + // + // Fail if r and s are not in [1, N-1]. + if sig.R.Cmp(curve.Params().N) != -1 { + return nil, errors.New("signature R is >= curve order") + } + + if sig.R.Sign() == 0 { + return nil, errors.New("signature R is 0") + } + + if sig.S.Cmp(curve.Params().N) != -1 { + return nil, errors.New("signature S is >= curve order") + } + + if sig.S.Sign() == 0 { + return nil, errors.New("signature S is 0") + } + // 1.1 x = (n * i) + r Rx := new(big.Int).Mul(curve.Params().N, new(big.Int).SetInt64(int64(iter/2))) @@ -393,7 +412,7 @@ func SignCompact(curve *KoblitzCurve, key *PrivateKey, // RecoverCompact verifies the compact signature "signature" of "hash" for the // Koblitz curve in "curve". If the signature matches then the recovered public -// key will be returned as well as a boolen if the original key was compressed +// key will be returned as well as a boolean if the original key was compressed // or not, else an error will be returned. func RecoverCompact(curve *KoblitzCurve, signature, hash []byte) (*PublicKey, bool, error) { diff --git a/btcec/signature_test.go b/btcec/signature_test.go index d2387414..ba02a03f 100644 --- a/btcec/signature_test.go +++ b/btcec/signature_test.go @@ -555,6 +555,40 @@ var recoveryTests = []struct { sig: "00000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000004", pub: "04A7640409AA2083FDAD38B2D8DE1263B2251799591D840653FB02DBBA503D7745FCB83D80E08A1E02896BE691EA6AFFB8A35939A646F1FC79052A744B1C82EDC3", }, + { + // Zero R value + // + // Test case contributed by Ethereum Swarm: GH-1651 + msg: "3060d2c77c1e192d62ad712fb400e04e6f779914a6876328ff3b213fa85d2012", + sig: "65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037a3", + err: fmt.Errorf("signature R is 0"), + }, + { + // Zero R value + // + // Test case contributed by Ethereum Swarm: GH-1651 + msg: "2bcebac60d8a78e520ae81c2ad586792df495ed429bd730dcd897b301932d054", + sig: "060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c", + err: fmt.Errorf("signature R is 0"), + }, + { + // R = N (curve order of secp256k1) + msg: "2bcebac60d8a78e520ae81c2ad586792df495ed429bd730dcd897b301932d054", + sig: "65fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414100000000000000000000000000000000000000000000000000000000000037a3", + err: fmt.Errorf("signature R is >= curve order"), + }, + { + // Zero S value + msg: "ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008", + sig: "0190f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549980000000000000000000000000000000000000000000000000000000000000000", + err: fmt.Errorf("signature S is 0"), + }, + { + // S = N (curve order of secp256k1) + msg: "ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008", + sig: "0190f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e54998fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + err: fmt.Errorf("signature S is >= curve order"), + }, } func TestRecoverCompact(t *testing.T) {