bnb-chain/tss-lib

repository·master·Indexed 21 days ago

https://github.com/bnb-chain/tss-lib

A Go implementation of multi-party {t,n}-threshold ECDSA and EdDSA protocols. It enables multiple parties to collaboratively generate signatures and manage secret shares without a trusted dealer. The library provides packages for key generation (keygen), signing, and resharing, requiring the application to implement its own transport layer and security measures such as end-to-end encryption and session IDs.

Tokens
1.5K
Snippets
4
Records
6
Agent score
27%

What's inside bnb-chain-tss-lib

  1. Perform Key Generation (Keygen)

    master

    Use the keygen.LocalParty to create secret shares without a trusted dealer.

    Upon successful completion, the protocol sends save data through the endCh. This data must be persisted to secure storage.

    If you did not pre-compute preParams, you can omit the last argument in keygen.NewLocalParty, but the library will compute them during the first round of the protocol.

    // party := keygen.NewLocalParty(params, outCh, endCh, preParams)
    party := keygen.NewLocalParty(params, outCh, endCh, preParams)
    go func() {
        err := party.Start()
        // handle err ...
    }()
  2. Generate a Threshold Signature (Signing)

    master

    Use the signing.LocalParty to generate a signature using existing key data.

    Requirements:

    • You must provide the message to be signed.
    • You must provide the ourKeyData obtained from a previous keygen protocol.
    • At least t+1 signers are required to participate. For optimal usage, no more than t+1 should be involved, and all signers must have the same view of the participating group.

    Once completed, the signature is sent through the endCh.

    party := signing.NewLocalParty(message, params, ourKeyData, outCh, endCh)
    go func() {
        err := party.Start()
        // handle err ...
    }()
  3. Implement Secure Transport for TSS

    master

    Because the library leaves transport to the application layer, you must implement the following security measures to ensure protocol safety:

    1. End-to-End Encryption: Use TLS with an AEAD cipher between all parties.
    2. Session IDs: Wrap every message with a session ID unique to a single run (keygen, signing, or resharing). This ID must be agreed upon out-of-band. Reject any message where the session ID does not match the current session.
    3. Reliable Broadcast: Implement a mechanism (e.g., comparing hashes of received messages) to ensure that when a party broadcasts, every other party receives the exact same message.
    4. Error and Timeout Handling: Handle timeouts in your application. Use party.WaitingFor() to check which parties you are still waiting for, and inspect *tss.Error to identify culprit parties that caused a failure.
  4. Re-distribute Secret Shares (Re-Sharing)

    master

    Use the resharing.LocalParty to change the group of participants while keeping the secret.

    Important Safety Note: During re-sharing, key data may be modified during the rounds. Do not overwrite any data saved on disk until the final struct has been received through the `end channel.

    Upon completion, the save data received through endCh should overwrite the existing key data in storage (or write new data if receiving a new share).

    party := resharing.NewLocalParty(params, ourKeyData, outCh, endCh)
    go func() {
        err := party.Start()
        // handle err ...
    }()
  5. Setup a TSS LocalParty

    master

    To use the library, you must first initialize a LocalParty from the appropriate package (keygen, signing, or resharing). This requires setting up tss.Parameters and defining the participant group.

    1. Pre-compute Preparams: For keygen, it is recommended to pre-compute safe primes and Paillier secrets using keygen.GeneratePreParams to save time during the protocol.
    2. Define Participants: Create *tss.PartyID instances for every peer. Use tss.NewPartyID to define a unique id, a moniker, and a uniqueKey (e.g., a p2p public key as a big.Int).
    3. Configure Parameters: Use tss.NewParameters with a chosen elliptic curve (tss.S256() for ECDSA or tss.Edwards() for EdDSA), a tss.PeerContext, the current party's *tss.PartyID, the total number of parties, and the threshold.
    // Pre-compute preparams
    preParams, _ := keygen.GeneratePreParams(1 * time.Minute)
    
    // Setup participants
    parties := tss.SortPartyIDs(getParticipantPartyIDs())
    thisParty := tss.NewPartyID(id, moniker, uniqueKey)
    ctx := tss.NewPeerContext(parties)
    
    // Select curve and create parameters
    curve := tss.S256() // or tss.Edwards()
    params := tss.NewParameters(curve, ctx, thisParty, len(parties), threshold)
    
    // Maintain a map for incoming message routing
    partyIDMap := make(map[string]*tss.PartyID)
    for _, id := range parties {
        partyIDMap[id.Id] = id
    }
  6. Handle Messaging and Protocol Updates

    master

    The library does not provide a transport layer; you must implement your own. The outCh collects outgoing messages, and the endCh receives final results (signatures or save data).

    To update a party's state with messages received from the network, use one of the following thread-safe methods:

    • UpdateFromBytes(wireBytes []byte, from *tss.PartyID, isBroadcast bool) (ok bool, err *tss.Error): The main entry point for updating state from raw bytes received over the wire.
    • Update(msg tss.ParsedMessage) (ok bool, err *tss.Error): Used for local updates or testing.

    To convert a tss.Message into data for transmission:

    • WireBytes() ([]byte, *tss.MessageRouting, error): Returns encoded bytes and routing information.
    • WireMsg() *tss.MessageWrapper: Returns a protobuf wrapper (primarily for specific use cases like mobile apps).