Privacy Pass TypeScript implementation. Compliant with:
- RFC 9576 — Privacy Pass Architecture (roles, deployment models, security considerations)
- RFC 9578 — Privacy Pass Issuance Protocol
- draft-hendrickson-privacypass-public-metadata — Public Metadata (Partially Blind RSA)
- draft-ietf-privacypass-batched-tokens — Batched Tokens
Reference implementations to cross-check behavior when debugging protocol issues.
- pat-go (Cloudflare's Go implementation),
- privacypass (Raphael Robert's Rust implementation).
| Type | Module | Crypto |
|---|---|---|
| Public-Verifiable | publicVerif |
Blind RSA |
| Public-Verifiable + Metadata | publicVerif |
Partially Blind RSA |
| Private-Verifiable | privateVerif |
VOPRF (P-384) |
| Batched (generic) | genericBatched |
Any of above |
src/
index.ts # exports, TOKEN_TYPES registry
pub_verif_token.ts # Blind RSA / Partially Blind RSA
priv_verif_token.ts # VOPRF
generic_batched_token.ts
issuance.ts # fetch helpers, MediaType enum
auth_scheme/ # WWW-Authenticate / Authorization parsing (RFC 9110)
test/
*.test.ts # vitest tests
test_data/ # JSON test vectors (Go, Rust implementations)
examples/ # usage examples — run with `npm run examples`
- Node.js ≥20
- npm (not pnpm/yarn)
- ESM-only — uses
"type": "module", all imports need.jsextensions
npm ci # install dependencies
npm run build # tsc -b (required before test/bench/examples)
npm run test # vitest
npm run lint # eslint
npm run format # prettier
npm run examples # run examples (builds first)
npm run bench # run benchmarks (builds first)See README.md for full usage.
All wire types implement serialize(): Uint8Array and static deserialize(bytes: Uint8Array): T.
class TokenRequest {
serialize(): Uint8Array {
/* ... */
}
static deserialize(tokenType: TokenTypeEntry, bytes: Uint8Array): TokenRequest {
/* ... */
}
}Per RFC 9576 (Privacy Pass Architecture), each token type exposes three roles:
Client— creates token requests, finalizes tokensIssuer— signs blinded requestsOrigin— creates challenges, verifies tokens
ESLint enforces consistent-type-imports and consistent-type-exports:
// use `import type` for type-only imports
import type { TokenChallenge } from './auth_scheme/private_token.js';
// use `export type` for type-only exports
export type { TokenReq, TokenRes };Prefix with underscore to satisfy noUnusedLocals/noUnusedParameters:
function example(_unusedParam: string): void {
/* ... */
}Vectors live in test/test_data/ as JSON. Sources:
- Go: RFC 9578 reference implementation
- Rust: raphaelrobert/privacypass
- Generate vectors from another implementation (see Interop Wiki)
- Place JSON in
test/test_data/with naming convention:{token_type}_{source}.json - Import in relevant
*.test.tsand add to test array
Example from pub_verif_token.test.ts:
import vectorsGo from './test_data/pub_verif_rfc9578.go.json';
import vectorsRust from './test_data/pub_verif_rfc9578.rust.json';
const vectors = [...vectorsGo, ...vectorsRust];
describe.each(vectors)('PublicVerifiable-Vector-%#', (v: Vectors) => {
/* ... */
});testSerialize(Type, instance)— roundtrip serialize/deserializetestSerializeType(tokenType, Type, instance)— same, with token type paramhexToUint8,uint8ToHex— hex conversion utilities
Partially Blind RSA verification does not work in browsers. WebCrypto implementations reject the large public exponents required by the protocol. See Chromium bug, Firefox bug.
Workaround: verify tokens server-side only.
Token requests can carry extensions per draft-hendrickson-privacypass-public-metadata. See Extensions class in src/auth_scheme/private_token.ts. This is draft-stage; API may change.
This library tracks IETF specifications. When drafts advance (new versions or RFC publication), update the implementation accordingly. Check the IETF Privacy Pass WG for current document status.
- @cloudflare/blindrsa-ts — Blind RSA, Partially Blind RSA
- @cloudflare/voprf-ts — VOPRF