tss-lib icon indicating copy to clipboard operation
tss-lib copied to clipboard

How to recover a valid {t,n}-threshold ecdsa (PublicKey, PrivateKey) pair

Open daevontkhplanesw opened this issue 2 years ago • 5 comments

Amazing library. We really love it. For work, we can't use the Signing function, instead we need to generate a valid {t,n} threshold ecdsa (PublicKey, PrivateKey) pair. We can even ask t out of n clients to send their local private keys to the final computer that will generate the final valid pair. Also this process will be used only once as we no longer use the same party again.

Let's rewrite here our scenario

  1. n computers generate their local secrets.
  2. Using signing function on a dummy message, {t,n} can recover the PublicKey of the final pair from the signed dummy message.
  3. At a certain point in time, t of these n computers will share their private keys (local secrets) to a special node.
  4. This special node will be able to recover (create) a valid {t,n} threshold ECDSA (Public,Private) key pair.
  5. We discard the party as we no longer need it.

Think it as a 3rd party authentication service that is able to create the final (PublicKey,PrivateKey) pair in order to allow the user download and import the private key of the address in his wallet.

Any idea of how to do it ? We really appreciate if you can help us, otherwise we can not use this amazing library in our work.

daevontkhplanesw avatar Aug 18 '22 02:08 daevontkhplanesw

You can use the github.com/binance-chain/tss-lib/crypto/vss package to recover the private key. I've wrote an example for you. I haven't tested it though.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"

	"github.com/binance-chain/tss-lib/crypto/vss"
	"github.com/binance-chain/tss-lib/ecdsa/keygen"
)

func reconstruct(threshold int, ec elliptic.Curve, shares []keygen.LocalPartySaveData) (*ecdsa.PrivateKey, error) {
	var vssShares = make(vss.Shares, len(shares))
	for i, share := range shares {
		vssShare := &vss.Share{
			Threshold: threshold,
			ID:        share.ShareID,
			Share:     share.Xi,
		}
		vssShares[i] = vssShare
	}

	d, err := vssShares.ReConstruct(ec)
	if err != nil {
		return nil, err
	}

	x, y := ec.ScalarBaseMult(d.Bytes())

	privateKey := &ecdsa.PrivateKey{
		D: d,
		PublicKey: ecdsa.PublicKey{
			Curve: ec,
			X:     x,
			Y:     y,
		},
	}

	return privateKey, nil
}

typestring avatar Aug 18 '22 05:08 typestring

You guys are so quick ! AMAZING! This is exactly what we were looking for ! We will give it a try today and come back later with a comment about your quick solution !

LE will the PublicKey from my step 2 (signing a dummy message) match with the (PublicKey, PrivateKey) from reconstruct function?

daevontkhplanesw avatar Aug 18 '22 09:08 daevontkhplanesw

We tested the code. It works. We really want to thank you so much. Now we can use your amazing library!

Just one question. It looks like using the keygen code, each participant will know the final public key ? Is it how it was supposed to be done ? I was thinking only the initiator will know it.

daevontkhplanesw avatar Aug 18 '22 18:08 daevontkhplanesw

You can use the github.com/binance-chain/tss-lib/crypto/vss package to recover the private key. I've wrote an example for you. I haven't tested it though.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"

	"github.com/binance-chain/tss-lib/crypto/vss"
	"github.com/binance-chain/tss-lib/ecdsa/keygen"
)

func reconstruct(threshold int, ec elliptic.Curve, shares []keygen.LocalPartySaveData) (*ecdsa.PrivateKey, error) {
	var vssShares = make(vss.Shares, len(shares))
	for i, share := range shares {
		vssShare := &vss.Share{
			Threshold: threshold,
			ID:        share.ShareID,
			Share:     share.Xi,
		}
		vssShares[i] = vssShare
	}

	d, err := vssShares.ReConstruct(ec)
	if err != nil {
		return nil, err
	}

	x, y := ec.ScalarBaseMult(d.Bytes())

	privateKey := &ecdsa.PrivateKey{
		D: d,
		PublicKey: ecdsa.PublicKey{
			Curve: ec,
			X:     x,
			Y:     y,
		},
	}

	return privateKey, nil
}

Hello, Thank you so much for the code. is there any implementation for eddsa keypairs?

levischechter avatar Aug 24 '22 12:08 levischechter

You can use the github.com/binance-chain/tss-lib/crypto/vss package to recover the private key. I've wrote an example for you. I haven't tested it though.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"

	"github.com/binance-chain/tss-lib/crypto/vss"
	"github.com/binance-chain/tss-lib/ecdsa/keygen"
)

func reconstruct(threshold int, ec elliptic.Curve, shares []keygen.LocalPartySaveData) (*ecdsa.PrivateKey, error) {
	var vssShares = make(vss.Shares, len(shares))
	for i, share := range shares {
		vssShare := &vss.Share{
			Threshold: threshold,
			ID:        share.ShareID,
			Share:     share.Xi,
		}
		vssShares[i] = vssShare
	}

	d, err := vssShares.ReConstruct(ec)
	if err != nil {
		return nil, err
	}

	x, y := ec.ScalarBaseMult(d.Bytes())

	privateKey := &ecdsa.PrivateKey{
		D: d,
		PublicKey: ecdsa.PublicKey{
			Curve: ec,
			X:     x,
			Y:     y,
		},
	}

	return privateKey, nil
}

Hello, Thank you so much for the code. is there any implementation for eddsa keypairs?

for everyone else want eddsa implementation i wrote this:

package main

import (
	"crypto/ed25519"
	"crypto/elliptic"

	"github.com/binance-chain/tss-lib/crypto/vss"
	"github.com/binance-chain/tss-lib/eddsa/keygen"
)

func Reconstruct(threshold int, ec elliptic.Curve, shares []keygen.LocalPartySaveData) (*ed25519.PrivateKey, error) {
	var vssShares = make(vss.Shares, len(shares))
	for i, share := range shares {
		vssShare := &vss.Share{
			Threshold: threshold,
			ID:        share.ShareID,
			Share:     share.Xi,
		}
		vssShares[i] = vssShare
	}

	d, err := vssShares.ReConstruct(ec)
	if err != nil {
		return nil, err
	}

	var private *ed25519.PrivateKey
	*private = d.Bytes()
	return private, nil
}

levischechter avatar Aug 24 '22 12:08 levischechter