swisspost-evoting-go-poc/pkg/party/codes_test.go
saymrwulf dbf00153f8 Voting cards: distributed return-code generation + confidential delivery
Setup now generates each voter's return-code card by collecting shares from all
four CCs over the bus (the GenEncLongCodeShares exchange) and distributes cards
and the mapping table over CONFIDENTIAL channels.

- codes.go: deriveReturnCodeKey is the SINGLE key-derivation function used by a
  CC both when contributing to card assembly and (later) at vote-time extraction
  — structurally preventing the setup/extraction derivation mismatch (F1). Each
  CC computes its choice/confirm shares from its private return-code secret;
  only the shares (validated as G_q members on decode) cross the bus.
- RunCards assembles cards, registers mapping-table entries, and delivers cards
  to voters + the mapping table to the server via sendConfidential (X25519 ECDH
  session key + AES-256-GCM, then Ed25519-signed) — exercising the secure
  channel in the ceremony, not just in tests.
- returncodes: MappingTable Export/ImportMappingTable for transport.

Test confirms every voter receives its card confidentially with the right code
count and the server receives the full mapping table + public election keys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:15:38 +02:00

47 lines
1.5 KiB
Go

package party
import (
"testing"
)
// TestRunCardsConfidentialDistribution runs setup + card generation and checks
// that each voter received its card over the confidential channel, the cards
// carry the right number of return codes, and the server received the mapping
// table plus public election parameters.
func TestRunCardsConfidentialDistribution(t *testing.T) {
cfg := testConfig(t, 4, 3)
c, err := NewCeremony(cfg, nil)
if err != nil {
t.Fatalf("NewCeremony: %v", err)
}
if err := c.RunSetup(); err != nil {
t.Fatalf("RunSetup: %v", err)
}
if err := c.RunCards(); err != nil {
t.Fatalf("RunCards: %v", err)
}
for v, voter := range c.Voters {
if voter.st.card == nil {
t.Fatalf("voter %d never received a card", v)
}
if voter.st.card.VerificationCardID == "" {
t.Fatalf("voter %d card missing vcID", v)
}
if len(voter.st.card.ChoiceReturnCodes) != cfg.NumOptions {
t.Fatalf("voter %d card has %d choice codes, want %d", v, len(voter.st.card.ChoiceReturnCodes), cfg.NumOptions)
}
}
if c.Server.st.mappingTable == nil {
t.Fatal("server never received the mapping table")
}
// Each voter contributes NumOptions choice entries + 1 confirm entry.
wantEntries := cfg.NumVoters * (cfg.NumOptions + 1)
if got := c.Server.st.mappingTable.Size(); got != wantEntries {
t.Fatalf("mapping table has %d entries, want %d", got, wantEntries)
}
if c.Server.st.electionPK.Elements == nil {
t.Fatal("server did not receive the election public key")
}
}