Skip to content

S3: GF(256) field + SLIP-39 Shamir split/recover#110

Draft
SachinMeier wants to merge 2 commits into
sachin--slip39-2-bip39from
sachin--slip39-3-gf256-shamir
Draft

S3: GF(256) field + SLIP-39 Shamir split/recover#110
SachinMeier wants to merge 2 commits into
sachin--slip39-2-bip39from
sachin--slip39-3-gf256-shamir

Conversation

@SachinMeier

Copy link
Copy Markdown
Collaborator

Stack: S0 → S1 → S2 → S3 → S4 → S5 → S6 (spec §3.4, §3.5)

  • Bitcoinex.SLIP39.GF256: Rijndael-poly (0x11B) field; exp/log tables generated in the module body at compile time with compile-time assertions that the generator-3 cycle is exactly 255; add/mul/divide/pow/interpolate (byte-lane Lagrange)
  • Bitcoinex.SLIP39.Shamir: split_secret/4 (digest share at 254, secret at 255, HMAC-SHA256 digest per SLIP-39), recover_secret/2 rejecting bad share sets via :invalid_digest
  • FIPS-197 0x57·0x13=0xFE KAT verified against an independent slow multiply over the full 256×256 field; algebraic properties (commutativity, distributivity, Fermat); every-T-subset recovery for (2,3)/(3,5)/(2,2)/(16,16) on 16- and 32-byte secrets; corruption/under-threshold rejection. Deterministic rng stub in all tests.

🤖 Generated with Claude Code

@SachinMeier
SachinMeier force-pushed the sachin--slip39-3-gf256-shamir branch from 595d728 to 6360071 Compare July 2, 2026 04:22
@SachinMeier

Copy link
Copy Markdown
Collaborator Author

Independent adversarial review (subagent) complete: math verified clean — exhaustive 65,536-pair mul sweep against an independent Python peasant-multiply; divide/pow/interpolate cross-checked; Shamir split matched a reference implementation byte-for-byte for (2,3)/(3,5)/(4,7)/(16,16); every under-threshold subset rejected; share uniformity chi-square clean.

Addressed in the follow-up commit:

  • split_secret/4 count capped at 16 in guards (fail closed — an uncapped count would emit the secret verbatim as the share at anchor index 255)
  • recover_secret/2 rejects duplicate share indices with :duplicate_share_indices instead of silently interpolating a wrong value only the digest would catch

SachinMeier and others added 2 commits July 1, 2026 21:26
Add Bitcoinex.SLIP39.GF256: arithmetic over GF(2^8) with the Rijndael
polynomial 0x11B. exp/log tables are generated in the module body at
compile time from generator 3 (with compile-time assertions that the
exp cycle closes at 255 and covers every nonzero element), plus
add/2, mul/2, divide/2, pow/2, and byte-lane-wise Lagrange
interpolate/2 per the SLIP-0039 formula.

Add Bitcoinex.SLIP39.Shamir: split_secret/4 and recover_secret/2 with
the SLIP-0039 digest scheme (secret at index 255, digest share at 254,
digest = HMAC-SHA256(random_part, secret)[0..3] ++ random_part),
create_digest/2 / valid_digest?/2, {:error, :invalid_digest} on wrong,
corrupted, or under-threshold share sets, and the threshold == 1
verbatim-copy case. Randomness is injected via an rng function;
production callers must pass a CSPRNG.

Tests cover the FIPS-197 known answer (0x57 * 0x13 == 0xFE), a full-
field cross-check against an independent slow multiplication, division
by zero raising ArithmeticError, algebraic properties (commutativity,
distributivity, inverses, Fermat), interpolation of known polynomials
from any T-subset of points, Shamir round-trips for (2,3)/(3,5)/(2,2)/
(16,16) over every T-subset with 16- and 32-byte secrets, digest
rejection of corrupted or insufficient shares, and property tests with
a deterministic rng stub (no CSPRNG in tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- cap split_secret count at 16 in the clause guards (fail closed: an
  uncapped count would emit the secret verbatim as the share at the
  anchor index 255)
- recover_secret rejects duplicate share indices with
  {:error, :duplicate_share_indices} instead of silently interpolating
  a wrong value that only the digest would catch

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SachinMeier
SachinMeier force-pushed the sachin--slip39-2-bip39 branch from 9896406 to 6b4eeab Compare July 2, 2026 04:27
@SachinMeier
SachinMeier force-pushed the sachin--slip39-3-gf256-shamir branch from 6360071 to d77750f Compare July 2, 2026 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant