swisspost-evoting-go-poc/presentation.html
saymrwulf e8b6f30871 Swiss Post E-Voting Go PoC
Proof-of-concept reimplementation of the Swiss Post e-voting
cryptographic protocol in Go. Single binary, 52 source files,
2 dependencies. Covers ElGamal encryption, Bayer-Groth verifiable
shuffles, zero-knowledge proofs, return codes, and a full
election ceremony demo.
2026-02-13 19:53:09 +01:00

1041 lines
46 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Swiss Post E-Voting: A Cryptographic Election</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0d1117;
color: #c9d1d9;
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', 'JetBrains Mono', 'Consolas', monospace;
font-size: 15px;
line-height: 1.6;
overflow: hidden;
height: 100vh;
}
#progress-bar {
position: fixed; top: 0; left: 0;
height: 3px; background: #58a6ff;
transition: width 0.4s ease;
z-index: 100;
}
#slide-counter {
position: fixed; top: 10px; right: 20px;
font-size: 12px; color: #484f58;
z-index: 100;
}
#nav-hint {
position: fixed; bottom: 15px; right: 20px;
font-size: 11px; color: #30363d;
z-index: 100;
}
#stage {
max-width: 920px;
margin: 0 auto;
padding: 40px 30px 60px;
height: 100vh;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #21262d #0d1117;
}
#stage::-webkit-scrollbar { width: 6px; }
#stage::-webkit-scrollbar-track { background: #0d1117; }
#stage::-webkit-scrollbar-thumb { background: #21262d; border-radius: 3px; }
.role-banner {
display: inline-block;
padding: 4px 14px;
border-radius: 4px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
margin-bottom: 16px;
}
.slide-title {
font-size: 22px;
font-weight: 700;
margin-bottom: 20px;
color: #e6edf3;
}
.content { margin-bottom: 20px; }
.decision-box {
border: 1px solid #30363d;
border-radius: 8px;
padding: 16px 20px;
margin: 20px 0;
background: #161b22;
}
.decision-question {
color: #8b949e;
font-size: 14px;
margin-bottom: 10px;
}
.decision-answer {
font-size: 16px;
font-weight: 700;
padding: 6px 16px;
border-radius: 4px;
display: inline-block;
}
.crypto-block {
background: #161b22;
border: 1px solid #21262d;
border-radius: 6px;
padding: 14px 18px;
margin: 12px 0;
font-size: 13px;
overflow-x: auto;
white-space: pre;
line-height: 1.5;
}
.crypto-block .label { color: #8b949e; }
.crypto-block .value { color: #79c0ff; }
.crypto-block .secret { color: #f85149; }
.crypto-block .public { color: #56d364; }
.crypto-block .highlight { color: #d2a8ff; }
.crypto-block .dim { color: #484f58; }
.card-box {
border: 2px solid #f0c040;
border-radius: 8px;
padding: 16px 20px;
margin: 14px 0;
background: #1c1a12;
white-space: pre;
font-size: 13px;
line-height: 1.5;
}
.proof-line { margin: 2px 0; font-size: 13px; }
.proof-pass { color: #56d364; font-weight: 700; }
.proof-tree { color: #484f58; }
.result-box {
border: 2px solid #58a6ff;
border-radius: 8px;
padding: 20px 24px;
margin: 16px 0;
background: #0d1926;
text-align: center;
}
.result-bar {
display: inline-block;
height: 18px;
border-radius: 3px;
margin-left: 8px;
vertical-align: middle;
}
.verdict-box {
border: 2px solid #56d364;
border-radius: 8px;
padding: 24px 28px;
margin: 16px 0;
background: #0d2818;
font-size: 14px;
line-height: 1.7;
}
.voter-table {
width: 100%;
border-collapse: collapse;
margin: 12px 0;
font-size: 13px;
}
.voter-table th, .voter-table td {
padding: 6px 12px;
text-align: left;
border-bottom: 1px solid #21262d;
}
.voter-table th { color: #8b949e; font-weight: 600; }
/* Title card */
.title-card {
text-align: center;
padding-top: 15vh;
}
.title-card h1 {
font-size: 32px;
color: #e6edf3;
margin-bottom: 12px;
font-weight: 800;
}
.title-card .subtitle {
font-size: 16px;
color: #8b949e;
margin-bottom: 40px;
}
.title-card .swiss-cross {
font-size: 60px;
color: #f85149;
margin-bottom: 30px;
}
.title-card .start-hint {
color: #484f58;
font-size: 13px;
margin-top: 50px;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; }
}
/* Role colors */
.role-chancellor { background: #f0c040; color: #0d1117; }
.role-cc { background: #00ff88; color: #0d1117; }
.role-voter { background: #60a0ff; color: #0d1117; }
.role-eb { background: #ff8844; color: #0d1117; }
.role-auditor { background: #ff4466; color: #0d1117; }
.role-system { background: #555; color: #e6edf3; }
.role-none { background: #30363d; color: #e6edf3; }
.color-chancellor { color: #f0c040; }
.color-cc { color: #00ff88; }
.color-voter { color: #60a0ff; }
.color-eb { color: #ff8844; }
.color-auditor { color: #ff4466; }
.color-system { color: #aaaaaa; }
.fade-in { animation: fadeIn 0.3s ease-in; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.em { color: #e6edf3; font-weight: 600; }
.dim { color: #484f58; }
.note { color: #8b949e; font-size: 13px; font-style: italic; }
</style>
</head>
<body>
<div id="progress-bar"></div>
<div id="slide-counter"></div>
<div id="nav-hint">Space / Enter / Arrow keys to navigate</div>
<div id="stage"></div>
<script>
const SLIDES = [
// 0: Title
{
role: null,
html: `
<div class="title-card">
<div class="swiss-cross">&#x1F1E8;&#x1F1ED;</div>
<h1>Swiss Post E-Voting</h1>
<div class="subtitle">A Cryptographic Election in 27 Steps</div>
<div style="color:#484f58; font-size:13px; margin-top:10px;">
6 voters &middot; 2 candidates &middot; 4 control components &middot; 5 shuffle proofs
</div>
<div class="start-hint">Press Space to begin</div>
</div>
`
},
// 1: Canton Chancellor - Open e-voting
{
role: 'Canton Chancellor',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Opening the Electronic Voting Channel',
html: `
<p>You are the <span class="em color-chancellor">Canton Chancellor</span>, the highest authority for this election.</p>
<p style="margin-top:12px">A federal referendum is coming. Before any cryptographic key is generated, before any ballot exists, you must authorize the opening of the electronic voting channel.</p>
<div class="decision-box">
<div class="decision-question">Should you open the electronic voting channel for this election?</div>
<div class="decision-answer role-chancellor">Yes, open e-voting</div>
</div>
<p class="note">The Canton Chancellor's authorization begins the entire ceremony.</p>
`
},
// 2: Canton Chancellor - Number of candidates
{
role: 'Canton Chancellor',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Defining the Ballot',
html: `
<p>How many candidates should appear on the ballot?</p>
<div class="decision-box">
<div class="decision-question">How many candidates should be on the ballot?</div>
<div class="decision-answer role-chancellor">2 candidates: Alice &amp; Bob</div>
</div>
`
},
// 3: Canton Chancellor - Sign crypto parameters
{
role: 'Canton Chancellor',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Signing the Cryptographic Group Parameters',
html: `
<p>The system needs a <span class="em">safe prime group</span> for ElGamal encryption. These parameters define the mathematical universe in which all cryptography will happen.</p>
<div class="decision-box">
<div class="decision-question">Sign the order to generate the cryptographic group parameters?</div>
<div class="decision-answer role-chancellor">Sign</div>
</div>
<div class="crypto-block"><span class="label">q</span> = <span class="value">102456006687220362937022221649168886313301978055082970105502958159943003448101</span>
<span class="label">p</span> = <span class="value">204912013374440725874044443298337772626603956110165940211005916319886006896203</span>
<span class="label">g</span> = <span class="value">4</span> <span class="dim">(generator)</span>
<span class="dim">q is 256 bits, p is 257 bits
p = 2q + 1 (safe prime, confirmed)
Both prime (confirmed)</span></div>
<p class="note">In production: |p| = 3072 bits. This demo uses 256-bit for readability.</p>
`
},
// 4: CC0 - Generate key
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Generating Your Secret Key',
html: `
<p>You are now the <span class="em color-cc">CC0 Operator in Bern</span>. You sit in a secure room with an air-gapped machine. Your job: generate a secret key that <em>only you</em> will ever know.</p>
<div class="decision-box">
<div class="decision-question">Ready to generate your key pair?</div>
<div class="decision-answer role-cc">Generate my key</div>
</div>
<div class="crypto-block"><span class="label">=== YOUR SECRET KEY (CC0, Bern) ===</span>
<span class="label">Secret key [0]:</span> <span class="secret">490298081064930195553874605087930422904...</span>
<span class="dim">(77 digits, only you can see this)</span>
<span class="label">Secret key [1]:</span> <span class="secret">906260723715705230338812685464779149349...</span>
<span class="dim">(77 digits, only you can see this)</span>
<span class="label">=== YOUR PUBLIC KEY (CC0, Bern) ===</span>
<span class="label">Public key [0]:</span> <span class="public">164907173125453164942397900344209023580...</span>
<span class="dim">Computed as: g ^ secret_key[0] mod p</span>
<span class="label">Public key [1]:</span> <span class="public">865964935823822544135231120873786690830...</span>
<span class="dim">Computed as: g ^ secret_key[1] mod p</span></div>
<p class="note">The public key can be shared with everyone. The secret key never leaves this machine.</p>
`
},
// 5: CC0 - Schnorr proof
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Generating a Schnorr Proof of Knowledge',
html: `
<p>You must <em>prove</em> you know your secret key without revealing it. This is a <span class="em">zero-knowledge proof</span>.</p>
<div class="decision-box">
<div class="decision-question">Generate the Schnorr proof?</div>
<div class="decision-answer role-cc">Generate proof</div>
</div>
<div class="crypto-block"><span class="label">--- Schnorr Proof Generation ---</span>
<span class="highlight">Step 1:</span> Pick a random number 'b' (kept secret)
b = <span class="secret">577282606851693328110434804545...</span> <span class="dim">(secret, thrown away after)</span>
<span class="highlight">Step 2:</span> Compute commitment c = g^b mod p
c = <span class="public">196993736651760197696345266502366676991...</span>
<span class="dim">(This is like sealing your answer in an envelope)</span>
<span class="highlight">Step 3:</span> Compute challenge e = Hash(p, q, g, public_key, c)
e = <span class="value">280555736662452288549346417809892436748...</span>
<span class="dim">(The hash makes this unpredictable -- you can't cheat)</span>
<span class="highlight">Step 4:</span> Compute response z = b + e * secret_key mod q
z = <span class="value">137947587127192694451619751067570738829...</span>
<span class="label">=== YOUR SCHNORR PROOF ===</span>
e = <span class="value">280555736662452288549346417809892436748...</span>
z = <span class="value">137947587127192694451619751067570738829...</span>
<span class="dim">Anyone can verify: g^z =?= c * public_key^e
If it checks out, you MUST know the secret.
But e and z reveal NOTHING about what the secret is.</span>
Self-check: g^z == c * PK^e <span class="public">PROOF VALID</span></div>
`
},
// 6: CC0 - Hand over to CC1-3
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Handing Over to the Other Control Components',
html: `
<p>Three more operators in Zurich, Geneva, and Lugano must do the same: generate keys and proofs on their air-gapped machines.</p>
<div class="decision-box">
<div class="decision-question">Hand over control to CC1, CC2, CC3?</div>
<div class="decision-answer role-cc">Skip to all done</div>
</div>
<div class="crypto-block"><span class="label">CC1 (Zurich):</span> Key generated + Schnorr proof <span class="public">VALID</span>
<span class="label">CC2 (Geneva):</span> Key generated + Schnorr proof <span class="public">VALID</span>
<span class="label">CC3 (Lugano):</span> Key generated + Schnorr proof <span class="public">VALID</span></div>
<p class="note">4 independent operators in 4 Swiss cities. No single one can decrypt anything alone.</p>
`
},
// 7: EB President - Generate EB key
{
role: 'EB President',
roleClass: 'role-eb',
colorClass: 'color-eb',
title: 'Electoral Board Key Generation',
html: `
<p>You are the <span class="em color-eb">Electoral Board President</span>. You and your board members each enter a password. Combined, these passwords derive the 5th secret key.</p>
<div class="decision-box">
<div class="decision-question">Set passwords and generate the Electoral Board key?</div>
<div class="decision-answer role-eb">Set passwords &amp; generate</div>
</div>
<div class="crypto-block"><span class="label">Electoral Board key generated.</span>
<span class="label">PK4 (Electoral Board):</span> <span class="public">111235888017674427027755775458...</span>
<span class="dim">This key is split across multiple board members' passwords.
On election night, ALL board members must enter their passwords
to reconstruct this key for the final decryption.</span></div>
`
},
// 8: System Admin - Combine keys
{
role: 'System Administrator',
roleClass: 'role-system',
colorClass: 'color-system',
title: 'Combining All 5 Public Keys',
html: `
<p>You are the <span class="em">System Administrator</span>. You combine all 5 public keys into one <span class="em">Election Public Key</span>.</p>
<div class="decision-box">
<div class="decision-question">Combine the 5 public keys?</div>
<div class="decision-answer role-system">Combine</div>
</div>
<div class="crypto-block"><span class="label"> PK0 CC0 (Bern) =</span> <span class="public">190643356536098883252739444301...</span>
<span class="label"> PK1 CC1 (Zurich) =</span> <span class="public">763639679015419285323116529301...</span>
<span class="label"> PK2 CC2 (Geneva) =</span> <span class="public">166178915117197264285723029147...</span>
<span class="label"> PK3 CC3 (Lugano) =</span> <span class="public">500402434679977682038657772038...</span>
<span class="label"> PK4 Electoral Board =</span> <span class="public">111235888017674427027755775458...</span>
<span class="dim">Multiplying all 5 together...</span>
<span class="label">Election Public Key =</span> <span class="highlight">187387247443810368528889414473024386338505450542...</span>
<span class="dim">This single key locks the ballot box.
To unlock it, you need ALL 5 secret keys working together.</span></div>
`
},
// 9: System Admin - Encode candidates
{
role: 'System Administrator',
roleClass: 'role-system',
colorClass: 'color-system',
title: 'Encoding the Candidates',
html: `
<p>Each candidate is mapped to a prime number, then squared to create a <span class="em">quadratic residue</span> in the group. This encoding enables efficient tallying after decryption.</p>
<div class="decision-box">
<div class="decision-question">Encode the candidates?</div>
<div class="decision-answer role-system">Encode candidates</div>
</div>
<div class="crypto-block"><span class="label">Candidate encoding:</span>
<span class="value">Alice</span> = prime 2 &rarr; squared: <span class="highlight">4</span> <span class="dim">(2&sup2; = 4)</span>
<span class="value">Bob</span> = prime 3 &rarr; squared: <span class="highlight">9</span> <span class="dim">(3&sup2; = 9)</span></div>
<p class="note">In the production system, the encoding uses larger primes for more complex ballots.</p>
`
},
// 10: Registry Officer - Show one card
{
role: 'Registry Officer',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Generating Voting Cards',
html: `
<p>You are the <span class="em color-chancellor">Registry Officer</span>. Each voter receives a physical card in the mail with their secret codes.</p>
<div class="decision-box">
<div class="decision-question">Generate the 6 voting cards?</div>
<div class="decision-answer role-chancellor">Show me one card first</div>
</div>
<div class="card-box"> +------------------------------------------------------+
| SWISS CONFEDERATION |
| Electronic Voting Card |
| |
| Voter ID: voter-0000 |
| Start Voting Key: <span class="highlight">SVK-0000</span> |
| Ballot Casting Key: <span class="highlight">BCK-0000</span> |
| |
| Choice Return Codes (to verify your vote): |
| Alice: <span class="value">CC00</span> |
| Bob: <span class="value">CC01</span> |
| |
| Vote Cast Code: <span class="value">VCC00</span> |
| |
| KEEP THIS CARD SAFE. DO NOT SHARE IT. |
+------------------------------------------------------+</div>
<p class="note">The codes on this card are the voter's only way to verify their vote was recorded correctly.</p>
`
},
// 11: Registry Officer - Generate all 6 cards
{
role: 'Registry Officer',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Mailing All 6 Voting Cards',
html: `
<div class="decision-box">
<div class="decision-question">Generate the remaining 5 cards and mail all 6?</div>
<div class="decision-answer role-chancellor">Generate &amp; mail all 6</div>
</div>
<table class="voter-table">
<tr><th>Voter</th><th>SVK</th><th>BCK</th><th>Alice</th><th>Bob</th><th>VCC</th></tr>
<tr><td>Anna</td><td>SVK-0000</td><td>BCK-0000</td><td>CC00</td><td>CC01</td><td>VCC00</td></tr>
<tr><td>Beat</td><td>SVK-0001</td><td>BCK-0001</td><td>CC10</td><td>CC11</td><td>VCC01</td></tr>
<tr><td>Clara</td><td>SVK-0002</td><td>BCK-0002</td><td>CC20</td><td>CC21</td><td>VCC02</td></tr>
<tr><td>Daniel</td><td>SVK-0003</td><td>BCK-0003</td><td>CC30</td><td>CC31</td><td>VCC03</td></tr>
<tr><td>Eva</td><td>SVK-0004</td><td>BCK-0004</td><td>CC40</td><td>CC41</td><td>VCC04</td></tr>
<tr><td>Fritz</td><td>SVK-0005</td><td>BCK-0005</td><td>CC50</td><td>CC51</td><td>VCC05</td></tr>
</table>
<p class="note">Each voter's codes are unique. Only they know their return codes.</p>
`
},
// 12: Voter Anna - Authenticate
{
role: 'Voter Anna',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Authentication',
html: `
<p>You are <span class="em color-voter">Anna</span>, a Swiss citizen. You received your voting card in the mail. You open your browser and navigate to the voting portal.</p>
<div class="decision-box">
<div class="decision-question">Enter your credentials to authenticate?</div>
<div class="decision-answer role-voter">Enter SVK-0000 + DOB</div>
</div>
<div class="crypto-block"><span class="public">Authentication successful.</span>
<span class="dim">Your Start Voting Key was verified using Argon2id (password hash).
The server never stores your SVK in plaintext.</span>
<span class="label">Ballot loaded:</span>
[ ] Alice
[ ] Bob</div>
`
},
// 13: Voter Anna - Vote + encryption
{
role: 'Voter Anna',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Casting Your Vote',
html: `
<div class="decision-box">
<div class="decision-question">Who do you vote for?</div>
<div class="decision-answer role-voter">Alice</div>
</div>
<div class="crypto-block"><span class="label">=== INSIDE YOUR BROWSER ===</span>
<span class="highlight">Step 1:</span> Encode your choice
Alice &rarr; prime 2 &rarr; squared: <span class="value">4</span>
Your vote is the number: <span class="value">4</span>
<span class="highlight">Step 2:</span> Generate random number 'r' (used once, then discarded)
r = <span class="secret">833704513946265251405968611993787771762...</span>
<span class="dim">This randomness makes YOUR ciphertext unique.
Even if someone else also votes Alice, their
encrypted ballot will look completely different.</span>
<span class="highlight">Step 3:</span> ElGamal encryption
gamma = g^r mod p
= <span class="value">183571083508143527798599308812874641392...</span>
phi = ElectionPK^r &times; vote mod p
= <span class="value">136498980446327435592002680136840893911...</span>
<span class="dim">Your vote (4) is now buried inside 'phi',
hidden behind ElectionPK^r -- a huge random-looking number.
To extract the 4, you'd need to compute gamma^(sum of all 5 secrets),
which requires ALL 5 key holders to cooperate.</span></div>
<div class="crypto-block"><span class="label">=== WHAT GETS SENT TO THE SERVER ===</span>
gamma = <span class="value">183571083508143527798599308812874641392...</span>
phi = <span class="value">136498980446327435592002680136840893911...</span>
<span class="dim">That's it. Two big numbers. Pure noise to anyone
who doesn't have all 5 secret keys.</span></div>
`
},
// 14: Voter Anna - Return code + confirm
{
role: 'Voter Anna',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Verifying with Return Codes',
html: `
<p>The server processes your encrypted vote through the 4 control components. Each one computes a partial return code. Combined, they produce a <span class="em">Choice Return Code</span> that appears on your screen.</p>
<div class="crypto-block"><span class="label">Server displays:</span> Choice Return Code = <span class="highlight">CC00</span>
<span class="dim">You look at your physical voting card...</span>
<span class="label">Your card says:</span> Alice = <span class="highlight">CC00</span> <span class="public">MATCH!</span>
<span class="dim">This confirms the server recorded "Alice" -- not something else.
No single component could fake this code. All 4 CCs contributed.</span></div>
<div class="decision-box">
<div class="decision-question">Return code matches. Confirm your vote?</div>
<div class="decision-answer role-voter">Enter BCK-0000 to confirm</div>
</div>
<div class="crypto-block"><span class="label">Server displays:</span> Vote Cast Code = <span class="highlight">VCC00</span>
<span class="label">Your card says:</span> VCC = <span class="highlight">VCC00</span> <span class="public">MATCH!</span>
<span class="dim">Your vote is sealed. Done.</span></div>
`
},
// 15: Voter Beat
{
role: 'Voter Beat',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Beat Votes',
html: `
<div class="decision-box">
<div class="decision-question">Who do you vote for?</div>
<div class="decision-answer role-voter">Alice</div>
</div>
<div class="crypto-block"><span class="dim">Authenticate with SVK-0001...</span> <span class="public">OK</span>
<span class="dim">Encrypt vote (Alice = 4)...</span> <span class="public">OK</span>
<span class="dim">Return code CC10...</span> <span class="public">MATCH</span>
<span class="dim">Confirm with BCK-0001...</span> <span class="public">OK</span>
<span class="dim">Vote Cast Code VCC01...</span> <span class="public">MATCH</span></div>
`
},
// 16: Voter Clara
{
role: 'Voter Clara',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Clara Votes',
html: `
<div class="decision-box">
<div class="decision-question">Who do you vote for?</div>
<div class="decision-answer role-voter">Alice</div>
</div>
<div class="crypto-block"><span class="dim">Authenticate with SVK-0002...</span> <span class="public">OK</span>
<span class="dim">Encrypt vote (Alice = 4)...</span> <span class="public">OK</span>
<span class="dim">Return code CC20...</span> <span class="public">MATCH</span>
<span class="dim">Confirm with BCK-0002...</span> <span class="public">OK</span>
<span class="dim">Vote Cast Code VCC02...</span> <span class="public">MATCH</span></div>
`
},
// 17: Voter Daniel
{
role: 'Voter Daniel',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Daniel Votes',
html: `
<div class="decision-box">
<div class="decision-question">Who do you vote for?</div>
<div class="decision-answer role-voter">Alice</div>
</div>
<div class="crypto-block"><span class="dim">Authenticate with SVK-0003...</span> <span class="public">OK</span>
<span class="dim">Encrypt vote (Alice = 4)...</span> <span class="public">OK</span>
<span class="dim">Return code CC30...</span> <span class="public">MATCH</span>
<span class="dim">Confirm with BCK-0003...</span> <span class="public">OK</span>
<span class="dim">Vote Cast Code VCC03...</span> <span class="public">MATCH</span></div>
`
},
// 18: Voter Eva
{
role: 'Voter Eva',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Eva Votes',
html: `
<div class="decision-box">
<div class="decision-question">Who do you vote for?</div>
<div class="decision-answer role-voter">Alice</div>
</div>
<div class="crypto-block"><span class="dim">Authenticate with SVK-0004...</span> <span class="public">OK</span>
<span class="dim">Encrypt vote (Alice = 4)...</span> <span class="public">OK</span>
<span class="dim">Return code CC40...</span> <span class="public">MATCH</span>
<span class="dim">Confirm with BCK-0004...</span> <span class="public">OK</span>
<span class="dim">Vote Cast Code VCC04...</span> <span class="public">MATCH</span></div>
`
},
// 19: Voter Fritz
{
role: 'Voter Fritz',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Fritz Votes',
html: `
<p class="note" style="margin-bottom:12px">The last voter. Will he break the streak?</p>
<div class="decision-box">
<div class="decision-question">Who do you vote for?</div>
<div class="decision-answer role-voter">Bob</div>
</div>
<div class="crypto-block"><span class="dim">Authenticate with SVK-0005...</span> <span class="public">OK</span>
<span class="dim">Encrypt vote (Bob = 9)...</span> <span class="public">OK</span>
<span class="dim">Return code CC51...</span> <span class="public">MATCH</span>
<span class="dim">Confirm with BCK-0005...</span> <span class="public">OK</span>
<span class="dim">Vote Cast Code VCC05...</span> <span class="public">MATCH</span></div>
`
},
// 20: Voting closed
{
role: null,
roleClass: 'role-none',
title: 'Voting Period Closed',
html: `
<p style="font-size:18px; color:#e6edf3; margin-bottom:20px;">6 ballots cast. The voting period is over.</p>
<div class="crypto-block"><span class="label">What the server sees (hex dump of encrypted ballots):</span>
<span class="dim">Voter 0 | Alice |</span> <span class="value">E01E83B2B8...B8A0745F41</span>
<span class="dim">Voter 1 | Alice |</span> <span class="value">DE73EEB649...C3E0A5FAF2</span>
<span class="dim">Voter 2 | Alice |</span> <span class="value">FF1D9B7179...735424E93A</span>
<span class="dim">Voter 3 | Alice |</span> <span class="value">4931157C26...9D6A03E982</span>
<span class="dim">Voter 4 | Alice |</span> <span class="value">13C275835F...BF41E92D91</span>
<span class="dim">Voter 5 | Bob |</span> <span class="value">1232996CD9...3791B60DCF</span>
<span class="dim">Every ballot looks like random noise.
The server knows WHO voted, but not WHAT they voted.
Even the labels above (Alice/Bob) are invisible to the server.</span></div>
`
},
// 21: CC0 Shuffle
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Shuffling the Ballots',
html: `
<p>Now you must <span class="em">shuffle</span> and <span class="em">re-encrypt</span> all 6 ballots. This breaks the link between voters and their encrypted votes.</p>
<div class="decision-box">
<div class="decision-question">Begin your shuffle?</div>
<div class="decision-answer role-cc">Shuffle now</div>
</div>
<div class="crypto-block"><span class="label">=== INPUT: 6 encrypted ballots (in voter order) ===</span>
<span class="dim">[0]</span> <span class="value">E01E83B2B8...B8A0745F41</span> <span class="dim">&larr; Anna's ballot</span>
<span class="dim">[1]</span> <span class="value">DE73EEB649...C3E0A5FAF2</span> <span class="dim">&larr; Beat's ballot</span>
<span class="dim">[2]</span> <span class="value">FF1D9B7179...735424E93A</span> <span class="dim">&larr; Clara's ballot</span>
<span class="dim">[3]</span> <span class="value">4931157C26...9D6A03E982</span> <span class="dim">&larr; Daniel's ballot</span>
<span class="dim">[4]</span> <span class="value">13C275835F...BF41E92D91</span> <span class="dim">&larr; Eva's ballot</span>
<span class="dim">[5]</span> <span class="value">1232996CD9...3791B60DCF</span> <span class="dim">&larr; Fritz's ballot</span>
<span class="dim">Generating random permutation...</span>
Secret permutation: <span class="secret">[0 2 4 3 5 1]</span>
<span class="dim">(This is destroyed after the proof is generated.)</span>
<span class="dim">Re-encrypting each ballot with fresh randomness...</span></div>
<div class="crypto-block"><span class="label">=== OUTPUT: 6 shuffled + re-encrypted ballots ===</span>
<span class="dim">[0]</span> <span class="value">D7B8715E1D...07B4756A00</span> <span class="dim">&larr; which voter? Nobody knows.</span>
<span class="dim">[1]</span> <span class="value">19EC21D022...03FA6B7A94</span> <span class="dim">&larr; which voter? Nobody knows.</span>
<span class="dim">[2]</span> <span class="value">1FC53796E9...50AD1138C8</span> <span class="dim">&larr; which voter? Nobody knows.</span>
<span class="dim">[3]</span> <span class="value">88B2E59D8B...AB2F8F9CB6</span> <span class="dim">&larr; which voter? Nobody knows.</span>
<span class="dim">[4]</span> <span class="value">F27A76A4D6...7E83ED14F6</span> <span class="dim">&larr; which voter? Nobody knows.</span>
<span class="dim">[5]</span> <span class="value">14F747B54D...A2C76D0315</span> <span class="dim">&larr; which voter? Nobody knows.</span>
<span class="dim">Compare input [0] to ANY output -- completely different numbers.
No way to match them.</span></div>
<div class="crypto-block"><span class="label">Generating Bayer-Groth shuffle proof...</span>
<span class="proof-tree">ShuffleArgument</span>
<span class="proof-tree">+-- ProductArgument</span>
<span class="proof-tree">| +-- HadamardArgument</span>
<span class="proof-tree">| | +-- ZeroArgument</span>
<span class="proof-tree">| +-- SingleValueProductArgument</span>
<span class="proof-tree">+-- MultiExponentiationArgument</span>
<span class="dim">This proof guarantees: same 6 votes, just reordered.
No votes added, removed, or changed.</span>
<span class="public">PROOF GENERATED</span> <span class="dim">(5ms)</span>
<span class="dim">Partial decryption: removing CC0's encryption layer...
4 layers remain (CC1 + CC2 + CC3 + Electoral Board)</span></div>
`
},
// 22: CC1-CC3 shuffle
{
role: null,
roleClass: 'role-none',
title: 'CC1, CC2, CC3 Shuffle in Sequence',
html: `
<p>Each control component receives the output of the previous one, shuffles again, re-encrypts, generates a proof, and removes its encryption layer.</p>
<div class="decision-box">
<div class="decision-question">Process the remaining 3 CC shuffles?</div>
<div class="decision-answer role-cc">Process all 3</div>
</div>
<div class="crypto-block"><span class="color-cc">CC1 (Zurich):</span> Shuffle <span class="public">OK</span> Re-encrypt <span class="public">OK</span> Partial decrypt <span class="public">OK</span> Proof <span class="public">OK</span>
<span class="dim">3 layers remain</span>
<span class="color-cc">CC2 (Geneva):</span> Shuffle <span class="public">OK</span> Re-encrypt <span class="public">OK</span> Partial decrypt <span class="public">OK</span> Proof <span class="public">OK</span>
<span class="dim">2 layers remain</span>
<span class="color-cc">CC3 (Lugano):</span> Shuffle <span class="public">OK</span> Re-encrypt <span class="public">OK</span> Partial decrypt <span class="public">OK</span> Proof <span class="public">OK</span>
<span class="dim">1 layer remains</span></div>
<p class="note">After 4 shuffles, the ballots have been permuted 4 times with 4 independent random permutations. The original order is mathematically unrecoverable.</p>
`
},
// 23: EB - Final shuffle + decrypt
{
role: 'EB President',
roleClass: 'role-eb',
colorClass: 'color-eb',
title: 'Opening the Ballot Box',
html: `
<p>The Electoral Board performs the 5th and final shuffle, then removes the last encryption layer. The ballots are now in plaintext -- but in a random order that nobody can trace back to voters.</p>
<div class="decision-box">
<div class="decision-question">Final shuffle + decrypt?</div>
<div class="decision-answer role-eb">Open the ballot box</div>
</div>
<div class="crypto-block"><span class="label">=== THE BALLOT BOX IS OPEN ===</span>
<span class="dim">Slot 0:</span> decrypted value = <span class="highlight">4</span> &rarr; <span class="value">Alice</span>
<span class="dim">Slot 1:</span> decrypted value = <span class="highlight">4</span> &rarr; <span class="value">Alice</span>
<span class="dim">Slot 2:</span> decrypted value = <span class="highlight">4</span> &rarr; <span class="value">Alice</span>
<span class="dim">Slot 3:</span> decrypted value = <span class="highlight">4</span> &rarr; <span class="value">Alice</span>
<span class="dim">Slot 4:</span> decrypted value = <span class="highlight">9</span> &rarr; <span class="value">Bob</span>
<span class="dim">Slot 5:</span> decrypted value = <span class="highlight">4</span> &rarr; <span class="value">Alice</span>
<span class="dim">The votes are in random order. Nobody can tell
which slot belongs to which voter.</span></div>
`
},
// 24: Election result
{
role: null,
roleClass: 'role-none',
title: 'Election Result',
html: `
<div class="result-box">
<div style="font-size:20px; font-weight:700; color:#e6edf3; margin-bottom:20px;">ELECTION RESULT</div>
<div style="text-align:left; display:inline-block;">
<div style="margin:8px 0; font-size:16px;">
<span class="value" style="display:inline-block; width:60px;">Alice</span>
<span class="em" style="display:inline-block; width:60px;">5 votes</span>
<span class="result-bar" style="width:250px; background:#56d364;"></span>
</div>
<div style="margin:8px 0; font-size:16px;">
<span class="value" style="display:inline-block; width:60px;">Bob</span>
<span class="em" style="display:inline-block; width:60px;">1 vote</span>
<span class="result-bar" style="width:50px; background:#60a0ff;"></span>
</div>
</div>
<div style="margin-top:20px; color:#56d364; font-weight:700; font-size:16px;">Alice is elected.</div>
</div>
<div class="crypto-block"><span class="label">Sanity check:</span>
Expected: Alice=5 Bob=1 <span class="dim">(from your votes)</span>
Got: Alice=5 Bob=1
<span class="public">RESULT MATCHES</span></div>
`
},
// 25: Public Auditor - Verification
{
role: 'Public Auditor',
roleClass: 'role-auditor',
colorClass: 'color-auditor',
title: 'Independent Verification',
html: `
<p>You are a <span class="em color-auditor">Public Auditor</span>. You have NO secret keys. You have NO special access. You only have the public data: the proofs, the public keys, and the encrypted/shuffled ballots.</p>
<p style="margin-top:10px">Can you verify the election is honest? <em>Yes.</em> That's the entire point.</p>
<div class="crypto-block"><span class="label">=== VERIFYING 4 KEY PROOFS (Schnorr) ===</span>
CC0 (Bern): g^z == c * PK^e <span class="proof-pass">[PASS]</span>
CC1 (Zurich): g^z == c * PK^e <span class="proof-pass">[PASS]</span>
CC2 (Geneva): g^z == c * PK^e <span class="proof-pass">[PASS]</span>
CC3 (Lugano): g^z == c * PK^e <span class="proof-pass">[PASS]</span></div>
<div class="crypto-block"><span class="label">=== VERIFYING 5 SHUFFLE PROOFS (Bayer-Groth) ===</span>
Shuffle 0 <span class="dim">(CC0, Bern)</span> -- 6 ciphertexts, 2x3 matrix
<span class="proof-tree">ProductArgument ..............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">HadamardArgument ...........</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">ZeroArgument .............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">SingleValueProductArgument .</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">MultiExponentiationArgument .</span> <span class="proof-pass">PASS</span>
==&gt; <span class="proof-pass">VERIFIED</span> <span class="dim">(2ms)</span>
Shuffle 1 <span class="dim">(CC1, Zurich)</span> -- 6 ciphertexts, 2x3 matrix
<span class="proof-tree">ProductArgument ..............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">HadamardArgument ...........</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">ZeroArgument .............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">SingleValueProductArgument .</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">MultiExponentiationArgument .</span> <span class="proof-pass">PASS</span>
==&gt; <span class="proof-pass">VERIFIED</span> <span class="dim">(2ms)</span>
Shuffle 2 <span class="dim">(CC2, Geneva)</span> -- 6 ciphertexts, 2x3 matrix
<span class="proof-tree">ProductArgument ..............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">HadamardArgument ...........</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">ZeroArgument .............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">SingleValueProductArgument .</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">MultiExponentiationArgument .</span> <span class="proof-pass">PASS</span>
==&gt; <span class="proof-pass">VERIFIED</span> <span class="dim">(2ms)</span>
Shuffle 3 <span class="dim">(CC3, Lugano)</span> -- 6 ciphertexts, 2x3 matrix
<span class="proof-tree">ProductArgument ..............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">HadamardArgument ...........</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">ZeroArgument .............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">SingleValueProductArgument .</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">MultiExponentiationArgument .</span> <span class="proof-pass">PASS</span>
==&gt; <span class="proof-pass">VERIFIED</span> <span class="dim">(2ms)</span>
Shuffle 4 <span class="dim">(Electoral Board)</span> -- 6 ciphertexts, 2x3 matrix
<span class="proof-tree">ProductArgument ..............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">HadamardArgument ...........</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">ZeroArgument .............</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">SingleValueProductArgument .</span> <span class="proof-pass">PASS</span>
<span class="proof-tree">MultiExponentiationArgument .</span> <span class="proof-pass">PASS</span>
==&gt; <span class="proof-pass">VERIFIED</span> <span class="dim">(2ms)</span></div>
<div class="crypto-block"><span class="label">Count Verification:</span>
Decrypted ballots: 6
Ballots submitted: 6
==&gt; <span class="proof-pass">PASS: Every ballot is accounted for.</span></div>
`
},
// 26: Final verdict
{
role: null,
roleClass: 'role-none',
title: 'The Verdict',
html: `
<div class="verdict-box">
<div style="text-align:center; font-size:18px; font-weight:700; color:#56d364; margin-bottom:16px;">ALL VERIFICATIONS PASSED.</div>
<p>As a public auditor, you have independently verified:</p>
<div style="margin:14px 0 14px 10px;">
<div style="margin:6px 0;"><span class="proof-pass">[x]</span> All 4 key holders proved they know their secrets</div>
<div style="margin:6px 0;"><span class="proof-pass">[x]</span> All 5 shuffles are mathematically honest</div>
<div style="margin:6px 0;"><span class="proof-pass">[x]</span> No votes were added, removed, or changed</div>
<div style="margin:6px 0;"><span class="proof-pass">[x]</span> The final count matches the number of ballots</div>
</div>
<p style="margin-top:14px;">You did this <span class="em">WITHOUT</span> any secret keys.</p>
<p>You did this <span class="em">WITHOUT</span> trusting anyone.</p>
<p style="margin-top:10px; color:#56d364; font-weight:600;">Pure mathematics.</p>
</div>
<div style="margin-top:24px; padding:20px; border:1px solid #21262d; border-radius:8px; background:#161b22;">
<div style="color:#8b949e; font-size:13px; margin-bottom:12px;">RECAP -- What you experienced:</div>
<div style="margin:10px 0;">
<span class="color-cc" style="font-weight:700;">AS THE CC OPERATOR:</span>
<span style="color:#c9d1d9;"> You generated keys, shuffled votes, created proofs. You never saw anyone's vote. Even you can't undo your own shuffle -- the permutation was destroyed.</span>
</div>
<div style="margin:10px 0;">
<span class="color-voter" style="font-weight:700;">AS THE VOTER:</span>
<span style="color:#c9d1d9;"> Your vote was encrypted in your browser. The server stored only noise. Return codes on your physical card confirmed it was recorded correctly.</span>
</div>
<div style="margin:10px 0;">
<span class="color-auditor" style="font-weight:700;">AS THE AUDITOR:</span>
<span style="color:#c9d1d9;"> You verified every step with public data and math. No trust required. No access to secrets. The proofs are either valid or they aren't. No gray area.</span>
</div>
</div>
<div style="margin-top:24px; padding:16px; border:1px solid #30363d; border-radius:8px; text-align:center;">
<p style="color:#8b949e; font-size:13px;">This is the same protocol used in real Swiss federal elections.</p>
<p style="color:#8b949e; font-size:13px; margin-top:6px;">The production system: 14 repositories, 500,000+ lines, Windows + Kubernetes + 50GB RAM.</p>
<p style="color:#8b949e; font-size:13px; margin-top:6px;">This demo: one 15MB Go binary on a Mac.</p>
<p style="color:#e6edf3; font-size:14px; font-weight:600; margin-top:10px;">The math is identical.</p>
</div>
`
}
];
let currentSlide = 0;
const stage = document.getElementById('stage');
const progressBar = document.getElementById('progress-bar');
const slideCounter = document.getElementById('slide-counter');
function render() {
const slide = SLIDES[currentSlide];
const total = SLIDES.length;
progressBar.style.width = ((currentSlide / (total - 1)) * 100) + '%';
slideCounter.textContent = (currentSlide + 1) + ' / ' + total;
let html = '<div class="fade-in">';
if (slide.role) {
html += '<div class="role-banner ' + (slide.roleClass || 'role-none') + '">' + slide.role + '</div>';
}
if (slide.title) {
html += '<div class="slide-title">' + slide.title + '</div>';
}
html += '<div class="content">' + slide.html + '</div>';
html += '</div>';
stage.innerHTML = html;
stage.scrollTop = 0;
}
window.addEventListener('keydown', function(e) {
if (e.key === ' ' || e.key === 'Enter' || e.key === 'ArrowRight' || e.key === 'ArrowDown') {
e.preventDefault();
e.stopPropagation();
if (currentSlide < SLIDES.length - 1) {
currentSlide++;
render();
}
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
e.preventDefault();
if (currentSlide > 0) {
currentSlide--;
render();
}
} else if (e.key === 'Home') {
e.preventDefault();
currentSlide = 0;
render();
} else if (e.key === 'End') {
e.preventDefault();
currentSlide = SLIDES.length - 1;
render();
}
});
// Touch support for mobile
let touchStartX = 0;
document.addEventListener('touchstart', function(e) {
touchStartX = e.changedTouches[0].screenX;
});
document.addEventListener('touchend', function(e) {
const diff = e.changedTouches[0].screenX - touchStartX;
if (Math.abs(diff) > 50) {
if (diff < 0 && currentSlide < SLIDES.length - 1) {
currentSlide++;
render();
} else if (diff > 0 && currentSlide > 0) {
currentSlide--;
render();
}
} else {
// Tap to advance
if (currentSlide < SLIDES.length - 1) {
currentSlide++;
render();
}
}
});
render();
</script>
</body>
</html>