mirror of
https://github.com/saymrwulf/swisspost-evoting-go-poc.git
synced 2026-11-03 04:41:16 +00:00
Docs: cast-as-intended return codes are now real
ARCHITECTURE: replace the "return codes simulated" scope limit with the cast-as-intended design (E2 + plaintext-equality proof, CC exponentiation + decryption, voter check, substitution-attack test) and document the remaining honest limits (single-选option; return-code lookup lets the server infer the selection — a privacy simplification that does not affect tally secrecy). README: note that netdemo return codes are cast-as-intended. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
127ae61d11
commit
048b16e9fa
2 changed files with 46 additions and 15 deletions
|
|
@ -71,8 +71,12 @@ Run it with `evote netdemo --verbose` to watch every signed envelope.
|
|||
long-code-share-resp` (return-code shares from the CC's private secret). Setup
|
||||
assembles cards and delivers them **confidentially**: `setup → voter:
|
||||
voting-card` and `setup → server: mapping-table` (both X25519-encrypted).
|
||||
- **Voting** — `voter → server: cast-ballot`; `server → CCj: verify-ballot`;
|
||||
`CCj → server: ballot-verdict`. Stored on unanimous acceptance (vcPK persisted).
|
||||
- **Voting** — `voter → server: cast-ballot` (ballot + E2 + equality proof);
|
||||
`server → CCj: verify-ballot`; `CCj → server: ballot-verdict`. Stored on
|
||||
unanimous acceptance (vcPK persisted). Then the return-code extraction:
|
||||
`server → CCj: rc-exp-req` / `rc-dec-req`; `CCj →: rc-exp-resp` / `rc-dec-resp`.
|
||||
The server returns the recovered code to the voter, who checks it against the
|
||||
card (cast-as-intended — see below).
|
||||
- **Tally** — `→ server: start-tally` (server pads the ballot box); then
|
||||
`→ CCj: shuffle` / `CCj →: shuffled` chained through all CCs; `→ EB: final-mix`
|
||||
/ `EB →: final-done`. Each party posts its shuffle + decryption proofs to the
|
||||
|
|
@ -108,17 +112,41 @@ addressed in the party layer:
|
|||
- **F12** (panic on malformed plaintext): tally uses `DecodeVoteChecked`, so a
|
||||
ballot that does not decode is spoiled, not fatal; handlers reject malformed
|
||||
messages cleanly rather than panicking.
|
||||
- **F5 / F16** (return codes were decorative): the return-code path is now
|
||||
genuinely **cast-as-intended** — see below.
|
||||
|
||||
### Honest scope limits
|
||||
## Cast-as-intended return codes
|
||||
|
||||
- **F5** (the unsound plaintext-equality proof) is *not* reimplemented. The
|
||||
multi-party ballot uses only the **sound exponentiation proof** binding the
|
||||
ballot to `vcPK`; the return-code path that encrypts partial choice codes under
|
||||
the return-codes key (E2 / plaintext-equality) is omitted.
|
||||
- Consistent with the original system (finding F16), the **cast-as-intended**
|
||||
return codes are derived deterministically from CC shares rather than
|
||||
homomorphically over the submitted ciphertext. The return-code *mechanism*
|
||||
(per-CC shares combined into a mapping-table lookup) is real and consistent
|
||||
across card generation and would-be extraction (fixing the old derivation
|
||||
mismatch, F1); the property that the shown code is computed *from the actual
|
||||
ballot* remains simulated.
|
||||
The return code the voter checks is computed by the control components **from the
|
||||
submitted ciphertext**, so client-side vote substitution is detected:
|
||||
|
||||
1. The voter submits the ballot `E1` plus a second ciphertext `E2 =
|
||||
Enc(vote, returnCodesPK)` and a **plaintext-equality proof** that `E1` and
|
||||
`E2` encrypt the same vote. Every CC verifies this proof; a ballot whose two
|
||||
ciphertexts disagree is rejected.
|
||||
2. The card code for option *i* is the algebraic base `prime_i^{Σ_j k_j}`, where
|
||||
`k_j` is CC *j*'s voter-specific return-code key. Setup builds it by combining
|
||||
per-CC shares; the same `k_j` is used at vote time (one derivation — the old
|
||||
F1 mismatch is gone by construction).
|
||||
3. At vote time each CC exponentiates `E2` by its `k_j` (product over CCs =
|
||||
`Enc(vote^{Σk})`), then contributes a partial-decryption factor. The server
|
||||
recovers `vote^{Σk}` — which equals `prime_sel^{Σk}` — and looks up the short
|
||||
code, returning it to the voter.
|
||||
4. The voter checks the returned code against the card for the chosen option. A
|
||||
mismatch means the tallied vote differs from the intended one.
|
||||
|
||||
**Soundness**: because the equality proof binds `E2` to `E1` (the tallied
|
||||
ballot), the code is derived from the vote that is actually counted. Malware that
|
||||
alters the vote cannot produce a matching card code without the CC secrets. A
|
||||
unit test drives the substitution attack (`E1`=A, `E2`=B) and confirms the CCs
|
||||
reject it.
|
||||
|
||||
### Remaining scope limits (honest)
|
||||
|
||||
- The return-code path is defined for a **single selected option** per voter (the
|
||||
demo case); multi-selection return codes are not implemented.
|
||||
- **Privacy**: the return-code lookup lets the server learn which mapping entry
|
||||
matched, so in this PoC the return-code-computing parties can infer the
|
||||
selection. The real system separates these roles to keep the vote secret from
|
||||
any single party; that role separation is simplified here. Vote secrecy of the
|
||||
**tally** is unaffected (it rests on the mix-net, not the return-code channel).
|
||||
|
|
|
|||
|
|
@ -79,7 +79,10 @@ The `demo` command runs the whole protocol in one process. `netdemo` runs the
|
|||
**multi-party** architecture: every party is a separate endpoint holding only its
|
||||
own private state, and every message between them is Ed25519-signed (and, for
|
||||
card/mapping-table delivery, X25519-encrypted) — all transport cryptography in
|
||||
Rust. See [ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
Rust. The `netdemo` return codes are **cast-as-intended**: each voter's code is
|
||||
recomputed by the control components from the submitted ciphertext (bound to the
|
||||
ballot by a plaintext-equality proof), so a substituted vote is detected. See
|
||||
[ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
If you build Go directly (`go build ./...`), run `make rust` first so the static library
|
||||
at `rust/transportsec/target/release/libtransportsec.a` exists for cgo to link.
|
||||
|
|
|
|||
Loading…
Reference in a new issue