KweliVote is a blockchain-based biometric identity management application designed to ensure transparent and credible electoral processes for Burundi's upcoming elections. The application addresses the challenges of electoral fraud by implementing a secure, decentralized platform for managing voter identities.
Burundi faces significant challenges in ensuring transparent electoral processes due to:
- Absence of unique identifiers for citizens
- Lack of biometric identity cards
- History of electoral fraud (multiple voting, identity manipulation)
- Data security and privacy concerns
KweliVote implements a blockchain-based biometric identity system that:
- Integrates biometric data (fingerprints) with a blockchain ledger
- Creates unique, tamper-proof identifiers for each voter
- Prevents identity duplication and manipulation
- Enables real-time verification at polling stations
- Enhances data security through cryptographic keys
KweliVote transforms a voter's biometric data (fingerprint) into a Decentralized Identifier (DID) through a secure, privacy-preserving process:
- Biometric Capture: Fingerprint data is captured using hardware readers or uploaded images
- Template Extraction: Raw fingerprint data is converted to ANSI-378 format
- Feature Stabilization: Techniques are applied to ensure consistent outputs
- Cryptographic Key Derivation: The stabilized template generates a cryptographic keypair
- DID Generation: A W3C-compliant DID:key is created
The system securely stores voter DIDs on the blockchain:
- Storage: Only public DIDs and public keys are stored on-chain
- Private Keys: Never stored; derived "just in time" from the voter's fingerprint
- Verification: During voting, fingerprints are scanned and DIDs reconstructed to verify against blockchain records
{
"voterId": "VOTER12345",
"did": "did:key:z6Mkf8a3...",
"publicKey": "03f9f5d91f5d7d8f5c5b9e4d69c6a8396ed8a60b0b6d2d7bbfdb0ae585b1e2152",
"status": "active",
"timestamp": 1714278900
}- Voter scans fingerprint at polling station
- System extracts stable secret from the fingerprint
- Private key is derived from the secret (never stored)
- Public key is computed from the derived private key
- DID is rebuilt from the public key
- System compares reconstructed DID/public key with blockchain record
- If they match, the voter is authenticated
- Mobile-responsive web application
- Secure biometric-based authentication
- Blockchain verification to prevent fraud
- Real-time validation at polling stations
- Transparent audit trail
| Role | Responsibility |
|---|---|
| Registration Clerks | Register new voters at designated centers |
| IEBC Constituency Election Coordinators | Register registration clerks, polling clerks, and other officials |
| Polling Clerks | Verify voters biometrically |
| Party Agents | Validate results count |
| Observers | Validate results count |
| Presiding Officers | Validate results count |
ImageMagick is used for image processing and conversion of fingerprint images.
-
Install via package manager:
sudo apt update sudo apt install imagemagick
-
Verify installation:
convert --version
NBIS provides essential tools for fingerprint minutiae extraction and template conversion.
-
Install build dependencies:
sudo apt update sudo apt install build-essential libpng-dev libjpeg-dev git
-
Download NBIS source code from GitHub:
mkdir -p ~/nbis_build cd ~/nbis_build git clone https://github.com/usnistgov/nbis.git nbis_source
-
Setup build environment:
cd ~/nbis_build # Create a build directory mkdir -p ~/nbis_build/nbis
-
Configure and build NBIS:
cd ~/nbis_build/nbis_source ./setup.sh ~/nbis_build/nbis cd ~/nbis_build/nbis make config make it
Note: The build process may show some warnings which can usually be ignored. The build might not complete fully but as long as the key components are built, we can proceed.
-
Compile key tools:
cd ~/nbis_build/nbis/mindtct/src/bin/mindtct && make cd ~/nbis_build/nbis/bozorth3/src/bin/bozorth3 && make
-
Install libraries and executables:
# Install libraries sudo cp -r ~/nbis_build/nbis/exports/lib/* /usr/local/lib/ sudo cp -r ~/nbis_build/nbis/exports/include/* /usr/local/include/ sudo ldconfig # Install binaries sudo mkdir -p /usr/local/bin sudo cp ~/nbis_build/nbis/mindtct/bin/mindtct /usr/local/bin/ sudo cp ~/nbis_build/nbis/bozorth3/bin/bozorth3 /usr/local/bin/
-
Verify installation:
# Check if executables are in path which mindtct which bozorth3
The key tools installed are:
- mindtct: Extracts minutiae data from fingerprint images
- bozorth3: Matches fingerprint minutiae and generates comparison scores
Example usage:
# Extract minutiae from a fingerprint image
mindtct fingerprint.png output_prefix
# Compare two fingerprints (returns a match score)
bozorth3 fingerprint1.xyt fingerprint2.xyt-
Navigate to the backend directory:
cd kwelivote-app/backend -
Create a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate -
Install required packages:
pip install -r requirements.txt -
Setup PostgreSQL:
sudo apt update sudo apt install postgresql postgresql-contrib # Start PostgreSQL service sudo systemctl start postgresql # Create database and user sudo -u postgres psql # Inside PostgreSQL console CREATE USER kwelivote WITH PASSWORD 'kwelivote'; ALTER USER kwelivote CREATEDB; CREATE DATABASE kwelivote_db OWNER kwelivote; GRANT ALL PRIVILEGES ON DATABASE kwelivote_db TO kwelivote; \q -
Configure Database Connection:
- Ensure the PostgreSQL connection is properly configured in
kwelivote_app/settings.py:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'kwelivote_db', 'USER': 'kwelivote', 'PASSWORD': 'kwelivote', 'HOST': 'localhost', 'PORT': '5432', } }
- Ensure the PostgreSQL connection is properly configured in
-
Install PostgreSQL adapter for Python:
pip install psycopg2-binary -
Run migrations:
python manage.py makemigrations python manage.py migrate -
Populate database with test data (optional):
python manage.py populate_db -
Create a superuser:
python manage.py createsuperuser -
Link KeyPersons to User accounts:
# Create user accounts for all KeyPersons who are not Observers and link them python manage.py link_keypersons_to_users # To first see what would be done without making changes, use --dry-run flag python manage.py link_keypersons_to_users --dry-runThis will:
- Generate username based on first name and last 4 digits of national ID
- Create strong random passwords (which will be displayed in the output)
- Set email addresses as username@kwelivote.example.com
- Link user accounts to the corresponding KeyPerson records
-
Create specific test data (optional):
# Create data in this sequence for proper relationships python manage.py create_keypersons python manage.py create_voters python manage.py create_candidates python manage.py create_results -
Run the development server:
python manage.py runserver
-
Navigate to the frontend directory:
cd kwelivote-app/frontend -
Install dependencies:
npm install -
Update API configuration:
- Ensure the API endpoint in src/services/apiService.js points to your backend server
-
Start the development server:
npm start -
Build for production:
npm run build
- Access the admin panel at http://localhost:8000/admin
- Login with superuser credentials
- Create test users with different roles (Registration Clerk, Polling Clerk, etc.)
- Access the frontend at http://localhost:3000
- Login with created user credentials to test the appropriate workflows
KweliVote uses fingerprint biometrics for secure voter identification. It's critical to ensure that template generation is consistent between enrollment and verification processes.
-
With API Server Running:
# Make sure the Django backend server is running first cd kwelivote-app/backend python manage.py runserver # In a separate terminal, run the fingerprint test script python3 test_fingerprint_processing.py
-
Using the Test Script Helper:
# The helper script will start the server if needed ./run_fingerprint_tests.sh # Or run in offline mode without starting a backend server ./run_fingerprint_tests.sh offline
-
In Offline Mode (for Development/Testing):
# Run in offline mode to simulate API responses python3 test_fingerprint_processing.py --offlineThe offline mode is useful for:
- Development environments without a running backend
- Demonstrating the test functionality in presentations
- Validating the test script logic itself
- CI/CD pipelines where starting a full backend isn't practical
In offline mode, the script simulates API responses and deliberately creates both matching and non-matching templates to demonstrate the detection capabilities.
Note: Offline mode still requires sample fingerprint images in the
docs/fingerprint_reader/sample_fingerprintsdirectory. The script will not create synthetic images.
The fingerprint test script verifies:
- Template Consistency: Ensures that fingerprint template generation is identical during both enrollment and verification phases.
- Self-Matching: Verifies that a fingerprint can be successfully matched against its own template.
- API Integration: Tests the full API workflow for fingerprint processing.
The test results will be saved in the docs/fingerprint_reader/test_output directory, including:
- Enrollment templates
- Verification templates
- Match results
If tests fail, check:
- Backend server is running and accessible
- NBIS tools (mindtct, bozorth3) are properly installed (see Installing Biometric Processing Tools)
- Sample fingerprint images exist in the expected directory
- All biometric processing happens client-side
- Raw fingerprint data never leaves the user's device
- Only derived DIDs (which cannot be reversed) are sent to the server
- Private keys are properly handled and secured
-
Node.js and npm:
# Check if Node.js and npm are installed node --version npm --version # If not installed, install them through your package manager # For Ubuntu/Debian: sudo apt update sudo apt install nodejs npm
-
Install Hardhat:
# Navigate to the frontend directory cd kwelivote-app/frontend # Install Hardhat locally npm install --save-dev hardhat
-
Configure Environment Variables:
- Create a
.envfile in the frontend directory:
touch .env
- Add your private key and API keys:
# Wallet private key for contract deployment (without 0x prefix) PRIVATE_KEY=your_private_key_here # Avalanche Fuji Testnet RPC URL and Chain ID REACT_APP_AVALANCHE_API=https://api.avax-test.network REACT_APP_AVALANCHE_CHAIN_ID=43113 REACT_APP_AVALANCHE_RPC_ENDPOINT=https://api.avax-test.network/ext/bc/C/rpc REACT_APP_AVALANCHE_EXPLORER_URL=https://testnet.snowtrace.io - Create a
-
Configure Hardhat:
- Make sure your
hardhat.config.jsincludes the Avalanche Fuji testnet configuration:
require("@nomicfoundation/hardhat-toolbox"); require('dotenv').config(); const PRIVATE_KEY = process.env.PRIVATE_KEY || ""; module.exports = { solidity: "0.8.19", networks: { fuji: { url: process.env.REACT_APP_AVALANCHE_RPC_ENDPOINT || "https://api.avax-test.network/ext/bc/C/rpc", chainId: parseInt(process.env.REACT_APP_AVALANCHE_CHAIN_ID || "43113"), accounts: PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : [], gasPrice: 225000000000, }, mainnet: { url: "https://api.avax.network/ext/bc/C/rpc", chainId: 43114, accounts: PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : [], gasPrice: 225000000000, } }, etherscan: { apiKey: process.env.SNOWTRACE_API_KEY, }, };
- Make sure your
-
Install Required Dependencies:
npm install --save-dev @nomicfoundation/hardhat-toolbox dotenv
-
Compile Smart Contract:
npx hardhat compile
-
Deploy to Avalanche Fuji Testnet:
- Create or modify a deployment script in
scripts/deploy.js:
const hre = require("hardhat"); async function main() { console.log("Deploying VoterDID contract to Avalanche Fuji Testnet..."); // Get the ContractFactory const VoterDID = await hre.ethers.getContractFactory("VoterDID"); // Deploy it const voterDID = await VoterDID.deploy(); // Wait for deployment to finish await voterDID.deployed(); console.log(`VoterDID deployed to: ${voterDID.address}`); console.log(`Transaction hash: ${voterDID.deployTransaction.hash}`); console.log(`Block number: ${voterDID.deployTransaction.blockNumber}`); console.log("View on explorer:", `${process.env.REACT_APP_AVALANCHE_EXPLORER_URL}/address/${voterDID.address}`); return voterDID; } main() .then(() => process.exit(0)) .catch((error) => { console.error("Deployment failed:", error); process.exit(1); });
- Run deployment:
npx hardhat run scripts/deploy.js --network fuji
- Create or modify a deployment script in
-
Verify Smart Contract on Snowtrace (Optional):
# Get your contract address from the deployment output npx hardhat verify --network fuji YOUR_CONTRACT_ADDRESS -
Update Frontend Configuration:
- Add the deployed contract address to your
.envfile:
REACT_APP_VOTER_DID_CONTRACT_ADDRESS=your_deployed_contract_address - Add the deployed contract address to your
-
Manual Testing via Hardhat Console:
npx hardhat console --network fuji # Inside the console: > const VoterDID = await ethers.getContractFactory("VoterDID") > const contract = await VoterDID.attach("YOUR_DEPLOYED_CONTRACT_ADDRESS") > await contract.registerDID("TEST123", "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK") > await contract.getDID("TEST123")
-
Run Contract Tests:
npx hardhat test --network fuji
-
Not enough funds: Ensure your wallet has enough AVAX for deployment
- Get test AVAX from the Fuji Testnet Faucet
-
Gas price errors: Update the gasPrice in hardhat.config.js
-
Network connectivity issues: Check that the RPC endpoint is correct and accessible
-
Transaction underpriced: Increase gasPrice in your hardhat.config.js
-
Nonce too high/low: If you're reusing a wallet address, you may need to reset your account nonce:
# Check your current nonce npx hardhat run scripts/check-nonce.js --network fuji -
Contract verification failures: Ensure you're using the exact compiler version used for deployment