Skip to content

Commit 9ec2bc7

Browse files
authored
Merge pull request #239 from zhelnov/ecdsa-private-key-recovery
crypto/tss/recovery: add private key recovery from dkg results
2 parents 09a854b + d555cf1 commit 9ec2bc7

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package recovery
2+
3+
import (
4+
"crypto/ecdsa"
5+
"errors"
6+
"fmt"
7+
"math/big"
8+
9+
"github.com/getamis/alice/crypto/birkhoffinterpolation"
10+
"github.com/getamis/alice/crypto/ecpointgrouplaw"
11+
"github.com/getamis/alice/crypto/elliptic"
12+
"github.com/getamis/alice/crypto/utils"
13+
)
14+
15+
var (
16+
ErrNotEnoughPeers = errors.New("not enough input peers, need at least 2")
17+
ErrAbsentCurve = errors.New("curve is nil")
18+
ErrPubKeyMismatch = errors.New("pubkey derived from recovered privkey is not equal to pubkey provided")
19+
)
20+
21+
type RecoveryPeer struct {
22+
share *big.Int
23+
bk *birkhoffinterpolation.BkParameter
24+
}
25+
26+
func RecoverPrivateKey(curve elliptic.Curve, threshold uint32, pubKey *ecpointgrouplaw.ECPoint, peers []RecoveryPeer) (*ecdsa.PrivateKey, error) {
27+
peerNum := len(peers)
28+
if peerNum < 2 {
29+
return nil, ErrNotEnoughPeers
30+
}
31+
if curve == nil {
32+
return nil, ErrAbsentCurve
33+
}
34+
if err := utils.EnsureThreshold(threshold, uint32(peerNum)); err != nil {
35+
return nil, err
36+
}
37+
38+
bks := make([]*birkhoffinterpolation.BkParameter, 0, peerNum)
39+
shares := make([]*big.Int, 0, peerNum)
40+
for _, peer := range peers {
41+
shares = append(shares, peer.share)
42+
bks = append(bks, peer.bk)
43+
}
44+
45+
fieldOrder := curve.Params().N
46+
bksInterface := birkhoffinterpolation.BkParameters(bks)
47+
48+
if err := bksInterface.CheckValid(threshold, fieldOrder); err != nil {
49+
return nil, fmt.Errorf("BKS are incorrect: %w", err)
50+
}
51+
coefs, err := bksInterface.ComputeBkCoefficient(threshold, fieldOrder)
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
privKeyBigInt := big.NewInt(0)
57+
for i, coef := range coefs {
58+
privKeyBigInt.Add(privKeyBigInt, new(big.Int).Mul(coef, shares[i]))
59+
}
60+
privKeyBigInt.Mod(privKeyBigInt, fieldOrder)
61+
62+
derivedPubKey := ecpointgrouplaw.NewBase(curve).ScalarMult(privKeyBigInt)
63+
if !derivedPubKey.Equal(pubKey) {
64+
return nil, ErrPubKeyMismatch
65+
}
66+
67+
privKey := &ecdsa.PrivateKey{
68+
PublicKey: *derivedPubKey.ToPubKey(),
69+
D: privKeyBigInt,
70+
}
71+
72+
return privKey, nil
73+
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package recovery
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"crypto/ecdsa"
8+
"crypto/rand"
9+
10+
"github.com/decred/dcrd/dcrec/edwards"
11+
"github.com/getamis/alice/crypto/birkhoffinterpolation"
12+
"github.com/getamis/alice/crypto/ecpointgrouplaw"
13+
"github.com/getamis/alice/crypto/elliptic"
14+
"github.com/getamis/alice/crypto/utils"
15+
16+
. "github.com/onsi/ginkgo"
17+
. "github.com/onsi/ginkgo/extensions/table"
18+
. "github.com/onsi/gomega"
19+
)
20+
21+
var _ = Describe("Private Key Recovery", func() {
22+
23+
Context("validation", func() {
24+
It("should fail on 0 peers provided", func() {
25+
result, err := RecoverPrivateKey(elliptic.Secp256k1(), 2, nil, []RecoveryPeer{})
26+
Expect(result).Should(BeNil())
27+
Expect(err).Should(MatchError(ErrNotEnoughPeers))
28+
})
29+
It("should fail on 1 peer provided", func() {
30+
result, err := RecoverPrivateKey(elliptic.Secp256k1(), 2, nil, []RecoveryPeer{{}})
31+
Expect(result).Should(BeNil())
32+
Expect(err).Should(MatchError(ErrNotEnoughPeers))
33+
})
34+
It("should fail if no curve provided", func() {
35+
result, err := RecoverPrivateKey(nil, 2, nil, []RecoveryPeer{{}, {}, {}})
36+
Expect(result).Should(BeNil())
37+
Expect(err).Should(MatchError(ErrAbsentCurve))
38+
})
39+
It("should fail on invalid threshold provided", func() {
40+
result, err := RecoverPrivateKey(elliptic.Secp256k1(), 3, nil, []RecoveryPeer{{}, {}})
41+
Expect(result).Should(BeNil())
42+
Expect(err).Should(MatchError(utils.ErrLargeThreshold))
43+
result, err = RecoverPrivateKey(elliptic.Secp256k1(), 1, nil, []RecoveryPeer{{}, {}})
44+
Expect(result).Should(BeNil())
45+
Expect(err).Should(MatchError(utils.ErrSmallThreshold))
46+
})
47+
It("should fail on invalid BKs", func() {
48+
result, err := RecoverPrivateKey(elliptic.Secp256k1(), 2, nil, []RecoveryPeer{
49+
{
50+
share: nil,
51+
bk: birkhoffinterpolation.NewBkParameter(nil, 0),
52+
},
53+
{
54+
share: nil,
55+
bk: birkhoffinterpolation.NewBkParameter(nil, 0),
56+
},
57+
{
58+
share: nil,
59+
bk: birkhoffinterpolation.NewBkParameter(nil, 0),
60+
},
61+
})
62+
Expect(result).Should(BeNil())
63+
Expect(err.Error()).Should(ContainSubstring("BKS are incorrect: invalid bks"))
64+
})
65+
It("should fail on invalid pubkey to match", func() {
66+
result, err := RecoverPrivateKey(elliptic.Secp256k1(), 2, nil, MakeRecoveryPeers(
67+
[]string{
68+
"104609342634350601677472000055166148093040084008779475605306409018125790763384",
69+
"24163161798290927046425102830821018901476566166011270959061926968896994036828",
70+
"63066954971367271319238473967771679083666721823457348774233036722916669993451",
71+
},
72+
[]string{
73+
"112236885864076099358310462008741642913349768825204638119688077310570757734766",
74+
"20713194488405082366064300662102750959499818497887478230897041769232303982022",
75+
"28909585968450592089400243672753269836965419090880450440411327286593499063726",
76+
},
77+
))
78+
Expect(result).Should(BeNil())
79+
Expect(err).Should(MatchError(ErrPubKeyMismatch))
80+
})
81+
})
82+
83+
DescribeTable(
84+
"RecoverPrivateKey() ecdsa",
85+
func(curve elliptic.Curve, threshold int, testDkgData []RecoveryPeer, pubKey *ecpointgrouplaw.ECPoint) {
86+
privKey, err := RecoverPrivateKey(curve, uint32(threshold), pubKey, testDkgData)
87+
if err != nil {
88+
Expect(err).Should(BeNil())
89+
}
90+
91+
data := []byte("some tx hash to sign")
92+
r, s, err := ecdsa.Sign(rand.Reader, privKey, data)
93+
Expect(err).Should(BeNil())
94+
95+
Expect(ecdsa.Verify(&privKey.PublicKey, data, r, s)).Should(BeTrue())
96+
},
97+
Entry(
98+
"3/3 quorum",
99+
elliptic.Secp256k1(),
100+
2,
101+
MakeRecoveryPeers(
102+
[]string{
103+
"104609342634350601677472000055166148093040084008779475605306409018125790763384",
104+
"24163161798290927046425102830821018901476566166011270959061926968896994036828",
105+
"63066954971367271319238473967771679083666721823457348774233036722916669993451",
106+
},
107+
[]string{
108+
"112236885864076099358310462008741642913349768825204638119688077310570757734766",
109+
"20713194488405082366064300662102750959499818497887478230897041769232303982022",
110+
"28909585968450592089400243672753269836965419090880450440411327286593499063726",
111+
},
112+
),
113+
MakePubKey(
114+
"24951056819363353476818025996777971284120929729704886050366724870604080939790",
115+
"47651179196288923110559855714961823695288337160431011508811998037385251801902",
116+
elliptic.Secp256k1(),
117+
),
118+
),
119+
Entry(
120+
"2/3 quorum",
121+
elliptic.Secp256k1(),
122+
2,
123+
MakeRecoveryPeers(
124+
[]string{
125+
"104609342634350601677472000055166148093040084008779475605306409018125790763384",
126+
"63066954971367271319238473967771679083666721823457348774233036722916669993451",
127+
},
128+
[]string{
129+
"112236885864076099358310462008741642913349768825204638119688077310570757734766",
130+
"28909585968450592089400243672753269836965419090880450440411327286593499063726",
131+
},
132+
),
133+
MakePubKey(
134+
"24951056819363353476818025996777971284120929729704886050366724870604080939790",
135+
"47651179196288923110559855714961823695288337160431011508811998037385251801902",
136+
elliptic.Secp256k1(),
137+
),
138+
),
139+
)
140+
141+
DescribeTable(
142+
"RecoverPrivateKey() eddsa",
143+
func(curve elliptic.Curve, threshold int, testDkgData []RecoveryPeer, pubKey *ecpointgrouplaw.ECPoint) {
144+
privKey, err := RecoverPrivateKey(curve, uint32(threshold), pubKey, testDkgData)
145+
if err != nil {
146+
Expect(err).Should(BeNil())
147+
}
148+
149+
data := []byte("some tx hash to sign")
150+
151+
priv, pub, err := edwards.PrivKeyFromScalar(edwards.Edwards(), privKey.D.Bytes())
152+
Expect(err).Should(BeNil())
153+
Expect(pub.X.Cmp(pubKey.GetX())).Should(BeZero())
154+
Expect(pub.Y.Cmp(pubKey.GetY())).Should(BeZero())
155+
156+
r, s, err := edwards.Sign(edwards.Edwards(), priv, data)
157+
Expect(err).Should(BeNil())
158+
159+
Expect(edwards.Verify(edwards.NewPublicKey(edwards.Edwards(), pubKey.GetX(), pubKey.GetY()), data, r, s)).Should(BeTrue())
160+
},
161+
Entry(
162+
"3/3 quorum",
163+
elliptic.Ed25519(),
164+
2,
165+
MakeRecoveryPeers(
166+
[]string{
167+
"3502109557042490544838324442604034236999785614721987641956076911051160401944",
168+
"1103861929415814231586749933547278014778471148545711715092471976804157872704",
169+
"3399052917932924022114908121544556052008108447896375201293210781395183567243",
170+
},
171+
[]string{
172+
"7230155880034998276592769027360707810284675615875867582236795781241695079804",
173+
"5272874863729098099670216622703886559502263393787069547616689911708461775143",
174+
"3925249267150905323417733485189576433991970087669974755241055176173816181663",
175+
},
176+
),
177+
MakePubKey(
178+
"38485518761780627407120390846860597853897888230510522807636948334195936489504",
179+
"10129321156846869276162967126585445016496573486379629538155569021664335120579",
180+
elliptic.Ed25519(),
181+
),
182+
),
183+
Entry(
184+
"2/3 quorum",
185+
elliptic.Ed25519(),
186+
2,
187+
MakeRecoveryPeers(
188+
[]string{
189+
"3502109557042490544838324442604034236999785614721987641956076911051160401944",
190+
"3399052917932924022114908121544556052008108447896375201293210781395183567243",
191+
},
192+
[]string{
193+
"7230155880034998276592769027360707810284675615875867582236795781241695079804",
194+
"3925249267150905323417733485189576433991970087669974755241055176173816181663",
195+
},
196+
),
197+
MakePubKey(
198+
"38485518761780627407120390846860597853897888230510522807636948334195936489504",
199+
"10129321156846869276162967126585445016496573486379629538155569021664335120579",
200+
elliptic.Ed25519(),
201+
),
202+
),
203+
)
204+
205+
})
206+
207+
func TestBinaryField(t *testing.T) {
208+
RegisterFailHandler(Fail)
209+
RunSpecs(t, "Privaate Key Recovery Test")
210+
}
211+
212+
func MakeRecoveryPeers(shares, bkxs []string) []RecoveryPeer {
213+
recPeers := make([]RecoveryPeer, 0, len(shares))
214+
for index, share := range shares {
215+
share, _ := big.NewInt(0).SetString(share, 10)
216+
bkx, _ := new(big.Int).SetString(bkxs[index], 10)
217+
recPeers = append(recPeers, RecoveryPeer{
218+
share: share,
219+
bk: birkhoffinterpolation.NewBkParameter(bkx, 0),
220+
// TODO: 0 its a rank, test it with different ranks
221+
})
222+
}
223+
return recPeers
224+
}
225+
226+
func MakePubKey(x, y string, curve elliptic.Curve) *ecpointgrouplaw.ECPoint {
227+
pubX, _ := big.NewInt(0).SetString(x, 10)
228+
pubY, _ := big.NewInt(0).SetString(y, 10)
229+
pubKey, _ := ecpointgrouplaw.NewECPoint(curve, pubX, pubY)
230+
return pubKey
231+
}

0 commit comments

Comments
 (0)