The cryptographic primitives and token utilities powering AnonVote.
This package is the canonical source of all crypto and token logic used across the AnonVote ecosystem. It is framework-agnostic, has zero runtime dependencies, and runs in Node.js and edge runtimes.
| Repo | Depends on this package |
|---|---|
| AnonVote/core | Yes — backend imports @anonvote/crypto |
| AnonVote/contracts | No — Soroban contracts use native Rust |
| AnonVote/protocol | References this package in spec docs |
| Export | Description |
|---|---|
hashIdentifier(id) |
SHA-256 hash of a voter identifier. Trims and lowercases before hashing. Never store originals — only hashes. |
generateToken() |
Generates a 32-byte (256-bit) CSPRNG token as a hex string. Used for one-time voter tokens. |
hashToken(token) |
SHA-256 hash of a raw token. Only the hash is ever persisted — the raw value is given to the voter and discarded. |
encryptVote(optionId, key) |
AES-256-GCM encryption of a vote option ID. Returns iv:authTag:ciphertext in base64. Requires a 32-byte hex key. |
decryptVote(payload, key) |
Decrypts a vote payload produced by encryptVote. Used only by the result tally engine. |
Core shared TypeScript types for votes, tokens, ballots, and audit events — used by both the backend API and any future client SDKs.
npm install @anonvote/cryptoimport {
hashIdentifier,
generateToken,
hashToken,
encryptVote,
decryptVote,
} from "@anonvote/crypto";
// Hash a voter identifier before storing
const identifierHash = hashIdentifier("alice@example.com");
// Issue a one-time anonymous token
const rawToken = generateToken(); // give this to the voter
const storedHash = hashToken(rawToken); // store only this
// Encrypt a vote option
const BALLOT_KEY = process.env.BALLOT_ENCRYPTION_KEY!; // 64-char hex
const encrypted = encryptVote("option-uuid-here", BALLOT_KEY);
// Decrypt during result tally
const optionId = decryptVote(encrypted, BALLOT_KEY);These primitives enforce AnonVote's structural unlinkability model:
hashIdentifierandhashTokenare one-way — original values are unrecoverable from the databasegenerateTokenuses Node.jscrypto.randomBytes— cryptographically secure and unpredictableencryptVoteuses AES-256-GCM — authenticated encryption; tampered ciphertexts are rejected at decryption- No identifier is ever stored alongside a token — the hash functions operate independently on different data
BALLOT_ENCRYPTION_KEYmust be a 64-character hex string (32 bytes). Generate one with:openssl rand -hex 32- Never log raw voter identifiers or raw tokens
- The result tally is the only place
decryptVoteshould be called
git clone https://github.com/AnonVote/js.git
cd js
npm install
npm test
npm run build| Command | Description |
|---|---|
npm run build |
Compile TypeScript to dist/ |
npm test |
Run unit tests with Jest |
npm run lint |
ESLint check |
js/
├── src/
│ ├── crypto.ts # Core cryptographic functions
│ ├── types.ts # Shared TypeScript types
│ └── index.ts # Public API re-exports
├── tests/
│ └── crypto.test.ts
├── package.json
└── tsconfig.json