Alexis Bruneteau 55995365be docs: Add proper openspec configuration for MVP
Created comprehensive openspec structure:

openspec/specs/:
- mvp.md: MVP feature overview
- architecture.md: System architecture and data flows

openspec/changes/add-pqc-voting-mvp/:
- proposal.md: Project proposal with scope and rationale
- tasks.md: Detailed implementation tasks (6 phases, 30+ tasks)
- design.md: Complete design document
  - Cryptographic algorithms (Paillier, Kyber, Dilithium, ZKP)
  - Data structures (Block, Blockchain, Ballot)
  - API endpoint specifications
  - Security properties matrix
  - Threat model and mitigations

Follows openspec three-stage workflow:
1. Creating changes (proposal-based)
2. Implementation (tracked via tasks)
3. Completion (with validation)

Ready for implementation phase with clear requirements.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 18:02:33 +01:00

7.2 KiB
Raw Permalink Blame History

Design Document

Change ID: add-pqc-voting-mvp

Cryptographic Design

Paillier Homomorphic Encryption

  • Key Generation: Generate (n, λ, g) keypair
  • Encryption: E(m) = g^m * r^n mod n^2 where r is random
  • Decryption: D(c) = L(c^λ mod n^2) / L(g^λ mod n^2) mod n
  • Homomorphic Property: E(m1) * E(m2) = E(m1 + m2) mod n^2
  • Vote Property: E(0) * E(0) * ... * E(1) = E(total_votes)
  • Usage: Encrypt votes (0 or 1), sum encrypted votes without decryption

Kyber (ML-KEM)

  • Key Encapsulation: Generate (ek, dk) keypair
  • Encapsulation: (c, ss) = Kyber.Encaps(ek) - shared secret ss
  • Decapsulation: ss = Kyber.Decaps(c, dk)
  • Usage: Encrypt Paillier private key with Kyber public key for PQC protection
  • Benefit: Protects vote counts against future quantum attacks

Dilithium (ML-DSA)

  • Key Generation: Generate (sk, vk) signing keypair per voter
  • Signature: σ = Dilithium.Sign(msg, sk)
  • Verification: 0/1 = Dilithium.Verify(msg, σ, vk)
  • Usage:
    • Voter signs encrypted ballot
    • Authority signs blockchain blocks
  • Benefit: Post-quantum resistant authentication

Zero-Knowledge Proof (Simplified)

  • Goal: Prove E(v) is encryption of 0 or 1 without revealing v
  • Protocol:
    1. Voter commits to 0 and 1: c0 = E(0), c1 = E(1)
    2. Voter selects random r, computes challenge response
    3. Server verifies without learning v
  • Implementation: Simple version using Paillier properties

SHA-256 Hash Chain

  • Block Hash: H = SHA256(index || prev_hash || timestamp || E(v) || signature)
  • Chain Property: Each block contains previous block's hash
  • Verification: Recompute all hashes and verify chain integrity
  • Immutability: Changing any block invalidates entire chain

Data Structures

Block

class Block:
    index: int                  # Block number (0, 1, 2, ...)
    prev_hash: str             # SHA256 of previous block
    timestamp: float           # Unix timestamp
    encrypted_vote: str        # Paillier encrypted ballot
    transaction_id: str        # Unique vote identifier
    block_hash: str            # SHA256 of this block
    signature: str             # Dilithium signature of block

Blockchain

class Blockchain:
    chain: List[Block]         # List of blocks
    authority_sk: str          # Authority's Dilithium private key
    authority_vk: str          # Authority's Dilithium public key
    paillier_pubkey: str       # Paillier public key

    def add_block(encrypted_vote, tx_id) -> Block
    def verify_chain() -> bool
    def get_encrypted_sum() -> int (homomorphic)

Vote Ballot

interface Ballot {
    voter_id: string           # Voter identifier (verified once)
    encrypted_vote: string     # E(v) with Paillier
    zkp_proof: string         # ZKP that E(v) is 0 or 1
    signature: string         # Dilithium signature
    timestamp: number         # Client-side timestamp
}

Election Setup

