Beersy
BRC-140

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.

Darren Kellenschwiler6 min read
private keysharerecovered key3 of 5

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 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
  • 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 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 . 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

. p is the curve's coordinate-field prime, not the group order n. A private key is a scalar in [1, n-1] and n < 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−1 are (xᵢ, yᵢ) where each xᵢ and yᵢ is a fresh 32-byte random value reduced mod p.

These n points uniquely determine a degree (n − 1) polynomial. The polynomial is evaluated at an arbitrary coordinate x by 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:

  1. Draw a single 64-byte master seed from a CSPRNG, used as the key for the whole split.
  2. For share index i (0 ≤ i < m), starting at attempt = 0: a. Build the message counter = [ i, attempt, r₀ … r₃₁ ] where r₀ … r₃₁ are 32 fresh CSPRNG bytes drawn for this attempt. b. Compute x = HMAC-SHA-512(seed, counter) mod p. c. If x = 0 or x was already used by an earlier share, increment attempt and retry from (a). d. Abort with an error if a share's coordinate is not resolved within 5 attempts.
  3. Record x, then compute y = f(x). The share is the point (x, y).

The combination of a per-split master , 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 (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>
FieldEncoding
xBase58 of the big-endian byte array of the x-coordinate (no checksum)
yBase58 of the big-endian byte array of the y-coordinate (no checksum)
thresholdThe threshold n, as a base-10 integer
integrityThe 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:

  1. Verify |S| ≥ n. (Additional shares beyond n are permitted; the first n are used.)

  2. Verify that no two of the shares used share the same x-coordinate; reject duplicates with an error.

  3. Reconstruct the polynomial from the n points and evaluate it at x = 0 by Lagrange interpolation:

    k = f(0) = Σ_{i=0}^{n-1}  yᵢ · Π_{j≠i} (0 − xⱼ) · (xᵢ − xⱼ)⁻¹   (mod p)
    
  4. 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.

  5. Return k as the recovered private key.

Parameter constraints

  • threshold (n) and totalShares (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 n shares reveal no information about k, 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 structured KeyShares object.
  • PrivateKey.toBackupShares(threshold, totalShares): string[] — split and serialize to backup strings.
  • PrivateKey.fromKeyShares(keyShares): PrivateKey — reconstruct from a KeyShares object.
  • PrivateKey.fromBackupShares(shares): PrivateKey — parse backup strings and reconstruct.
  • The KeyShares class (points, threshold, integrity) with toBackupFormat() / fromBackupFormat().
  • The Polynomial and PointInFiniteField primitives implementing the field arithmetic and Lagrange interpolation.

References

Was this helpful?

Search Beersy

Search standards by number, title, author or topic