This is the guide for recovering a chela secret from the paper cards with nothing but pen, paper, and patience. No computer, no calculator beyond basic arithmetic, no internet. The point is to give a future you (or your descendants, or you-in-a-bunker) a recovery path that survives even if every chela tool, mirror, archive, and copy of the code is gone.
It's a long doc. Set aside a day. The actual cryptographic math is small - a few hundred bit-flips, additions, and table lookups - but every step is spelled out from the ground up so a careful reader who's never seen binary numbers before can still get through it.
This doc teaches everything you need from scratch. You can skip the "pre-flight" section if you already know binary, hex, and XOR.
| Item | Why |
|---|---|
At least M of your N paper cards |
The input. M is the "Required to recover" number printed on each card. |
| A printed copy of the BIP-39 English wordlist | Look up word → number and number → word |
| The GF(2⁸) inverse table in the appendix of this doc | Division step (Step 5) |
| Plain paper, pen, eraser | For all the working-out |
| Time | First-time, a careful walkthrough takes ~4 hours |
The BIP-39 wordlist is a list of 2048 short English words. It's the same list every BIP-39 wallet in the world uses. Print or write down a copy before you need it; without the list, you can't decode the words on the cards. (You can get it from anywhere - the canonical source is https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt.)
The words are all you need. Everything recovery depends on - each
card's coordinate x, the threshold M, the generation tag that groups
cards, and the kind of secret stored - is packed inside the words
themselves. The CHELA-… code printed on the card is a convenience for
sorting cards by eye; you never read a number off the label to recover.
If all you have is the list of words from each card, you can still finish.
Each card ends with a single checksum word that catches transcription typos. It's a CRC - an 11-bit long division you can do by hand (Step 4 and Appendix C describe it), but it only tells you whether a card was copied correctly, not what the secret is. The recovery math runs without it, so the main walkthrough computes the recovery first and treats the checksum as an end-of-run cross-check.
This guide needs no SHA-256 and no hashing of any kind. The whole procedure is XOR, one byte-multiply rule, table lookups, and (if you want the checksum) one long division.
The body you recover also carries a one-byte SHA-256 integrity check (the integrity byte, Step 6). The chela tool uses it to confirm the right cards were combined; by hand you simply strip it off and never recompute it, so the procedure stays hash-free.
Skip these if you already know binary, hex, bytes, and XOR.
We humans usually write numbers in decimal (base 10): each digit is one of 0–9. Computers use binary (base 2): each digit is 0 or 1. There's a third form, hexadecimal (or "hex", base 16): each digit is one of 0–9 or A, B, C, D, E, F (A means 10, B means 11, …, F means 15).
Hex is convenient because every hex digit equals exactly four binary digits. So if you know the table for 0–15, you can flip between hex and binary trivially.
Here's the table you have to memorise. Just the first column ↔ third column matters; decimal is shown for reference.
| Decimal | Hex | Binary |
|---|---|---|
| 0 | 0 | 0000 |
| 1 | 1 | 0001 |
| 2 | 2 | 0010 |
| 3 | 3 | 0011 |
| 4 | 4 | 0100 |
| 5 | 5 | 0101 |
| 6 | 6 | 0110 |
| 7 | 7 | 0111 |
| 8 | 8 | 1000 |
| 9 | 9 | 1001 |
| 10 | A | 1010 |
| 11 | B | 1011 |
| 12 | C | 1100 |
| 13 | D | 1101 |
| 14 | E | 1110 |
| 15 | F | 1111 |
Throughout this doc, when you see 0x in front of something it means
"this is a hex number". So 0x68 = the hex number "68" = 6 × 16 + 8
= 104 in decimal.
To turn 2 hex digits into 8 binary digits: write the left digit's 4-bit pattern from the table, then the right digit's 4-bit pattern.
0x68→6is0110,8is1000, side by side:0110 1000
To turn 8 binary digits into 2 hex digits: take 4 bits at a time, look up each group.
0110 0010→0110is 6,0010is 2, joined:0x62
A space between every 4 binary digits makes this easier to do by eye - get in the habit of writing them like that.
A bit is one binary digit. A byte is 8 bits in a row. A byte can
hold any value from 0 to 255 (or 0x00 to 0xFF in hex). The text character
h (the letter "h") is the byte 0x68. The character i is 0x69. The
word "hi" is two bytes: 0x68 0x69.
(You don't have to memorise every letter's byte value - there's a table in the appendix for ASCII text. You only need it at the very end if your secret was text.)
XOR (written ⊕, said "ex-or") is the most important operation in
this guide. It takes two bits and produces one bit:
| a | b | a ⊕ b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
In words: "if the two bits are the same, the answer is 0; if they're different, the answer is 1."
To XOR two bytes, line them up and XOR each pair of bits in turn. Always work bit by bit, top to bottom or left to right - never try to do it in your head.
0x68 = 0110 1000 0x42 = 0100 0010 ──────────────── XOR = 0010 1010 = 0x2AChecking column by column:
- column 1: 0 and 0 are the same → 0
- column 2: 1 and 1 are the same → 0
- column 3: 1 and 0 are different → 1
- column 4: 0 and 0 are the same → 0
- column 5: 1 and 0 are different → 1
- column 6: 0 and 0 are the same → 0
- column 7: 0 and 1 are different → 1
- column 8: 0 and 0 are the same → 0
Joined:
00101010→0010 1010→0x2A. ✓
Two properties to remember:
- XOR is its own undo.
(a ⊕ b) ⊕ b = a. This is why chela can hide secrets inside seemingly random bytes and pull them back out. - In this guide, addition and subtraction of bytes are both XOR. When
you see
+or−between bytes, do XOR.
That's all the maths-prerequisite. Take a break here if it's a lot.
Pick up one of your cards and look at it. Here are all the parts that matter for recovery (everything else on the card is human-readable labelling - feel free to ignore the description, the "share holders" list, the brand stamp, etc.):
┌─────────────────────────────────────────────────────────┐
│ │
│ ALICE'S NOTE │ ← title (ignore)
│ │
│ Group tag: 02C9 │ ← (1) generation tag
│ Required: 2 of 3 │ ← (2) M of N
│ Card code: CHELA-02C9-6-2-3-6 │ ← (3) full code
│ │
│ Your share words: │
│ 1. chimney │
│ 2. float │
│ 3. vintage │ ← (4) the words
│ 4. before │
│ 5. learn │
│ 6. film │
│ │
│ …recovery instructions… │
│ │
└─────────────────────────────────────────────────────────┘
Everything below is printed for convenience - useful for sorting and sanity-checking cards, but recovery reads it all back out of the words, so you don't have to trust the label.
(1) Generation tag - four hex characters (02C9 here). This is a
random number chosen once when the cards were made, and printed on every
card from that one making. It's not derived from the secret. Its only
job is to group cards: every card from the same making shows the same tag.
If two cards show different tags, they're from different makings and won't
combine. (You'll re-read this same tag out of the words in Step 1, so a
smudged label doesn't matter.)
(2) Required / total - the threshold "M" (the number of cards you need; the LEFT number in "2 of 3") and total "N" (the right number). You need at least M cards to recover. Extras don't hurt; missing cards below M mean no recovery, ever. (M also comes back out of the words; N is only ever a human hint and recovery never needs it.)
(3) Card code - the whole header in one line.
CHELA-02C9-6-2-3-6 decomposes as:
02C9- the generation tag (same as item 1)6- this card's coordinate (calledxin the math). It is a random number in the range 1–32, not the card's position in the set. Card "number one" of a making will usually not show1here, and that's correct - every card just gets a randomx.2- threshold M (same as the left of item 2)3- total N (same as the right of item 2)6- number of words on this card
(4) The words - the actual data, and the only thing recovery truly needs. Every card from one making has the same number of words. Each word stands in for an 11-bit number from the BIP-39 wordlist. The words split into four parts, which Step 1 pulls apart:
word 1 the metadata word → this card's x, and M
word 2 the generation tag → the same 4-hex number on every card
words 3 … (W−1) the share body → the numbers we'll combine
word W (last) the checksum word → optional typo check (Step 4 / Appendix C)
There are always at least 4 words on a card (one each for metadata, tag, body, checksum). A card with fewer than 4 words is not a valid chela card.
To read the first word you need the ranges the format allows:
x(this card's coordinate) is 1 to 32. In the words it's stored as a 5-bit field0..31; the stored field plus 1 isx.M(threshold) is 2 to 32. Stored as a 5-bit field0..30; the stored field plus 2 isM. (A field of 31 would mean M = 33 - invalid; if you ever decode that, you mis-read a word.)N(total) is at most 32. It is never needed to recover.
You'll use the "+1 for x, +2 for M" offsets in Step 1.
Here's the whole shape of what we're going to do. Read this through once before starting Step 1; it's a map.
- Read each card's words and pull out its four parts.
- Look up each body word in the BIP-39 wordlist to get a number.
- Convert each number to 11 bits of binary and stitch them together.
- Cut the bits into bytes - these are the "share bytes" for the card.
- Lagrange-combine the share bytes from the M cards you have. This is the only step with non-trivial maths; the appendix has the lookup table that makes it manageable.
- Read out the body bytes: drop the last byte (it names the kind of secret) and the byte before it (a one-byte integrity check you can't redo by hand), then for text look up each remaining byte in the ASCII table, or for a BIP-39 seed follow the guidance at the end.
We'll do all of it with a complete worked example: a 2-of-3 split of
the text "hi". The three cards from that split are:
CHELA-02C9-6-2-3-6
chimney float vintage before learn film
CHELA-02C9-3-2-3-6
avoid float chat ancient orphan produce
CHELA-02C9-20-2-3-6
object float assist elevator also hole
We'll recover using the first two cards (the ones with x = 6 and
x = 3). The third card (x = 20) is shown so you can practise on it
separately if you want. Notice the coordinates are 6, 3, 20 - random,
not 1, 2, 3. That's expected.
For each card you have, write down (on a fresh sheet of paper) the words in order, then split them into the four parts. We do the metadata and tag words by hand here; the body and checksum words wait for Step 2.
Take card 1 (chimney float vintage before learn film). Look up the
first word and the second word in the wordlist:
| Word | Line # | Number |
|---|---|---|
| chimney | 321 | 320 |
| float | 714 | 713 |
(The wordlist is numbered from 0, so number = line number − 1. Step 2 explains the lookup in full; for now you only need these two.)
The metadata word (word 1) → x and M. Write the first word's
number, 320, in 11 bits (Step 3 teaches the conversion; the answer is):
chimney = 320 = 0 0 1 0 1 │ 0 0 0 0 0 │ 0
└─ X ──┘ └─ M ──┘ └ reserved
bits 10..6 bits 5..1 bit 0
Read it left to right and split it 5 | 5 | 1:
- X field =
00101= 5.x = field + 1 = 6. - M field =
00000= 0.M = field + 2 = 2. - reserved bit =
0. It must be 0. If it's 1, you mis-read the word - go back and check it.
So card 1 is coordinate x = 6, threshold M = 2. (The 6 and 2
printed in the card code match - that's the cross-check, not the source.)
The generation tag (word 2). The second word's number is the tag.
float = 713 = 0x2C9. Every card from this making must give the same
713 here. (It matches the 02C9 printed on the label.)
Now do the same for card 2 (avoid float chat ancient orphan produce):
| Word | Line # | Number |
|---|---|---|
| avoid | 129 | 128 |
| float | 714 | 713 |
avoid = 128 = 0 0 0 1 0 │ 0 0 0 0 0 │ 0
└─ X ──┘ └─ M ──┘ └ reserved
- X field =
00010= 2.x = 3. - M field =
00000= 0.M = 2. - reserved = 0. Good.
- tag word =
float= 713 =0x2C9. Same tag as card 1 - good.
Write the running summary:
CARD 1 x = 6 M = 2 tag = 713 (0x2C9) words = 6
body words: vintage, before, learn checksum word: film
CARD 2 x = 3 M = 2 tag = 713 (0x2C9) words = 6
body words: chat, ancient, orphan checksum word: produce
The body words are everything from word 3 up to but not including the last word. The last word is the checksum (we handle it in Step 4). With 6 words per card: words 3, 4, 5 are the body; word 6 is the checksum.
Stop and check before going on:
- All cards must show the same tag. Different tag → the cards are from different makings and can't be combined.
- All cards must show the same M and the same word count. If they don't, you mis-read a metadata word - recheck.
- You need at least M cards. Here M = 2 and we have 2. Good.
Now the body words. The BIP-39 wordlist is alphabetical and numbered
starting at zero. The first word in the list (abandon) is number 0.
The second (ability) is number 1. And so on.
If your wordlist is printed with line numbers starting at 1 (most are), then the word's number = its line number minus 1.
For each body word on each card, look it up and write down its number. (You already did the metadata and tag words in Step 1.)
Worked example (body words of cards 1 and 2):
| Card | Word | Line # | Word number |
|---|---|---|---|
| 1 | vintage | 1954 | 1953 |
| 1 | before | 162 | 161 |
| 1 | learn | 1015 | 1014 |
| 2 | chat | 311 | 310 |
| 2 | ancient | 69 | 68 |
| 2 | orphan | 1255 | 1254 |
Smudged or unreadable word? The BIP-39 wordlist was specifically designed so every word has a unique 4-letter prefix. If you can read the first 4 letters, look those up - only one word will match.
Every BIP-39 word number fits in exactly 11 binary digits. The biggest
number (2047) is 1111 1111 111 - 11 ones. The smallest (0) is
0000 0000 000 - 11 zeros.
For each body number, write it in 11 bits.
Procedure (slow but foolproof):
- Write your number at the top of a column.
- Divide it by 2, write the new (smaller) number under it, and write the remainder (0 or 1) on the right.
- Repeat with the new number until you get to 0.
- Read the remainders bottom to top - that's your binary.
- Pad with leading zeros on the left until you have 11 digits total.
number ÷ 2 new number remainder
────── ───── ─────────── ─────────
1953 ÷ 2 = 976 r 1 ← first remainder (rightmost bit)
976 ÷ 2 = 488 r 0
488 ÷ 2 = 244 r 0
244 ÷ 2 = 122 r 0
122 ÷ 2 = 61 r 0
61 ÷ 2 = 30 r 1
30 ÷ 2 = 15 r 0
15 ÷ 2 = 7 r 1
7 ÷ 2 = 3 r 1
3 ÷ 2 = 1 r 1
1 ÷ 2 = 0 r 1 ← last remainder (leftmost bit)
Read the remainders bottom-to-top: 1 1 1 1 0 1 0 0 0 0 1. That's exactly
11 digits, so no padding is needed: 11110100001.
Don't panic if this is slow at first. With practice you can do a three-digit number in about a minute.
Shortcut for small numbers: if the number is below 1024, the leftmost bit is 0; if below 512, the two leftmost bits are 00; etc. The biggest power of 2 less than your number tells you how many leading zeros you'll need.
You should be able to verify each of these by repeating the process above.
| Card | Word | Number | 11-bit binary |
|---|---|---|---|
| 1 | vintage | 1953 | 11110100001 |
| 1 | before | 161 | 00010100001 |
| 1 | learn | 1014 | 01111110110 |
| 2 | chat | 310 | 00100110110 |
| 2 | ancient | 68 | 00001000100 |
| 2 | orphan | 1254 | 10011100110 |
Critical: every row is exactly 11 digits. Count them. Off-by-one here is the single most common cause of a failed recovery.
For each card separately, write the card's three body words' 11-bit binary side by side, in order. Don't add any spaces; the bits run together. (The metadata word, the tag word, and the checksum word are not part of this - only the body words.)
Card 1 body stitched (vintage before learn):
vintage before learn
11110100001 00010100001 01111110110
All together (33 bits):
111101000010001010000101111110110
Card 2 body stitched (chat ancient orphan):
chat ancient orphan
00100110110 00001000100 10011100110
All together (33 bits):
001001101100000100010010011100110
(body_word_count × 11 = 3 × 11 = 33. Sanity-check yourself: count the
bits. There should be exactly 33 in each card's body string.)
Group the bit string into chunks of 8, starting from the LEFT. A byte is 8 bits.
Card 1:
1111 0100 │ 0010 0010 │ 1000 0101 │ 1111 1011 │ 0
byte 1 │ byte 2 │ byte 3 │ byte 4 │ pad bit
We have 33 bits = 4 full bytes + 1 leftover bit. That leftover bit is
padding chela added to fill out the final word; it is 0. If it is
1, you typed a body word wrong. Throw the padding away.
Convert each byte from binary to hex using the hex shortcut from the pre-flight section (4 bits → 1 hex digit):
| Byte # | Binary | Left hex | Right hex | Hex byte |
|---|---|---|---|---|
| 1 | 1111 0100 |
F | 4 | 0xF4 |
| 2 | 0010 0010 |
2 | 2 | 0x22 |
| 3 | 1000 0101 |
8 | 5 | 0x85 |
| 4 | 1111 1011 |
F | B | 0xFB |
Card 1 share bytes: 0xF4 0x22 0x85 0xFB
Card 2:
0010 0110 │ 1100 0001 │ 0001 0010 │ 0111 0011 │ 0
byte 1 │ byte 2 │ byte 3 │ byte 4 │ pad bit
| Byte # | Binary | Left hex | Right hex | Hex byte |
|---|---|---|---|---|
| 1 | 0010 0110 |
2 | 6 | 0x26 |
| 2 | 1100 0001 |
C | 1 | 0xC1 |
| 3 | 0001 0010 |
1 | 2 | 0x12 |
| 4 | 0111 0011 |
7 | 3 | 0x73 |
Card 2 share bytes: 0x26 0xC1 0x12 0x73
These are the share bytes - four per card here - the numbers we combine in Step 5. There is no checksum inside this bit string: the checksum is its own separate word (word 6), which we never stitched in.
How many share bytes? The share-byte count is the same on every card and equals the length of the recovered body. Here it's 4, and the body we recover at the end of Step 5 is 4 bytes. (The original text
"hi"is 2 bytes; the body adds a 1-byte integrity check and a 1-byte kind marker, so 4 total - see Step 6.)
Three body words hold 33 bits = 4 full bytes + 1 padding bit, so the body could in principle be 3 bytes or 4 bytes (3 bytes = 24 bits would leave 9 padding bits; 4 bytes = 32 bits leaves 1). The chela tool decides between candidate lengths using the kind byte (the last non-zero body byte); by hand the simpler clue is what you stored:
- A text secret of n characters gives a body of n + 2 bytes (a 1-byte integrity check, then a 1-byte kind marker - Step 6).
- A BIP-39 seed of 16/20/24/28/32 entropy bytes gives a body of 18/22/26/30/34 bytes (again +2), plus any passphrase bytes.
For our "hi" example the body is 4 bytes (2 characters + 1 integrity byte
- 1 kind byte), which is the longer of the two candidates here - so we take all 4 bytes.
If you want to confirm a card was transcribed correctly before spending an hour on the combine, you can check its checksum word. It's an 11-bit CRC over the card's decoded numbers; the full by-hand procedure is in Appendix C. It's a long division, not a hash - tedious but elementary. Most people skip it and just re-read each word carefully against the wordlist (the unique 4-letter prefixes make substitution errors rare). If a chela tool is ever available again, typing the words in checks every card's checksum automatically and names the bad card.
This is the heart of the procedure. We mathematically combine the share bytes from M cards to recover the original body bytes.
The math is Lagrange interpolation, in a special arithmetic where addition is XOR and multiplication is a special "shift-and-XOR" procedure. Don't worry about why it works - just follow the procedure.
When chela split your secret, it built a tiny recipe (a polynomial, in math jargon) for each byte of the body. The recipe was designed so that:
- Plugging in
x = 0gives the body byte itself. - Plugging in each card's
xgives that card's share byte.
The recipes are degree M − 1 (a line if M = 2, a curve if M = 3,
etc.). From any M of the (x, value) points, you can mathematically
rebuild the recipe and read off the value at x = 0.
That last step is "Lagrange interpolation". The formula looks scary but it's just multiplications and XORs.
Two rules. Both happen entirely on bytes (8-bit values, 0x00–0xFF).
a + b = a − b = a ⊕ b. There's no "minus" - subtraction is the same
operation as addition. (Yes, this is weird if you're used to normal
arithmetic. It's a feature of this number system.)
Multiplying by some powers of 2 is simple:
× 1- value unchanged× 2- shift the byte's bits left by one place. If the leftmost bit was a 1, then XOR the result with0x1B(0001 1011).× 4= multiply by 2 twice× 8= multiply by 2 three times- etc.
Multiplying by something that's not a power of 2: break it into a sum of powers of 2 (i.e. write the multiplier in binary), and XOR all the shifted copies.
Worked: 0x37 × 6. The multiplier 6 in binary is 110 (= 4 + 2). So
0x37 × 6 = (0x37 × 4) ⊕ (0x37 × 2).
0x37 × 2:0x37=0011 0111. Leftmost bit is 0, so just shift:0110 1110=0x6E.0x37 × 4: do× 2twice. From0x6E=0110 1110. Leftmost bit is 0, so shift:1101 1100=0xDC.- XOR:
0xDC ⊕ 0x6E= ?0xDC = 1101 1100 0x6E = 0110 1110 ──────────────── XOR = 1011 0010 = 0xB2
So 0x37 × 6 = 0xB2.
The reduction step. When you shift a byte left and the leftmost bit was 1, the shifted value would overflow the 8-bit range. The XOR with
0x1Bis the way this arithmetic handles overflow. Forget WHY; just do it: leftmost bit was 1 → after shifting, XOR with0x1B.
Worked with reduction: 0xF6 × 2. 0xF6 = 1111 0110. Leftmost bit
is 1. Shift: 1110 1100 = 0xEC. Now XOR with 0x1B:
0xEC = 1110 1100
0x1B = 0001 1011
────────────────
XOR = 1111 0111 = 0xF7
So 0xF6 × 2 = 0xF7.
To do a ÷ b, find b's inverse (call it inv(b)), then compute
a × inv(b). The inverse of a byte b is a special byte that satisfies
b × inv(b) = 1.
You don't compute inverses by hand - look them up in the table in the appendix. Print the table or write it out before you start.
A few from the table for reference:
b |
inv(b) |
|---|---|
| 0x01 | 0x01 |
| 0x02 | 0x8D |
| 0x03 | 0xF6 |
| 0x04 | 0xCB |
| 0x05 | 0x52 |
For each card you're using, you need one Lagrange coefficient. Call
them L_a, L_b, …, one per card, regardless of how many bytes the body is.
The formula, for the card whose coordinate is x_i, given you're combining
cards with coordinates {x_1, x_2, …, x_M}:
L_i = (x_1 × inv(x_i ⊕ x_1)) × (x_2 × inv(x_i ⊕ x_2)) × …
for every other card
(That is: multiply together (x_j × inv(x_i ⊕ x_j)) for every other card
j ≠ i. Skip the case where j == i. All multiplications and XORs are in
the special arithmetic.)
For our worked example, M = 2 and we're combining card 1 (x = 6) and
card 2 (x = 3). Each coefficient has just one term:
L_6 = x_3 × inv(x_6 ⊕ x_3) = 3 × inv(6 ⊕ 3) = 3 × inv(5)
L_3 = x_6 × inv(x_3 ⊕ x_6) = 6 × inv(3 ⊕ 6) = 6 × inv(5)
(6 ⊕ 3: 0110 ⊕ 0011 = 0101 = 5. Same both ways - XOR doesn't care
about order.) From the inverse table, inv(5) = 0x52. So:
L_6 = 3 × 0x52. The multiplier3is11= 2 + 1, so3 × 0x52 = (0x52 × 2) ⊕ (0x52 × 1).0x52 × 2:0x52=0101 0010, leftmost bit 0, shift =1010 0100=0xA4.0x52 × 1 = 0x52.- XOR:
0xA4 ⊕ 0x52:0xA4 = 1010 0100 0x52 = 0101 0010 ──────────────── XOR = 1111 0110 = 0xF6
L_6 = 0xF6.L_3 = 6 × 0x52. The multiplier6is110= 4 + 2, so6 × 0x52 = (0x52 × 4) ⊕ (0x52 × 2).0x52 × 2 = 0xA4(just computed).0x52 × 4 = 0xA4 × 2:0xA4=1010 0100, leftmost bit 1, shift =0100 1000=0x48, XOR0x1B:0x48 = 0100 1000 0x1B = 0001 1011 ──────────────── XOR = 0101 0011 = 0x53- XOR:
0x53 ⊕ 0xA4:0x53 = 0101 0011 0xA4 = 1010 0100 ──────────────── XOR = 1111 0111 = 0xF7
L_3 = 0xF7.
Write these down - L_6 = 0xF6, L_3 = 0xF7 - you'll use them for every
byte.
For each byte position of the body (0, 1, 2, 3):
body[byte position] = (L_6 × card_1_share[byte position])
⊕ (L_3 × card_2_share[byte position])
⊕ …
for every card you have
Recall the share bytes from Step 4:
card 1 (x = 6): 0xF4 0x22 0x85 0xFB
card 2 (x = 3): 0x26 0xC1 0x12 0x73
Our body is 4 bytes, so we do this four times. To save work, first build
the doubling chains for both coefficients (each entry is the previous
one × 2):
L_6 = 0xF6 : ×1=F6 ×2=F7 ×4=F5 ×8=F1 ×16=F9 ×32=E9 ×64=C9 ×128=89
L_3 = 0xF7 : ×1=F7 ×2=F5 ×4=F1 ×8=F9 ×16=E9 ×32=C9 ×64=89 ×128=09
Spot-check a couple so you trust the chain:
0xF6 × 2 = 0xF7(worked in 5.2).0xF7 × 2:1111 0111, leftmost 1, shift1110 1110=0xEE, XOR0x1B=1111 0101=0xF5. ✓ (this isL_6 × 4andL_3 × 2).0xC9 × 2(to get×128ofL_6):1100 1001, leftmost 1, shift1001 0010=0x92, XOR0x1B=1000 1001=0x89. ✓
To multiply a coefficient by a share byte, write the share byte in binary, note which power-of-two columns are 1, and XOR the matching chain entries.
card_1_share[0]=0xF4,card_2_share[0]=0x26
First term: L_6 × 0xF4. 0xF4 = 1111 0100 = 128 + 64 + 32 + 16 + 4.
XOR the L_6 chain at ×128, ×64, ×32, ×16, ×4 =
0x89 ⊕ 0xC9 ⊕ 0xE9 ⊕ 0xF9 ⊕ 0xF5:
0x89 = 1000 1001
0xC9 = 1100 1001
0xE9 = 1110 1001
0xF9 = 1111 1001
0xF5 = 1111 0101
───────────────────
XOR = 1010 0101 = 0xA5
Second term: L_3 × 0x26. 0x26 = 0010 0110 = 32 + 4 + 2.
XOR the L_3 chain at ×32, ×4, ×2 = 0xC9 ⊕ 0xF1 ⊕ 0xF5:
0xC9 = 1100 1001
0xF1 = 1111 0001
0xF5 = 1111 0101
───────────────────
XOR = 1100 1101 = 0xCD
(XOR several bytes by going column by column, counting how many 1s - odd count → 1, even count → 0.)
XOR the two terms:
0xA5 = 1010 0101
0xCD = 1100 1101
───────────────────
XOR = 0110 1000 = 0x68
Body byte 0 = 0x68. That's the byte for the letter h.
card_1_share[1]=0x22,card_2_share[1]=0xC1
First term: L_6 × 0x22. 0x22 = 0010 0010 = 32 + 2.
XOR the L_6 chain at ×32, ×2 = 0xE9 ⊕ 0xF7:
0xE9 = 1110 1001
0xF7 = 1111 0111
───────────────────
XOR = 0001 1110 = 0x1E
Second term: L_3 × 0xC1. 0xC1 = 1100 0001 = 128 + 64 + 1.
XOR the L_3 chain at ×128, ×64, ×1 = 0x09 ⊕ 0x89 ⊕ 0xF7:
0x09 = 0000 1001
0x89 = 1000 1001
0xF7 = 1111 0111
───────────────────
XOR = 0111 0111 = 0x77
XOR the two terms:
0x1E = 0001 1110
0x77 = 0111 0111
───────────────────
XOR = 0110 1001 = 0x69
Body byte 1 = 0x69. That's the byte for the letter i.
card_1_share[2]=0x85,card_2_share[2]=0x12
First term: L_6 × 0x85. 0x85 = 1000 0101 = 128 + 4 + 1.
XOR the L_6 chain at ×128, ×4, ×1 = 0x89 ⊕ 0xF5 ⊕ 0xF6:
0x89 = 1000 1001
0xF5 = 1111 0101
0xF6 = 1111 0110
───────────────────
XOR = 1000 1010 = 0x8A
Second term: L_3 × 0x12. 0x12 = 0001 0010 = 16 + 2.
XOR the L_3 chain at ×16, ×2 = 0xE9 ⊕ 0xF5:
0xE9 = 1110 1001
0xF5 = 1111 0101
───────────────────
XOR = 0001 1100 = 0x1C
XOR the two terms:
0x8A = 1000 1010
0x1C = 0001 1100
───────────────────
XOR = 1001 0110 = 0x96
Body byte 2 = 0x96. This is the integrity byte (Step 6 explains it).
card_1_share[3]=0xFB,card_2_share[3]=0x73
First term: L_6 × 0xFB. 0xFB = 1111 1011 = 128 + 64 + 32 + 16 + 8 + 2 + 1.
XOR the L_6 chain at ×128, ×64, ×32, ×16, ×8, ×2, ×1 =
0x89 ⊕ 0xC9 ⊕ 0xE9 ⊕ 0xF9 ⊕ 0xF1 ⊕ 0xF7 ⊕ 0xF6:
0x89 = 1000 1001
0xC9 = 1100 1001
0xE9 = 1110 1001
0xF9 = 1111 1001
0xF1 = 1111 0001
0xF7 = 1111 0111
0xF6 = 1111 0110
───────────────────
XOR = 1010 0000 = 0xA0
Second term: L_3 × 0x73. 0x73 = 0111 0011 = 64 + 32 + 16 + 2 + 1.
XOR the L_3 chain at ×64, ×32, ×16, ×2, ×1 =
0x89 ⊕ 0xC9 ⊕ 0xE9 ⊕ 0xF5 ⊕ 0xF7:
0x89 = 1000 1001
0xC9 = 1100 1001
0xE9 = 1110 1001
0xF5 = 1111 0101
0xF7 = 1111 0111
───────────────────
XOR = 1010 1011 = 0xAB
XOR the two terms:
0xA0 = 1010 0000
0xAB = 1010 1011
───────────────────
XOR = 0000 1011 = 0x0B
Body byte 3 = 0x0B. This is the kind byte (Step 6 explains it).
Recovered body bytes: 0x68 0x69 0x96 0x0B.
You have the body bytes: 0x68 0x69 0x96 0x0B. Three things happen here.
The last body byte is the kind byte - it names what the rest of the body is. Strip it off and look it up:
| Kind byte | What the rest of the body is |
|---|---|
0x01 |
BIP-39 seed, 12 words (16 B entropy), no passphrase |
0x02 |
BIP-39 seed, 15 words (20 B entropy), no passphrase |
0x03 |
BIP-39 seed, 18 words (24 B entropy), no passphrase |
0x04 |
BIP-39 seed, 21 words (28 B entropy), no passphrase |
0x05 |
BIP-39 seed, 24 words (32 B entropy), no passphrase |
0x06 |
BIP-39 seed, 12 words, with passphrase |
0x07 |
BIP-39 seed, 15 words, with passphrase |
0x08 |
BIP-39 seed, 18 words, with passphrase |
0x09 |
BIP-39 seed, 21 words, with passphrase |
0x0A |
BIP-39 seed, 24 words, with passphrase |
0x0B |
Text |
For our example the kind byte is 0x0B → Text. (Had the kind byte been
some value not in this table, you'd have mis-read a word or picked the wrong
body length - go back and check.) That leaves 0x68 0x69 0x96.
The kind byte means you don't have to remember what you stored - the cards tell you. (You can still sanity-check against what you expected.)
The next byte in from the end - here 0x96 - is the integrity byte. It's
a one-byte check the chela tool computes with SHA-256 over the rest of the
body, so that combining a random wrong set of cards is caught with a
255-in-256 probability instead of usually handing back a plausible-but-wrong
secret. By hand you can't recompute it (it
needs SHA-256), so just strip it off, the same way you skipped the checksum
word. What remains is the payload: 0x68 0x69.
(The SPEC calls the generation tag the recovery set id (a nonce) and this integrity byte the integrity tag; the names differ but they're the same two fields.)
Look up each payload byte in the ASCII table (Appendix B). For our example:
0x68→ letterh0x69→ letteri
Recovered secret: "hi" ✓
(For text with non-English characters - accented letters, emoji - the bytes use a slightly more involved encoding called UTF-8. ASCII covers all the unaccented Latin letters, digits, and common punctuation; for anything else, any UTF-8 lookup chart will translate the bytes.)
The payload (body minus the integrity byte and the kind byte) is the raw entropy the BIP-39 mnemonic encodes, possibly followed by a passphrase as UTF-8 bytes. The kind byte already told you the word count and whether there's a passphrase; the entropy length is the cross-check:
| Kind byte | Entropy bytes | BIP-39 word count |
|---|---|---|
0x01 / 0x06 |
16 | 12 words |
0x02 / 0x07 |
20 | 15 words |
0x03 / 0x08 |
24 | 18 words |
0x04 / 0x09 |
28 | 21 words |
0x05 / 0x0A |
32 | 24 words |
For the no-passphrase kinds (0x01–0x05) the payload is exactly the
entropy. For the with-passphrase kinds (0x06–0x0A) the first
16/20/24/28/32 bytes are the entropy and everything after is the
passphrase, UTF-8 encoded.
(So, counting the integrity and kind bytes, a 24-word seed with no passphrase recovers as a 34-byte body: 32 entropy + 1 integrity + 1 kind. A 12-word seed with a 4-character passphrase recovers as 16 + 4 + 1 + 1 = 22 bytes.)
The payload bytes you recovered are the BIP-39 entropy. Every major wallet accepts entropy in one of two ways:
- Import as hex / raw entropy. Look for "import from hex", "raw seed", or an "advanced" import option in your wallet. Type the entropy bytes as hex (two characters per byte). This is the easiest path and works for any word count.
- Import as 12/24 words. The wallet wants the actual BIP-39 mnemonic. Turning entropy into the mnemonic needs one SHA-256 computation (BIP-39's own checksum) - which you'd do with a tool, at which point you'd presumably just use chela. If you're stuck without any tool, the brute-force shortcut for a 12-word mnemonic is to try all 16 possible last-words: BIP-39's checksum is only 4 bits → 16 candidates → exactly one makes the wallet accept the phrase.
If there's a passphrase (kind 0x06–0x0A): the bytes after the
entropy are the passphrase, UTF-8 encoded. The wallet asks for it
separately ("BIP-39 passphrase", "25th word", or "seed extension").
| Symptom | Likely cause | Action |
|---|---|---|
| Reserved bit (last bit of word 1) isn't 0 | Metadata word mis-read | Re-check the first word against the wordlist; you have its 11 bits wrong |
| Padding bits in Step 4 aren't all 0 | A body word mis-typed | Re-check that card's body words (use 4-letter prefix matching) |
Recovered last body byte isn't 0x01–0x0B |
A word substitution, or wrong body length picked | Re-check every word; if two body lengths fit, try the other length; redo Step 5 on a fresh sheet |
| Recovered text/seed is garbage | An undetected word substitution (one valid word swapped for another), OR a foreign card mixed in | Re-check every word; confirm all cards show the same generation tag (word 2); redo Step 5 |
| Generation tags differ between cards | Cards are from independent makings | Recovery impossible from this mix. Find more cards with the matching tag. |
| You have fewer than M cards | Not enough cards | Recovery impossible. Find at least M cards. |
| You're not sure if your payload was text or a seed | - | You don't have to guess - the recovered last body byte is the kind (0x0B = text, 0x01–0x0A = seed). |
For Step 5 (Lagrange coefficient computation). Print this. The inv(0) =
0x00 entry is a convention - you never actually divide by zero in a
valid recovery.
inv[0x00]=0x00 inv[0x01]=0x01 inv[0x02]=0x8d inv[0x03]=0xf6
inv[0x04]=0xcb inv[0x05]=0x52 inv[0x06]=0x7b inv[0x07]=0xd1
inv[0x08]=0xe8 inv[0x09]=0x4f inv[0x0a]=0x29 inv[0x0b]=0xc0
inv[0x0c]=0xb0 inv[0x0d]=0xe1 inv[0x0e]=0xe5 inv[0x0f]=0xc7
inv[0x10]=0x74 inv[0x11]=0xb4 inv[0x12]=0xaa inv[0x13]=0x4b
inv[0x14]=0x99 inv[0x15]=0x2b inv[0x16]=0x60 inv[0x17]=0x5f
inv[0x18]=0x58 inv[0x19]=0x3f inv[0x1a]=0xfd inv[0x1b]=0xcc
inv[0x1c]=0xff inv[0x1d]=0x40 inv[0x1e]=0xee inv[0x1f]=0xb2
inv[0x20]=0x3a inv[0x21]=0x6e inv[0x22]=0x5a inv[0x23]=0xf1
inv[0x24]=0x55 inv[0x25]=0x4d inv[0x26]=0xa8 inv[0x27]=0xc9
inv[0x28]=0xc1 inv[0x29]=0x0a inv[0x2a]=0x98 inv[0x2b]=0x15
inv[0x2c]=0x30 inv[0x2d]=0x44 inv[0x2e]=0xa2 inv[0x2f]=0xc2
inv[0x30]=0x2c inv[0x31]=0x45 inv[0x32]=0x92 inv[0x33]=0x6c
inv[0x34]=0xf3 inv[0x35]=0x39 inv[0x36]=0x66 inv[0x37]=0x42
inv[0x38]=0xf2 inv[0x39]=0x35 inv[0x3a]=0x20 inv[0x3b]=0x6f
inv[0x3c]=0x77 inv[0x3d]=0xbb inv[0x3e]=0x59 inv[0x3f]=0x19
inv[0x40]=0x1d inv[0x41]=0xfe inv[0x42]=0x37 inv[0x43]=0x67
inv[0x44]=0x2d inv[0x45]=0x31 inv[0x46]=0xf5 inv[0x47]=0x69
inv[0x48]=0xa7 inv[0x49]=0x64 inv[0x4a]=0xab inv[0x4b]=0x13
inv[0x4c]=0x54 inv[0x4d]=0x25 inv[0x4e]=0xe9 inv[0x4f]=0x09
inv[0x50]=0xed inv[0x51]=0x5c inv[0x52]=0x05 inv[0x53]=0xca
inv[0x54]=0x4c inv[0x55]=0x24 inv[0x56]=0x87 inv[0x57]=0xbf
inv[0x58]=0x18 inv[0x59]=0x3e inv[0x5a]=0x22 inv[0x5b]=0xf0
inv[0x5c]=0x51 inv[0x5d]=0xec inv[0x5e]=0x61 inv[0x5f]=0x17
inv[0x60]=0x16 inv[0x61]=0x5e inv[0x62]=0xaf inv[0x63]=0xd3
inv[0x64]=0x49 inv[0x65]=0xa6 inv[0x66]=0x36 inv[0x67]=0x43
inv[0x68]=0xf4 inv[0x69]=0x47 inv[0x6a]=0x91 inv[0x6b]=0xdf
inv[0x6c]=0x33 inv[0x6d]=0x93 inv[0x6e]=0x21 inv[0x6f]=0x3b
inv[0x70]=0x79 inv[0x71]=0xb7 inv[0x72]=0x97 inv[0x73]=0x85
inv[0x74]=0x10 inv[0x75]=0xb5 inv[0x76]=0xba inv[0x77]=0x3c
inv[0x78]=0xb6 inv[0x79]=0x70 inv[0x7a]=0xd0 inv[0x7b]=0x06
inv[0x7c]=0xa1 inv[0x7d]=0xfa inv[0x7e]=0x81 inv[0x7f]=0x82
inv[0x80]=0x83 inv[0x81]=0x7e inv[0x82]=0x7f inv[0x83]=0x80
inv[0x84]=0x96 inv[0x85]=0x73 inv[0x86]=0xbe inv[0x87]=0x56
inv[0x88]=0x9b inv[0x89]=0x9e inv[0x8a]=0x95 inv[0x8b]=0xd9
inv[0x8c]=0xf7 inv[0x8d]=0x02 inv[0x8e]=0xb9 inv[0x8f]=0xa4
inv[0x90]=0xde inv[0x91]=0x6a inv[0x92]=0x32 inv[0x93]=0x6d
inv[0x94]=0xd8 inv[0x95]=0x8a inv[0x96]=0x84 inv[0x97]=0x72
inv[0x98]=0x2a inv[0x99]=0x14 inv[0x9a]=0x9f inv[0x9b]=0x88
inv[0x9c]=0xf9 inv[0x9d]=0xdc inv[0x9e]=0x89 inv[0x9f]=0x9a
inv[0xa0]=0xfb inv[0xa1]=0x7c inv[0xa2]=0x2e inv[0xa3]=0xc3
inv[0xa4]=0x8f inv[0xa5]=0xb8 inv[0xa6]=0x65 inv[0xa7]=0x48
inv[0xa8]=0x26 inv[0xa9]=0xc8 inv[0xaa]=0x12 inv[0xab]=0x4a
inv[0xac]=0xce inv[0xad]=0xe7 inv[0xae]=0xd2 inv[0xaf]=0x62
inv[0xb0]=0x0c inv[0xb1]=0xe0 inv[0xb2]=0x1f inv[0xb3]=0xef
inv[0xb4]=0x11 inv[0xb5]=0x75 inv[0xb6]=0x78 inv[0xb7]=0x71
inv[0xb8]=0xa5 inv[0xb9]=0x8e inv[0xba]=0x76 inv[0xbb]=0x3d
inv[0xbc]=0xbd inv[0xbd]=0xbc inv[0xbe]=0x86 inv[0xbf]=0x57
inv[0xc0]=0x0b inv[0xc1]=0x28 inv[0xc2]=0x2f inv[0xc3]=0xa3
inv[0xc4]=0xda inv[0xc5]=0xd4 inv[0xc6]=0xe4 inv[0xc7]=0x0f
inv[0xc8]=0xa9 inv[0xc9]=0x27 inv[0xca]=0x53 inv[0xcb]=0x04
inv[0xcc]=0x1b inv[0xcd]=0xfc inv[0xce]=0xac inv[0xcf]=0xe6
inv[0xd0]=0x7a inv[0xd1]=0x07 inv[0xd2]=0xae inv[0xd3]=0x63
inv[0xd4]=0xc5 inv[0xd5]=0xdb inv[0xd6]=0xe2 inv[0xd7]=0xea
inv[0xd8]=0x94 inv[0xd9]=0x8b inv[0xda]=0xc4 inv[0xdb]=0xd5
inv[0xdc]=0x9d inv[0xdd]=0xf8 inv[0xde]=0x90 inv[0xdf]=0x6b
inv[0xe0]=0xb1 inv[0xe1]=0x0d inv[0xe2]=0xd6 inv[0xe3]=0xeb
inv[0xe4]=0xc6 inv[0xe5]=0x0e inv[0xe6]=0xcf inv[0xe7]=0xad
inv[0xe8]=0x08 inv[0xe9]=0x4e inv[0xea]=0xd7 inv[0xeb]=0xe3
inv[0xec]=0x5d inv[0xed]=0x50 inv[0xee]=0x1e inv[0xef]=0xb3
inv[0xf0]=0x5b inv[0xf1]=0x23 inv[0xf2]=0x38 inv[0xf3]=0x34
inv[0xf4]=0x68 inv[0xf5]=0x46 inv[0xf6]=0x03 inv[0xf7]=0x8c
inv[0xf8]=0xdd inv[0xf9]=0x9c inv[0xfa]=0x7d inv[0xfb]=0xa0
inv[0xfc]=0xcd inv[0xfd]=0x1a inv[0xfe]=0x41 inv[0xff]=0x1c
For the "decode payload as text" step. Only the printable subset is shown
(0x20 / space through 0x7E / ~); anything outside this range in a text
payload usually means accented letters or emoji, encoded via UTF-8.
0x20 = (space) 0x30 = 0 0x40 = @ 0x50 = P 0x60 = ` 0x70 = p
0x21 = ! 0x31 = 1 0x41 = A 0x51 = Q 0x61 = a 0x71 = q
0x22 = " 0x32 = 2 0x42 = B 0x52 = R 0x62 = b 0x72 = r
0x23 = # 0x33 = 3 0x43 = C 0x53 = S 0x63 = c 0x73 = s
0x24 = $ 0x34 = 4 0x44 = D 0x54 = T 0x64 = d 0x74 = t
0x25 = % 0x35 = 5 0x45 = E 0x55 = U 0x65 = e 0x75 = u
0x26 = & 0x36 = 6 0x46 = F 0x56 = V 0x66 = f 0x76 = v
0x27 = ' 0x37 = 7 0x47 = G 0x57 = W 0x67 = g 0x77 = w
0x28 = ( 0x38 = 8 0x48 = H 0x58 = X 0x68 = h 0x78 = x
0x29 = ) 0x39 = 9 0x49 = I 0x59 = Y 0x69 = i 0x79 = y
0x2a = * 0x3a = : 0x4a = J 0x5a = Z 0x6a = j 0x7a = z
0x2b = + 0x3b = ; 0x4b = K 0x5b = [ 0x6b = k 0x7b = {
0x2c = , 0x3c = < 0x4c = L 0x5c = \ 0x6c = l 0x7c = |
0x2d = - 0x3d = = 0x4d = M 0x5d = ] 0x6d = m 0x7d = }
0x2e = . 0x3e = > 0x4e = N 0x5e = ^ 0x6e = n 0x7e = ~
0x2f = / 0x3f = ? 0x4f = O 0x5f = _ 0x6f = o
The last word of each card is an 11-bit CRC - a checksum that catches a mistyped word. It's a long division in binary, not a hash, so you can do it by hand. It tells you only whether the card was copied correctly; it plays no part in recovering the secret, so skip it unless you want the extra confidence.
The CRC is computed over the card's decoded values, in this byte order:
[ x ] [ M ] [ tag high byte ] [ tag low byte ] [ share byte 0 ] [ share byte 1 ] …
x and M are one byte each (the decoded coordinate and threshold, not
the raw 5-bit fields). The tag is the generation tag as a 2-byte
number, high byte first. Then every share byte from Step 4, in order.
For card 1 of our example (x = 6, M = 2, tag 0x02C9, share bytes
0xF4 0x22 0x85 0xFB):
06 02 02 C9 F4 22 85 FB
The CRC uses the generator number 0x307 = 011 0000 0111 (11 bits below
an implied leading 1, i.e. the 12-bit pattern 1 0110 0000 111). The
recipe:
- Lay out all the input bytes as one long string of bits, MSB-first.
- Append 11 zero bits on the right.
- Run an 11-bit register starting at 0. Feed in the bits one at a time,
most-significant first. For each bit:
- Look at the register's top bit (bit 10) before shifting.
- Shift the register left by 1, drop in the next input bit at the bottom, and keep only the low 11 bits.
- If the top bit you noted was 1, XOR the register with
0x307.
- After the last appended zero bit, the 11-bit register is the CRC.
Equivalently (the byte-at-a-time form chela uses): start the register at
0; for each input byte, XOR (byte << 3) into the register, then do 8
rounds of "note top bit, shift left within 11 bits, XOR 0x307 if the top
bit was 1".
Compare the result to the card's last word. Match → the card's
x, M, tag, and share bytes were all transcribed correctly. No
match → at least one word on that card is wrong; re-read them.
Sanity-check your CRC routine first. Run it over the ASCII bytes of
"123456789"(that's31 32 33 34 35 36 37 38 39). A correct CRC-11 gives0x061. If you get that, your hand-procedure is right; then check your real cards.
For reference, the checksum words of the example cards are: card 1
(film) = 690 = 0x2B2; card 2 (produce) = 1373 = 0x55D.
- bit - one binary digit (0 or 1).
- byte - 8 bits in a row; holds a number 0–255 (or 0x00–0xFF in hex).
- hex (hexadecimal) - base 16; digits 0–9, A–F. Each hex digit = 4 bits.
- XOR (⊕) - bit-by-bit operation: 0 if same, 1 if different. On bytes, XOR each pair of corresponding bits.
- BIP-39 - the standard wordlist (2048 English words) used to encode bytes as easy-to-write words. Each word represents 11 bits.
- generation tag - an 11-bit random number chosen once when a set of cards is made, written into word 2 of every card from that making (and printed as 4 hex on the label). It groups cards that belong together; it is not derived from the secret, so two makings of the same secret get different tags and won't (and shouldn't) combine. A different tag marks a foreign card.
- kind byte - the last byte of the recovered body. It names the
payload type (
0x0B= text,0x01–0x0A= the various BIP-39 seed variants) and is stripped off before you read the payload. - share - one card's worth of data. By itself, a single card tells you nothing about the secret. Combine M cards to recover.
- threshold (M) - the minimum number of cards needed to recover.
- coordinate (x) - each card's random number in 1–32, stored in its metadata word. It is the card's place in the Shamir math, not its position in the set.
- GF(2⁸) - short for "the finite field with 2⁸ = 256 elements". Mathematician's name for the special byte arithmetic used here: addition is XOR, multiplication is the shift-and-XOR procedure in Step 5.2.
- Lagrange interpolation - the procedure in Step 5 that recombines M share bytes to recover a body byte. Named after Joseph-Louis Lagrange (1736–1813).
- Shamir's Secret Sharing - the method chela uses to split a secret into N shares such that any M reconstruct it. Invented by Adi Shamir in 1979.
- CRC - the 11-bit checksum in each card's last word; catches a mistyped word (Appendix C). It is a long division, not a hash.
For an engineer's reference of chela's wire formats (for reimplementing the tool in another language), see SPEC.md.
For the chela tool's normal usage, see README.md and the user-facing recovery walkthrough at RECOVERY.md.