Brief Intro to Privacy Pass
The Privacy Pass protocol has been out there for a long time. It's simple to implement and deploy, and provides a straightforward way for services to provide safe private credits to their users.
In short, Privacy Pass is a protocol between a client and a server, where the server, after authenticating the client, issues credits that the client can redeem to use a service later. The special property it provides is that the credits are blinded, meaning that after the credits are issued to the client, the service can't know who's redeeming them.
This is suitable in cases where the check that is being made is homogenous among usages and state isn't required. Examples include:
CAPTCHA - instead of showing you're a human to multiple websites, making the user experience cumbersome, you get multiple "humanity credits" that you can use
Memory-less ChatGPT and Sora
Privacy Pass itself, along with a few extensions of it, has been standardized by the IETF, a community that is responsible for standardizing important protocols that are used throughout the world. It's been deployed in practice by Cloudflare, who did a lot of the early work on it, and by Apple, in the form of Private Access Tokens, which is now native in iOS devices. Both have mainly focused on the CAPTCHA use case, and they're not at all limited to it.
It even has an HTTP authentication scheme that allows you to introduce it relatively seamlessly into existing systems by augmenting the HTTP server to support the PrivateToken variant.
It's important to note that Privacy Pass does not allow malicious actors to thrive, in contrast to privacy related protocols. The issuer has full discretion on who to issue credits to and the origin has all the information related to the action being performed, just without a direct link to the original identity of the client.
x402
x402 is an emerging standard for, in the authors' own words, internet-native payments. It allows services to gate access by requiring payments. The novelty is that it's done in a protocoled and automated way. It's implemented on top of HTTP and uses the "402 payment required" HTTP code.
When a client tries to access such a service, they're presented with a payment request and supported payment methods. The client makes a payment and responds with their payment authorization. Once the payment settles, the client is allowed access.
With the advent of digital money, and especially fast-settling crypto payments, it's possible to imagine services can charge per micro-usage, rather than charging for large subscriptions. This is even more important when you have AI agents working with you, using MANY sources of data at a speed you can't keep up with. They're going to consume more content and it's not surprising services will want to charge for it, and the current method of the humans having to manually obtain API keys for every service isn't tenable.
Some examples from the x402 white paper:
Agents accessing premium news articles for research, paying per article
Agents paying for GPU costs, charging per minute used
Agents consuming video streaming, charging per second costs
Agents accessing legal court rulings, charging per court ruling
You got to admit, these all sounds pretty homogenous and uniform... how about we try to compose these two protocols together?
Privacy Pass, a bit deeper
Before we go further into it, let's look at some Privacy Pass terminology.
We have:
Client - wants to use a service
Origin - the service that requires a client to authenticate, presenting the client with a challenge
Attester - verifies that the client completed a challenge
Issuer - once a client completes a challenge, issues blinded credits to the client
Now let's think what happens in the following case - the client purchases from origin $10 worth of credits by sending a stablecoin payment, presenting the transaction hash and a proof of ownership of the address.
The math!
You can ignore this section if you don't want to see the math, it's not really necessary. If you're curious, feel free to stay!
Let's look at a simplified case where the Origin, Attester and Issuer are joint, and the client is looking to buy some AI image generation credits.
We will discuss the elliptic curve variant, although the one that is mostly used today is the RSA one. The protocol goes as follows:
1. The origin sends the client a challenge that identifies a specific context where the credits should be valid. E.g. which issuer can be used and during what time window. It's an opaque string that's hashed to a group element, together with a unique nonce chosen by the client.
2. The client chooses a random field element b, blinds the credit and sends it to the issuer, together with the requirement the issuer wants satisfied - e.g. ID card to prove age > 18.
3. The issuer signs it by using their secret key s and sends the signed blinded credit to the client. The issuer further sends a proof that the credit was signed using the same secret key that they use for everyone else. It's a discrete log equality proof, or DLEQ, showing that the discrete log is equal between the signed credit and unsigned credit, and the public key and the group's base point.
4. The client verifies the proofs, unblinds the credit and has it ready to use!
5. When the client wants to redeem a credit towards an origin, they send them D and their nonce. The origin goes ahead and uses the same context to get the same T, and verifies that the token is indeed signed using s. This is possible in this case since the issuer and the origin are the same. In the RSA variant it's possible to do it even when they're separate. The origin does the following check:
Following this check, the origin is now assured that the client has a valid signed token and can provide the service.
6. The origin maintains a list of used credits, so they can't be redeemed twice.
Note that we discussed a single token issuance, and it's possible to issue many of these together efficiently, which is often what you'd want.
The HTTP scheme
Now let's see how this can be done as an HTTP authentication scheme.
The WWW-Authenticate header is used to request clients to authenticate to a server, specifying which authentication scheme to use.
In the Privacy Pass case, it looks like this:
WWW-Authenticate:
PrivateToken challenge="abc...", token-key="123..."
This specifies that the scheme is PrivateToken, meaning Privacy Pass, the challenge, as above, and the token-key, which is the issuer's public key.
The client goes ahead and runs the protocol described above, and then responds to the server with the usual Authorization header.
Authorization:
PrivateToken token="abc..."Essentially, all that we've done to make it usable as an HTTP scheme is specifying that the delivery mechanism of the challenge and credit is through HTTP headers.
Side note - extensions
Privacy pass and, more generally, Anonymous Tokens have been extended in a few different ways, some of them are being standardized as well. A few examples:
x402, in a nutshell
Let's now talk about how x402 works in a very high level. It uses the existing 402 Payment Required HTTP response code, and adds structure on top of it. Specifically, it allows services to specify allowed payment methods. It could be blockchain-based, like stablecoins, and could be anything else that is verifiable. This is an example from the whitepaper:
{
"maxAmountRequired": "0.10",
"resource": "/api/market-data",
"description": "Access to real-time market data requires payment.",
"payTo": "0xABCDEF1234567890ABCDEF1234567890ABCDEF12",
"asset": "0xA0b86991C6218b36c1d19D4a2e9Eb0cE3606EB48",
"network": "ethereum-mainnet"
}The client goes ahead, prepares the payment and makes another request, this time with an additional X-PAYMENTheader, containing the payment details. E.g. the signed transaction ready for submission.
The server uses the help of a Facilitator to settle the payment - e.g. submit the signed transaction and wait for it to be confirmed. Once it is confirmed, the server responds with a X-PAYMENT RESPONSE header, signaling to the client they can move forward and use the service.
Tying these two together - blind x402!
One way to do it would be to run these two protocols concurrently.
This would mean, for example:
A client wants to use a service, so they try to figure out how to access it. They receive a 402 Payment Required status.
The client goes ahead and prepares the payment, and in addition it does the following: it estimates it use for the next month of this service and prepares to buy a bulk. It prepares a bunch of blinded credits representing single uses of the service.
The client submits this through the X-PAYMENT header.
The service uses a facilitator that supports Privacy Pass, which in addition to settling the transaction also issues Privacy Pass credits.
The service responds to the client and sends the blinded credits through the X-PAYMENT-RESPONSE header.
The client can then unblind the credits and use them later.
When the service receives a credit to be redeemed, they would run the protocol again, through a process where the facilitator checks whether the credit was used and maintains the list of used credits, perhaps on-chain.
Nice and simple, isn't it?
Who can use this?
This would be natural to use in many of the cases we mentioned in the beginning. It could be agents that need to access data from sources they frequent (e.g. a legal database), or can at least access through an aggregator. Agents can now use services in a way that doesn't expose their identity directly to services and other snoopers, preventing attacks on them such as unfair censorship or data manipulation, while still being fully compliant.
What did we get out of it? We retain the advantage of machine-to-machine interactions, fast settlement and a structured and extensible protocol. The main downside is that we went back a bit on the flexibility that x402 gave us - first and foremost, micro-uses and granularly-pricing of services that clients can consume immediately.
One really interesting benefit is the possibility of offchain payments - the used credit list doesn’t have to be updated on chain, at least not immediately. The facilitator can contact a centralized service run by the issuer in the interim, while maintaining the single-use nature.
It's not obvious to solve it within this model, and I hope it will give someone some food for thought!
If you have ideas to bounce, building around this topic or have questions, I'd love to hear from you :) Reach out to me on email.
Acknowledgements
Thanks to Sam who suggested to discuss offchain payments and Andrija for reviewing.