secure_voting_app_basic
GitHub Codespaces does not support running Docker containers directly inside the codespace. Instead, use the built-in PostgreSQL service provided by Codespaces, or connect to a remote/local PostgreSQL instance.
- Open the Codespaces configuration (gear icon > "Add Dev Service").
- Add the PostgreSQL service and configure:
- Username:
postgres - Password:
password - Database:
voting_db - Port:
5432
- Username:
- Update your
.envfile if needed:DATABASE_URL=postgresql://postgres:password@localhost:5432/voting_db
If you have PostgreSQL running on your local machine or a remote server, update your .env file with the correct host and credentials.
You can use cloud providers like AWS RDS, Azure Database, or Google Cloud SQL. Update your .env file with the connection string provided by your service.
To install Docker, run the following commands:
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginAfter installation, verify Docker is installed:
docker --versionThis is a small demo secure voting system with:
- Frontend UI: Streamlit app (
streamlit_app.py+pages/*) providing six pages (Registration, Request Token, Cast Vote, Mix Network, Tally, Logs). - Services: core crypto and protocol logic in
services(RSA,VotingAuthority,VoterClient,MixNet). - Database layer: simple repository wrappers in
db/repositoriesusingdb.connection.get_conn()(Postgres). - Utilities: cryptographic helpers and logging helpers in
utils. - DB schema initializer:
.devcontainer/init-db.sql.
The system demonstrates blind-signature token issuance, casting votes with blinded tokens, a simple mixnet shuffle, and tallying.
Below is an in-depth walk-through.
- UI layer (Streamlit)
streamlit_app.pyis the main UI glue. It initializes repositories, services, and session state, and exposes a sidebar navigation among pages.pages/*.pycontain page components (optionally used in a multipage Streamlit layout). They call services/repositories.
- Services
voting_authority.py: central authority holding RSA keys and methods to issue blind signatures and verify tokens when casting ballots. Also inserts ballots into DB viaBallotRepository.voter_client.py: client-side logic for voters to create blind tokens and unblind signatures; interacts withVotingAuthorityto obtain blind signature and withTokenRepositoryto store tokens.secure_rsa.py: RSA implementation (key generation,mod_pow, sign/verify, blind/unblind). Custom implementation (non-library).mixnet.py: mixnet simulator that shuffles ballots and generates commitment-bound proof hashes. Can optionally persist proofs via a repository.
- Database & repositories
connection.pyreadsDATABASE_URLand returns a psycopg2 connection withRealDictCursor.- Repositories:
VoterRepository,TokenRepository,BallotRepository,MixNetRepository,LogRepository— thin wrappers around SQL operations (INSERT/SELECT/UPDATE). Each opens a connection, makes a single change or query, commits, and closes.
- Utilities
crypto.py—sha256_hexhelper.logger.py— wrapper aroundLogRepository.add_log()to centralize logging.
- DB initializer:
.devcontainer/init-db.sqlcreates tables (voters,tokens,ballots,mixnet_proofs,logs).
- .devcontainer/
- .env
- .git/
- README.md
- requirements.txt
- streamlit_app.py
- venv/
- crypto/
- __init__.py
- hashing.py
- rng.py
- rsa.py
- db/
- __init__.py
- connection.py
- repositories/
- __init__.py
- ballot_repository.py
- log_repository.py
- mixnet_repository.py
- token_repository.py
- voter_repository.py
- pages/
- 01_registration.py
- 02_request_token.py
- 03_cast_vote.py
- 04_mixnet.py
- 05_tally.py
- 06_logs.py
- services/
- __init__.py
- mixnet.py
- secure_rsa.py
- voter_client.py
- voting_authority.py
- utils/
- __init__.py
- crypto.py
- logger.py
- tests/
- Voter (table
voters):- columns:
voter_id TEXT PRIMARY KEY,name TEXT,has_token BOOLEAN,has_voted BOOLEAN
- columns:
- Token (table
tokens):- columns:
voter_id TEXT PRIMARY KEY REFERENCES voters(voter_id),token_hash TEXT,signature TEXT token_hashis hex SHA-256 of the token;signatureis hex string produced by RSA blind-signature protocol.
- columns:
- Ballot (table
ballots):- columns:
ballot_id TEXT PRIMARY KEY,candidate TEXT,token_hash TEXT,encrypted BOOLEAN DEFAULT TRUE
- columns:
- Mixnet proofs (
mixnet_proofs):- columns:
id SERIAL PRIMARY KEY,layer INT,input_count INT,output_count INT,proof_hash TEXT - The code stores
proof->proof_hash, and the mixnet now produces commitment hashes and includesinput_commit/output_commitin the proof dict (but repo only persistsprooftoproof_hashcolumn).
- columns:
- Logs (
logs):- columns:
id SERIAL,message TEXT,log_type TEXT,created_at TIMESTAMP DEFAULT NOW().
- columns:
See the repository files for exact call sites; in short:
-
Registration (UI: Registration page)
voter_repo.add_voter(voter_id, name)inserts the voter.VotingAuthority.register_voter(voter_id)updates authority's in-memory set.
-
Request Token (Blind signature issuance)
- VoterClient generates a token, computes
token_hash, blinds the hash, callsVotingAuthority.issue_blind_signature(blinded_hash, voter_id), unblinds the signature, and storestoken_hashand signature withTokenRepository.add_token().
- VoterClient generates a token, computes
-
Cast Vote
- Token and signature are read from DB, signature verified with
SecureRSA.verify_hash(), aballot_idis generated and inserted viaBallotRepository.add_ballot().VoterRepository.mark_voted()marks voter as voted.
- Token and signature are read from DB, signature verified with
-
Mix Network
BallotRepository.get_all_ballots()->VerifiableMixNet.mix(ballots), produce shuffled ballots and per-layer proofs.MixNetRepository.save_proof()inserts proofs intomixnet_proofs.
-
Tally
BallotRepository.get_all_ballots()then server computes counts percandidateand displays results.
(summarized; see code comments and issues in repo for details):
- Ballots stored in plaintext (privacy not guaranteed). Use encryption and verifiable shuffle for privacy.
- Token reuse prevention is incomplete (see
used_token_hashes). Add DB-backed atomic mark-as-used during ballot insert. - RSA keys are generated in-memory; persist keys to survive restarts.
- Use vetted crypto libs for production.
- Ensure
DATABASE_URLis correct for your runtime (usedbhostname in Docker Compose).
If you'd like, I can implement one of the recommended fixes (prevent double-voting atomically, persist RSA keys, or extend mixnet proof persistence). Tell me which and I'll implement it.