Skip to content

Implement core crypto primitives and type definitions #6

@grantfox-oss

Description

@grantfox-oss

Problem
The @anonvote/crypto package is the foundation every other repo
in the AnonVote ecosystem depends on — and it is entirely empty.
core cannot encrypt votes without encryptVote. contracts cannot
be tested without the TypeScript types. protocol cannot reference
concrete implementations in its specs. The package exports
nothing. The type definitions file is a placeholder. There are
no tests. Until this package ships working implementations,
every other repo in the org is blocked from meaningful progress.

The absence of these primitives also means the privacy model
that AnonVote is built on has never been exercised in code —
it exists only as a description in the protocol repo.

Solution
Implement all five core crypto functions and the three shared
TypeScript types that form the complete @anonvote/crypto surface,
with full test coverage and correct exports so every consuming
repo can immediately depend on this package.

Crypto Functions

  • hashIdentifier(identifier: string): string
    SHA-256 hash of a raw voter identifier. Must normalize input
    by trimming whitespace and lowercasing before hashing. Output
    is always a 64-character lowercase hex string. This function
    is the privacy boundary — raw identifiers must never be
    accessible after it returns.

  • generateToken(): string
    Generates a cryptographically secure 32-byte random token
    using crypto.randomBytes(32). Returns a 64-character lowercase
    hex string. Must produce a unique value on every call —
    never cache or reuse token values.

  • hashToken(token: string): string
    SHA-256 hash of a raw token. Same algorithm and output
    format as hashIdentifier. Kept as a separate function to
    make the two-step token design explicit and independently
    testable.

  • encryptVote(option: string, key: string): EncryptedPayload
    AES-256-GCM encryption of a raw vote option string. Generates
    a fresh 12-byte IV on every call via crypto.randomBytes(12).
    Returns a typed EncryptedPayload with ciphertext, iv, and
    authTag all as hex strings. The same option encrypted twice
    must produce two different ciphertexts due to unique IVs.

  • decryptVote(payload: EncryptedPayload, key: string): string
    Decrypts an EncryptedPayload back to the original option
    string. Must verify the GCM auth tag — throw a descriptive
    error if verification fails. A tampered ciphertext or auth
    tag must never silently produce wrong output.

TypeScript Types

  • Token: { value: string; hash: string }
  • Vote: { ballotId: string; option: string; timestamp: number }
  • EncryptedPayload: { ciphertext: string; iv: string; authTag: string }

All types and functions must be exported as named exports from
src/index.ts. Use only Node.js built-in crypto module — no
external cryptography dependencies.

Tests

  • hashIdentifier: deterministic output, 64-char hex, normalizes
    whitespace and casing, different inputs produce different hashes
  • generateToken: 64-char hex output, 1000 consecutive calls
    produce 1000 unique values
  • hashToken: deterministic, 64-char hex, different tokens
    produce different hashes
  • encryptVote / decryptVote: full roundtrip passes, two
    encryptions of same option produce different ciphertexts,
    tampered ciphertext throws, tampered auth tag throws
  • All types correctly exported and usable in consuming TypeScript
    files without type errors

Acceptance Criteria

  • All five functions implemented and exported from src/index.ts
  • All three types defined in src/types.ts and exported
  • Only Node.js built-in crypto used — no external dependencies
  • Raw identifiers and raw vote options never appear in logs,
    errors, or return values beyond their intended function
  • encryptVote produces a unique ciphertext on every call
    for the same input
  • decryptVote throws on tampered ciphertext or auth tag
  • All unit tests pass with no warnings
  • Package builds cleanly with tsc and exports resolve correctly

Note for contributors
Use only the Node.js built-in crypto module. Do not introduce
external cryptography libraries — the entire point of this
package is to be a minimal, auditable, zero-dependency
primitive layer. Any PR that adds a crypto dependency will
be closed.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignenhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions