mirror of
https://github.com/SwingbyProtocol/tss-lib.git
synced 2026-01-09 13:57:58 -05:00
experiment with shared schnorr verify
This commit is contained in:
@@ -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())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user