Skip to content

S4: SLIP-39 Feistel encryption (Cipher)#111

Draft
SachinMeier wants to merge 2 commits into
sachin--slip39-3-gf256-shamirfrom
sachin--slip39-4-cipher
Draft

S4: SLIP-39 Feistel encryption (Cipher)#111
SachinMeier wants to merge 2 commits into
sachin--slip39-3-gf256-shamirfrom
sachin--slip39-4-cipher

Conversation

@SachinMeier

Copy link
Copy Markdown
Collaborator

Stack: S0 → S1 → S2 → S3 → S4 → S5 → S6 (spec §3.6)

  • Bitcoinex.SLIP39.Cipher: 4-round balanced Feistel, round fn PBKDF2-HMAC-SHA256 with 2500 << e iterations/round; salt "shamir" <> id (ext=false) or empty (ext=true); one shared feistel helper for both directions
  • Known-answer test: official vector update base58 to have simpler logic and more tests #1's EMS hand-extracted from the mnemonic per the §3.8 wire layout (derivation shown in comments, pre-verified against an independent Python reference) — asserts both decrypt(ems)==ms and encrypt(ms)==ems
  • Round-trip property over random MS/passphrase/e/ext/id

🤖 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 SachinMeier force-pushed the sachin--slip39-4-cipher branch from b7440e5 to 9c16226 Compare July 2, 2026 04:22
@SachinMeier

Copy link
Copy Markdown
Collaborator Author

Independent adversarial review (subagent) complete: APPROVE, no correctness/security findings. Independent Python implementation of the SLIP-39 cipher matched byte-for-byte on all 10 random tuples (including decrypt-applied-to-plaintext, which catches round-order errors round-trips cannot); the known-answer test's hand-extracted vector-0 parameters (id 7945, ext false, e 0) were independently re-derived and confirmed.

Addressed in the follow-up commit:

  • guards on encrypt/5/decrypt/5 rejecting odd-length and empty input at the public boundary (FunctionClauseError instead of a deep MatchError)

SachinMeier and others added 2 commits July 1, 2026 21:26
Add Bitcoinex.SLIP39.Cipher with encrypt/5 and decrypt/5: the four-round
balanced Feistel cipher SLIP-39 uses to turn a master secret into an
encrypted master secret under a passphrase. Both directions share one
private feistel/6 helper; the round function is PBKDF2-HMAC-SHA256 with
2500 <<< iteration_exponent iterations per round, salted with
"shamir" <> <<identifier::16>> (or an empty salt for extendable share
sets).

Tests cover round-trip identity across ext/e/passphrase/MS-length
combinations, salt and iteration-exponent threading, a stream_data
round-trip property, and a known-answer test that hand-extracts the EMS
and cipher parameters from official SLIP-39 vector 1's mnemonic wire
layout and asserts decryption recovers the vector's master secret.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guard encrypt/5 and decrypt/5 against odd-length and empty input at the
public boundary so misuse fails as a clear FunctionClauseError rather
than a MatchError inside the Feistel split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SachinMeier SachinMeier force-pushed the sachin--slip39-4-cipher branch from 9c16226 to cad06d0 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