Security
Security model & precautions
Voting software that only markets “blockchain” or “military grade” without an honest threat model is not enough. Here is what PublicVoting enforces, how Mode B works, how operators should behave, and what we still do not claim.
Non-negotiable product rules
- Ballots cast only from a device with a valid binding signature
- Officer portal has no ballot cast API
- Officers cannot assign privileged roles to themselves
- Public decision artefacts live on an append-only hash chain
- Procedure packs and branding never weaken cast or crypto rules
Controls we enforce
| Control | Detail |
|---|---|
| Device-only cast | Ed25519 device signature over canonical cast envelope; admin sessions rejected |
| No self-dealing roles | Self-assign of privileged roles rejected and logged |
| Mode B sealing | Choice encrypted to ballot key; tracker for inclusion; choice not on receipt |
| Threshold talliers | Default 2-of-3 Shamir on ballot private seed; API holds public only during vote |
| Append-only log | seq, prev_hash, entry_hash; public chain; optional audit pack signatures |
| Roll commitment | Eligible set frozen at open; cast must match committed roll |
| Dual-control membership | Propose/approve by distinct officers when enabled |
| Two-admin gate | Optional block on first live decision until ≥2 officers |
| Bind token hygiene | ≥128-bit entropy, short TTL, single use, rate limited |
| Recovery hardening | Argon2id verifier only; lockout; optional cast cooling-off |
| Fingerprint anti-phishing | Stable system fingerprint on bind and public pages |
| Officer MFA | TOTP + recovery codes; pack can require before mutations |
| Rate limits | Auth, MFA, bind, public HTML scrape protection, inclusion lookups |
| Content-hash binding | Cast rejected if decision content diverges from freeze |
Mode B crypto suite (publicvoting-modeb-v1)
| Function | Algorithm |
|---|---|
| Device authentication | Ed25519 |
| Ballot recipient key | X25519 |
| Ballot seal | Ephemeral X25519 + HKDF-SHA256 + AES-256-GCM |
| Tracker | SHA-256 with domain separation |
| Log chain | SHA-256 hash chain |
| Threshold | Shamir t-of-n on 32-byte ballot seed (default 2-of-3) |
| Recovery KDF | Argon2id (pinned parameters in suite doc) |
Cross-language test vectors ship in publicvoting-protocol. Implementations must pass vectors before production cast.
Three key classes
| Key | Who holds private | Purpose |
|---|---|---|
| K_device | Phone secure storage | Authenticate cast / bind / rotate |
| K_ballot | Split among talliers | Encrypt choice until after close |
| K_log | Log / audit signing (optional) | Authenticate history artefacts |
Rule: device keys never provide ballot secrecy alone. Secrecy = K_ballot. Authenticity of cast = K_device. History = log chain (+ optional signatures).
Threat model (summary)
| Adversary | Goal | Mitigations |
|---|---|---|
| External attacker | Alter tallies, DoS, steal accounts | Device sigs, TLS, rate limits, mirrors |
| Malicious voter | Double vote, note spam | One effective vote; rate limits; moderation log |
| Compromised admin | Stuff ballots, self-deal roles | No portal cast; no self-role; dual control; roll commit; public log |
| Coercer | Buy/force vote | Mode B inclusion ≠ choice; Mode C later |
| Phone malware | Change vote before seal | OS security; residual risk accepted for remote voting |
| Curious DB thief | Link person → choice mid-election | Sealed ballots; threshold; minimise public PII |
| Fake system QR | Phish memberships | Fingerprint confirm; publish official fp |
| Colluding talliers | Decrypt early | Pick distinct trusted officers; same as any threshold scheme |
Operational precautions (checklist)
For officers
- Enable TOTP on production systems; rotate sessions after staff changes
- Use dual control for membership on anything beyond a tiny trusted club
- Distribute tallier shares to distinct people; never store full private key on the API host in production
- Issue bind tokens in person or authenticated channels; keep TTL short
- Publish and display the system fingerprint everywhere members join
- Treat emergency open as an incident-worthy event; review log presets after
- Watch for membership spikes and recovery spikes before high-stakes windows
- Export and archive audit packs after critical decisions
For voters
- Only bind after fingerprint match
- Write recovery secrets offline; never photo them into cloud chat
- Verify tracker inclusion after cast
- Prefer transfer over recover when replacing a phone
- Keep device OS updated
For operators / hosters
- TLS everywhere; restrict admin surfaces; monitor 429/auth anomalies
- Backups of private DB + log; tested restore
- Separate demo tenants from production; wipe demo on schedule
- Pin protocol VERSION; run vector tests in CI
- Security reports to the project security contact — not public issues
Residual risks (accepted for remote voting)
- Malware on the phone can change a choice before encrypt/sign
- Colluding talliers at threshold can decrypt early
- Colluding dual admins can invent members — mitigated by visibility and logs, not crypto-impossible
- Coercion reduced vs public ballots but not eliminated (Mode C later)
- Legal fitness for statutory elections remains the institution’s responsibility
Report a vulnerability
Do not use public GitHub issues for security reports. Contact security@publicvoting.org and follow project SECURITY.md.