class ElectionSetup:
    paillier_pubkey: str       # Distributed to voters
    paillier_privkey: str      # Kyber-encrypted, kept secret
    authority_sign_sk: str     # Dilithium signing key
    authority_sign_vk: str     # Dilithium verification key (public)

API Endpoints

POST /api/votes/setup

Purpose: Initialize election with cryptographic keys Request:

{
  "election_id": "2025-vote-01"
}

Response:

{
  "public_keys": {
    "paillier": "...base64...",
    "dilithium": "...base64..."
  },
  "status": "initialized"
}

GET /api/votes/public-keys

Purpose: Retrieve public keys for client encryption Response:

{
  "paillier_pubkey": "...base64...",
  "authority_pubkey": "...base64..."
}

POST /api/votes/register-voter

Purpose: Register voter with their Dilithium public key Request:

{
  "voter_id": "user123",
  "dilithium_pubkey": "...base64..."
}

Response:

{
  "status": "registered",
  "voter_id": "user123"
}

POST /api/votes/submit

Purpose: Submit encrypted ballot Request:

{
  "voter_id": "user123",
  "encrypted_vote": "...base64...",
  "zkp_proof": "...base64...",
  "signature": "...base64..."
}

Response:

{
  "status": "recorded",
  "transaction_id": "tx-abc123",
  "block_index": 42
}

GET /api/votes/blockchain

Purpose: Retrieve blockchain state Response:

{
  "blocks": [
    {
      "index": 0,
      "prev_hash": "0000...",
      "timestamp": 1730000000,
      "encrypted_vote": "...base64...",
      "transaction_id": "tx-0",
      "block_hash": "abc123...",
      "signature": "...base64..."
    }
  ],
  "verification": {
    "chain_valid": true,
    "signatures_valid": true,
    "total_votes": 42
  }
}

GET /api/votes/results

Purpose: Get vote counting results Response:

{
  "total_votes": 42,
  "results": {
    "yes": 28,
    "no": 14
  },
  "verification": {
    "chain_valid": true,
    "homomorphic_verified": true,
    "proofs": "...base64..."
  }
}

Security Properties

Property Implementation Guarantee
Vote Secrecy Paillier encryption Encrypted before leaving client
Vote Integrity Blockchain + Dilithium Immutable, cryptographically signed
Anonymity Transaction IDs Voter verified once, not stored with vote
Individual Verifiability ZKP + blockchain Voter can verify their vote is counted
Universal Verifiability Public blockchain Anyone can verify results are correct
Post-Quantum Kyber + Dilithium Resistant to quantum attacks

Threat Model

Threats & Mitigations

  1. Vote Tampering

    • Threat: Attacker modifies a vote after submission
    • Mitigation: Blockchain prevents modification; hash chain breaks
    • Verification: Recompute hashes to detect tampering
  2. Double Voting

    • Threat: Voter votes multiple times
    • Mitigation: Emission list (voted voters tracked)
    • Verification: Check voter_id not already submitted
  3. Vote Disclosure

    • Threat: Server leaks which voter voted for which candidate
    • Mitigation: Paillier encryption; server never sees plaintext votes
    • Verification: Homomorphic summation prevents vote disclosure
  4. Authority Fraud

    • Threat: Authority publishes false results
    • Mitigation: Blockchain is public; results are cryptographically verified
    • Verification: Anyone can recompute homomorphic sum
  5. Quantum Attack

    • Threat: Future quantum computer breaks encryption
    • Mitigation: Kyber and Dilithium are post-quantum
    • Verification: Algorithms certified by NIST (FIPS 203, 204)
  6. Denial of Service

    • Threat: Attacker prevents votes from being submitted
    • Mitigation: Rate limiting on API endpoints
    • Verification: Blockchain size and performance monitoring

Implementation Notes

  • Vote Encoding: 0 = "No", 1 = "Yes" (can extend for multiple candidates)
  • Key Storage: Paillier private key protected by Kyber encryption at rest
  • Database: Store blocks, voter keys, emission list
  • Frontend Crypto: Use WASM or Node.js crypto libs for performance
  • Verification: Anyone can download blockchain and verify
  • Audit Trail: Complete blockchain is the audit trail