@substrate-system/shamirs-secret-sharing
v0.0.2
Published
Shamir's Secret Sharing for the browser
Readme
Shamir's Secret Sharing
Shamir's Secret Sharing for the browser. Take your private key, and split it into multiple "shards", and distribute the shards among other devices. The other devices could be your friends, or simply other machines that you own.
Install
Install from npm:
npm i -S @substrate-system/shamirs-secret-sharingQuick start
Split a secret into shares and reconstruct it later.
import { split, reconstruct } from '@substrate-system/shamirs-secret-sharing'
// Any bytes work. Here we use UTF-8 text for simplicity.
const secretBytes = new TextEncoder().encode('correct horse battery staple')
// Create 5 shares, any 3 can reconstruct
const shares = split(secretBytes, { min: 3, total: 5 })
// Pick any 3 shares to reconstruct
const recovered = reconstruct([shares[0], shares[2], shares[4]])
console.log(new TextDecoder().decode(recovered)) // "correct horse battery staple"Ed25519 with Web Crypto
Generate an Ed25519 key pair with the Web Crypto API, export the private key, and split it into shares. Works in modern browsers and Node 20+.
import { split, reconstruct } from '@substrate-system/shamirs-secret-sharing'
// In modern browsers and Node 20+, globalThis.crypto is available.
// For wider compatibility, you can use:
// import { webcrypto as crypto } from '@substrate-system/one-webcrypto'
const crypto = globalThis.crypto
// 1) Generate Ed25519 key pair
const keypair = await crypto.subtle.generateKey(
{ name: 'Ed25519' },
true, // extractable: we will export the private key
['sign', 'verify']
)
// 2) Export the private key as PKCS#8 bytes
const pkcs8 = await crypto.subtle.exportKey('pkcs8', keypair.privateKey)
const privateKeyBytes = new Uint8Array(pkcs8)
// 3) Split into 5 shares where any 3 can reconstruct
const shares = split(privateKeyBytes, { min: 3, total: 5 })
// 4) Later, reconstruct from any 3 shares
const recovered = reconstruct([shares[0], shares[2], shares[4]])
// 5) (Optional) Import the reconstructed key back to a CryptoKey
const importedPrivateKey = await crypto.subtle.importKey(
'pkcs8',
recovered,
{ name: 'Ed25519' },
true,
['sign']
)
console.log('Reconstruction successful:', importedPrivateKey instanceof CryptoKey)Use
Node/Browser ESM
import { split, reconstruct, type Share } from '@substrate-system/shamirs-secret-sharing'
const msg = 'hello shamir'
const secret = new TextEncoder().encode(msg)
const shares:Share[] = split(secret, { min: 2, total: 3 })
const restored = reconstruct([shares[0], shares[2]])
console.log(new TextDecoder().decode(restored)) // 'hello shamir'CommonJS
const { split, reconstruct } = require('@substrate-system/shamirs-secret-sharing')
const secret = Buffer.from('my secret', 'utf8')
const shares = split(secret, { min: 2, total: 3 })
const restored = reconstruct([shares[0], shares[1]])
console.log(Buffer.from(restored).toString('utf8'))Pre-built JS for browsers
This package exposes minified JS files too. Copy them to a location that is accessible to your web server, then link to them in HTML.
copy
cp ./node_modules/@substrate-system/shamirs-secret-sharing/dist/index.min.js ./public/sss.min.jsHTML
<script type="module" src="./sss.min.js"></script>API
split
Return an array of Share objects. At least min number of distinct
shares are needed to reconstruct the secret.
function split (
secret:Uint8Array,
options:{ min:number; total:number }
):Share[]secret: The bytes to protect (anyUint8Array: text, keys, binary data).options.min: Threshold k. Must be >= 2 and <=total.options.total: Number of shares n to generate. Max 254.
reconstruct
Return the original Uint8Array secret. Throws if insufficient
shares are provided.
function reconstruct(shares:Share[]):Uint8Arrayshares: An array of shares with the samethresholdand length.
Share
interface Share {
x:number // share index in [1..254]
y:Uint8Array // share bytes (same length as secret)
threshold?:number // k; included for convenience
}Serialization:
import { toString as u8ToString } from 'uint8arrays/to-string'
import { fromString as u8FromString } from 'uint8arrays/from-string'
const encoded = u8ToString(share.y, 'base64')
const decoded = u8FromString(encoded, 'base64')Limits and notes
- Maximum shares: 254 (GF(256) constraint).
- Threshold must be between 2 and
total. - All shares must be from the same
splitcall (same secret length and params). - Randomness: coefficients are generated using WebCrypto via
@substrate-system/one-webcryptofor wide platform compatibility. - Performance: linear in secret length and number of shares.
FAQ
- How big are shares? – Exactly the same length as the secret bytes plus
small constant metadata (
x, optionalthreshold). - Can I split strings? – Yes. Encode to bytes with
TextEncoder, and decode withTextDecoderafter reconstruction. - Is Node supported? – Yes. Use ESM or CJS as shown above.
- Is there a demo UI? – Yes, see
example/and the hosted demo linked above.
