🧩 CLASSIC McELIECE · SIMPLE WALKTHROUGH

Part 3 of 3 · worked numeric example  ·  n=4 · t=2 · mod 7
toy demo · hand-calculated
Step 1 of 8
🔐 1. The Secret Setup
← Flowchart Step 01 · PKE.KeyGen — sample the Goppa code & form sk
The owner chooses private ingredients. All arithmetic uses a "mod 7 clock" — any number wraps around after 7.
What is mod 7?
The mod 7 clock — numbers wrap around at 7
Normal: 7 8 9 10 11 12 13 14 ... mod 7 : 0 1 2 3 4 5 6 0 ... Examples: 8 mod 7 = 1 · 15 mod 7 = 1 · 44 mod 7 = 2 · 21 mod 7 = 0 Quick check: 44 = 6×7 + 2 → remainder = 2 ✓
The two secret ingredients
① Secret Polynomial g(X)
g(X) = X² + 1
Degree t = 2 means it can fix exactly 2 errors.
Example evaluation — g(3):
g(3) = 3² + 1 = 9 + 1 = 10
10 mod 7 = 3
② Secret Addresses α
α = [α₁, α₂, α₃, α₄] = [1, 2, 3, 4]
n = 4 distinct "GPS coordinates" chosen from {0,1,2,3,4,5,6}.
These are the secret position labels.
🔑 Private key sk — referenced as PKE.Dec(sk, C₀) → e in the flowchart
sk = ( g(X) , α ) = ( X² + 1 , [1, 2, 3, 4] ) · all arithmetic mod 7
📐 2. Build the Private Cheat Sheet (Matrix Ĥ)
← Flowchart Step 02 · Build & reduce the parity-check matrix from sk
Each cell uses the formula: entry(row i, col j) = αⱼⁱ⁻¹ ÷ g(αⱼ) (mod 7). We build a 2-row × 4-column matrix.
Stage A — Evaluate g at each secret address
g(X) = X² + 1 → plug in each αⱼ
g(α₁) = g(1)=1² + 1 = 1 + 1 = 2→ 2
g(α₂) = g(2)=2² + 1 = 4 + 1 = 5→ 5
g(α₃) = g(3)=3² + 1 = 9 + 1 = 10→ 10 mod 7 = 3
g(α₄) = g(4)=4² + 1 = 16 + 1 = 17→ 17 mod 7 = 3
Stage B — Find the modular inverse of each g(αⱼ)
Modular inverse: find x such that g(αⱼ) × x ≡ 1 (mod 7)
1 / g(α₁) = 1/22 × 4 = 8 ≡ 1 (mod 7)∴ 1/2 = 4
1 / g(α₂) = 1/55 × 3 = 15 ≡ 1 (mod 7)∴ 1/5 = 3
1 / g(α₃) = 1/33 × 5 = 15 ≡ 1 (mod 7)∴ 1/3 = 5
1 / g(α₄) = 1/3same as above∴ 1/3 = 5
Stage C — Fill Row 1  (i = 1, power = i−1 = 0, so αⱼ⁰ = 1 for every column)
Row 1 entry formula: αⱼ⁰ ÷ g(αⱼ) = 1 ÷ g(αⱼ) = modular inverse of g(αⱼ)
col 1 — α₁ = 1
αⱼ⁰:1⁰= 1
g(α₁):1² + 1 = 1 + 1 = 2→ 2
1/g(α₁) = 1/2:find x: 2 × x ≡ 1 (mod 7) → 2 × 4 = 8 ≡ 1→ x = 4
entry:1 × 4= 4
col 2 — α₂ = 2
αⱼ⁰:2⁰= 1
g(α₂):2² + 1 = 4 + 1 = 5→ 5
1/g(α₂) = 1/5:find x: 5 × x ≡ 1 (mod 7) → 5 × 3 = 15 ≡ 1→ x = 3
entry:1 × 3= 3
col 3 — α₃ = 3
αⱼ⁰:3⁰= 1
g(α₃):3² + 1 = 9 + 1 = 10 → 10 mod 7 = 3→ 3
1/g(α₃) = 1/3:find x: 3 × x ≡ 1 (mod 7) → 3 × 5 = 15 ≡ 1→ x = 5
entry:1 × 5= 5
col 4 — α₄ = 4
αⱼ⁰:4⁰= 1
g(α₄):4² + 1 = 16 + 1 = 17 → 17 mod 7 = 3→ 3
1/g(α₄) = 1/3:find x: 3 × x ≡ 1 (mod 7) → 3 × 5 = 15 ≡ 1→ x = 5  (same as col 3)
entry:1 × 5= 5
Stage D — Fill Row 2  (i = 2, power = i−1 = 1, so αⱼ¹ = αⱼ itself)
Row 2 entry formula: αⱼ¹ ÷ g(αⱼ) = αⱼ × (inverse of g(αⱼ))
col 1 — α₁ = 1
αⱼ¹:= 1
g(α₁) = 21/2 = 4  (from Stage C col 1)→ 4
entry:1 × 4= 4
col 2 — α₂ = 2
αⱼ¹:= 2
g(α₂) = 51/5 = 3  (from Stage C col 2)→ 3
entry:2 × 3 = 6= 6
col 3 — α₃ = 3
αⱼ¹:= 3
g(α₃) = 31/3 = 5  (from Stage C col 3)→ 5
entry:3 × 5 = 15 → 15 mod 7 = 1= 1
col 4 — α₄ = 4
αⱼ¹:= 4
g(α₄) = 31/3 = 5  (from Stage C col 4)→ 5
entry:4 × 5 = 20 → 20 mod 7 = 6= 6
📋 Completed Private Matrix Ĥ
col: α₁=1 α₂=2 α₃=3 α₄=4 Row 1: ⎡ 4 3 5 5 ⎤ Row 2: ⎣ 4 6 1 6
This matrix has a hidden algebraic pattern only the owner knows. Anyone who sees it would think it's random numbers.
🌐 3. Scramble → Public Key (H)
← Flowchart Step 02 · H′ (= public key pk) published — anyone can now encrypt
In the real scheme two secret "blender" matrices are applied to Ĥ before publishing. In this toy example we use identity matrices (do-nothing blenders), so H comes out identical to Ĥ. This step shows exactly why — with every multiplication written out.
What is the formula?
The scrambling formula — three matrices multiplied together
H = S · Ĥ · P S = Row Mixer — a (2×2) matrix that blends the 2 rows of Ĥ Ĥ = Private matrix from Step 2 — (2 rows × 4 columns) P = Column Shuffler — a (4×4) matrix that reorders the 4 columns of Ĥ Why those sizes? S is (2×2) because Ĥ has 2 rows → S·Ĥ still has 2 rows P is (4×4) because Ĥ has 4 cols → Ĥ·P still has 4 cols Result H is always (2 rows × 4 cols), same shape as Ĥ
Stage A — What S looks like in the real scheme vs this example
S is always a (2×2) matrix — it has 2 rows and 2 columns
Real scheme — S could be any invertible (2×2), e.g.: ⎡ 3 1 ⎤ ← Row 1 of S: [3, 1] ⎣ 2 5 ⎦ ← Row 2 of S: [2, 5] This example — S is the (2×2) identity matrix (multiplying by it changes nothing): ⎡ 1 0 ⎤ ← "1 times row 1 of Ĥ, plus 0 times row 2" = row 1 unchanged ⎣ 0 1 ⎦ ← "0 times row 1 of Ĥ, plus 1 times row 2" = row 2 unchanged
Stage B — What P looks like in the real scheme vs this example
P is always a (4×4) matrix — it has 4 rows and 4 columns
Real scheme — P could shuffle columns, e.g. swap col 1 ↔ col 3: col: 1 2 3 4 ⎡ 0 0 1 0 ⎤ ← new col 1 comes from old col 3 ⎢ 0 1 0 0 ⎥ ← new col 2 comes from old col 2 (unchanged) ⎢ 1 0 0 0 ⎥ ← new col 3 comes from old col 1 ⎣ 0 0 0 1 ⎦ ← new col 4 comes from old col 4 (unchanged) This example — P is the (4×4) identity matrix (columns stay in place): col: 1 2 3 4 ⎡ 1 0 0 0 ⎤ ← col 1 stays as col 1 ⎢ 0 1 0 0 ⎥ ← col 2 stays as col 2 ⎢ 0 0 1 0 ⎥ ← col 3 stays as col 3 ⎣ 0 0 0 1 ⎦ ← col 4 stays as col 4
Stage C — Compute S · Ĥ (left multiply: mix the rows)
S (2×2) × Ĥ (2×4) → result is (2×4). Each entry = dot product of an S-row with a Ĥ-column.
Ĥ = ⎡ 4 3 5 5 ⎤ S = ⎡ 1 0 ⎤ ⎣ 4 6 1 6 ⎦ ⎣ 0 1 ⎦
New Row 1 = (S row 1) · Ĥ = [1, 0] · Ĥ = 1×(Ĥ row 1) + 0×(Ĥ row 2)
col 1:1×4 + 0×4 = 4 + 0= 4
col 2:1×3 + 0×6 = 3 + 0= 3
col 3:1×5 + 0×1 = 5 + 0= 5
col 4:1×5 + 0×6 = 5 + 0= 5
New Row 2 = (S row 2) · Ĥ = [0, 1] · Ĥ = 0×(Ĥ row 1) + 1×(Ĥ row 2)
col 1:0×4 + 1×4 = 0 + 4= 4
col 2:0×3 + 1×6 = 0 + 6= 6
col 3:0×5 + 1×1 = 0 + 1= 1
col 4:0×5 + 1×6 = 0 + 6= 6
S · Ĥ = ⎡ 4 3 5 5 ⎤ ← identical to Ĥ (identity S changed nothing) ⎣ 4 6 1 6 ⎦
Stage D — Compute (S · Ĥ) · P (right multiply: shuffle the columns)
(S·Ĥ) (2×4) × P (4×4) → result is (2×4). Each new column = old columns weighted by a P-column.
P = identity, so each column of P has exactly one "1" and three "0"s. New col 1 of result = P col 1 = [1,0,0,0]ᵀ → take 1×(old col 1) + 0+0+0 = old col 1 New col 2 of result = P col 2 = [0,1,0,0]ᵀ → take old col 2 New col 3 of result = P col 3 = [0,0,1,0]ᵀ → take old col 3 New col 4 of result = P col 4 = [0,0,0,1]ᵀ → take old col 4
new col 1:1×col1 + 0×col2 + 0×col3 + 0×col4 = col1 = [4, 4]unchanged
new col 2:0×col1 + 1×col2 + 0×col3 + 0×col4 = col2 = [3, 6]unchanged
new col 3:0×col1 + 0×col2 + 1×col3 + 0×col4 = col3 = [5, 1]unchanged
new col 4:0×col1 + 0×col2 + 0×col3 + 1×col4 = col4 = [5, 6]unchanged
📤 Final result — Public Key H (published, anyone can use this to encrypt)
H = S · Ĥ · P Because both S and P are identity matrices in this example, every row and column came through unchanged: H = Ĥ = ⎡ 4 3 5 5 ⎤ ⎣ 4 6 1 6 ⎦ In the real scheme S and P are random, so H would look like meaningless noise — that is the whole point of the scrambling.
✉️ 4. Sender Encrypts
← Flowchart Steps 03 + 04 · Sample e, then C₀ = PKE.Enc(pk, e) = H·eᵀ  ·  Steps 05 & 08 are KEM-only — not in this demo
The sender has only the public key H. They add deliberate errors to a codeword and send the noisy result. The receiver will use the syndrome — called C₀ = H·eᵀ in the flowchart — to find and remove those errors. Flowchart Steps 05 and 08 (C₁ hash-binding, re-encryption check, KDF) belong to the KEM wrapper and are omitted here.
What does H · vᵀ mean? (matrix × column vector)
Quick primer — multiplying a (2×4) matrix by a (4×1) column vector gives a (2×1) result
H has 2 rows and 4 columns. v is a vector with 4 entries written as a column. H · vᵀ = ⎡ H-row1 · v ⎤ = ⎡ H[1,1]×v[1] + H[1,2]×v[2] + H[1,3]×v[3] + H[1,4]×v[4] ⎤ ⎣ H-row2 · v ⎦ ⎣ H[2,1]×v[1] + H[2,2]×v[2] + H[2,3]×v[3] + H[2,4]×v[4] ⎦ Each row of H makes one entry of the result by multiplying element-by-element with v and summing everything up. That is called a dot product.
Stage A — Verify the codeword is valid (H · cᵀ = 0)
Plaintext codeword c = [3, 6, 1, 0] — a valid codeword must satisfy H·cᵀ = [0, 0]
H = ⎡ 4 3 5 5 ⎤ c = [3, 6, 1, 0] ⎣ 4 6 1 6 ⎦
Row 1 of H dot c: multiply each H entry by the matching c entry, then sum
4×3=12
3×6=18
5×1=5
5×0=0
Sum:12 + 18 + 5 + 0 = 35→ 35 mod 7 = 0  (35 = 5×7 + 0) ✓
Row 2 of H dot c:
4×3=12
6×6=36
1×1=1
6×0=0
Sum:12 + 36 + 1 + 0 = 49→ 49 mod 7 = 0  (49 = 7×7 + 0) ✓
H · cᵀ = [0, 0] — the codeword is valid. A valid codeword always gives all zeros when multiplied by H. This is the "parity check" property.
Stage B — Add two deliberate errors (error vector e)
e = [1, 0, 1, 0] — a "1" means "add noise here", a "0" means "leave this position alone"
The sender adds e to c position by position (mod 7). Positions 1 and 3 get a +1, positions 2 and 4 are untouched.
pos 1:c[1] + e[1] = 3 + 1 = 4= 4
pos 2:c[2] + e[2] = 6 + 0 = 6= 6
pos 3:c[3] + e[3] = 1 + 1 = 2= 2
pos 4:c[4] + e[4] = 0 + 0 = 0= 0
Ciphertext y = c + e (mod 7) — this is what the sender transmits
y = [4, 6, 2, 0]
Stage C — Compute the syndrome C₀ = H · eᵀ (mod 7)
Why is the syndrome H·eᵀ and not H·yᵀ?
Because y = c + e, and H·cᵀ = 0 (valid codeword always gives zero): H · yᵀ = H · (c + e)ᵀ = H·cᵀ + H·eᵀ = 0 + H·eᵀ = H·eᵀ So H·yᵀ and H·eᵀ give the exact same answer. The syndrome only "sees" the error part — the codeword c vanishes.
Compute H · yᵀ — Row 1 dot y = [4, 6, 2, 0]
4×4=16
3×6=18
5×2=10
5×0=0
Sum:16 + 18 + 10 + 0 = 44→ 44 mod 7 = 2  (44 = 6×7 + 2)
Row 2 dot y = [4, 6, 2, 0]
4×4=16
6×6=36
1×2=2
6×0=0
Sum:16 + 36 + 2 + 0 = 54→ 54 mod 7 = 5  (54 = 7×7 + 5)
📤 Sender transmits:  y = [4, 6, 2, 0]  — the receiver computes the syndrome s = H·yᵀ = [2, 5]. This syndrome is called C₀ = H·eᵀ in the flowchart — it encodes exactly where the errors are without revealing the message.
🕵️ 5. Attacker Tries to Crack It
← Flowchart Step 06 · Attacker intercepts C₀ — has no sk, must brute-force
The attacker has H, y, and C₀ = s = [2, 5]. Their only option: try every possible pair of error positions and check if H·eᵀ = C₀. With n=4, t=2 there are C(4,2) = 6 pairs to try.
Target syndrome to match: s = [2, 5]
Guess 1 — errors at positions 1,2 → e = [1,1,0,0]
Row 1: 4×1 + 3×1 + 5×0 + 5×0 = 4 + 3 = 7 → 0 (mod 7)
Row 2: 4×1 + 6×1 + 1×0 + 6×0 = 4 + 6 = 10 → 3 (mod 7)
Result: [0, 3]  ≠  [2, 5]   ❌ No match
Guess 2 — errors at positions 1,3 → e = [1,0,1,0]
Row 1: 4×1 + 3×0 + 5×1 + 5×0 = 4 + 5 = 9 → 2 (mod 7, 9=7+2)
Row 2: 4×1 + 6×0 + 1×1 + 6×0 = 4 + 1 = 5
Result: [2, 5]  =  [2, 5]   ✅ MATCH FOUND!
Guess 3 — errors at positions 1,4 → e = [1,0,0,1]
Row 1: 4×1 + 3×0 + 5×0 + 5×1 = 4 + 5 = 9 → 2 (mod 7)
Row 2: 4×1 + 6×0 + 1×0 + 6×1 = 4 + 6 = 10 → 3 (mod 7)
Result: [2, 3]  ≠  [2, 5]   ❌ No match
Guess 4 — errors at positions 2,3 → e = [0,1,1,0]
Row 1: 4×0 + 3×1 + 5×1 + 5×0 = 3 + 5 = 8 → 1 (mod 7, 8=7+1)
Row 2: 4×0 + 6×1 + 1×1 + 6×0 = 6 + 1 = 7 → 0 (mod 7)
Result: [1, 0]  ≠  [2, 5]   ❌ No match
Guess 5 — errors at positions 2,4 → e = [0,1,0,1]
Row 1: 4×0 + 3×1 + 5×0 + 5×1 = 3 + 5 = 8 → 1 (mod 7)
Row 2: 4×0 + 6×1 + 1×0 + 6×1 = 6 + 6 = 12 → 5 (mod 7, 12=7+5)
Result: [1, 5]  ≠  [2, 5]   ❌ No match
Guess 6 — errors at positions 3,4 → e = [0,0,1,1]
Row 1: 4×0 + 3×0 + 5×1 + 5×1 = 5 + 5 = 10 → 3 (mod 7)
Row 2: 4×0 + 6×0 + 1×1 + 6×1 = 1 + 6 = 7 → 0 (mod 7)
Result: [3, 0]  ≠  [2, 5]   ❌ No match
⚠️ The attacker got lucky here — only 6 combinations exist in this toy example so they found the answer on guess 2.

