Guardians choose their own shares from their existing keys — the owner collects them, publishes a short auxiliary value φ, and guardians can recover the secret on demand without ever having stored anything extra.
Bottom-Up Secret Sharing inverts the classical trust model of Shamir SSS.
In traditional schemes, the dealer chooses all shares and distributes them to
guardians — who must store them securely forever. In BUSS, each guardian
independently derives their own share from their existing secret key:
σⱼ = H(owner_id ‖ skⱼ). The owner collects these
guardian-chosen shares, builds the unique polynomial of degree n through (0, secret) and all
guardian points, then publishes a short auxiliary value φ to a public bulletin board.
When recovery is needed, any t+1 guardians recompute their share on demand — the
same hash call, no stored state.
The key insight: a single guardian key serves as guardian for many key-owners.
Each distinct owner_id yields an independent, uncorrelated
share — so a guardian protecting their own hardware wallet key automatically becomes
a potential guardian for any friend who registers them.
BUSS operates in two phases. During backup, each guardian derives their share from their own secret key and the owner's identifier. The owner collects all guardian shares, builds a unique degree-(n−1) polynomial through (0, secret) and the n−1 guardian points, then publishes a short auxiliary value φ to a public bulletin board.
Guardian j computes σⱼ = H(owner_id ‖ skⱼ)
deterministically from their own secret key and the owner's public identifier.
No extra storage, no round-trip coordination — the same call always yields the same σⱼ.
The owner builds the unique degree-(n−1) polynomial f through
(0, s), (1, σ₁), …, (n−1, σ_{n−1}), then evaluates at
the negative integers to produce
φ = (f(−1), …, f(−(n−t−1))) — just n−t−1 field elements
posted publicly (e.g., on-chain).
Any t+1 guardians recompute their σ on-demand (same hash call as step 01). Combined with the n−t−1 public φ points, they have exactly n evaluations of f — enough to recover s = f(0) via Lagrange interpolation.
The guardian_share function is the primitive that enables
stateless recovery. It hashes owner_id ‖ guardian_sk using a
64-byte hash function and reduces the output to a field element via
FromUniformBytes<64> — bias < 2⁻¹²⁸ for 256-bit fields.
use arc_pleiades::{BottomUpSSS, guardian_share};
use arc_pleiades::bottom_up::BottumUpSS;
use arc_pleiades::bottom_up::buss::Share;
use midnight_curves::Fq as Scalar;
use sha2::Sha512;
use rand::thread_rng;
let owner_id = b"alice@example.com";
let secret = Scalar::random(&mut thread_rng());
// ── Backup: each guardian independently derives σ from their own key ──
let g_keys: Vec<Scalar> = (0..4).map(|_| Scalar::random(&mut thread_rng())).collect();
let sigma_b: Vec<Share<Scalar>> = g_keys.iter().enumerate()
.map(|(i, &sk)| Share {
x: Scalar::from((i + 1) as u64),
y: guardian_share::<Scalar, Sha512>(owner_id, sk),
})
.collect();
// ── Owner collects all σ, builds f, publishes φ (2 field elements) ──
let buss = BottomUpSSS::new(2, 5)?; // t=2, n=5 → 4 shares, threshold 3
let phi = buss.split(secret, &sigma_b)?;
// Publish phi to a bulletin board (n−t−1 = 2 field elements)
// ── Recovery: any 3 guardians recompute σ on-demand — no stored state ──
let sigma_r = vec![
Share { x: Scalar::from(1u64), y: guardian_share::<Scalar, Sha512>(owner_id, g_keys[0]) },
Share { x: Scalar::from(3u64), y: guardian_share::<Scalar, Sha512>(owner_id, g_keys[2]) },
Share { x: Scalar::from(4u64), y: guardian_share::<Scalar, Sha512>(owner_id, g_keys[3]) },
];
let recovered = buss.reconstruct(&phi, &sigma_r)?;
assert_eq!(secret, recovered);