A Go implementation of multi-party {t,n}-threshold ECDSA and EdDSA signature schemes based on Gennaro and Goldfeder CCS 2018 1. Provides distributed key generation, signing, and dynamic group re-sharing with no trusted dealer.
Based on bnb-chain/tss-lib with security hardening, constant-time arithmetic, session-bound Fiat-Shamir challenges, VSS correctness fixes, and adversarial input-validation hardening at protocol message boundaries.
This source tree is the breaking v4 release line, published concurrently with v3.1.0:
- Go module:
github.com/AnvoIO/tss-lib/v4 - Release branch:
release/v4.0.0 - First release:
v4.0.0
The v3 API is not bundled into the v4 source tree. It remains available and
maintained independently through the v3.1.0 tag and the
release/v3.1.0 branch using module path
github.com/AnvoIO/tss-lib/v3. Releasing v4 does not delete or invalidate
tagged v3 source or existing v3 module downloads.
Choose the major version through the Go import path. Do not mix v3 and v4 participants in one keygen, signing, or resharing session. Although v4 does not add protobuf fields relative to v3.1, its mandatory session nonce and stricter API contracts form a coordinated migration boundary.
| Area | v3.1.0 | v4.0.0 |
|---|---|---|
| Release intent | Wire-compatible v3 maintenance | Breaking migration |
| Go module | github.com/AnvoIO/tss-lib/v3 |
github.com/AnvoIO/tss-lib/v4 |
| Session nonce | Optional legacy fallback; fresh positive nonce strongly recommended | Fresh positive coordinated nonce required before Party.Start() |
| Party identities | Reference-backed; callers must treat contexts and IDs as immutable | Constructor snapshots and identity accessors are defensive deep copies; SetIDs removed |
Public Party API |
Low-level validation/storage/round hooks remain exposed but are not concurrent application entry points | Public interface narrowed to serialized lifecycle/update and concurrency-safe status/error operations |
| Wire and transcript | v3 protobuf format and legacy transcript fallback retained | Same protobuf fields as v3.1, but mandatory nonce changes transcript acceptance |
| Session deployment | Use only v3 participants | Use only v4 participants; migrate every party together |
Both versions remain available from their separate tags and maintenance branches.
- ECDSA threshold signatures -- {t,n}-threshold signing on secp256k1 and other curves
- EdDSA threshold signatures -- Edwards-curve variant following the same approach
- Distributed key generation -- no trusted dealer, each party holds one secret share
- Dynamic re-sharing -- change the group of participants while preserving the key
- Constant-time arithmetic -- centralized via
filippo.io/bigmodto prevent timing side channels - Session-bound proofs -- all Fiat-Shamir challenges bound to protocol SSID, preventing cross-session replay
- Paillier, DLN, range, and factor proofs -- with optional build-tag disable for testing (
-tags insecure_noproofs)
- Go 1.25+
- Protocol Buffers compiler (for regenerating wire format, not required to build)
go build ./...# Unit tests
make test_unit
# Unit tests with race detector
make test_unit_race
# Repeated adversarial lifecycle/wire concurrency regressions
make test_lifecycle_raceCreate a LocalParty from the keygen, signing, or resharing package and wire it to your network transport.
// Pre-compute safe primes and Paillier secret (can take time)
preParams, err := keygen.GeneratePreParams(1 * time.Minute)
if err != nil {
// handle error
}
// Create PartyIDs for each peer
parties := tss.SortPartyIDs(getParticipantPartyIDs())
thisParty := tss.NewPartyID(id, moniker, uniqueKey)
ctx := tss.NewPeerContext(parties)
// Select curve: tss.S256() for ECDSA, tss.Edwards() for EdDSA
params, err := tss.NewParameters(tss.S256(), ctx, thisParty, len(parties), threshold)
if err != nil {
// handle error
}
// Required: use a fresh positive nonce agreed by every participant.
// Never reuse it across protocol runs.
params.SetSessionNonce(sessionNonce)Party.Start() rejects a missing, zero, or negative nonce before protocol preparation begins. Set a fresh coordinated nonce before every keygen, signing, or resharing run.
party := keygen.NewLocalParty(params, outCh, endCh, preParams)
go func() {
err := party.Start()
// handle err ...
}()party := signing.NewLocalParty(message, params, ourKeyData, outCh, endCh)
go func() {
err := party.Start()
// handle err ...
}()For EdDSA, pass the exact message bytes so leading zeros are preserved:
party := eddsasigning.NewLocalPartyWithBytes(messageBytes, params, ourKeyData, outCh, endCh)party := resharing.NewLocalParty(params, ourKeyData, outCh, endCh)
go func() {
err := party.Start()
// handle err ...
}()// Receiving updates from the wire
UpdateFromBytes(wireBytes []byte, from *tss.PartyID, isBroadcast bool) (ok bool, err *tss.Error)
// Sending messages to the wire
WireBytes() ([]byte, *tss.MessageRouting, error)Concurrent transports may call Start, Update, and UpdateFromBytes on the
same party; updates are serialized, valid messages that arrive just before
Start are queued, and valid messages already queued after successful completion
are ignored. A fatal protocol error terminalizes the party and clears temporary
secrets before any queued update can run. Running, WaitingFor, String,
and WrapError are safe status/error helpers during concurrent delivery.
The public Party interface exposes only serialized update/lifecycle operations
and concurrency-safe status/error helpers. Concrete protocol types retain
ValidateMessage, StoreMessage, and FirstRound solely as implementation
hooks; application code should not type-assert and call them directly.
NewPeerContext and parameter constructors take deep identity snapshots, and
PeerContext.IDs() / Parameters.PartyID() return deep copies. Compare party
keys (or use SortedPartyIDs.IndexOf), never *PartyID pointer addresses.
PeerContext.IDs() allocates a deep copy on each call; applications targeting
very large committees should take one snapshot outside hot loops and reuse it
within that operation. The copy preserves sorted order and committee-local index
correspondence.
The transport layer is your responsibility. You must provide:
- Broadcast and point-to-point channels with end-to-end encryption (TLS with AEAD recommended)
- Session IDs unique to each protocol run, agreed upon out-of-band before rounds begin; pass the positive value with
SetSessionNonce - Reliable broadcast so all parties receive identical messages (hash-and-compare)
- Timeouts and error handling -- use
Party.WaitingFor()and*tss.Errorculprit info
Inbound transports should reject messages above 4 MiB before buffering; ParseWireMessage enforces the same ceiling as defense in depth.
Released concurrently with v3.1.0, v4.0.0 requires a fresh positive session nonce before every
Party.Start(), removes the legacy session fallback, changes the module path to
/v4, freezes committee and local identity snapshots, and narrows the public
Party interface to serialized application entry points. See the
CHANGELOG for migration details.
The v3.1 release remains at module path
github.com/AnvoIO/tss-lib/v3. It preserves the v3 protobuf/wire format and
legacy nonce fallback while adding protocol-boundary hardening, terminal party
lifecycle handling, race regressions, dependency maintenance, and governance
controls. A fresh positive nonce is strongly recommended. v3 source remains
available from its own tag and maintenance branch; it is not copied into this
v4 tree.
Security patch. No breaking API or wire-format changes — interoperable with honest
v3.0.0/v3.0.1 peers (the only API change is the additive ScalarMultChecked /
ScalarBaseMultChecked helpers). Closes a resharing-continuity authentication
bypass (SRC-2026-1155) and a resharing Paillier/NTilde modulus-size gap, ports
the upstream dual-committee resharing fix (bnb-chain/tss-lib#128) to EdDSA,
corrects abort attribution in several resharing/signing paths, and adds a batch of
defense-in-depth guards. A follow-up pass closed a reachable zero-scalar verifier
DoS (K13), hardened two one-time secret-modulus inversions against timing leaks,
and added ECDSA dual-committee test coverage. Found by a security audit of
resharing continuity and protocol-logic invariants. See the CHANGELOG and
Appendix C of the audit report.
Security patch. No API or wire-format changes — interoperable with honest v3.0.0
peers. Closes two input-validation gaps cross-referenced from upstream advisories
(SRC-2026-573, SRC-2026-644), including a remote denial-of-service against EdDSA
signers, plus six defense-in-depth / canonicality hardenings found by a security
boundary-validation audit. See the CHANGELOG and
Appendix B of the audit report.
- Every protocol run requires a fresh positive
Parameters.SetSessionNoncevalue agreed by all parties.Party.Start()fails before preparation when the nonce is absent or invalid. - Legacy zero/message-derived session fallbacks have been removed.
- The Go module and internal import path changed from
github.com/AnvoIO/tss-lib/v3togithub.com/AnvoIO/tss-lib/v4; integrations must update their imports. - Peer contexts and parameter identities are deep snapshots;
SetIDswas removed and identity accessors return copies. - The public
Partyinterface no longer exposes low-level validation/storage/round hooks; deliver throughUpdateorUpdateFromBytes. - No protobuf wire fields changed from v3.1, but every participant must coordinate the required nonce and migrate together.
PaillierSK.P and PaillierSK.Q fields were added. Key vaults from v1.x must be regenerated via re-sharing.
- Module path changed from
github.com/bnb-chain/tss-lib/v2togithub.com/AnvoIO/tss-lib/v3 tss.NewParameters()andtss.NewReSharingParameters()now returnerrorPrepareForSigning()(ECDSA/EdDSA) now returnserrorSetNoProofMod()/SetNoProofFac()blocked unless built with-tags insecure_noproofs- DLN proof, MTA range proof, and Alice init functions now require
Session []byteas first parameter - Proof hashes include additional inputs (
NTilde,h1,h2) and use tagged hashing -- wire-incompatible with v2 vss.Shares.ReConstruct()correctly requiresthreshold + 1shares (was silently wrong withthreshold)
All parties in a session must run the same version.
tss-lib/
tss/ Core types: Party, Parameters, PartyID, message routing
common/ Shared utilities, constant-time ModInt (bigmod), hash functions
crypto/ Elliptic curve helpers, Paillier, commitments, proofs
dlnproof/ Dlog-based non-interactive proofs
facproof/ Factor proofs
modproof/ Modular proofs
mta/ Multiplicative-to-additive conversion proofs
vss/ Verifiable secret sharing
ecdsa/
keygen/ ECDSA distributed key generation
signing/ ECDSA threshold signing
resharing/ ECDSA dynamic group re-sharing
eddsa/
keygen/ EdDSA distributed key generation
signing/ EdDSA threshold signing
resharing/ EdDSA dynamic group re-sharing
test/ Test helpers and configuration
Stratovera LLC (July 2026) -- Resharing-continuity and protocol-logic audit. A review of the resharing continuity invariants and the protocol-logic bug class (wrong indices, no-op consistency checks, unaborted proof failures, mis-attributed aborts) across ECDSA/EdDSA keygen, signing, and resharing, with adversarial verification of every candidate. Fixed a resharing-continuity authentication bypass (SRC-2026-1155) and a resharing Paillier/NTilde modulus-size gap, ported the upstream dual-committee resharing fix (bnb-chain/tss-lib#128) to EdDSA, corrected abort attribution in several paths, and added defense-in-depth guards. Released in v3.0.2. Documented in Appendix C of the report below.
Stratovera LLC (June 2026) -- Follow-up boundary-validation audit, cross-referencing upstream advisories (SRC-2026-573, SRC-2026-644) against this fork and reviewing adversarial input validation at every protocol message boundary. Fixed a remote denial-of-service (nil-pointer dereference on an off-curve point in EdDSA signing) and non-canonical EC point acceptance, plus six defense-in-depth / canonicality hardenings (J1–J8); no further exploitable vulnerability was found. Released in v3.0.1. Documented in Appendix B of the report below.
Stratovera LLC (February 2026) -- Full-scope audit of the ECDSA/EdDSA threshold signature implementation, covering keygen, signing, resharing, and all supporting cryptographic primitives. Identified 13 findings (2 critical, 3 high, 5 medium, 3 low). All findings have been addressed. The full report is available at security/2026-02-24-tss-lib-full-audit.md.
Kudelski Security (October 2019) -- Review of the original bnb-chain/tss-lib. The report is available in the upstream v1.0.0 release.
1 R. Gennaro and S. Goldfeder, "Fast Multiparty Threshold ECDSA with Fast Trustless Setup," CCS 2018. https://eprint.iacr.org/2019/114.pdf
Copyright (c) 2026 Stratovera LLC and its contributors.
See CONTRIBUTING.md for guidelines.