- Research
- Double-Blind Verification: Unlinkable, Meterable Age Assurance with Server-Side Anonymous Credentials
Double-Blind Verification: Unlinkable, Meterable Age Assurance with Server-Side Anonymous Credentials
Relay is the verification architecture behind Persona Relay. This post documents how it addresses the problem of preserving privacy during age assurance, as well as the cryptographic design behind it.
The problem: providers are positioned to see too much
Remote verification is typically a three-party interaction: a relying party (the business gating a service), an end user whose attribute must be attested, and a verification provider that collects identity data, evaluates it, and returns a result.
In the conventional arrangement, the verification provider observes the end user’s information and the relying party's identity for dispute resolution, audit, and billing purposes. But the arrangement can result in two undesirable outcomes:
The provider could link a particular person to a particular business's use case, producing a cross-service profile that privacy-conscious users and regulators object to.
The relying party may receive more than the minimal derived facts it needs to make a specific decision.
As jurisdictions raise the bar on age assurance and impose audit obligations on the businesses they oversee, this concentration of data becomes a liability and a compliance burden. The research question is whether we can create a usable and compliant arrangement without these undesirable outcomes.
The idea: separate issuance from redemption
The core idea is to split a verification interaction into two phases that are unlinkable from the provider's vantage point:
An issuance phase over an authenticated channel, where the provider knows which relying party it is serving and can therefore meter and bill, and
A redemption phase over an anonymized channel, where the provider releases a result but cannot correlate that redemption to the business that obtained the credential.
An analogy helps convey part of the goal. Consider a coat check at a busy restaurant. A patron can use their tag to retrieve their coat, and the attendant can confirm that the tag is genuine. However, the tag doesn’t record who the patron is, and the attendant does not know when the tag was issued or what the patron ordered at the restaurant.
Relay aims for the cryptographic equivalent by allowing a provider to verify an end user’s credentials without knowing what the end user is doing, and relying parties to receive a verified claim without learning anything else about the end user.
We build it on established, standardized primitives rather than novel cryptography:
Anonymous credentials via Privacy Pass (IETF RFC 9576/9577/9578) with RSA blind signatures (RFC 9474): The provider signs a credential without seeing the plaintext and, at redemption, verifies that a presented credential is consistent with some prior issuance.
Oblivious HTTP (RFC 9458): Anonymizes the redemption connection to avoid identifying the relying party at the network layer.
Relying-party-supplied encryption: The relying party holds the only key to the encrypted result, so intermediate components never see a plaintext result.
The design choice that distinguishes Relay from typical Privacy Pass deployments is that the relying party's server acts as the Privacy Pass client, rather than the end-user device. This keeps the device out of the anonymous-credential protocol entirely, and lets the provider authenticate the business at issuance (for billing) while remaining blind to it at redemption.
One remaining issue was that an anonymous credential cannot be bound to a verification session without breaking use-case blindness. But we resolved this with a provider-issued, per-access, rotating session-identifying authenticator that’s carried out-of-band from the credential.
The Relay protocol in brief
The full protocol comprises four phases: Privacy Pass issuance, anonymous session creation, identity verification, and redemption. sk_V/pk_V are the provider's signing keypair, ek_S/dk_S are the relying party's result-encryption keypair, and dk_S never leaves the relying party.
Here’s an overview of how it works:
Issuance (authenticated): The relying party blinds a random token and sends it to the provider, which blind-signs it under sk_V without seeing the token and records a billing receipt keyed to the relying party. The relying party unblinds to obtain an anonymous credential. Challenge metadata (expiry, audience, context) is bound by a MAC so it can round-trip and be re-validated at redemption without provider-side state.
Session creation (anonymized): Over Oblivious HTTP, the relying party opens a session and registers ek_S. The provider returns a session access token and a session-identifying authenticator encrypted under ek_S. The provider never stores the relying party’s identity.
Verification: The end user completes the verification flow, such as a document or selfie verification, with the provider over an independent channel. The provider derives claims, such as a Boolean age-threshold result plus methodology metadata.
Redemption (anonymized): The relying party presents the credential and session authenticator. The provider checks that the credential is consistent with some prior issuance, verifies the challenge Message Authentication Code (MAC) and expiry, enforces one-time use, rotates the session authenticator, and returns the result encrypted under ek_S.
Reference implementation
Consistent with the norms of security research, we are releasing reference implementations so the design can be independently scrutinized and reproduced. We are publishing reference server SDKs (Go first, with Node and Python to follow) and the Relay core components under the Apache 2.0 license, on GitHub under the persona-id organization. Before release, repositories were rebuilt from clean history with full secret scanning, reviewed for security and repository configuration, and cleared for the appropriate export classification (EAR99).
The relying-party integration surface is deliberately small: issue and unblind a credential, open a session, and redeem for an encrypted result.
// Issuance (authenticated channel): obtain and unblind an anonymous credential.
cred, err := relay.Issue(ctx, relay.IssueRequest{ClaimType: "age_over_18"})
// Session creation (anonymized channel): register a result-encryption key.
sess, err := relay.NewSession(ctx, cred, relay.EncryptionKey(ekS))
// ... end user completes verification via the embedded flow ...
// Redemption (anonymized channel): retrieve the encrypted claim result.
res, err := relay.Redeem(ctx, sess, cred)
claim, err := res.Decrypt(dkS) // e.g. {"age_over_18": true, "method": "..."}
(Illustrative; see the repositories for the current API.)
Security properties and limitations
We argue that Relay provides:
Use-case blindness: The provider verifies consistency rather than retrieving a specific issuance record, and Oblivious HTTP removes network-layer re-identification.
Data minimization: The relying party only receives a derived claim.
Result confidentiality: The results are encrypted to a key that the provider never holds after issuance.
Integrity and one-time use: The blind-signature scheme and one-time-use store limit forgery and replay attacks.
Issuance/redemption unlinkability: Issuance and redemption records are held in separate, uncross-linked tenants. And the relying party holds the blinding factor that relates the issuance and redemption credentials.
Meterability: The issuance receipts enable per-event billing without a join key to any redemptions.
We are deliberate about what is not yet established. The unlinkability argument is informal, and a formal indistinguishability model and proof still need to be completed. A residual timing- and volume-based side channel exists across the two channels, which coarse-grained expiry and the oblivious relay mitigate but do not provably eliminate. And we do not report measurements from a production deployment where latency, cryptographic overhead, and throughput characterization are open. We also intend to analyze Relay-collusion and compromised-key failure modes explicitly.
We welcome scrutiny of both the protocol and the code. If you find a weakness, please contact us at research@withpersona.com.