Split vote configuration into static trust anchor and signed dynamic round registry - #20
Open
p0mvn wants to merge 3 commits into
Open
Conversation
…round registry The previous shape conflated the trust anchor, operational endpoints, and round binding into a single CDN-served document with no cryptographic chain of trust. A CDN compromise could substitute any field undetectably, and the per-config `vote_round_id` pin coupled publishers to chain-round activation timing, producing transient validation failures during round transitions. This change splits the configuration into two artifacts: - A wallet-bundled static configuration carrying the trusted admin pubkeys and the dynamic-config URL. Travels with the signed wallet binary and is the root of trust. - A server-published dynamic configuration containing operational endpoints (vote servers, PIR endpoints, supported versions) and a `rounds` registry keyed by `vote_round_id`. Each entry carries its own Ed25519 signatures over the entry's `ea_pk`, attested by one of the static-config trusted keys. The resulting trust chain is two steps per round: admin signature attests the entry's `ea_pk`, chain query attests that the round in progress uses that key. A single break in either step is detected. The registry is append-only at the cryptographic level: adding a round signs only the new entry, and existing entries' signatures remain valid forever. This naturally supports multiple concurrent authenticated rounds and historic rounds for tally review and audit, without the per-round CDN republish race the old design suffered from. Per-entry `auth_version` is the per-round extension hook. Future revisions can introduce wider signature scopes (e.g., per-round endpoint pins) without invalidating older entries; wallets that don't recognise an `auth_version` reject only that entry, not the whole configuration. The split also decouples operator participation from wallet releases and chain deploys: bringing a new vote server or PIR operator into rotation is an ordinary PR merge to the publishing repository. v1 keeps the signature scope deliberately narrow (entry `ea_pk` only). Wrapper fields and `rounds` membership remain CDN-trusted in v1, with explicit threat-model documentation and a forward path via `auth_version` / `config_version` bumps. The list shape of `signatures` admits multi-admin co-signing and m-of-n thresholds as future, non-breaking extensions. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Restructures the vote configuration in the Shielded Voting Wallet API ZIP to introduce a cryptographic chain of trust and a per-round authentication registry, replacing the single-document CDN-trusted shape.
Motivation
The previous shape had three problems:
vote_servers,pir_endpoints,ea_pk— and the wallet had no way to verify the EA public key against an out-of-band trust anchor.vote_round_idpin coupled the publisher to chain-round activation timing, producing transient validation failures during round transitions and requiring an awkward stale-config recovery path in wallet code.Design
Configuration is split into two artifacts:
trusted_keys(Ed25519 admin pubkeys) anddynamic_config_url. Travels with the signed app and is the trust anchor.vote_servers,pir_endpoints,supported_versions) plus aroundsregistry keyed byvote_round_id. Each round entry carries its own `signatures` list, attesting to the entry'sea_pkunder one of the static-config trusted keys.Trust binding (per round)
vote_round_idinrounds.A break in either step (signature or chain mismatch) is detected. Failures are scoped to the affected round; other authenticated rounds remain usable.
Versioning hooks
static_config_version— versions the static document.config_version— versions the dynamic config wrapper.auth_version(per-entry) — versions the round-entry schema and signature scope.Each is independently bumpable, so widening the v1 signature scope (e.g. to cover endpoints) is a non-breaking addition for older entries.
Append-only registry
Per-entry signatures (rather than one signature over the whole `rounds` map) make the registry append-only at the cryptographic level. Adding a round signs only the new entry; existing entries' signatures remain valid forever and are never re-signed. This decouples each round's authenticity from the publisher's current state and naturally supports admin key rotation across the registry's lifetime.
Operational decoupling
Bringing a new vote server or PIR operator into rotation is now governed by ordinary PR-merge to the publishing repository — not coupled to wallet releases or chain deploys. Adding, removing, or swapping operators picks up on the wallet's next configuration fetch.
v1 trust scope (deliberately narrow)
The signature covers an entry's `ea_pk` only. v1 does NOT defend against:
These are documented gaps with a forward path: widen signature scope by introducing `auth_version: 2` (entry-level) or `config_version: 2` (wrapper-level).
Spec sections changed
Test plan
ea_pkis unique per round (no cross-round reuse). If reuse is possible, add tiebreaker field at `auth_version: 2`.Made with Cursor