Skip to content

hashIdentifier produces different hashes for equivalent identifiers #7

@grantfox-oss

Description

@grantfox-oss

Problem
hashIdentifier currently produces different SHA-256 outputs for
identifiers that should be treated as identical. The following
three inputs all represent the same voter but produce three
different hashes:

hashIdentifier("alice@example.com")
hashIdentifier("Alice@example.com")
hashIdentifier(" alice@example.com ")

This is a critical correctness bug. In production it means the
same voter could be issued multiple tokens if their identifier
is entered with different casing or leading whitespace at
different points in the eligibility upload flow. This breaks
the one-person-one-vote guarantee without any error being
raised — the system silently treats one voter as multiple
distinct people.

The bug exists because the current implementation hashes the
raw input string without any normalization. The function
receives the string exactly as provided and passes it directly
to crypto.createHash — no trim, no toLower, no canonical form.

Root Cause
src/crypto.ts line 4 — hashIdentifier passes the raw identifier
string to the hash update without normalization:

export function hashIdentifier(identifier: string): string {
return crypto.createHash('sha256').update(identifier).digest('hex')
}

Solution
Normalize the identifier before hashing — trim leading and
trailing whitespace and convert to lowercase:

export function hashIdentifier(identifier: string): string {
return crypto
.createHash('sha256')
.update(identifier.trim().toLowerCase())
.digest('hex')
}

This is a one-line fix but the implications are significant.
Any existing eligibility data hashed with the unnormalized
function will produce hashes that no longer match after this
fix is applied. core must be notified of this change so any
existing test fixtures or seeded eligibility data are
regenerated.

Tests to Add

Acceptance Criteria

  • hashIdentifier normalizes input before hashing — trim
    and lowercase applied
  • Equivalent identifiers always produce identical hashes
    regardless of casing or whitespace
  • All five normalization test cases pass
  • A note is added to the function JSDoc warning that this
    fix is a breaking change for any existing hashed data
  • AnonVote/core is notified via a cross-repo comment to
    regenerate any test fixtures using hashIdentifier

Note for contributors
Do not change the hashing algorithm or output format — only
the normalization step. The fix is one line. The test suite
expansion is the majority of the work in this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignbugSomething isn't working

    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