Internet-Draft | Private Access Tokens | October 2021 |
Hendrickson, et al. | Expires 28 April 2022 | [Page] |
This document defines a protocol for issuing and redeeming privacy-preserving access tokens. These tokens can adhere to an issuance policy, allowing a service to limit access according to the policy without tracking client identity.¶
This note is to be removed before publishing as an RFC.¶
Source for this draft and an issue tracker can be found at https://github.com/tfpauly/privacy-proxy.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 28 April 2022.¶
Copyright (c) 2021 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.¶
Servers commonly use passive and persistent identifiers associated with clients, such as IP addresses or device identifiers, for enforcing access and usage policies. For example, a server might limit the amount of content an IP address can access over a given time period (referred to as a "metered paywall"), or a server might rate-limit access from an IP address to prevent fraud and abuse. Servers also commonly use the client's IP address as a strong indicator of the client's geographic location to limit access to services or content to a specific geographic area (referred to as "geofencing").¶
However, passive and persistent client identifiers can be used by any entity that has access to it without the client's express consent. A server can use a client's IP address or its device identifier to track client activity. A client's IP address, and therefore its location, is visible to all entities on the path between the client and the server. These entities can trivially track a client, its location, and servers that the client visits.¶
A client that wishes to keep its IP address private can hide its IP address using a proxy service or a VPN. However, doing so severely limits the client's ability to access services and content, since servers might not be able to enforce their policies without a stable and unique client identifier.¶
This document describes an architecture for Private Access Tokens (PATs), using RSA Blind Signatures as defined in [BLINDSIG], as an explicit replacement for these passive client identifiers. These tokens are privately issued to clients upon request and then redeemed by servers in such a way that the issuance and redemption events for a given token are unlinkable.¶
At first glance, using PATs in lieu of passive identifiers for policy enforcement suggests that some entity needs to know both the client's identity and the server's policy, and such an entity would be trivially able to track a client and its activities. However, with appropriate mediation and separation between the parties involved in the issuance and the redemption protocols, it is possible to eliminate this information concentration without any functional regressions. This document describes such a protocol.¶
The relationship of this work to Privacy Pass ([I-D.ietf-privacypass-protocol]) is discussed in Appendix A.¶
This section describes classes of use cases where an origin would traditionally use a stable and unique client identifier for enforcing attribute-based policy. Hiding these identifiers from origins would therefore require an alternative for origins to continue enforcing their policies. Using the Privacy Address Token architecture for addressing these use cases is described in Section 6.¶
An origin provides rate-limited access to content to a client over a fixed period of time. The origin does not need to know the client's identity, but needs to know that a requesting client has not exceeded the maximum rate set by the origin.¶
One example of this use case is a metered paywall, where an origin limits the number of page requests to each unique user over a period of time before the user is required to pay for access. The origin typically resets this state periodically, say, once per month. For example, an origin may serve ten (major content) requests in a month before a paywall is enacted. Origins may want to differentiate quick refreshes from distinct accesses.¶
Another example of this use case is rate-limiting page accesses to a client to help prevent fraud. Operations that are sensitive to fraud, such as account creation on a website, often employ rate-limiting as a defense in depth strategy. Captchas or additional verification can be required by these pages when a client exceeds a set rate-limit.¶
Origins routinely use client IP addresses for this purpose.¶
An origin provides access to or customizes content based on the geo-location of the client. The origin does not need to know the client's identity, but needs to know the geo-location, with some level of accuracy, for providing service.¶
A specific example of this use case is "geo-fencing", where an origin restricts the available content it can serve based on the client's geographical region.¶
Origins almost exclusively use client IP addresses for this purpose.¶
An origin provides access to content for clients that have been authorized by a delegated or known mediator. The origin does not need to know the client's identity.¶
A specific example of this use case is a federated service that authorizes users for access to specific sites, such as a federated news service or a federated video streaming service. The origin trusts the federator to authorize users and needs proof that the federator authorized a particular user, but it does not need the user's identity to provide access to content.¶
Origins could currently redirect clients to a federator for authentication, but origins could then track the client's federator user ID or the client's IP address across accesses.¶
At a high level, the PAT architecture seeks to solve the following problem: in the absence of a stable Client identifier, an Origin needs to verify the identity of a connecting Client and enforce access policies for the incoming Client. To accomplish this, the PAT architecture employs four functional components:¶
In the PAT architecture, these four components interact as follows.¶
An Origin designates a trusted Issuer to issue tokens for it. The Origin then redirects any incoming Clients to the Issuer for policy enforcement, expecting the Client to return with a proof from the Issuer that the Origin's policy has been enforced for this Client.¶
The Client employs a trusted Mediator through which it communicates with the Issuer for this proof. The Mediator performs three important functions:¶
When a Mediator-anonymized Client requests a token from an Issuer, the Issuer enforces the Origin's policies based on the received Client issuance state and Origin policy. Issuers know the Origin's policies and enforce them on behalf of the Origin. An example policy is: "Limit 10 accesses per Client". More examples and their use cases are discussed in Section 6. The Issuer does not learn the Client's true identity.¶
Finally, the Origin provides access to content or services to a Client upon verifying a PAT presented by the Client. Verification of this token serves as proof that the Client meets the Origin's policies as enforced by the delegated Issuer with the help of a Mediator. The Origin can then provide any services or content gated behind these policies to the Client.¶
Figure 1 shows the components of the PAT architecture described in this document. Protocol details follow in Section 4 and Section 5.¶
In this architecture, the Mediator, Issuer, and Origin each have partial knowledge of the Client's identity and actions, and each entity only knows enough to serve its function (see Section 2 for more about the pieces of information):¶
Since an Issuer enforces policies on behalf of Origins, a Client is required to reveal the Origin's identity to the delegated Issuer. It is a requirement of this architecture that the Mediator not learn the Origin's identity so that, despite knowing the Client's identity, a Mediator cannot track and concentrate information about Client activity.¶
An Issuer expects a Mediator to verify its Clients' identities correctly, but an Issuer cannot confirm a Mediator's efficacy or the Mediator-Client relationship directly without learning the Client's identity. Similarly, an Origin does not know the Mediator's identity, but ultimately relies on the Mediator to correctly verify or authenticate a Client for the Origin's policies to be correctly enforced. An Issuer therefore chooses to issue tokens to only known and reputable Mediators; the Issuer can employ its own methods to determine the reputation of a Mediator.¶
A Mediator is expected to employ a stable Client identifier, such as an IP address, a device identifier, or an account at the Mediator, that can serve as a reasonable proxy for a user with some creation and maintenance cost on the user.¶
For the Issuance protocol, a Client is expected to create and maintain stable and explicit secrets for time periods that are on the scale of Issuer policy windows. Changing these secrets arbitrarily during a policy window can result in token issuance failure for the rest of the policy window; see Section 5.1.1 for more details. A Client can use a service offered by its Mediator or a third-party to store these secrets, but it is a requirement of the PAT architecture that the Mediator not be able to learn these secrets.¶
The privacy guarantees of the PAT architecture, specifically those around separating the identity of the Client from the names of the Origins that it accesses, are based on the expectation that there is not collusion between the entities that know about Client identity and those that know about Origin identity. Clients choose and share information with Mediators, and Origins choose and share policy with Issuers; however, the Mediator is generally expected to not be colluding with Issuers or Origins. If this occurs, it can become possible for a Mediator to learn or infer which Origins a Client is accessing, or for an Origin to learn or infer the Client identity. For further discussion, see Section 8.4.¶
The PAT architecture does not enforce strong constraints around the definition of a Client identity and allows it to be defined entirely by a Mediator. If a user can create an arbitrary number of Client identities that are accepted by one or more Mediators, a malicious user can easily abuse the system to defeat the Issuer's ability to enforce per-Client policies.¶
These multiple identities could be fake or true identities.¶
A Mediator alone is responsible for detecting and weeding out fake Client identities in the PAT architecture. An Issuer relies on a Mediator's reputation; as explained in Section 1.3, the correctness of the architecture hinges on Issuers issuing tokens to only known and reputable Mediators.¶
Users have multiple true identities on the Internet however, and as a result, it seems possible for a user to abuse the system without having to create fake identities. For instance, a user could use multiple Mediators, authenticating with each one using a different true identity.¶
The PAT architecture offers no panacea against this potential abuse. We note however that the usages of PATs will cause the ecosystem to evolve and offer practical mitigations, such as:¶
When used in contexts like websites, origin servers that challenge clients for Private Access Tokens need to consider how to optimize their interaction model to ensure a good user experience.¶
Private Access Tokens are designed to be used without explicit user involvement. Since tokens are only valid for a single origin and in response to a specific challenge, there is no need for a user to manage a limited pool of tokens across origins. The information that is available to an origin upon token redemption is limited to the fact that this is a client that passed a Mediator's checks and has not exceeded the per-origin limit defined by an Issuer. Generally, if a user is willing to use Private Access Tokens with a particular origin (or all origins), there is no need for per-challenge user interaction. Note that the Issuance flow may separately involve user interaction if the Mediator needs to authenticate the Client.¶
Since tokens are issued using a separate connection through a Mediator to an Issuer, the process of issuance can add user-perceivable latency. Origins SHOULD NOT block useful work on token authentication. Instead, token authentication can be used in similar ways to CAPTCHA validation today, but without the need for user interaction. If issuance is taking a long time, a website could show an indicator that it is waiting, or fall back to another method of user validation.¶
If an origin is requesting an unexpected number of tokens, such as requesting token authentication more than once for a single website load, it can indicate that the server is not functioning correctly, or is trying to attack or overload the client or issuance servers. In such cases, the client SHOULD ignore redundant token challengers, or else alert the user.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
Unless said otherwise, this document encodes protocol messages in TLS notation from [TLS13], Section 3.¶
This draft includes pseudocode that uses the functions and conventions defined in [HPKE].¶
Encoding an integer to a sequence of bytes in network byte order is described using the function "encode(n, v)", where "n" is the number of bytes and "v" is the integer value. The function "len()" returns the length of a sequence of bytes.¶
The following terms are defined to refer to the different pieces of information passed through the system:¶
The Issuer Name identifies which Issuer is able to provide tokens for a Client. The Client sends the Issuer Name to the Mediator so the Mediator know where to forward requests. Each Issuer is associated with a specific ISSUER_POLICY_WINDOW.¶
The period over which an Issuer will track access policy, defined in terms of seconds and represented as a uint64. The state that the Mediator keeps for a Client is specific to a policy window. The effective policy window for a specific Client starts when the Client first sends a request associated with an Issuer.¶
The public key used when generating and verifying Private Access Tokens. Each Origin Token Key is unique to a single Origin. The corresponding private key is held by the Issuer.¶
The public key used to encrypt values such as ORIGIN_NAME in requests from Clients to the Issuer, so that Mediators cannot learn the ORIGIN_NAME value. Each ISSUER_KEY is used across all requests on the Issuer, for different Origins.¶
The name of the Origin that requests and verifies Private Access Tokens.¶
An identifier that is generated by the Client and marked on requests to the Mediator, which represents a specific Origin anonymously. The Client generates a stable ANON_ORIGIN_ID for each ORIGIN_NAME, to allow the Mediator to count token access without learning the ORIGIN_NAME.¶
A public key chosen by the Client and shared only with the Mediator.¶
The secret key used by the Client during token issuance, whose public key (CLIENT_KEY) is shared with the Mediator.¶
The secret key used by the Issuer during token issuance, whose public key is not shared with anyone.¶
An identifier that is generated by Issuer based on an ORIGIN_SECRET that is per-Client and per-Origin. See Section 5.6 for details of derivation.¶
Issuers MUST provide three parameters for configuration:¶
KeyConfig
as defined in [OHTTP] to use when
encrypting the ORIGIN_NAME in issuance requests. This parameter uses resource media type
"application/ohttp-keys".¶
These parameters can be obtained from an Issuer via a directory object, which is a JSON object whose field names and values are raw values and URLs for the parameters.¶
Field Name | Value |
---|---|
issuer-key | ISSUER_KEY resource URL as a JSON string |
issuer-policy-window | ISSUER_POLICY_WINDOW as a JSON number |
issuer-request-uri | ISSUER_REQUEST_URI resource URL as a JSON string |
As an example, the Issuer's JSON directory could look like:¶
{ "issuer-key": "https://issuer.example.net/key", "issuer-token-window": 86400, "issuer-request-uri": "https://issuer.example.net/access-token-request" } ~~~ Mediators MUST provide a single parameter for configuration, MEDIATOR_REQUEST_URI, wich is Private Access Token request URL for proxying protocol messages to Issuers. For example, a Mediator URL might be https://mediator.example.net/relay-access-token-request. Similar to Issuers, Mediators make this parameter available by a directory object with the following contents: | Field Name | Value | |:---------------------|:----------------------------------| | mediator-request-uri | MEDIATOR_REQUEST_URI resource URL | As an example, the Mediator's JSON dictionary could look like:¶
{ "mediator-request-uri": "https://mediator.example.net/relay-access-token-request." } ~~~¶
Issuer and Mediator directory resources have the media type "application/json" and are located at the well-known location /.well-known/private-access-tokens-directory.¶
This section describes the interactive protocol for the token challenge and redemption flow between a Client and an Origin.¶
Token redemption is performed using HTTP Authentication ([RFC7235]), with the scheme "PrivateAccessToken". Origins challenge Clients to present a unique, single-use token from a specific Issuer. Once a Client has received a token from that Issuer, it presents the token to the Origin.¶
Token redemption only requires Origins to verify token signatures computed using the Blind Signature protocol from [BLINDSIG]. Origins are not required to implement the complete Blind Signature protocol. (In contrast, token issuance requires Clients and Issuers to implement the Blind Signature protocol, as described in Section 5.)¶
Origins send a token challenge to Clients in an "WWW-Authenticate" header with the "PrivateAccessToken" scheme. This challenge includes a TokenChallenge message, along with information about what keys to use when requesting a token from the Issuer.¶
The TokenChallenge message has the following structure:¶
struct { uint8_t version; opaque origin_name<1..2^16-1>; opaque issuer_name<1..2^16-1>; opaque redemption_nonce[32]; } TokenChallenge;¶
The structure fields are defined as follows:¶
When used in an authentication challenge, the "PrivateAccessToken" scheme uses the following attributes:¶
KeyConfig
as defined
in [OHTTP] to use when encrypting the ORIGIN_NAME in issuance requests
(ISSUER_KEY).¶
Origins MAY also include the standard "realm" attribute, if desired.¶
As an example, the WWW-Authenticate header could look like this:¶
WWW-Authenticate: PrivateAccessToken challenge=abc..., token-key=123..., issuer-key=456...¶
Upon receipt of this challenge, a Client uses the message and keys in the Issuance protocol (see Section 5). If the TokenChallenge has a version field the Client does not recognize or support, it MUST NOT parse or respond to the challenge. This document defines version 1, which indicates use of private tokens based on RSA Blind Signatures [BLINDSIG], and determines the rest of the structure contents.¶
Note that it is possible for the WWW-Authenticate header to include multiple challenges, in order to allow the Client to fetch a batch of multiple tokens for future use.¶
For example, the WWW-Authenticate header could look like this:¶
WWW-Authenticate: PrivateAccessToken challenge=abc..., token-key=123..., issuer-key=456..., PrivateAccessToken challenge=def..., token-key=234..., issuer-key=567...¶
The output of the issuance protocol is a token that corresponds to the Origin's challenge (see Section 4.1). A token is a structure that begins with a single byte that indicates a version, which MUST match the version in the TokenChallenge structure.¶
struct { uint8_t version; uint8_t token_key_id[32]; uint8_t message[32]; uint8_t signature[Nk]; } Token;¶
The structure fields are defined as follows:¶
When used for client authorization, the "PrivateAccessToken" authentication scheme defines one parameter, "token", which contains the base64url-encoded Token struct. All unknown or unsupported parameters to "PrivateAccessToken" authentication credentials MUST be ignored.¶
Clients present this Token structure to Origins in a new HTTP request using the Authorization header as follows:¶
Authorization: PrivateAccessToken token=abc...¶
Origins verify the token signature using the corresponding policy verification key from the Issuer, and validate that the message matches the hash of a TokenChallenge it previously issued and is still valid, SHA256(TokenChallenge), and that the version of the Token matches the version in the TokenChallenge. The TokenChallenge MAY be bound to a specific HTTP session with Client, but Origins can also accept tokens for valid challenges in new sessions.¶
If a Client's issuance request fails with a 401 error, as described in Section 5.4, the Client MUST react to the challenge as if it could not produce a valid Authorization response.¶
This section describes the Issuance protocol for a Client to request and receive a token from an Issuer. Token issuance involves a Client, Mediator, and Issuer, with the following steps:¶
The Issuance protocol has a number of underlying cryptographic dependencies for operation:¶
Clients and Issuers are required to implement all of these dependencies, whereas Mediators are required to implement POG and POK support.¶
The Issuance protocol requires each participating endpoint to maintain some necessary state, as described in this section.¶
A Client is required to have the following information, derived from a given TokenChallenge:¶
Clients maintain a stable CLIENT_ID that they use for all communication with a specific Mediator. CLIENT_ID is a public key, where the corresponding private key CLIENT_SECRET is known only to the client.¶
If the client loses this (CLIENT_ID, CLIENT_SECRET), they may generate a new tuple. The mediator will enforce if a client is allowed to use this new CLIENT_ID. See #mediator-state for details on this enforcement.¶
Clients also need to be able to generate an ANON_ORIGIN_ID value that corresponds to the ORIGIN_NAME, to send in requests to the Mediator.¶
ANON_ORIGIN_ID MUST be a stable and unpredictable 32-byte value computed by the Client. Clients MUST NOT change this value across token requests for the same ORIGIN_NAME. Doing so will result in token issuance failure (specifically, when a Mediator rejects a request upon detecting two ANON_ORIGIN_ID values that map to the same Origin).¶
One possible mechanism for implementing this identifier is for the Client to store a mapping between the ORIGIN_NAME and a randomly generated ANON_ORIGIN_ID for future requests. Alternatively, the Client can compute a PRF keyed by a per-client secret (CLIENT_SECRET) over the ORIGIN_NAME, e.g., ANON_ORIGIN_ID = HKDF(secret=CLIENT_SECRET, salt="", info=ORIGIN_NAME).¶
A Mediator is required to maintain state for every authenticated Client. The mechanism of identifying a Client is specific to each Mediator, and is not defined in this document. As examples, the Mediator could use device-specific certificates or account authentication to identify a Client.¶
Mediators must enforce that Clients don't change their CLIENT_ID frequently, to ensure Clients can't regularily evade the per-client policy as seen by the issuer. Mediators MUST NOT allow Clients to change their CLIENT_ID more than once within a policy window, or in the subsequent policy window after a previous CLIENT_ID change. Alternative schemes where the mediator stores the encrypted (CLIENT_ID, CLIENT_SECRET) tuple on behalf of the client are possble but not described here.¶
Mediators are expected to know the ISSUER_POLICY_WINDOW for any ISSUER_NAME to which they allow access. This information can be retrieved using the URIs defined in Section 3.¶
For each Client-Issuer pair, a Mediator maintains a policy window start and end time for each Issuer from which a Client requests a token.¶
For each tuple of (CLIENT_ID, ANON_ORIGIN_ID, policy window), the Mediator maintains the following state:¶
Issuers maintain a stable ORIGIN_SECRET that they use in calculating values returned to the Mediator for each origin. If this value changes, it will open up a possibility for Clients to request extra tokens for an Origin without being limited, within a policy window.¶
Issuers are expected to have the private key that corresponds to ISSUER_KEY, which allows them to decrypt the ORIGIN_NAME values in requests.¶
Issuers also need to know the set of valid ORIGIN_TOKEN_KEY public keys and corresponding private key, for each ORIGIN_NAME that is served by the Issuer. Origins SHOULD update their view of the ORIGIN_TOKEN_KEY regularly to ensure that Client requests do not fail after ORIGIN_TOKEN_KEY rotation.¶
The Issuance protocol defines four new HTTP headers that are used in requests and responses between Clients, Mediators, and Issuers (see Section 10.2).¶
The "Sec-Token-Origin" is an Item Structured Header [RFC8941]. Its value MUST be a Byte Sequence. This header is sent both on Client-to-Mediator requests (Section 5.3) and on Issuer-to-Mediator responses (Section 5.5). Its ABNF is:¶
Sec-Token-Origin = sf-binary¶
The "Sec-Token-Client" is an Item Structured Header [RFC8941]. Its value MUST be a Byte Sequence. This header is sent on Client-to-Mediator requests (Section 5.3), and contains the bytes of CLIENT_KEY. Its ABNF is:¶
Sec-Token-Client = sf-binary¶
The "Sec-Token-Nonce" is an Item Structured Header [RFC8941]. Its value MUST be a Byte Sequence. This header is sent on Client-to-Mediator requests (Section 5.3), and contains a per-request nonce value. Its ABNF is:¶
Sec-Token-Nonce = sf-binary¶
The "Sec-Token-Count" is an Item Structured Header [RFC8941]. Its value MUST be an Integer. This header is sent on Mediator-to-Issuer requests (Section 5.3), and contains the number of times a Client has previously received a token for an Origin. Its ABNF is:¶
Sec-Token-Count = sf-integer¶
The Client and Mediator MUST use a secure and Mediator-authenticated HTTPS connection. They MAY use mutual authentication or mechanisms such as TLS certificate pinning, to mitigate the risk of channel compromise; see Section 7 for additional about this channel.¶
Issuance begins by Clients hashing the TokenChallenge to produce a token input as message = SHA256(challenge), and then blinding message as follows:¶
blinded_req, blind_inv = rsabssa_blind(ORIGIN_TOKEN_KEY, message)¶
The Client MUST use a randomized variant of RSABSSA in producing this signature with a salt length of at least 32 bytes.¶
The Client uses CLIENT_SECRET to generate proof of its request.¶
blind = RandomScalar() blind_key = blind * CLIENT_SECRET blind_generator = blind * Generator() key_proof = SchnorrProof(CLIENT_SECRET, blind_key, blind_generator)¶
The Client then transforms this proof into "mapping_nonce", "mapping_key", "mapping_generator", and "mapping_proof".¶
mapping_nonce = SerializeScalar(blind) mapping_key = SerializeElement(blind_key) mapping_generator = SerializeElement(blind_generator) mapping_proof = SerializeProof(key_proof)¶
The Client then constructs a Private Access Token request using mapping_key, mapping_generator, mapping_proof, blinded_req, and origin information.¶
struct { uint8_t version; uint8_t mapping_generator[Ne]; uint8_t mapping_key[Ne]; uint8_t mapping_proof[Np]; uint8_t token_key_id; uint8_t blinded_req[Nk]; uint8_t name_key_id[32]; uint8_t encrypted_origin_name<1..2^16-1>; } AccessTokenRequest;¶
The structure fields are defined as follows:¶
The Client then generates an HTTP POST request to send through the Mediator to the Issuer, with the AccessTokenRequest as the body. The media type for this request is "message/access-token-request". The Client includes the "Sec-Token-Origin" header, whose value is ANON_ORIGIN_ID; the "Sec-Token-Client" header, whose value is CLIENT_KEY; and the "Sec-Token-Nonce" header, whose value is mapping_nonce. The Client sends this request to the Mediator's proxy URI. An example request is shown below, where Nk = 512.¶
:method = POST :scheme = https :authority = issuer.net :path = /access-token-request accept = message/access-token-response cache-control = no-cache, no-store content-type = message/access-token-request content-length = 512 sec-token-origin = ANON_ORIGIN_ID sec-token-client = CLIENT_KEY sec-token-nonce = mapping_nonce <Bytes containing the AccessTokenRequest>¶
If the Mediator detects a version in the AccessTokenRequest that it does not recognize or support, it MUST reject the request with an HTTP 400 error.¶
The Mediator also checks to validate that the name_key_id in the client's AccessTokenRequest matches a known ISSUER_KEY public key for the Issuer. For example, the Mediator can fetch this key using the API defined in Section 3. This check is done to help ensure that the Client has not been given a unique key that could allow the Issuer to fingerprint or target the Client. If the key does not match, the Mediator rejects the request with an HTTP 400 error. Note that Mediators need to be careful in cases of key rotation; see Section 8.¶
The Mediator finally checks to ensure that the AccessTokenRequest.mapping_proof is valid for the given CLIENT_KEY; see Section 5.8 for verification details. If the index is invalid, the Mediator rejects the request with an HTTP 400 error.¶
If the Mediator accepts the request, it will look up the state stored for this Client. It will look up the count of previously generate tokens for this Client using the same ANON_ORIGIN_ID. See Section 5.1.2 for more details.¶
If the Mediator has stored state that a previous request for this ANON_ORIGIN_ID was rejected by the Issuer in the current policy window, it SHOULD reject the request without forwarding it to the Issuer.¶
If the Mediator detects this Client has changed their CLIENT_ID more frequently than allowed as described in #mediator-state, it SHOULD reject the request without forwarding it to the Issuer.¶
The Mediator and the Issuer MUST use a secure and Issuer-authenticated HTTPS connection. Also, Issuers MUST authenticate Mediators, either via mutual TLS or another form of application-layer authentication. They MAY additionally use mechanisms such as TLS certificate pinning, to mitigate the risk of channel compromise; see Section 7 for additional about this channel.¶
Before copying and forwarding the Client's AccessTokenRequest request to the Issuer, the Mediator adds a header that includes the count of previous tokens as "Sec-Token-Count". The Mediator MAY also add additional context information, but MUST NOT add information that will uniquely identify a Client.¶
:method = POST :scheme = https :authority = issuer.net :path = /access-token-request accept = message/access-token-response cache-control = no-cache, no-store content-type = message/access-token-request content-length = 512 sec-token-count = 3 <Bytes containing the AccessTokenRequest>¶
Upon receipt of the forwarded request, the Issuer validates the following conditions:¶
If any of these conditions is not met, the Issuer MUST return an HTTP 400 error to the Mediator, which will forward the error to the client.¶
If the request is valid, the Issuer then can use the value from "Sec-Token-Count" to determine if the Client is allowed to receive a token for this Origin during the current policy window. If the Issuer refuses to issue more tokens, it responds with an HTTP 429 (Too Many Requests) error to the Mediator, which will forward the error to the client.¶
The Issuer determines the correct ORIGIN_TOKEN_KEY by using the decrypted ORIGIN_NAME value and AccessTokenRequest.token_key_id. If there is no ORIGIN_TOKEN_KEY whose truncated key ID matches AccessTokenRequest.token_key_id, the Issuer MUST return an HTTP 401 error to Mediator, which will forward the error to the client. The Mediator learns that the client's view of the Origin key was invalid in the process.¶
If the Issuer is willing to give a token to the Client, the Issuer verifies the token request using "mapping_generator", "mapping_key", and "mapping_proof":¶
valid = SchnorrVerify(mapping_generator, mapping_key, mapping_proof)¶
If this fails, the Issuer rejects the request with a 400 error. Otherwise, the Issuer decrypts AccessTokenRequest.encrypted_origin_name to discover "origin". If this fails, the Issuer rejects the request with a 400 error. The Issuer then evaluates the mapping over the ORIGIN_SECRET pertaining to the origin for this issuer:¶
mapping_input = DeserializeElement(AccessTokenRequest.mapping_key) index = ORIGIN_SECRET * mapping_input mapping_index = SerializeElement(index)¶
If DeserializeElement fails, or if AccessTokenRequest.mapping_key is the identity element, the Issuer rejects the request with a 400 error.¶
The Issuer completes the issuance flow by computing a blinded response as follows:¶
blind_sig = rsabssa_blind_sign(skP, AccessTokenRequest.blinded_req)¶
skP
is the private key corresponding to ORIGIN_TOKEN_KEY, known only to the Issuer.¶
The Issuer generates an HTTP response with status code 200 whose body consists of blind_sig, with the content type set as "message/access-token-response" and the mapping_tag set in the "Sec-Token-Origin" header.¶
:status = 200 content-type = message/access-token-response content-length = 512 sec-token-origin = mapping_index <Bytes containing the blind_sig>¶
Upon receipt of a successful response from the Issuer, the Mediator extracts the "Sec-Token-Origin" header, and uses the value to determine ANON_ISSUER_ORIGIN_ID.¶
index = DeserializeElement(mapping_index) nonce = DeserializeScalar(mapping_nonce) ANON_ISSUER_ORIGIN_ID = (nonce^(-1)) * index¶
If the "Sec-Token-Origin" is missing, or if the same ANON_ISSUER_ORIGIN_ID was previously received in a response for a different ANON_ORIGIN_ID within the same policy window, the Mediator MUST drop the token and respond to the client with an HTTP 400 status. If there is not an error, the ANON_ISSUER_ORIGIN_ID is stored alongside the state for the ANON_ORIGIN_ID.¶
For all other cases, the Mediator forwards all HTTP responses unmodified to the Client as the response to the original request for this issuance.¶
When the Mediator detects successful token issuance, it MUST increment the counter in its state for the number of tokens issued to the Client for the ANON_ORIGIN_ID.¶
Upon receipt, the Client handles the response and, if successful, processes the body as follows:¶
sig = rsabssa_finalize(ORIGIN_TOKEN_KEY, nonce, blind_sig, blind_inv)¶
If this succeeds, the Client then constructs a Private Access Token as described in Section 4.1 using the token input message and output sig.¶
Given a KeyConfig
(ISSUER_KEY), Clients produce encrypted_origin_name and authenticate
all other contents of the AccessTokenRequest using the following values:¶
Beyond the key configuration inputs, Clients also require the AccessTokenRequest inputs.
Together, these are used to encapsulate ORIGIN_NAME (origin_name
) and produce
ENCRYPTED_ORIGIN_NAME (encrypted_origin
) as follows:¶
Note that enc is of fixed-length, so there is no ambiguity in parsing this structure.¶
In pseudocode, this procedure is as follows:¶
enc, context = SetupBaseS(pkI, "AccessTokenRequest") aad = concat(encode(1, keyID), encode(2, kemID), encode(2, kdfID), encode(2, aeadID), encode(1, version), encode(Ne, mapping_generator), encode(Ne, mapping_key), encode(Np, mapping_proof), encode(1, token_key_id), encode(Nk, blinded_req), encode(32, name_key_id)) ct = context.Seal(aad, origin_name) encrypted_origin_name = concat(enc, ct)¶
Issuers reverse this procedure to recover ORIGIN_NAME by computing the AAD as described above and decrypting encrypted_origin_name with their private key skI, the private key corresponding to pkI. In pseudocode, this procedure is as follows:¶
enc, ct = parse(encrypted_origin_name) aad = concat(encode(1, keyID), encode(2, kemID), encode(2, kdfID), encode(2, aeadID), encode(1, version), encode(Ne, mapping_generator), encode(Ne, mapping_key), encode(Np, mapping_proof), encode(1, token_key_id), encode(Nk, blinded_req), encode(32, name_key_id)) enc, context = SetupBaseR(enc, skI, "AccessTokenRequest") origin_name, error = context.Open(aad, ct)¶
Each Issuance request requires evaluation and verification of a Schnorr proof-of-knowledge. Given input secret "secret" and two elements, "base" and "target", generation of this proof (u, c, z), denoted SchnorrProof(secret, base, target), works as follows:¶
r = RandomScalar() u = r * base c = HashToScalar(SerializeElement(base) || SerializeElement(target) || SerializeElement(mask), dst = "PrivateAccessTokensProof") z = r + (c * secret)¶
The proof is encoded by serializing (u, c, z) as follows:¶
struct { uint8_t u[Ne]; uint8_t c[Ns]; uint8_t z[Ns]; } Proof;¶
The size of this structure is Np = Ne + 2*Ns bytes.¶
Verification of a proof (u, c, z), denoted SchnorrVerify(base, target, proof), works as follows:¶
c = HashToScalar(SerializeElement(base) || SerializeElement(target) || SerializeElement(mask), dst = "PrivateAccessTokensProof") expected_left = base * z expected_right = u + (target * c)¶
The proof is considered valid if expected_left is the same as expected_right.¶
This section describes various instantiations of this protocol to address use cases described in Section 1.1.¶
To instantiate this case, the site acts as an Origin and registers a "bounded token" policy with the Issuer. In this policy, the Issuer enforces a fixed number of tokens that it will allow a Client to request for a single ORIGIN_NAME.¶
Origins request tokens from Clients and, upon successful redemption, the Origin knows the Client was able to request a token for the given ORIGIN_NAME within its budget. Failure to present a token can be interpreted as a signal that the client's token budget was exceeded.¶
Clients can redeem a token from a specific challenge up to the max-age
in the challenge.
Servers can choose to issue many challenges in a single HTTP 401 response, providing the
client with many challenge nonces which can be used to redeem tokens over a longer period
of time.¶
To instantiate this use case, the Issuer has an issuing key pair per geographic region, i.e., each region has a unique policy key. When verifying the key for the Client request, the Mediator obtains the per-region key from the Issuer based on the Client's perceived location. During issuance, the Mediator checks that this key matches that of the Client's request. If it matches, the Mediator forwards the request to complete issuance. The number of key pairs is then the cross product of the number of Origins that require per-region keys and the number of regions.¶
During redemption, Clients present their geographic location to Origins in a "Sec-CH-Geohash" header. Origins use this to obtain the appropriate policy verification key. Origins request tokens from Clients and, upon successful redemption, the Origin knows the Client obtained a token for the given ORIGIN_NAME in the specified region.¶
To instantiate this case, the site acts as an Origin and registers an "unlimited token" policy with the Issuer. In this policy, the Issuer does not enforce any limit on the number of tokens a given user will obtain.¶
Origins request tokens from Clients and, upon successful redemption, the Origin knows the Client was able to request a token for the given ORIGIN_NAME tuple. As a result, the Origin knows this is an authentic client.¶
This section discusses security considerations for the protocol.¶
[OPEN ISSUE: discuss trust model]¶
The HTTPS connection between Client and Mediator is minimally Mediator-authenticated. Mediators can also require Client authentication if they wish to restrict Private Access Token proxying to trusted or otherwise authenticated Clients. Absent some form of Client authentication, Mediators can use other per-Client information for the client identifier mapping, such as IP addresses.¶
Requesting and verifying a Private Access Token is more expensive than checking an implicit signal, such as an IP address, especially since malicious clients can generate garbage Private Access Tokens and for Origins to work. However, similar DoS vectors already exist for Origins, e.g., at the underlying TLS layer.¶
An attacker that can act as an intermediate between Mediator and Issuer communication can influence or disrupt the ability for the Issuer to correctly rate-limit token issuance. All communication channels use server-authenticated HTTPS. Some connections, e.g., between a Mediator and an Issuer, require mutual authentication between both endpoints. Where appropriate, endpoints MAY use further enhancements such as TLS certificate pinning to mitigate the risk of channel compromise.¶
An attacker that can intermediate the channel between Client and Origin can observe a TokenChallenge, and can view a Token being presented for authentication to an Origin. Scoping the TokenChallenge nonce to the Client HTTP session prevents Tokens being collected in one session and then presented to the Origin in another. Note that an Origin cannot distinguish between a connection to a single Client and a connection to an attacker intermediating multiple Clients. Thus, it is possible for an attacker to collect and later present Tokens from multiple clients over the same Origin session.¶
Origins SHOULD only generate token challenges based on client action, such as when a user loads a website. Clients SHOULD ignore token challenges if an Origin tries to force the client to present tokens multiple times without any new client-initiated action. Failure to do so can allow malicious origins to track clients across contexts. Specifically, an origin can abuse per-user token limits for tracking by assigning each new client a random token count and observing whether or not the client can successfully redeem that many tokens in a given context. If any token redemption fails, then the origin learns information about how many tokens that client had previously been issued.¶
By rejecting repeated or duplicative challenges within a single context, the origin only learns a single bit of information: whether or not the client had any token quota left in the given policy window.¶
Private Access Tokens are defined in terms of a Client authenticating to an Origin, where the "origin" is used as defined in [RFC6454]. In order to limit cross-origin correlation, Clients MUST verify that the origin_name presented in the TokenChallenge structure (Section 4.1) matches the origin that is providing the HTTP authentication challenge, where the matching logic is defined for same-origin policies in [RFC6454]. Clients MAY further limit which authentication challenges they are willing to respond to, for example by only accepting challenges when the origin is a web site to which the user navigated.¶
Client activity could be linked if an Origin and Issuer collude to have unique keys targeted at specific Clients or sets of Clients.¶
To mitigate the risk of a targeted ISSUER_KEY, the Mediator can observe and validate the name_key_id presented by the Client to the Issuer. As described in Section 5, Mediators MUST validate that the name_key_id in the Client's AccessTokenRequest matches a known public key for the Issuer. The Mediator needs to support key rotation, but ought to disallow very rapid key changes, which could indicate that an Origin is colluding with an Issuer to try to rotate the key for each new Client in order to link the client activity.¶
To mitigate the risk of a targeted ORIGIN_TOKEN_KEY, the protocol expects that an Issuer has only a single valid public key for signing tokens at a time. The Client does not present the name_key_id of the token public key to the Issuer, but instead expects the Issuer to infer the correct key based on the information the Issuer knows, specifically the origin_name itself.¶
Collusion among the different entities in the PAT architecture can result in violation of the Client's privacy.¶
Issuers and Mediators should be run by mutually distinct organizations to limit information sharing. A single entity running an issuer and mediator for a single redemption can view the origins being accessed by a given client. Running the issuer and mediator in this 'single issuer/mediator' fashion reduces the privacy promises to those of the [I-D.ietf-privacypass-protocol]; see Appendix A for more discussion. This may be desirable for a redemption flow that is limited to specific issuers and mediators, but should be avoided where hiding origins from the mediator is desirable.¶
If a Mediator and Origin are able to collude, they can correlate a client's identity and origin access patterns through timestamp correlation. The timing of a request to an Origin and subsequent token issuance to a Mediator can reveal the Client identity (as known to the Mediator) to the Origin, especially if repeated over multiple accesses.¶
Issuers SHOULD generate a new (ORIGIN_TOKEN_KEY, ORIGIN_SECRET) regularly, and
SHOULD maintain old and new secrets to allow for graceful updates. The RECOMMENDED
rotation interval is two times the length of the policy window for that
information. During generation, issuers must ensure the token_key_id
(the 8-bit
prefix of SHA256(ORIGIN_TOKEN_KEY)) is different from all other token_key_id
values for that origin currently in rotation. One way to ensure this uniqueness
is via rejection sampling, where a new key is generated until its token_key_id
is
unique among all currently in rotation for the origin.¶
This document registers the "PrivateAccessToken" authentication scheme in the "Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry" established by [RFC7235].¶
Authentication Scheme Name: PrivateAccessToken¶
Pointer to specification text: Section 4.1 of this document¶
This document registers four new headers for use on the token issuance path in the "Permanent Message Header Field Names" <https://www.iana.org/assignments/message-headers>.¶
This specification defines the following protocol messages, along with their corresponding media types:¶
The definition for each media type is in the following subsections.¶
message¶
access-token-request¶
N/A¶
None¶
only "8bit" or "binary" is permitted¶
N/A¶
this specification¶
N/A¶
N/A¶
see Authors' Addresses section¶
COMMON¶
N/A¶
see Authors' Addresses section¶
IESG¶
message¶
access-token-response¶
N/A¶
None¶
only "8bit" or "binary" is permitted¶
N/A¶
this specification¶
N/A¶
N/A¶
see Authors' Addresses section¶
COMMON¶
N/A¶
see Authors' Addresses section¶
IESG¶
Private Access Tokens has many similarities to the existing Privacy Pass protocol ([I-D.ietf-privacypass-protocol]). Both protocols allow clients to redeem signed tokens while not allowing linking between token issuance and token redemption.¶
There are several important differences between the protocols, however:¶