experiment with shared schnorr verify

This commit is contained in:
creamwhip
2022-01-29 20:14:42 +08:00
parent 68d2ee89ce
commit 29b2a87fd7
4 changed files with 16 additions and 230 deletions

View File

@@ -61,13 +61,6 @@ func (p *ECPoint) ScalarMult(k *big.Int) *ECPoint {
return newP
}
func (p *ECPoint) ToBtcecPubKey() *btcec.PublicKey {
var x, y btcec.FieldVal
x.SetByteSlice(p.X().Bytes())
y.SetByteSlice(p.Y().Bytes())
return btcec.NewPublicKey(&x, &y)
}
func (p *ECPoint) ToSecp256k1PubKey() *secp256k1.PublicKey {
var x, y btcec.FieldVal
x.SetByteSlice(p.X().Bytes())

View File

@@ -17,6 +17,8 @@ import (
"github.com/binance-chain/tss-lib/common"
"github.com/binance-chain/tss-lib/tss"
"github.com/decred/dcrd/dcrec/edwards/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v2/schnorr"
)
func (round *finalization) Start() *tss.Error {
@@ -97,18 +99,17 @@ func (round *finalization) Start() *tss.Error {
return round.WrapError(fmt.Errorf("edwards signature verification failed"))
}
} else if isSecp256k1Curve {
ok = SchnorrVerify(round.key.EDDSAPub.ToBtcecPubKey(), round.temp.m.Bytes(), round.temp.r, s)
if !ok {
pk1 := round.key.EDDSAPub.ToSecp256k1PubKey().ToECDSA()
pk2 := secp256k1.PublicKey(*pk1)
if ok = schnorr.Verify(&pk2, round.temp.m.Bytes(), round.temp.r, s); !ok {
return round.WrapError(fmt.Errorf("schnorr signature verification failed"))
}
}
round.end <- *round.data
return nil
}
func (round *finalization) CanAccept(msg tss.ParsedMessage) bool {
func (round *finalization) CanAccept(_ tss.ParsedMessage) bool {
// not expecting any incoming messages in this round
return false
}

View File

@@ -15,6 +15,8 @@ import (
"github.com/agl/ed25519/edwards25519"
"github.com/decred/dcrd/dcrec/edwards/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v2/schnorr"
"github.com/ipfs/go-log"
"github.com/stretchr/testify/assert"
@@ -176,14 +178,14 @@ func TestE2EConcurrentS256Schnorr(t *testing.T) {
updater := test.SharedPartyUpdater
msg_, _ := hex.DecodeString("304502210088BE0644191B935DB1CD786B43FF27798006578D8C908906B49E89") // big.NewInt(200).Bytes()
msg := big.NewInt(0).SetBytes(msg_)
msg, _ := hex.DecodeString("304502210088BE0644191B935DB1CD786B43FF27798006578D8C908906B49E89") // big.NewInt(200).Bytes()
msgI := big.NewInt(0).SetBytes(msg)
// init the parties
for i := 0; i < len(signPIDs); i++ {
params := tss.NewParameters(tss.S256(), p2pCtx, signPIDs[i], len(signPIDs), threshold)
P := NewLocalParty(msg, params, keys[i], outCh, endCh).(*LocalParty)
P := NewLocalParty(msgI, params, keys[i], outCh, endCh).(*LocalParty)
parties = append(parties, P)
go func(P *LocalParty) {
if err := P.Start(); err != nil {
@@ -239,9 +241,11 @@ signing:
r := new(big.Int).SetBytes(parties[0].data.GetR())
s := new(big.Int).SetBytes(parties[0].data.GetS())
ok := SchnorrVerify(keys[0].EDDSAPub.ToBtcecPubKey(), msg_, r, s)
assert.True(t, ok, "eddsa verify must pass")
pk1 := keys[0].EDDSAPub.ToSecp256k1PubKey().ToECDSA()
pk2 := secp256k1.PublicKey(*pk1)
if ok := schnorr.Verify(&pk2, msg, r, s); !assert.True(t, ok, "eddsa verify must pass") {
return
}
t.Log("EdDSA signing test done.")
// END EdDSA verify

View File

@@ -8,17 +8,11 @@ package signing
import (
"crypto/elliptic"
"encoding/hex"
"fmt"
"math/big"
"github.com/agl/ed25519/edwards25519"
"github.com/binance-chain/tss-lib/common"
"github.com/binance-chain/tss-lib/crypto"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr"
)
func encodedBytesToBigInt(s *[32]byte) *big.Int {
@@ -136,209 +130,3 @@ func ecPointToExtendedElement(ec elliptic.Curve, x *big.Int, y *big.Int) edwards
func OddY(a *crypto.ECPoint) bool {
return a.Y().Bit(0) > 0
}
func SchnorrVerify(p *btcec.PublicKey, m []byte, r_ *big.Int, s_ *big.Int) bool {
var r secp256k1.FieldVal
var s secp256k1.ModNScalar
r.SetByteSlice(r_.Bytes())
s.SetByteSlice(s_.Bytes())
err := schnorrVerify(m, p, r, s)
return err == nil
}
func schnorrVerify(hash []byte, pubKey *btcec.PublicKey, sigR secp256k1.FieldVal, sigS secp256k1.ModNScalar) error {
// The algorithm for producing a BIP-340 signature is described in
// README.md and is reproduced here for reference:
//
// 1. Fail if m is not 32 bytes
// 2. P = lift_x(int(pk)).
// 3. r = int(sig[0:32]); fail is r >= p.
// 4. s = int(sig[32:64]); fail if s >= n.
// 5. e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n.
// 6. R = s*G - e*P
// 7. Fail if is_infinite(R)
// 8. Fail if not hash_even_y(R)
// 9. Fail is x(R) != r.
// 10. Return success iff not failure occured before reachign this
// point.
// Step 1.
//
// Fail if m is not 32 bytes
if len(hash) != 32 {
str := fmt.Sprintf("wrong size for message (got %v, want %v)",
len(hash), 32)
return schnorr.Error{Err: schnorr.ErrorKind("ErrInvalidHashLen"), Description: str}
}
// Before we proceed, we want to ensure that the public key we're using
// for verification always has an even y-coordinate. So we'll serialize
// it, then parse it again to esure we only proceed with points that
// have an even y-coordinate.
// Step 2.
//
// Fail if Q is not a point on the curve
if !pubKey.IsOnCurve() {
str := "pubkey point is not on curve"
return schnorr.Error{Err: schnorr.ErrorKind("ErrPubKeyNotOnCurve"), Description: str}
}
// Step 3.
//
// Fail if r >= p
//
// Note this is already handled by the fact r is a field element.
// Step 4.
//
// Fail if s >= n
//
// Note this is already handled by the fact s is a mod n scalar.
// Step 5.
//
// e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n.
var rBytes [32]byte
sigR.PutBytesUnchecked(rBytes[:])
pBytes := pubKey.SerializeCompressed()
logBytes("finalize schnorrVerify - ", rBytes[:], pBytes[1:], hash)
common.Logger.Debugf("finalize schnorrVerify - sigR: %v", sigR.String())
commitment := chainhash.TaggedHash(
[]byte("BIP0340/challenge"), rBytes[:], pBytes[1:], hash,
)
var e btcec.ModNScalar
if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 {
str := "hash of (r || P || m) too big"
return schnorr.Error{Err: schnorr.ErrorKind("ErrSchnorrHashValue"), Description: str}
}
common.Logger.Debugf("finalize schnorrVerify - e: %v", e.String())
// Negate e here so we can use AddNonConst below to subtract the s*G
// point from e*P.
e.Negate()
// Step 6.
//
// R = s*G - e*P
var P, R, sG, eP btcec.JacobianPoint
pubKey.AsJacobian(&P)
btcec.ScalarBaseMultNonConst(&sigS, &sG)
btcec.ScalarMultNonConst(&e, &P, &eP)
var _sGAffine btcec.JacobianPoint
_sGAffine.X, _sGAffine.Y, _sGAffine.Z = sG.X, sG.Y, sG.Z
_sGAffine.ToAffine()
var _ePAffine btcec.JacobianPoint
_ePAffine.X, _ePAffine.Y, _ePAffine.Z = eP.X, eP.Y, eP.Z
_ePAffine.ToAffine()
common.Logger.Debugf("finalize - (minus)e: %v, P: %v, _sGAffine: %v, -ePAffine: %v", e.String(),
JacobianPointToString(P),
JacobianPointToString(_sGAffine),
JacobianPointToString(_ePAffine))
btcec.AddNonConst(&sG, &eP, &R)
// Step 7.
//
// Fail if R is the point at infinity
if (R.X.IsZero() && R.Y.IsZero()) || R.Z.IsZero() {
str := "calculated R point is the point at infinity"
return schnorr.Error{Err: schnorr.ErrorKind("ErrSigRNotOnCurve"), Description: str}
}
// Step 8.
//
// Fail if R.y is odd
//
// Note that R must be in affine coordinates for this check.
R.ToAffine()
common.Logger.Debugf("finalize - R (calculated) (after affine): %v", JacobianPointToString(R))
if R.Y.IsOdd() {
str := "calculated R y-value is odd"
return schnorr.Error{Err: schnorr.ErrorKind("ErrSigRYIsOdd"), Description: str}
}
// Step 9.
//
// Verified if R.x == r
//
// Note that R must be in affine coordinates for this check.
common.Logger.Debugf("sigR: %s, R.X (calculated): %s", sigR.String(), R.X.String())
if !sigR.Equals(&R.X) {
str := "calculated R point was not given R"
return schnorr.Error{Err: schnorr.ErrorKind("ErrUnequalRValues"), Description: str}
}
// Step 10.
//
// Return success iff not failure occured before reachign this
return nil
}
func logBytes(logMsg string, r, p, h []byte) {
common.Logger.Debugf("%s r: %s, p: %s, h: %s", logMsg, hex.EncodeToString(r), hex.EncodeToString(p), hex.EncodeToString(h))
}
func JacobianPointToString(point secp256k1.JacobianPoint) string {
return "[X:" + point.X.String() + ", Y:" + point.Y.String() + ", Z:" + point.Z.String() + "]"
}
func ParsePubKey(pubKeyStr []byte) (*btcec.PublicKey, error) {
if pubKeyStr == nil {
err := fmt.Errorf("nil pubkey byte string")
return nil, err
}
if len(pubKeyStr) != 32 {
err := fmt.Errorf("bad pubkey byte string size (want %v, have %v)",
32, len(pubKeyStr))
return nil, err
}
// We'll manually prepend the compressed byte so we can re-use the
// existing pubkey parsing routine of the main btcec package.
var keyCompressed [btcec.PubKeyBytesLenCompressed]byte
keyCompressed[0] = secp256k1.PubKeyFormatCompressedEven
copy(keyCompressed[1:], pubKeyStr)
return btcec.ParsePubKey(keyCompressed[:])
}
func RSBytesToBtcec(r_ []byte, s_ []byte) (btcec.FieldVal, btcec.ModNScalar) {
var r btcec.FieldVal
var s btcec.ModNScalar
r.SetByteSlice(r_)
s.SetByteSlice(s_)
return r, s
}
func RSToSchnorrSignature(r_ *big.Int, s_ *big.Int) *schnorr.Signature {
var r btcec.FieldVal
var s btcec.ModNScalar
r.SetByteSlice(r_.Bytes())
s.SetByteSlice(s_.Bytes())
signature := schnorr.NewSignature(&r, &s)
return signature
}
func RSByesToSchnorrSignature(r_ []byte, s_ []byte) *schnorr.Signature {
var r btcec.FieldVal
var s btcec.ModNScalar
r.SetByteSlice(r_)
s.SetByteSlice(s_)
return schnorr.NewSignature(&r, &s)
}
func NextPointEvenY(curve elliptic.Curve, P *crypto.ECPoint) (*crypto.ECPoint, int) {
G := crypto.ScalarBaseMult(curve, big.NewInt(1))
a := 0
Q := *P
Qptr := &Q
for ; OddY(Qptr); a++ { // Y cannot be odd
Qptr, _ = Qptr.Add(G)
}
return Qptr, a
}