Skip to content

Commit 20b4325

Browse files
committed
feat: derive eddsa child keys
1 parent 7113b68 commit 20b4325

File tree

4 files changed

+94
-35
lines changed

4 files changed

+94
-35
lines changed

crypto/ckd/child_key_derivation.go

+15-19
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package ckd
44

55
import (
66
"bytes"
7-
"crypto/ecdsa"
87
"crypto/elliptic"
98
"crypto/hmac"
109
"crypto/sha256"
@@ -22,7 +21,7 @@ import (
2221
)
2322

2423
type ExtendedKey struct {
25-
ecdsa.PublicKey
24+
PublicKey *crypto.ECPoint
2625
Depth uint8
2726
ChildIndex uint32
2827
ChainCode []byte // 32 bytes
@@ -70,7 +69,7 @@ func (k *ExtendedKey) String() string {
7069
serializedBytes = append(serializedBytes, k.ParentFP...)
7170
serializedBytes = append(serializedBytes, childNumBytes[:]...)
7271
serializedBytes = append(serializedBytes, k.ChainCode...)
73-
pubKeyBytes := serializeCompressed(k.PublicKey.X, k.PublicKey.Y)
72+
pubKeyBytes := serializeCompressed(k.PublicKey.X(), k.PublicKey.Y())
7473
serializedBytes = append(serializedBytes, pubKeyBytes...)
7574

7675
checkSum := doubleHashB(serializedBytes)[:4]
@@ -103,24 +102,23 @@ func NewExtendedKeyFromString(key string, curve elliptic.Curve) (*ExtendedKey, e
103102
chainCode := payload[13:45]
104103
keyData := payload[45:78]
105104

106-
var pubKey ecdsa.PublicKey
105+
var pubKey *crypto.ECPoint
106+
var err error
107107

108108
if c, ok := curve.(*btcec.KoblitzCurve); ok {
109109
pk, err := btcec.ParsePubKey(keyData)
110110
if err != nil {
111111
return nil, err
112112
}
113-
pubKey = ecdsa.PublicKey{
114-
Curve: c,
115-
X: pk.X(),
116-
Y: pk.Y(),
113+
pubKey, err = crypto.NewECPoint(c, pk.X(), pk.Y())
114+
if err != nil {
115+
return nil, err
117116
}
118117
} else {
119118
px, py := elliptic.Unmarshal(curve, keyData)
120-
pubKey = ecdsa.PublicKey{
121-
Curve: curve,
122-
X: px,
123-
Y: py,
119+
pubKey, err = crypto.NewECPoint(curve, px, py)
120+
if err != nil {
121+
return nil, err
124122
}
125123
}
126124

@@ -208,13 +206,9 @@ func DeriveChildKey(index uint32, pk *ExtendedKey, curve elliptic.Curve) (*big.I
208206
return nil, nil, errors.New("cannot derive key beyond max depth")
209207
}
210208

211-
cryptoPk, err := crypto.NewECPoint(curve, pk.X, pk.Y)
212-
if err != nil {
213-
common.Logger.Error("error getting pubkey from extendedkey")
214-
return nil, nil, err
215-
}
209+
cryptoPk := pk.PublicKey
216210

217-
pkPublicKeyBytes := serializeCompressed(pk.X, pk.Y)
211+
pkPublicKeyBytes := serializeCompressed(pk.PublicKey.X(), pk.PublicKey.Y())
218212

219213
data := make([]byte, 37)
220214
copy(data, pkPublicKeyBytes)
@@ -227,7 +221,9 @@ func DeriveChildKey(index uint32, pk *ExtendedKey, curve elliptic.Curve) (*big.I
227221
il := ilr[:32]
228222
childChainCode := ilr[32:]
229223
ilNum := new(big.Int).SetBytes(il)
224+
ilNum = ilNum.Mod(ilNum, curve.Params().N)
230225

226+
var err error
231227
if ilNum.Cmp(curve.Params().N) >= 0 || ilNum.Sign() == 0 {
232228
// falling outside of the valid range for curve private keys
233229
err = errors.New("invalid derived key")
@@ -248,7 +244,7 @@ func DeriveChildKey(index uint32, pk *ExtendedKey, curve elliptic.Curve) (*big.I
248244
}
249245

250246
childPk := &ExtendedKey{
251-
PublicKey: *childCryptoPk.ToECDSAPubKey(),
247+
PublicKey: childCryptoPk,
252248
Depth: pk.Depth + 1,
253249
ChildIndex: index,
254250
ChainCode: childChainCode,

crypto/ckd/child_key_derivation_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,19 @@
77
package ckd_test
88

99
import (
10+
"crypto/elliptic"
11+
"crypto/rand"
12+
"math/big"
1013
"testing"
1114

15+
"github.com/bnb-chain/tss-lib/v2/common"
16+
"github.com/bnb-chain/tss-lib/v2/crypto"
17+
"github.com/bnb-chain/tss-lib/v2/crypto/ckd"
1218
. "github.com/bnb-chain/tss-lib/v2/crypto/ckd"
1319
"github.com/btcsuite/btcd/btcec/v2"
20+
"github.com/btcsuite/btcd/chaincfg"
21+
"github.com/decred/dcrd/dcrec/edwards/v2"
22+
"github.com/stretchr/testify/assert"
1423
)
1524

1625
func TestPublicDerivation(t *testing.T) {
@@ -130,3 +139,69 @@ tests:
130139
}
131140
}
132141
}
142+
143+
func TestEddsaPublicDerivation(t *testing.T) {
144+
prikey := common.GetRandomPositiveInt(rand.Reader, edwards.Edwards().N)
145+
pubkey := crypto.ScalarBaseMult(edwards.Edwards(), prikey)
146+
147+
chainCode := make([]byte, 32)
148+
max32b := new(big.Int).Lsh(new(big.Int).SetUint64(1), 256)
149+
max32b = new(big.Int).Sub(max32b, new(big.Int).SetUint64(1))
150+
fillBytes(common.GetRandomPositiveInt(rand.Reader, max32b), chainCode)
151+
152+
il, extendedChildPk, errorDerivation := derivingPubkeyFromPath(pubkey, chainCode, []uint32{12, 209, 3}, edwards.Edwards())
153+
assert.NoErrorf(t, errorDerivation, "there should not be an error deriving the child public key")
154+
155+
keyDerivationDelta := il
156+
157+
childPrivkey := new(big.Int).Add(prikey, keyDerivationDelta)
158+
childPrivkey.Mod(childPrivkey, edwards.Edwards().N)
159+
160+
sk, pk, err := edwards.PrivKeyFromScalar(common.PadToLengthBytesInPlace(childPrivkey.Bytes()[:], 32))
161+
assert.NoError(t, err)
162+
assert.Equal(t, pk.X, extendedChildPk.PublicKey.X())
163+
164+
data := big.NewInt(200).Bytes()
165+
166+
r, s, err := edwards.Sign(sk, data)
167+
assert.NoError(t, err, "sign should not throw an error")
168+
169+
pubk := edwards.PublicKey{
170+
Curve: edwards.Edwards(),
171+
X: extendedChildPk.PublicKey.X(),
172+
Y: extendedChildPk.PublicKey.Y(),
173+
}
174+
ok := edwards.Verify(&pubk, data, r, s)
175+
assert.True(t, ok)
176+
assert.NoError(t, err)
177+
}
178+
179+
func derivingPubkeyFromPath(masterPub *crypto.ECPoint, chainCode []byte, path []uint32, ec elliptic.Curve) (*big.Int, *ckd.ExtendedKey, error) {
180+
net := &chaincfg.MainNetParams
181+
extendedParentPk := &ckd.ExtendedKey{
182+
PublicKey: masterPub,
183+
Depth: 0,
184+
ChildIndex: 0,
185+
ChainCode: chainCode[:],
186+
ParentFP: []byte{0x00, 0x00, 0x00, 0x00},
187+
Version: net.HDPrivateKeyID[:],
188+
}
189+
190+
return ckd.DeriveChildKeyFromHierarchy(path, extendedParentPk, ec.Params().N, ec)
191+
}
192+
193+
func fillBytes(x *big.Int, buf []byte) []byte {
194+
b := x.Bytes()
195+
if len(b) > len(buf) {
196+
panic("buffer too small")
197+
}
198+
offset := len(buf) - len(b)
199+
for i := range buf {
200+
if i < offset {
201+
buf[i] = 0
202+
} else {
203+
buf[i] = b[i-offset]
204+
}
205+
}
206+
return buf
207+
}

ecdsa/signing/key_derivation_util.go

+3-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
package signing
44

55
import (
6-
"crypto/ecdsa"
76
"crypto/elliptic"
87
"math/big"
98

@@ -15,15 +14,11 @@ import (
1514
"github.com/btcsuite/btcd/chaincfg"
1615
)
1716

18-
func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.LocalPartySaveData, extendedChildPk *ecdsa.PublicKey, ec elliptic.Curve) error {
17+
func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.LocalPartySaveData, extendedChildPk *crypto.ECPoint, ec elliptic.Curve) error {
1918
var err error
2019
gDelta := crypto.ScalarBaseMult(ec, keyDerivationDelta)
2120
for k := range keys {
22-
keys[k].ECDSAPub, err = crypto.NewECPoint(ec, extendedChildPk.X, extendedChildPk.Y)
23-
if err != nil {
24-
common.Logger.Errorf("error creating new extended child public key")
25-
return err
26-
}
21+
keys[k].ECDSAPub = extendedChildPk
2722
// Suppose X_j has shamir shares X_j0, X_j1, ..., X_jn
2823
// So X_j + D has shamir shares X_j0 + D, X_j1 + D, ..., X_jn + D
2924
for j := range keys[k].BigXj {
@@ -38,16 +33,9 @@ func UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta *big.Int, keys []keygen.Lo
3833
}
3934

4035
func derivingPubkeyFromPath(masterPub *crypto.ECPoint, chainCode []byte, path []uint32, ec elliptic.Curve) (*big.Int, *ckd.ExtendedKey, error) {
41-
// build ecdsa key pair
42-
pk := ecdsa.PublicKey{
43-
Curve: ec,
44-
X: masterPub.X(),
45-
Y: masterPub.Y(),
46-
}
47-
4836
net := &chaincfg.MainNetParams
4937
extendedParentPk := &ckd.ExtendedKey{
50-
PublicKey: pk,
38+
PublicKey: masterPub,
5139
Depth: 0,
5240
ChildIndex: 0,
5341
ChainCode: chainCode[:],

ecdsa/signing/local_party_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func TestE2EWithHDKeyDerivation(t *testing.T) {
246246

247247
keyDerivationDelta := il
248248

249-
err = UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta, keys, &extendedChildPk.PublicKey, btcec.S256())
249+
err = UpdatePublicKeyAndAdjustBigXj(keyDerivationDelta, keys, extendedChildPk.PublicKey, btcec.S256())
250250
assert.NoErrorf(t, err, "there should not be an error setting the derived keys")
251251

252252
// PHASE: signing

0 commit comments

Comments
 (0)