Inspiration

Threshold custody is usually treated as a solved problem: split the key, reconstruct it when needed, cite Shamir or Asmuth–Bloom, done. The reconstruction step is where the leak lives. Garner's algorithm needs an inverse modulo the next modulus, and that prefix depends on which shares were presented. Compute it with Euclid or binary GCD and the instruction trace becomes a function of secret-derived integers. That is not hypothetical — the 2025 mbedTLS binary-GCD inversion defect (CVE-2025-54764) is exactly that shape. So the question I started from was narrow: can a $(t,n)$ scheme be built where the unwrap path never divides, never runs a GCD, and never exponentiates anything secret?

What it does

Tessera-1 keeps a 256-bit master key under 3-of-5 custody. The seed is masked behind a uniform multiple of m0 and split into residues over a redundant residue number system, so any three holders reconstruct it and any two learn nothing. The reconstruction path is addition, subtraction, Montgomery multiplication, shifts and masked selects — every modular inverse it needs is a fact about the public moduli, so all 16 admissible subset coefficient vectors are tabulated and never computed at run time. On top of that sits an epoch ratchet that rotates the residue vector while the seed stays invariant, and an encrypt-then-MAC record layer for the payloads.

There is also a demo of where this is meant to run: an agent pipeline where a model is asked to charge a card it must never see. Ingress replaces digit runs with handles, the resolver releases the plaintext only inside the egress call, only for an allow-listed tool, and only with three holders present. Two of five is refused, a tool the model invents is refused, a flipped ciphertext bit fails authentication before decryption, and the dump written afterwards contains zero of the original digits.

How we built it

Zero dependencies, no_std core, because the interesting properties should be auditable without a supply chain. That meant writing SHA-256, HMAC, ChaCha20 and a DRBG in-crate, and doing arithmetic over nine 64-bit limbs with R = 2^576 and finely-integrated Montgomery REDC. The public tables — moduli, n0' , R² , Garner coefficients — are generated by a Python model that re-derives and asserts every published invariant and refuses to emit anything if one fails, which also gives an independent source for the known-answer vectors.

Timing was measured with paired adjacent sampling rather than absolute timings, because the test host could not be frequency-pinned without root: drift was up to 8.6% per round and cancels in the paired statistic. Thirteen attack tests encode the failures that must happen, and a CI job builds both examples, runs the suite and re-runs the pipeline self-check on every push.

Challenges we ran into

The parameter choice was the first surprise. Textbook illustrations pick share moduli a bit or two apart, which satisfies the Asmuth–Bloom inequality and still leaks: with nearly equal moduli a two-share view leaves only four candidate masks, so the "secret" is a handful of candidates. Making that hiding gap 2²⁴¹ instead of 4 forces the share moduli to roughly twice the seed width — 520-bit moduli for a 257-bit m0 . That is the real parameter, and it is a cost you pay for hiding, not for the threshold.

The ratchet was the second. The obvious version — every holder adds a fresh multiple of m0 to its residue — is only correct if the multiple is bounded. Unbounded, the value walks past the product of the moduli, CRT returns a different residue class, and the seed silently changes with no error raised anywhere. So steps are bounded and drift is detected by a seed commitment rather than assumed away.

The third was the honest part of the timing study. A reproducible effect remains: a narrow operand class unwraps about 4.8% faster than a broad one. It survives controls for store path, warm-up, a 62 MiB working set past a 12 MiB L3, clock drift, the number of distinct values and the number of differing limbs, and it carries no information about the seed. The mechanism is not explained, and the paper says so instead of rounding it off.

Accomplishments that we're proud of

A custody scheme whose unwrap path provably contains no division, no GCD and no secret-dependent exponentiation — with the inverses moved into compile-time constants because they were never secret to begin with. A quantified hiding gap rather than an inequality citation. A ratchet with a proof that it must be bounded, plus a demonstration of the silent corruption the unbounded version causes. And a deployment demo that ends with nine checks passing and zero plaintext digits in a dump it greps itself, so the claim runs rather than reads.

What we learned

That the threshold condition is not the safety parameter. Asmuth–Bloom's inequality can hold comfortably while the scheme is trivially breakable, because what matters is how many masks remain consistent with a $(t{-}1)$-share view. That timing claims are cheap to assert and expensive to support: without frequency pinning the only defensible statement was a paired comparison with an explicit effect size and an unexplained residual. And that differential testing pays — it found two implementation bugs that the correctness argument would have happily waved through.

What's next for Tessera-1: threshold custody without a modular inverse

Three concrete directions. Making the boundary real: today's demo is one process and says so on its first line, so the next step is putting the resolver behind a second process or a TEE instead of an interface. Explaining the 4.8% residual rather than tolerating it, which probably means a cycle-accurate model of the reduction path. And moving the masking from single-order boolean to something that resists higher-order power and EM analysis, which is the largest gap in the current threat model. The standing caveat stays as it is: this is a compositional improvement, not a new hardness assumption, and the paper is written to make that distinction easy to check.

Built With

  • asmuth?bloom-secret-sharing
  • cargo
  • chacha20
  • chinese-remainder-theorem
  • github
  • hmac-sha-256
  • latex
  • montgomery-arithmetic
  • no-std
  • python
  • rust
  • sha-256
Share this project:

Updates

Submission history