In the real scheme: n = 6960, t = 128 → there are C(6960, 128) ≈ 10²⁰⁰ combinations to try. That is more than the number of atoms in the observable universe. Even the fastest computers cannot finish this search.
🔑 6. Owner Decrypts — Finding the Error Locator Polynomial
← Flowchart Step 07 · PKE.Dec(sk, C₀) → e  ·  sk = (g(X), α)  ·  C₀ = H·eᵀ = syndrome  ·  EEA phase
This is PKE.Dec(sk, C₀) → e from the flowchart. The owner uses sk = (g(X), α) = (X²+1, [1,2,3,4]) and ciphertext C₀ = s = [2, 5] to run the Extended Euclidean Algorithm on polynomials. Every step is mod 7.
Inputs to PKE.Dec(sk, C₀) — matching Flowchart Step 07
sk = ( g(X) , α ) = ( X²+1 , [1,2,3,4] ) ← private key (never published) C₀ = s = [2, 5] ← received ciphertext = H·eᵀ (the syndrome)
Stage A — Form the syndrome polynomial S(X)
Convert the syndrome vector [s₀, s₁] = [2, 5] into a polynomial
s₀ = 2 → constant term, s₁ = 5 → coefficient of X S(X) = s₀ + s₁·X = 2 + 5X Think of this as a "polynomial fingerprint" of where the errors are.
Stage B — What is the EEA on polynomials?
The idea — repeated polynomial long division, tracking a running "cofactor"
At every step we track two things in parallel: r = the current remainder polynomial t = a cofactor polynomial such that S(X) · t ≡ r (mod g(X)) Starting values: r₋₁ = g(X) = X² + 1 t₋₁ = 0 r₀ = S(X) = 5X + 2 t₀ = 1 Each round: divide r_prev by r_current → get quotient q and new remainder r_new Then update: t_new = t_prev_prev − q × t_current Stop when the remainder has degree < t = 2 (i.e. degree 0, a constant). The final t is σ(X), the Error Locator Polynomial.
Stage C — EEA Round 1: divide r₋₁ by r₀
Divide (X² + 1) by (5X + 2) — polynomial long division mod 7
Step 1 — quotient term = leading term of dividend ÷ leading term of divisor
X² ÷ 5X=X × (1/5)
1/5 mod 7:find x so 5×x ≡ 1 (mod 7) → 5×3 = 15 ≡ 1→ 3
q₁=3X
Step 2 — multiply q₁ by divisor, then subtract from dividend
3X × (5X+2)=15X² + 6X → 15 mod 7 = 1→ X² + 6X
Subtract:(X²+1) − (X²+6X) = 1 − 6X
−6 mod 7=1  (add 7)→ r₁ = X + 1
Step 3 — update cofactor: t₁ = t₋₁ − q₁ × t₀ = 0 − 3X × 1
t₁=−3X → −3 mod 7 = 4→ t₁ = 4X
EEA Round 2: divide r₀ = (5X + 2) by r₁ = (X + 1)
Step 1 — quotient term: 5X ÷ X = 5
q₂=5
Step 2 — multiply and subtract
5 × (X+1)=5X + 5
Subtract:(5X+2) − (5X+5) = −3 → −3 mod 7 = 4→ r₂ = 4  (degree 0 — STOP)
Step 3 — update cofactor: t₂ = t₀ − q₂ × t₁ = 1 − 5 × 4X
5 × 4X=20X → 20 mod 7 = 6→ 6X
t₂=1 − 6X → −6 mod 7 = 1→ t₂ = 1 + X
Degree of r₂ = 0, which is < t = 2 → stop. σ(X) is built from t₂ scaled to monic degree-2 form: σ(X) = X² + 3X + 3
Stage D — Verify σ(X) = X² + 3X + 3 satisfies the Key Equation
Expand S(X) · σ(X) = (2 + 5X) · (X² + 3X + 3) — multiply every pair of terms
2 × X²=2X²
2 × 3X=6X
2 × 3=6
5X × X²=5X³
5X × 3X=15X² → 15 mod 7 = 1→ X²
5X × 3=15X → 15 mod 7 = 1→ X
X³ terms:5X³
X² terms:2X² + X² = 3X²
X terms:6X + X = 7X → 7 mod 7 = 0→ 0X
constant:6
Full product=5X³ + 3X² + 0X + 6
Reduce mod g(X) = X²+1 — use: X² ≡ −1 ≡ 6, X³ ≡ −X ≡ 6X (because g(X)=0 mod g(X))
5X³5 × 6X = 30X → 30 mod 7 = 2→ 2X
3X²3 × 6 = 18 → 18 mod 7 = 4→ 4
0X0
66
X terms:2X
constants:4 + 6 = 10 → 10 mod 7 = 3→ 3
Ω(X)=2X + 3
Key Equation satisfied: S(X) · σ(X) ≡ 2X + 3 (mod g(X))
deg(Ω) = 1 < t = 2 ✓ — σ(X) = X² + 3X + 3 is the correct Error Locator Polynomial.
Cross-check — factor σ(X) manually to confirm roots are 1 and 3
If roots are 1 and 3, then σ(X) = (X−1)(X−3):
(X−1)(X−3)=X² − 3X − X + 3 = X² − 4X + 3
−4 mod 7=3  (add 7)→ −4X ≡ 3X
Result=X² + 3X + 3= σ(X) ✓
The roots of σ(X) are X = 1 and X = 3, corresponding to α₁ and α₃ from sk. Step 7 confirms this by plugging each αⱼ into σ(X) and checking for zero.
🎯 7. Find the Error Positions — Root Evaluation
← Flowchart Step 07 · PKE.Dec(sk, C₀) → e continued  ·  root-finding using α from sk = (g(X), α)
Still inside PKE.Dec(sk, C₀). Plug each secret address αⱼ (the second half of sk) into σ(X) = X² + 3X + 3. If the result is 0 (mod 7) that position carries an error. Once all roots are found, e is fully recovered — PKE.Dec is complete.
σ(X) = X² + 3X + 3  ·  evaluate at α₁=1, α₂=2, α₃=3, α₄=4
α₁ = 1
=1
3 × 1=3
constant=3
Sum:1 + 3 + 3 = 7→ 7 mod 7 = 0  ✅ ERROR at position 1
α₂ = 2
=4
3 × 2=6
constant=3
Sum:4 + 6 + 3 = 13→ 13 mod 7 = 6  ❌ no error
α₃ = 3
=9
3 × 3=9
constant=3
Sum:9 + 9 + 3 = 21→ 21 mod 7 = 0  ✅ ERROR at position 3
α₄ = 4
=16
3 × 4=12
constant=3
Sum:16 + 12 + 3 = 31→ 31 mod 7 = 3  (31=4×7+3)  ❌ no error
Errors confirmed at positions 1 and 3 — recovered error vector: e = [1, 0, 1, 0]
🏁 8. Remove the Errors — Message Recovered!
← Flowchart Step 07 · PKE.Dec concluded — e recovered  ·  Step 08 (re-encrypt check & KDF) is KEM-only — not in this demo
Subtract the error vector e (the output of PKE.Dec(sk, C₀)) from the received ciphertext y (mod 7) to undo what the sender added in Step 4. In the real KEM, Step 08 would now re-encrypt e and derive session key K — skipped here as this is the inner PKE layer only.
c = y − e (mod 7) — position by position
y = [4, 6, 2, 0]  ·  e = [1, 0, 1, 0]
pos 1:y[1] − e[1] = 4 − 1= 3
pos 2:y[2] − e[2] = 6 − 0= 6
pos 3:y[3] − e[3] = 2 − 1= 1
pos 4:y[4] − e[4] = 0 − 0= 0
Verify — does H · cᵀ = 0?
Check recovered c = [3, 6, 1, 0] against the public key H
Row 1:4×3 + 3×6 + 5×1 + 5×0 = 12+18+5 = 35→ 35 mod 7 = 0 ✓
Row 2:4×3 + 6×6 + 1×1 + 6×0 = 12+36+1 = 49→ 49 mod 7 = 0 ✓
🎉 Original message recovered:  c = [3, 6, 1, 0]
This matches exactly what the sender encoded in Step 4.
Why could the owner do this instantly while the attacker is still searching?
The private key (g(X), α) gives the owner a secret algebraic shortcut — the Extended Euclidean Algorithm on polynomials — that jumps straight to σ(X) in two steps. The attacker has no shortcut: they must test all C(n, t) ≈ 10²⁰⁰ combinations one by one.
Input value
Result / answer
Error position
Valid / match
Calculation block