Privacy Pass
Origin / relying-party side of Privacy Pass (RFC 9577 HTTP authentication scheme, RFC 9578 issuance protocols) — issue a token challenge and verify a presented token without learning who the client is. An origin asks for a token with a WWW-Authenticate: PrivateToken challenge; the client obtains a token from an issuer and presents it; the origin verifies it cryptographically.
This implements the publicly verifiable token type 0x0002 (Blind RSA, 2048-bit): the token's authenticator is an RSA Blind Signature (RFC 9474) that any party holding the issuer's public key can verify with RSASSA-PSS — so the origin verifies tokens itself, with no issuer secret and no callback. The privately verifiable VOPRF type (0x0001) requires the issuer's secret key and is an issuer-side operation, not implemented here.
Blind RSA is the algorithm Privacy Pass defines on the wire; like the framework's DNSSEC / DANE verifiers it validates an external protocol's signatures (RSASSA-PSS, SHA-384) rather than introducing classical crypto as a framework default.
b.privacyPass.parseToken(token) #
Parse a Privacy Pass token (RFC 9577 §2.2) into its fields: the tokenType, the client nonce, the challengeDigest (SHA-256 of the TokenChallenge the token answers), the tokenKeyId (SHA-256 of the issuer public key), and the authenticator. Structural only — call verifyToken to check the signature.
var t = b.privacyPass.parseToken(tokenBytes);
// → { tokenType: 2, nonce, challengeDigest, tokenKeyId, authenticator }
b.privacyPass.verifyToken(opts) #
{
{
token: Buffer|base64, // the presented token
issuerPublicKey: KeyObject|Buffer(SPKI DER)|PEM,
challenge?: Buffer|base64, // the TokenChallenge this token must answer
}
}
Verify a publicly verifiable Privacy Pass token (type 0x0002, Blind RSA — RFC 9578 §8.2). The authenticator is checked as an RSASSA-PSS (SHA-384, MGF1-SHA-384, 48-byte salt) signature over token_input = token_type ‖ nonce ‖ challenge_digest ‖ token_key_id using the issuer's public key. The token is bound to that key — its token_key_id must equal the SHA-256 of the supplied key's SubjectPublicKeyInfo — and, when opts.challenge is given, to that challenge (its SHA-256 must equal the token's challenge_digest), so a token minted for a different origin's challenge is refused.
var r = b.privacyPass.verifyToken({ token: tok, issuerPublicKey: issuerSpki });
// → { ok: true, tokenType: 2, nonce, challengeDigest, tokenKeyId }
b.privacyPass.buildChallenge(opts) #
{
{
issuerName: string, // the token issuer's name
tokenType?: number, // default 0x0002 (Blind RSA)
originInfo?: string, // origin name(s) the token is scoped to (default: any)
redemptionContext?: Buffer, // 0 or 32 bytes (default: empty)
tokenKey?: Buffer|KeyObject, // issuer SPKI, included as token-key= when given
}
}
Build a TokenChallenge (RFC 9577 §2.1) and the matching WWW-Authenticate: PrivateToken header value an origin returns to ask a client for a token. The challenge binds the token to this issuer (and optionally this origin and a redemption context); its SHA-256 is the challenge_digest that verifyToken checks.
var c = b.privacyPass.buildChallenge({ issuerName: "issuer.example", originInfo: "origin.example" });
res.setHeader("WWW-Authenticate", c.wwwAuthenticate);
Last updated 2026-08-08T16:39:49.652Z by seeder.