From fe129c5c873ea379a0b1820006830dfa00eaa54d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 16 Feb 2020 02:28:50 -0600 Subject: [PATCH] secp256k1: Optimize sig verify with mod n scalar. This modifies the signature verification function use the new ModNScalar type instead of big ints. This is work towards eventually using the new more efficient mod n scalar throughout. The following benchmark shows a before and after comparison of typical signature verification: benchmark old ns/op new ns/op delta ------------------------------------------------------ BenchmarkSigVerify 219346 180815 -17.57% benchmark old allocs new allocs delta ------------------------------------------------------ BenchmarkSigVerify 57 36 -36.84% benchmark old bytes new bytes delta ------------------------------------------------------ BenchmarkSigVerify 2818 1697 -39.78% --- dcrec/secp256k1/signature.go | 49 +++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/dcrec/secp256k1/signature.go b/dcrec/secp256k1/signature.go index 927818e8..366a5f42 100644 --- a/dcrec/secp256k1/signature.go +++ b/dcrec/secp256k1/signature.go @@ -89,6 +89,16 @@ func (sig *Signature) Serialize() []byte { return b } +// fieldToModNScalar converts a field value to scalar modulo the curve order. +func fieldToModNScalar(v *fieldVal) ModNScalar { + var buf [32]byte + v.PutBytes(&buf) + var s ModNScalar + s.SetBytes(&buf) + zeroArray32(&buf) + return s +} + // Verify returns whether or not the signature is valid for the provided hash // and secp256k1 public key. func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool { @@ -116,56 +126,61 @@ func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool { // Step 1. // // Fail if R and S are not in [1, N-1]. - N := curveParams.N - if sig.R.Sign() <= 0 || sig.S.Sign() <= 0 || - sig.R.Cmp(N) >= 0 || sig.S.Cmp(N) >= 0 { - + var R, S ModNScalar + if overflow := R.SetByteSlice(sig.R.Bytes()); overflow || R.IsZero() { + return false + } + if overflow := S.SetByteSlice(sig.S.Bytes()); overflow || S.IsZero() { return false } // Step 2. // // e = H(m) - e := hashToInt(hash) + var e ModNScalar + e.SetByteSlice(hash) // Step 3. // // w = S^-1 mod N - w := new(big.Int).ModInverse(sig.S, N) + w := new(ModNScalar).InverseValNonConst(&S) // Step 4. // // u1 = e * w mod N // u2 = R * w mod N - u1 := e.Mul(e, w) - u1.Mod(u1, N) - u2 := w.Mul(w, sig.R) - u2.Mod(u2, N) + u1 := new(ModNScalar).Mul2(&e, w) + u2 := new(ModNScalar).Mul2(&R, w) // Step 5. // // X = u1G + u2Q - curve := S256() - x1, y1 := curve.ScalarBaseMult(u1.Bytes()) - x2, y2 := curve.ScalarMult(pubKey.X, pubKey.Y, u2.Bytes()) - x, y := curve.Add(x1, y1, x2, y2) + var X, Q, u1G, u2Q jacobianPoint + bigAffineToJacobian(pubKey.X, pubKey.Y, &Q) + scalarBaseMultJacobian(u1, &u1G) + scalarMultJacobian(u2, &Q, &u2Q) + addJacobian(&u1G, &u2Q, &X) // Step 6. // // Fail if X is the point at infinity - if x.Sign() == 0 || y.Sign() == 0 { + if (X.x.IsZero() && X.y.IsZero()) || X.z.IsZero() { return false } // Step 7. // // x = X.x mod N (X.x is the x coordinate of X) - x.Mod(x, N) + // + // Note that the point must be in affine coordinates since R is in affine + // coordinates. + X.ToAffine() + x := fieldToModNScalar(&X.x) // Step 8. // // Verified if x == R - return x.Cmp(sig.R) == 0 + return x.Equals(&R) } // IsEqual compares this Signature instance to the one passed, returning true if