From 20b43259efe54ae1b0a022a430b2d40e0e17c775 Mon Sep 17 00:00:00 2001 From: felicityin Date: Tue, 9 Jul 2024 20:02:07 +0800 Subject: [PATCH] feat: derive eddsa child keys --- crypto/ckd/child_key_derivation.go | 34 +++++------ crypto/ckd/child_key_derivation_test.go | 75 +++++++++++++++++++++++++ ecdsa/signing/key_derivation_util.go | 18 +----- ecdsa/signing/local_party_test.go | 2 +- 4 files changed, 94 insertions(+), 35 deletions(-) diff --git a/crypto/ckd/child_key_derivation.go b/crypto/ckd/child_key_derivation.go index 422cffed..56702f6c 100644 --- a/crypto/ckd/child_key_derivation.go +++ b/crypto/ckd/child_key_derivation.go @@ -4,7 +4,6 @@ package ckd import ( "bytes" - "crypto/ecdsa" "crypto/elliptic" "crypto/hmac" "crypto/sha256" @@ -22,7 +21,7 @@ import ( ) type ExtendedKey struct { - ecdsa.PublicKey + PublicKey *crypto.ECPoint Depth uint8 ChildIndex uint32 ChainCode []byte // 32 bytes @@ -70,7 +69,7 @@ func (k *ExtendedKey) String() string { serializedBytes = append(serializedBytes, k.ParentFP...) serializedBytes = append(serializedBytes, childNumBytes[:]...) serializedBytes = append(serializedBytes, k.ChainCode...) - pubKeyBytes := serializeCompressed(k.PublicKey.X, k.PublicKey.Y) + pubKeyBytes := serializeCompressed(k.PublicKey.X(), k.PublicKey.Y()) serializedBytes = append(serializedBytes, pubKeyBytes...) checkSum := doubleHashB(serializedBytes)[:4] @@ -103,24 +102,23 @@ func NewExtendedKeyFromString(key string, curve elliptic.Curve) (*ExtendedKey, e chainCode := payload[13:45] keyData := payload[45:78] - var pubKey ecdsa.PublicKey + var pubKey *crypto.ECPoint + var err error if c, ok := curve.(*btcec.KoblitzCurve); ok { pk, err := btcec.ParsePubKey(keyData) if err != nil { return nil, err } - pubKey = ecdsa.PublicKey{ - Curve: c, - X: pk.X(), - Y: pk.Y(), + pubKey, err = crypto.NewECPoint(c, pk.X(), pk.Y()) + if err != nil { + return nil, err } } else { px, py := elliptic.Unmarshal(curve, keyData) - pubKey = ecdsa.PublicKey{ - Curve: curve, - X: px, - Y: py, + pubKey, err = crypto.NewECPoint(curve, px, py) + if err != nil { + return nil, err } } @@ -208,13 +206,9 @@ func DeriveChildKey(index uint32, pk *ExtendedKey, curve elliptic.Curve) (*big.I return nil, nil, errors.New("cannot derive key beyond max depth") } - cryptoPk, err := crypto.NewECPoint(curve, pk.X, pk.Y) - if err != nil { - common.Logger.Error("error getting pubkey from extendedkey") - return nil, nil, err - } + cryptoPk := pk.PublicKey - pkPublicKeyBytes := serializeCompressed(pk.X, pk.Y) + pkPublicKeyBytes := serializeCompressed(pk.PublicKey.X(), pk.PublicKey.Y()) data := make([]byte, 37) copy(data, pkPublicKeyBytes) @@ -227,7 +221,9 @@ func DeriveChildKey(index uint32, pk *ExtendedKey, curve elliptic.Curve) (*big.I il := ilr[:32] childChainCode := ilr[32:] ilNum := new(big.Int).SetBytes(il) + ilNum = ilNum.Mod(ilNum, curve.Params().N) + var err error if ilNum.Cmp(curve.Params().N) >= 0 || ilNum.Sign() == 0 { // falling outside of the valid range for curve private keys err = errors.New("invalid derived key") @@ -248,7 +244,7 @@ func DeriveChildKey(index uint32, pk *ExtendedKey, curve elliptic.Curve) (*big.I } childPk := &ExtendedKey{ - PublicKey: *childCryptoPk.ToECDSAPubKey(), + PublicKey: childCryptoPk, Depth: pk.Depth + 1, ChildIndex: index, ChainCode: childChainCode, diff --git a/crypto/ckd/child_key_derivation_test.go b/crypto/ckd/child_key_derivation_test.go index 6511e4ef..fa1a8408 100644 --- a/crypto/ckd/child_key_derivation_test.go +++ b/crypto/ckd/child_key_derivation_test.go @@ -7,10 +7,19 @@ package ckd_test import ( + "crypto/elliptic" + "crypto/rand" + "math/big" "testing" + "github.com/bnb-chain/tss-lib/v2/common" + "github.com/bnb-chain/tss-lib/v2/crypto" + "github.com/bnb-chain/tss-lib/v2/crypto/ckd" . "github.com/bnb-chain/tss-lib/v2/crypto/ckd" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/decred/dcrd/dcrec/edwards/v2" + "github.com/stretchr/testify/assert" ) func TestPublicDerivation(t *testing.T) { @@ -130,3 +139,69 @@ tests: } } } + +func TestEddsaPublicDerivation(t *testing.T) { + prikey := common.GetRandomPositiveInt(rand.Reader, edwards.Edwards().N) + pubkey := crypto.ScalarBaseMult(edwards.Edwards(), prikey) + + chainCode := make([]byte, 32) + max32b := new(big.Int).Lsh(new(big.Int).SetUint64(1), 256) + max32b = new(big.Int).Sub(max32b, new(big.Int).SetUint64(1)) + fillBytes(common.GetRandomPositiveInt(rand.Reader, max32b), chainCode) + + il, extendedChildPk, errorDerivation := derivingPubkeyFromPath(pubkey, chainCode, []uint32{12, 209, 3}, edwards.Edwards()) + assert.NoErrorf(t, errorDerivation, "there should not be an error deriving the child public key") + + keyDerivationDelta := il + + childPrivkey := new(big.Int).Add(prikey, keyDerivationDelta) + childPrivkey.Mod(childPrivkey, edwards.Edwards().N) + + sk, pk, err := edwards.PrivKeyFromScalar(common.PadToLengthBytesInPlace(childPrivkey.Bytes()[:], 32)) + assert.NoError(t, err) + assert.Equal(t, pk.X, extendedChildPk.PublicKey.X()) + + data := big.NewInt(200).Bytes() + + r, s, err := edwards.Sign(sk, data) + assert.NoError(t, err, "sign should not throw an error") + + pubk := edwards.PublicKey{ + Curve: edwards.Edwards(), + X: extendedChildPk.PublicKey.X(), + Y: extendedChildPk.PublicKey.Y(), + } + ok := edwards.Verify(&pubk, data, r, s) + assert.True(t, ok) + assert.NoError(t, err) +} + +func derivingPubkeyFromPath(masterPub *crypto.ECPoint, chainCode []byte, path []uint32, ec elliptic.Curve) (*big.Int, *ckd.ExtendedKey, error) { + net := &chaincfg.MainNetParams + extendedParentPk := &ckd.ExtendedKey{ + PublicKey: masterPub, + Depth: 0, + ChildIndex: 0, + ChainCode: chainCode[:], + ParentFP: []byte{0x00, 0x00, 0x00, 0x00}, + Version: net.HDPrivateKeyID[:], + } + + return ckd.DeriveChildKeyFromHierarchy(path, extendedParentPk, ec.Params().N, ec) +} + +func fillBytes(x *big.Int, buf []byte) []byte { + b := x.Bytes() + if len(b) > len(buf) { + panic("buffer too small") + } + offset := len(buf) - len(b) + for i := range buf { + if i < offset { + buf[i] = 0 + } else { + buf[i] = b[i-offset] + } + } + return buf +} diff --git a/ecdsa/signing/key_derivation_util.go b/ecdsa/signing/key_derivation_util.go index 668d9d4f..54a9909b 100644 --- a/ecdsa/signing/key_derivation_util.go +++ b/ecdsa/signing/key_derivation_util.go @@ -3,7 +3,6 @@ package signing import ( - "crypto/ecdsa" "crypto/elliptic" "math/big" @@ -15,15 +14,11 @@ import ( "github.com/btcsuite/btcd/chaincfg" ) -func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.LocalPartySaveData, extendedChildPk *ecdsa.PublicKey, ec elliptic.Curve) error { +func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.LocalPartySaveData, extendedChildPk *crypto.ECPoint, ec elliptic.Curve) error { var err error gDelta := crypto.ScalarBaseMult(ec, keyDerivationDelta) for k := range keys { - keys[k].ECDSAPub, err = crypto.NewECPoint(ec, extendedChildPk.X, extendedChildPk.Y) - if err != nil { - common.Logger.Errorf("error creating new extended child public key") - return err - } + keys[k].ECDSAPub = extendedChildPk // Suppose X_j has shamir shares X_j0, X_j1, ..., X_jn // So X_j + D has shamir shares X_j0 + D, X_j1 + D, ..., X_jn + D for j := range keys[k].BigXj { @@ -38,16 +33,9 @@ func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.Lo } func derivingPubkeyFromPath(masterPub *crypto.ECPoint, chainCode []byte, path []uint32, ec elliptic.Curve) (*big.Int, *ckd.ExtendedKey, error) { - // build ecdsa key pair - pk := ecdsa.PublicKey{ - Curve: ec, - X: masterPub.X(), - Y: masterPub.Y(), - } - net := &chaincfg.MainNetParams extendedParentPk := &ckd.ExtendedKey{ - PublicKey: pk, + PublicKey: masterPub, Depth: 0, ChildIndex: 0, ChainCode: chainCode[:], diff --git a/ecdsa/signing/local_party_test.go b/ecdsa/signing/local_party_test.go index 33f41dc5..cab4a580 100644 --- a/ecdsa/signing/local_party_test.go +++ b/ecdsa/signing/local_party_test.go @@ -246,7 +246,7 @@ func TestE2EWithHDKeyDerivation(t *testing.T) { keyDerivationDelta := il - err = UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta, keys, &extendedChildPk.PublicKey, btcec.S256()) + err = UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta, keys, extendedChildPk.PublicKey, btcec.S256()) assert.NoErrorf(t, err, "there should not be an error setting the derived keys") // PHASE: signing