Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the derivation of EDDSA child keys #299

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
75 changes: 75 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,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) {
Expand Down Expand Up @@ -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
}
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