@@ -11,6 +11,8 @@ import (
1111
1212 base58 "github.com/bitcoin-sv/go-sdk/compat/base58"
1313 crypto "github.com/bitcoin-sv/go-sdk/primitives/hash"
14+ keyshares "github.com/bitcoin-sv/go-sdk/primitives/keyshares"
15+ "github.com/bitcoin-sv/go-sdk/util"
1416)
1517
1618var (
@@ -41,7 +43,7 @@ const compressMagic byte = 0x01
4143// package.
4244type PrivateKey e.PrivateKey
4345
44- // PrivateKeyFromBytes returns a private and public key for `curve' based on the
46+ // PrivateKeyFromBytes returns a private and public key based on the
4547// private key passed as an argument as a byte slice.
4648func PrivateKeyFromBytes (pk []byte ) (* PrivateKey , * PublicKey ) {
4749 x , y := S256 ().ScalarBaseMult (pk )
@@ -57,7 +59,6 @@ func PrivateKeyFromBytes(pk []byte) (*PrivateKey, *PublicKey) {
5759}
5860
5961// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey
60- // instead of the normal ecdsa.PrivateKey.
6162func NewPrivateKey () (* PrivateKey , error ) {
6263 key , err := e .GenerateKey (S256 (), rand .Reader )
6364 if err != nil {
@@ -66,7 +67,7 @@ func NewPrivateKey() (*PrivateKey, error) {
6667 return (* PrivateKey )(key ), nil
6768}
6869
69- // PrivateKey is an ecdsa.PrivateKey with additional functions to
70+ // PrivateKeyFromHex returns a private key from a hex string.
7071func PrivateKeyFromHex (privKeyHex string ) (* PrivateKey , error ) {
7172 if len (privKeyHex ) == 0 {
7273 return nil , errors .New ("private key hex is empty" )
@@ -79,9 +80,12 @@ func PrivateKeyFromHex(privKeyHex string) (*PrivateKey, error) {
7980 return privKey , nil
8081}
8182
82- // PrivateKey is an ecdsa.PrivateKey with additional functions to
83+ // PrivateKeyFromWif returns a private key from a WIF string.
8384func PrivateKeyFromWif (wif string ) (* PrivateKey , error ) {
84- decoded := base58 .Decode (wif )
85+ decoded , err := base58 .Decode (wif )
86+ if err != nil {
87+ return nil , err
88+ }
8589 decodedLen := len (decoded )
8690 var compress bool
8791
@@ -198,3 +202,131 @@ func (p *PrivateKey) DeriveChild(pub *PublicKey, invoiceNumber string) (*Private
198202 privKey , _ := PrivateKeyFromBytes (newPrivKey .Bytes ())
199203 return privKey , nil
200204}
205+
206+ func (p * PrivateKey ) ToPolynomial (threshold int ) (* keyshares.Polynomial , error ) {
207+ // Check for invalid threshold
208+ if threshold < 2 {
209+ return nil , fmt .Errorf ("threshold must be at least 2" )
210+ }
211+
212+ curve := keyshares .NewCurve ()
213+ points := make ([]* keyshares.PointInFiniteField , 0 )
214+
215+ // Set the first point to (0, key)
216+ points = append (points , keyshares .NewPointInFiniteField (big .NewInt (0 ), p .D ))
217+
218+ // Generate random points for the rest of the polynomial
219+ for i := 1 ; i < threshold ; i ++ {
220+ x := util .Umod (util .NewRandomBigInt (32 ), curve .P )
221+ y := util .Umod (util .NewRandomBigInt (32 ), curve .P )
222+
223+ points = append (points , keyshares .NewPointInFiniteField (x , y ))
224+ }
225+ return keyshares .NewPolynomial (points , threshold ), nil
226+ }
227+
228+ /**
229+ * Splits the private key into shares using Shamir's Secret Sharing Scheme.
230+ *
231+ * @param threshold The minimum number of shares required to reconstruct the private key.
232+ * @param totalShares The total number of shares to generate.
233+ * @returns A KeyShares object containing the shares, threshold and integrity.
234+ *
235+ * @example
236+ * key, _ := NewPrivateKey()
237+ * shares, _ := key.ToKeyShares(2, 5)
238+ */
239+ func (p * PrivateKey ) ToKeyShares (threshold int , totalShares int ) (keyShares * keyshares.KeyShares , error error ) {
240+ if threshold < 2 {
241+ return nil , errors .New ("threshold must be at least 2" )
242+ }
243+ if totalShares < 2 {
244+ return nil , errors .New ("totalShares must be at least 2" )
245+ }
246+ if threshold > totalShares {
247+ return nil , errors .New ("threshold should be less than or equal to totalShares" )
248+ }
249+
250+ poly , err := p .ToPolynomial (threshold )
251+ if err != nil {
252+ return nil , err
253+ }
254+
255+ points := make ([]* keyshares.PointInFiniteField , 0 )
256+ for range totalShares {
257+ pk , err := NewPrivateKey ()
258+ if err != nil {
259+ return nil , err
260+ }
261+ x := new (big.Int ).Set (pk .D )
262+ y := new (big.Int ).Set (poly .ValueAt (x ))
263+ points = append (points , keyshares .NewPointInFiniteField (x , y ))
264+ }
265+
266+ integrity := hex .EncodeToString (p .PubKey ().ToHash ())[:8 ]
267+ return keyshares .NewKeyShares (points , threshold , integrity ), nil
268+ }
269+
270+ // PrivateKeyFromKeyShares combines shares to reconstruct the private key
271+ func PrivateKeyFromKeyShares (keyShares * keyshares.KeyShares ) (* PrivateKey , error ) {
272+ if keyShares .Threshold < 2 {
273+ return nil , errors .New ("threshold should be at least 2" )
274+ }
275+
276+ if len (keyShares .Points ) < keyShares .Threshold {
277+ return nil , fmt .Errorf ("at least %d shares are required to reconstruct the private key" , keyShares .Threshold )
278+ }
279+
280+ // check to see if two points have the same x value
281+ for i := 0 ; i < keyShares .Threshold ; i ++ {
282+ for j := i + 1 ; j < keyShares .Threshold ; j ++ {
283+ if keyShares .Points [i ].X .Cmp (keyShares .Points [j ].X ) == 0 {
284+ return nil , fmt .Errorf ("duplicate share detected, each must be unique" )
285+ }
286+ }
287+ }
288+
289+ poly := keyshares .NewPolynomial (keyShares .Points , keyShares .Threshold )
290+ polyBytes := poly .ValueAt (big .NewInt (0 )).Bytes ()
291+ privateKey , publicKey := PrivateKeyFromBytes (polyBytes )
292+ integrityHash := hex .EncodeToString (publicKey .ToHash ())[:8 ]
293+ if keyShares .Integrity != integrityHash {
294+ return nil , fmt .Errorf ("integrity hash mismatch %s != %s" , keyShares .Integrity , integrityHash )
295+ }
296+ return privateKey , nil
297+ }
298+
299+ /**
300+ * @method ToBackupShares
301+ *
302+ * Creates a backup of the private key by splitting it into shares.
303+ *
304+ *
305+ * @param threshold The number of shares which will be required to reconstruct the private key.
306+ * @param shares The total number of shares to generate for distribution.
307+ * @returns
308+ */
309+ func (p * PrivateKey ) ToBackupShares (threshold int , shares int ) ([]string , error ) {
310+ keyShares , err := p .ToKeyShares (threshold , shares )
311+ if err != nil {
312+ return nil , err
313+ }
314+ return keyShares .ToBackupFormat ()
315+ }
316+
317+ /**
318+ *
319+ * @method PrivateKeyFromBackupShares
320+ *
321+ * Creates a private key from backup shares.
322+ *
323+ * @param shares in backup format
324+ * @returns PrivateKey
325+ */
326+ func PrivateKeyFromBackupShares (shares []string ) (* PrivateKey , error ) {
327+ keyShares , err := keyshares .NewKeySharesFromBackupFormat (shares )
328+ if err != nil {
329+ return nil , err
330+ }
331+ return PrivateKeyFromKeyShares (keyShares )
332+ }
0 commit comments