Skip to content

Commit

Permalink
feat: derive eddsa child keys
Browse files Browse the repository at this point in the history
  • Loading branch information
felicityin committed Jul 9, 2024
1 parent 7113b68 commit 7896333
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 35 deletions.
34 changes: 15 additions & 19 deletions crypto/ckd/child_key_derivation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package ckd

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/hmac"
"crypto/sha256"
Expand All @@ -22,7 +21,7 @@ import (
)

type ExtendedKey struct {
ecdsa.PublicKey
PublicKey *crypto.ECPoint
Depth uint8
ChildIndex uint32
ChainCode []byte // 32 bytes
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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)
Expand All @@ -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")
Expand All @@ -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,
Expand Down
78 changes: 78 additions & 0 deletions crypto/ckd/child_key_derivation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@
package ckd_test

import (
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"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) {
Expand Down Expand Up @@ -130,3 +140,71 @@ tests:
}
}
}

func TestEddsaPublicDerivation(t *testing.T) {
priKeyBytes, err := hex.DecodeString("ae1e5bf5f3d6bf58b5c222088671fcbe78b437e28fae944c793897b26091f241")
assert.NoError(t, err)
prikey := new(big.Int).SetBytes(priKeyBytes)
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
}
18 changes: 3 additions & 15 deletions ecdsa/signing/key_derivation_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package signing

import (
"crypto/ecdsa"
"crypto/elliptic"
"math/big"

Expand All @@ -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 {
Expand All @@ -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[:],
Expand Down
2 changes: 1 addition & 1 deletion ecdsa/signing/local_party_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7896333

Please sign in to comment.