Threshold Key Sharing and Backup via Shamir's Secret Sharing Scheme
A single key is a single point of failure: lose it and everything is gone, and copying it multiplies the chance of theft. This splits it into pieces where a chosen number of them rebuild it and any fewer reveal nothing.
Summary
- Why
- A single stored private key is either a single point of loss or, once copied for safety, a single point of theft, so a way is needed to back it up that survives losing some copies without any one copy being enough to steal.
- What
- BRC-140 defines how to split a secp256k1 private key into m shares, using Shamir's Secret Sharing over the curve's field, such that any n of them reconstruct the key and specifies an exact textual format for storing and recombining those shares.
- How
- A developer calls something like toBackupShares(threshold, totalShares) on a private key to get an array of serialized share strings to distribute, then calls fromBackupShares on any threshold-sized subset later to recover the original key.
What this lets you do
- Split a private key into m shares with a recovery threshold of n
- Serialize each share to a portable text string for offline storage
- Recover the original key from any n of the m shares
- Detect mismatched or wrong-key shares via a built-in integrity tag
- Reject malformed shares or shares from different splits during parsing
Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.
Reference for an AI
Everything an assistant needs to answer questions about BRC-140 accurately, including what it depends on.
The specification
Abstract
This standard defines a method for splitting a secp256k1 private key into m shares such that any n of those shares (where 2 ≤ n ≤ m) can reconstruct the original key, while any fewer than n shares reveal nothing about it. The scheme is an application of Shamir's Secret Sharing over the secp256k1 prime field, augmented with a cryptographically secure, non-deterministic share-coordinate generation procedure and a short integrity tag. A canonical textual ("backup") serialization is specified so that individual shares can be written down, stored offline, and later recombined. This codifies the toKeyShares / fromKeyShares and toBackupShares / fromBackupShares methods of the PrivateKey class in the BSV TypeScript SDK.
Motivation
A single private key is a single point of failure: lose it and funds or identity are gone; leak it and they are stolen. Common mitigations — full copies in multiple locations — multiply the leak surface for every copy made. Threshold secret sharing breaks this trade-off: a key is distributed across m custodians or locations, a quorum of n is required to recover it, and a minority of compromised or lost shares neither reveals the key nor prevents recovery.
This standard targets the backup and recovery use case (e.g. toBackupShares(2, 3): split into three shares, any two recover the key). It specifies the field arithmetic, the share-coordinate generation, the integrity tag, and the textual serialization precisely enough that an independent implementation can produce shares recoverable by, and recover keys from, the reference implementation.
This scheme covers storage and reconstruction of a key. It is not a threshold-signature scheme: the key is fully reassembled in one place at recovery time, and the dealer (the party performing the split) sees the whole key. It does not provide verifiable secret sharing (a malicious dealer is not provably constrained beyond the integrity tag), nor does it protect the key while reassembled.
Specification
Field
All share arithmetic is performed in the prime field 𝔽<sub>p</sub>, where p is the secp256k1 field prime:
p = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F
Note.
pis the curve's coordinate-field prime, not the group ordern. A private key is a scalar in[1, n-1]andn < p, so every valid private key is also a valid element of 𝔽<sub>p</sub>. The secret is treated purely as an integer; no elliptic-curve point operations are involved in the sharing itself.
Secret encoding
Let k be the private key interpreted as a big-endian unsigned integer. k is the secret and is defined as the value of the sharing polynomial at x = 0:
f(0) = k
Polynomial
To support a threshold of n, the dealer defines a polynomial of degree n − 1 over 𝔽<sub>p</sub>. The reference implementation represents this polynomial implicitly by n points rather than by explicit coefficients:
- Point 0 is
(0, k)— the secret. - Points
1 … n−1are(xᵢ, yᵢ)where eachxᵢandyᵢis a fresh 32-byte random value reduced modp.
These n points uniquely determine a degree (n − 1) polynomial. The polynomial is evaluated at an arbitrary coordinate x by Lagrange interpolation through its defining points:
f(x) = Σ_{i=0}^{n-1} yᵢ · Π_{j≠i} (x − xⱼ) · (xᵢ − xⱼ)⁻¹ (mod p)
All additions, subtractions, multiplications, and the modular inverse (·)⁻¹ are performed mod p.
Share-coordinate generation
For each of the m shares the dealer must choose a distinct, non-zero x-coordinate at which to evaluate f. Coordinates MUST be generated as follows so that a split is non-deterministic and remains safe even if the system RNG is partially compromised:
- Draw a single 64-byte master
seedfrom a CSPRNG, used as the HMAC key for the whole split. - For share index
i(0 ≤ i < m), starting atattempt = 0: a. Build the messagecounter = [ i, attempt, r₀ … r₃₁ ]wherer₀ … r₃₁are 32 fresh CSPRNG bytes drawn for this attempt. b. Computex = HMAC-SHA-512(seed, counter) mod p. c. Ifx = 0orxwas already used by an earlier share, incrementattemptand retry from (a). d. Abort with an error if a share's coordinate is not resolved within 5 attempts. - Record
x, then computey = f(x). The share is the point(x, y).
The combination of a per-split master seed, the per-share index, the retry counter, and fresh per-attempt randomness ensures non-determinism, uniqueness, and resistance to RNG bias.
Integrity tag
Each share carries an 8-character integrity tag binding it to its key. The tag is the first 8 hexadecimal characters (4 bytes) of the HASH160 (RIPEMD-160(SHA-256(·))) of the compressed public key corresponding to the private key:
integrity = hex( HASH160( compressedPublicKey ) )[0:8]
All shares of one key carry the same tag. The tag lets a recovery implementation reject shares that belong to different keys and confirm that a reconstructed key matches the original. It is a non-secret checksum, not an authentication mechanism: 4 bytes is enough to catch accidental mismatch but MUST NOT be relied upon to prove key ownership.
Backup serialization
A share is serialized to text as four .-separated fields:
<x>.<y>.<threshold>.<integrity>
| Field | Encoding |
|---|---|
x | Base58 of the big-endian byte array of the x-coordinate (no checksum) |
y | Base58 of the big-endian byte array of the y-coordinate (no checksum) |
threshold | The threshold n, as a base-10 integer |
integrity | The 8-character hex integrity tag |
x and y together form the point; the leading <x>.<y> substring is the point serialization. Example shares for a (3, 5) split (threshold 3, integrity 2f804d43):
45s4vLL2hFvqmxrarvbRT2vZoQYGZGocsmaEksZ64o5M.A7nZrGux15nEsQGNZ1mbfnMKugNnS6SYYEQwfhfbDZG8.3.2f804d43
7aPzkiGZgvU4Jira5PN9Qf9o7FEg6uwy1zcxd17NBhh3.CCt7NH1sPFgceb6phTRkfviim2WvmUycJCQd2BxauxP9.3.2f804d43
9GaS2Tw5sXqqbuigdjwGPwPsQuEFqzqUXo5MAQhdK3es.8MLh2wyE3huyq6hiBXjSkJRucgyKh4jVY6ESq5jNtXRE.3.2f804d43
A parser MUST reject any share that does not split into exactly four fields, and MUST reject a set of shares whose threshold fields or integrity fields are not all equal.
Reconstruction
Given a set S of distinct shares with common threshold n:
-
Verify
|S| ≥ n. (Additional shares beyondnare permitted; the firstnare used.) -
Verify that no two of the shares used share the same x-coordinate; reject duplicates with an error.
-
Reconstruct the polynomial from the
npoints and evaluate it atx = 0by Lagrange interpolation:k = f(0) = Σ_{i=0}^{n-1} yᵢ · Π_{j≠i} (0 − xⱼ) · (xᵢ − xⱼ)⁻¹ (mod p) -
Derive the public key from
k, compute its integrity tag as above, and compare against the shares' tag. If they differ, abort with an integrity-mismatch error. -
Return
kas the recovered private key.
Parameter constraints
threshold(n) andtotalShares(m) MUST be integers.n ≥ 2.m ≥ 2.n ≤ m.
A threshold of 1 is disallowed: it would make any single share equal to the key.
Security considerations
- Dealer trust. The party that splits the key sees the whole key and, at recovery, the key is fully reassembled in one place. This scheme protects keys at rest across distributed shares; it does not provide threshold signing or guard the key while in use.
- Information-theoretic secrecy below threshold. Fewer than
nshares reveal no information aboutk, because the remaining coefficient(s) of the degree(n − 1)polynomial are uniformly random over 𝔽<sub>p</sub>. - Integrity tag is not authentication. The 4-byte tag detects accidental share mismatch and confirms a correct reconstruction. It is short and public; it does not prevent a motivated adversary who controls share distribution from substituting a different key, and it MUST NOT be treated as proof of ownership.
- Coordinate generation. Implementations MUST use the HMAC-based, per-attempt-randomized coordinate generation so that repeated splits of the same key produce different shares and a biased or partially compromised RNG does not yield colliding or predictable coordinates.
- Share storage. Each serialized share is sufficient, in quorum, to recover spending authority. Shares SHOULD be stored with the same care as the key fragments they are, in mutually independent locations.
Implementations
The scheme is implemented in the BSV TypeScript SDK (@bsv/sdk) on the PrivateKey primitive:
import { PrivateKey } from '@bsv/sdk'
// Split into 3 shares, any 2 of which can recover the key.
const key = PrivateKey.fromRandom()
const shares: string[] = key.toBackupShares(2, 3)
// Later, recover from any 2 of the 3 shares.
const recovered = PrivateKey.fromBackupShares([shares[0], shares[2]])
// recovered.toWif() === key.toWif()
Relevant methods:
PrivateKey.toKeyShares(threshold, totalShares): KeyShares— split into a structuredKeySharesobject.PrivateKey.toBackupShares(threshold, totalShares): string[]— split and serialize to backup strings.PrivateKey.fromKeyShares(keyShares): PrivateKey— reconstruct from aKeySharesobject.PrivateKey.fromBackupShares(shares): PrivateKey— parse backup strings and reconstruct.- The
KeySharesclass (points,threshold,integrity) withtoBackupFormat()/fromBackupFormat(). - The
PolynomialandPointInFiniteFieldprimitives implementing the field arithmetic and Lagrange interpolation.
References
- <a name="footnote-1">1</a>: Shamir, A. (1979). "How to share a secret." Communications of the ACM, 22(11), 612–613.
- <a name="footnote-2">2</a>: BRC-42: BSV Key Derivation Scheme (BKDS)
- <a name="footnote-3">3</a>: SEC 2: Recommended Elliptic Curve Domain Parameters (secp256k1)
- <a name="footnote-4">4</a>: BSV Blockchain TypeScript SDK —
PrivateKeyprimitive: https://github.com/bsv-blockchain/ts-sdk