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

ControlDetail
Device-only castEd25519 device signature over canonical cast envelope; admin sessions rejected
No self-dealing rolesSelf-assign of privileged roles rejected and logged
Mode B sealingChoice encrypted to ballot key; tracker for inclusion; choice not on receipt
Threshold talliersDefault 2-of-3 Shamir on ballot private seed; API holds public only during vote
Append-only logseq, prev_hash, entry_hash; public chain; optional audit pack signatures
Roll commitmentEligible set frozen at open; cast must match committed roll
Dual-control membershipPropose/approve by distinct officers when enabled
Two-admin gateOptional block on first live decision until ≥2 officers
Bind token hygiene≥128-bit entropy, short TTL, single use, rate limited
Recovery hardeningArgon2id verifier only; lockout; optional cast cooling-off
Fingerprint anti-phishingStable system fingerprint on bind and public pages
Officer MFATOTP + recovery codes; pack can require before mutations
Rate limitsAuth, MFA, bind, public HTML scrape protection, inclusion lookups
Content-hash bindingCast rejected if decision content diverges from freeze

Mode B crypto suite (publicvoting-modeb-v1)

FunctionAlgorithm
Device authenticationEd25519
Ballot recipient keyX25519
Ballot sealEphemeral X25519 + HKDF-SHA256 + AES-256-GCM
TrackerSHA-256 with domain separation
Log chainSHA-256 hash chain
ThresholdShamir t-of-n on 32-byte ballot seed (default 2-of-3)
Recovery KDFArgon2id (pinned parameters in suite doc)

Cross-language test vectors ship in publicvoting-protocol. Implementations must pass vectors before production cast.

Three key classes

KeyWho holds privatePurpose
K_devicePhone secure storageAuthenticate cast / bind / rotate
K_ballotSplit among talliersEncrypt choice until after close
K_logLog / 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)

AdversaryGoalMitigations
External attackerAlter tallies, DoS, steal accountsDevice sigs, TLS, rate limits, mirrors
Malicious voterDouble vote, note spamOne effective vote; rate limits; moderation log
Compromised adminStuff ballots, self-deal rolesNo portal cast; no self-role; dual control; roll commit; public log
CoercerBuy/force voteMode B inclusion ≠ choice; Mode C later
Phone malwareChange vote before sealOS security; residual risk accepted for remote voting
Curious DB thiefLink person → choice mid-electionSealed ballots; threshold; minimise public PII
Fake system QRPhish membershipsFingerprint confirm; publish official fp
Colluding talliersDecrypt earlyPick 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.