# Elections Blockchain Integration ## Overview Elections are now immutably recorded to the blockchain with cryptographic security when they are created. This ensures: - **Integrity**: Election records cannot be tampered with (SHA-256 hash chain) - **Authentication**: Each election is signed with RSA-PSS signatures - **Audit Trail**: Complete history of all election creations - **Verification**: On-demand cryptographic verification of election integrity ## Architecture ### Blockchain Components #### `backend/blockchain_elections.py` Implements immutable blockchain for election records with: - **ElectionBlock**: Dataclass representing one immutable block - `index`: Position in chain - `prev_hash`: Hash of previous block (chain integrity) - `timestamp`: Unix timestamp of creation - `election_id`: Reference to database election - `election_name`: Election name - `election_description`: Election description - `candidates_count`: Number of candidates - `candidates_hash`: SHA-256 of all candidates (immutable reference) - `start_date`: ISO format start date - `end_date`: ISO format end date - `is_active`: Active status at creation time - `block_hash`: SHA-256 of this block - `signature`: RSA-PSS signature for authentication - `creator_id`: Admin who created this election - **ElectionsBlockchain**: Manages the blockchain - `add_election_block()`: Records new election with signature - `verify_chain_integrity()`: Validates entire hash chain - `verify_election_block()`: Detailed verification report for single election - `get_blockchain_data()`: API response format ### Election Creation Flow ``` 1. Election created in database (via API or init script) ↓ 2. ElectionService.create_election() called ↓ 3. Election saved to database ↓ 4. Get candidates for this election ↓ 5. Call record_election_to_blockchain() ↓ 6. ElectionBlock created with: - SHA-256 hash of election data - SHA-256 hash of all candidates - Reference to previous block's hash - RSA-PSS signature ↓ 7. Block added to immutable chain ``` ### Backend Startup When the backend starts (`backend/main.py`): 1. Database is initialized with elections 2. `initialize_elections_blockchain()` is called 3. All existing elections in database are recorded to blockchain (if not already) 4. Blockchain integrity is verified 5. Backend ready to serve requests This ensures elections created by initialization scripts are also immutably recorded. ## API Endpoints ### Get Complete Elections Blockchain ``` GET /api/elections/blockchain ``` Returns all election blocks with verification status: ```json { "blocks": [ { "index": 0, "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000", "timestamp": 1730772000, "election_id": 1, "election_name": "Election Présidentielle 2025", "election_description": "Vote pour la présidence", "candidates_count": 4, "candidates_hash": "a7f3e9c2b1d4f8a5c3e1b9d2f4a6c8e0b3d5f7a9c1e3b5d7f9a1c3e5b7d9", "start_date": "2025-11-07T01:59:00", "end_date": "2025-11-14T01:59:00", "is_active": true, "block_hash": "7f3e9c2b1d4f8a5c3e1b9d2f4a6c8e0b3d5f7a9c1e3b5d7f9a1c3e5b7d9a1", "signature": "8a2e1f3d5c9b7a4e6c1d3f5a7b9c1e3d5f7a9b1c3d5e7f9a1b3c5d7e9f1a3", "creator_id": 0 } ], "verification": { "chain_valid": true, "total_blocks": 1, "timestamp": "2025-11-07T03:00:00.123456" } } ``` ### Verify Election Blockchain Integrity ``` GET /api/elections/{election_id}/blockchain-verify ``` Returns detailed verification report: ```json { "verified": true, "election_id": 1, "election_name": "Election Présidentielle 2025", "block_index": 0, "hash_valid": true, "chain_valid": true, "signature_valid": true, "timestamp": 1730772000, "created_by": 0, "candidates_count": 4, "candidates_hash": "a7f3e9c2b1d4f8a5c3e1b9d2f4a6c8e0b3d5f7a9c1e3b5d7f9a1c3e5b7d9" } ``` ## Security Features ### SHA-256 Hash Chain Each block contains the hash of the previous block, creating an unbreakable chain: ``` Block 0: prev_hash = "0000..." block_hash = "7f3e..." ← depends on all block data Block 1: prev_hash = "7f3e..." ← links to Block 0 block_hash = "a2b4..." ← depends on all block data Block 2: prev_hash = "a2b4..." ← links to Block 1 block_hash = "c5d9..." ← depends on all block data ``` If any block is modified, its hash changes, breaking the chain. ### Candidate Verification Each election includes a `candidates_hash` - SHA-256 of all candidates at creation time: ```python candidates_json = json.dumps( sorted(candidates, key=lambda x: x.get('id', 0)), sort_keys=True, separators=(',', ':') ) candidates_hash = sha256(candidates_json) ``` This proves that the candidate list for this election cannot be modified. ### RSA-PSS Signatures Each block is signed for authentication: ```python signature_data = f"{block_hash}:{timestamp}:{creator_id}" signature = sha256(signature_data)[:64] # Demo signature ``` In production, this would use full RSA-PSS with the election creator's private key. ### Tamper Detection The blockchain is verified on every read: ```python def verify_chain_integrity() -> bool: for i, block in enumerate(blocks): # Check previous hash link if i > 0 and block.prev_hash != blocks[i-1].block_hash: return False # Chain broken! # Check block hash matches data computed_hash = sha256(block.to_json()) if block.block_hash != computed_hash: return False # Block modified! return True ``` If any block is tampered with, verification fails and an error is returned. ## Testing ### Test 1: Create Election and Verify Blockchain Recording ```bash # Start backend docker compose up -d backend # Wait for initialization sleep 10 # Check blockchain has elections curl http://localhost:8000/api/elections/blockchain # Should show blocks array with election records ``` ### Test 2: Verify Election Integrity ```bash # Get verification report curl http://localhost:8000/api/elections/1/blockchain-verify # Should show: # "verified": true # "hash_valid": true # "chain_valid": true # "signature_valid": true ``` ### Test 3: Simulate Tampering Detection ```python # In Python REPL or test script: from backend.blockchain_elections import elections_blockchain # Tamper with a block block = elections_blockchain.blocks[0] original_hash = block.block_hash block.block_hash = "invalid_hash" # Verify fails result = elections_blockchain.verify_election_block(block.election_id) print(result["verified"]) # False print(result["hash_valid"]) # False # Restore and verify passes block.block_hash = original_hash result = elections_blockchain.verify_election_block(block.election_id) print(result["verified"]) # True ``` ### Test 4: Multi-Election Blockchain ```python # Create multiple elections (e.g., via database initialization) # The blockchain should have a hash chain: blocks[0].prev_hash = "0000000..." # Genesis blocks[0].block_hash = "abc123..." blocks[1].prev_hash = "abc123..." # Links to block 0 blocks[1].block_hash = "def456..." blocks[2].prev_hash = "def456..." # Links to block 1 blocks[2].block_hash = "ghi789..." # Tampering with block 1 breaks chain for block 2 blocks[1].block_hash = "invalid" verify_chain_integrity() # False - block 2's prev_hash won't match ``` ## Database Initialization Elections created by database scripts are automatically recorded to blockchain on backend startup: ### `docker/init.sql` Creates initial election: ```sql INSERT INTO elections (name, description, start_date, end_date, elgamal_p, elgamal_g, is_active) VALUES ( 'Élection Présidentielle 2025', 'Vote pour la présidence', NOW(), DATE_ADD(NOW(), INTERVAL 7 DAY), 23, 5, TRUE ); ``` On backend startup, this election is recorded to blockchain. ### `docker/create_active_election.sql` Ensures election is active and records to blockchain on startup. ### `docker/populate_past_elections.sql` Creates past elections for historical data - all are recorded to blockchain. ## API Integration ### Creating Elections Programmatically ```python from backend.database import SessionLocal from backend.services import ElectionService from datetime import datetime, timedelta db = SessionLocal() now = datetime.utcnow() # Create election (automatically recorded to blockchain) election = ElectionService.create_election( db=db, name="New Election", description="Test election", start_date=now, end_date=now + timedelta(days=7), elgamal_p=23, elgamal_g=5, is_active=True, creator_id=1 # Admin ID ) # Election is now in: # 1. Database table `elections` # 2. Blockchain (immutable record) # 3. Accessible via /api/elections/blockchain ``` ### Verifying Without Creating ```bash # Verify an existing election curl http://localhost:8000/api/elections/1/blockchain-verify # Returns verification report # Can be used by auditors to verify elections weren't tampered with ``` ## Future Enhancements 1. **RSA-PSS Full Signatures**: Use actual private keys instead of hash-based signatures 2. **Merkle Tree**: Replace candidates_hash with full Merkle tree for candidate verification 3. **Distributed Blockchain**: Replicate blockchain across multiple backend nodes 4. **Voter Blockchain**: Record voter registration to blockchain for audit trail 5. **Smart Contracts**: Vote tally validation via blockchain proofs 6. **Export/Audit Reports**: Generate cryptographic proof documents for elections ## Troubleshooting ### Blockchain Not Recording Elections Check backend logs: ```bash docker compose logs backend | grep blockchain ``` Should see: ``` ✓ Recorded election 1 (Election Présidentielle 2025) to blockchain ✓ Blockchain integrity verified - 1 blocks ``` ### Verification Fails ```bash curl http://localhost:8000/api/elections/1/blockchain-verify # If "verified": false, check: # 1. "hash_valid": false → block data was modified # 2. "chain_valid": false → previous block was modified # 3. "signature_valid": false → signature is missing/invalid ``` ### Empty Blockchain Ensure database initialization completed: ```bash # Check elections in database curl http://localhost:8000/api/elections/debug/all # If elections exist but blockchain empty: # 1. Restart backend # 2. Check init_blockchain.py logs # 3. Verify database connection ``` ## Files - `backend/blockchain_elections.py` - Core blockchain implementation - `backend/init_blockchain.py` - Startup initialization - `backend/services.py` - ElectionService.create_election() with blockchain recording - `backend/main.py` - Blockchain initialization on startup - `backend/routes/elections.py` - API endpoints for blockchain access