RFC 9458

Oblivious HTTP

๐Ÿ“… published January 2024, from ietf

Oblivious HTTP ๐Ÿ’–

Okay bestie, this RFC is literally about making your web requests invisible to the server you're talking to ๐Ÿ™ˆโœจ It's giving privacy main character energy, and I am obsessed. Let's get into it ๐Ÿ’…

Quick title-block tea โ˜• This doc is RFC 9458, written by M. Thomson (Mozilla) and C. A. Wood (Cloudflare), published in January 2024. It's on the Standards Track, which means it's an actual Internet standard, not just a suggestion ๐Ÿ‘‘ It's tagged ISSN 2070-1721. It doesn't obsolete or update any other RFCs, so it's fresh and doesn't step on anyone's toes ๐Ÿฆ‹

Abstract

Okay so the abstract is the elevator pitch ๐ŸŽ€ It says Oblivious HTTP is a protocol for forwarding encrypted HTTP messages. The vibe? A client can send a bunch of requests to an origin server, and the server can't link them back to the client or realize they all came from the same person ๐Ÿ˜ญ The client also doesn't have to trust the middle nodes that forward the messages very much. Iconic ๐Ÿซถ

Status of This Memo

Literally just paperwork, but it's important paperwork ๐Ÿ“‹ This is an Internet Standards Track document from the IETF, and it has been reviewed publicly and approved by the IESG ๐ŸŒธ For more on Internet Standards, check out Section 2 of RFC 7841. The current status, errata, and feedback info all live at https://www.rfc-editor.org/info/rfc9458 ๐Ÿงš

Copyright (c) 2024 IETF Trust and the document authors, all rights reserved ๐Ÿ’œ This doc is subject to BCP 78 and the IETF Trust's Legal Provisions, which spell out your rights and restrictions, so read them carefully ๐Ÿ™ˆ Any Code Components pulled out of this document must include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty under that license ๐Ÿ’…

Table of Contents

1.  Introduction
2.  Overview
  2.1.  Applicability
  2.2.  Conventions and Definitions
3.  Key Configuration
  3.1.  Key Configuration Encoding
  3.2.  Key Configuration Media Type
4.  HPKE Encapsulation
  4.1.  Request Format
  4.2.  Response Format
  4.3.  Encapsulation of Requests
  4.4.  Encapsulation of Responses
  4.5.  Request and Response Media Types
  4.6.  Repurposing the Encapsulation Format
5.  HTTP Usage
  5.1.  Informational Responses
  5.2.  Errors
  5.3.  Signaling Key Configuration Problems
6.  Security Considerations
  6.1.  Client Responsibilities
  6.2.  Relay Responsibilities
    6.2.1.  Differential Treatment
    6.2.2.  Denial of Service
    6.2.3.  Traffic Analysis
  6.3.  Server Responsibilities
  6.4.  Key Management
  6.5.  Replay Attacks
    6.5.1.  Use of Date for Anti-replay
    6.5.2.  Correcting Clock Differences
  6.6.  Forward Secrecy
  6.7.  Post-Compromise Security
  6.8.  Client Clock Exposure
  6.9.  Media Type Security
  6.10. Separate Gateway and Target
7.  Privacy Considerations
8.  Operational and Deployment Considerations
  8.1.  Performance Overhead
  8.2.  Resource Mappings
  8.3.  Network Management
9.  IANA Considerations
  9.1.  application/ohttp-keys Media Type
  9.2.  message/ohttp-req Media Type
  9.3.  message/ohttp-res Media Type
  9.4.  Registration of "date" Problem Type
  9.5.  Registration of "ohttp-key" Problem Type
10. References
  10.1.  Normative References
  10.2.  Informative References
Appendix A.  Complete Example of a Request and Response
Acknowledgments
Authors' Addresses

1. Introduction ๐ŸŒˆ

Okay so, HTTP requests spill a lot of info about who you are to the server ๐Ÿ˜ญ The content of your request is totally in your control, but other stuff is way harder to hide and can still be used to identify you ๐Ÿ’€

Even if an IP address isn't tied to one specific person, all the requests coming from it can be lined up over time to build a profile of what you do ๐Ÿ™ˆ Connection reuse makes things faster, but it also lets servers link together every request that shares a connection. Not ideal!

The source IP of the connection is a big giveaway, and you don't have much control over it. Proxies can help hide your IP, but they have a messy trade-off ๐Ÿ“ Without TLS, the proxy can read everything you send. With TLS, you need a brand-new connection for every single request so the origin server can't correlate them, and that's a huge performance hit ๐Ÿ˜ฉ

So this document defines Oblivious HTTP ๐Ÿฆ‹ It's a protocol for encrypting HTTP messages and sending them from a client to a gateway. It uses a trusted relay so the metadata that could identify the client (like IP address and connection info) is mostly hidden, and it still keeps performance reasonable โœจ Here's what the doc covers:

  1. An algorithm for encapsulating binary HTTP messages [BINARY] using Hybrid Public Key Encryption (HPKE) [HPKE] to protect their contents,
  2. A method for forwarding Encapsulated Requests between Clients and an Oblivious Gateway Resource through a trusted Oblivious Relay Resource using HTTP, and
  3. Requirements for how the Oblivious Gateway Resource handles Encapsulated Requests and produces Encapsulated Responses for the Client.

Put together, encapsulation plus relaying means the Oblivious Gateway Resource never sees the Client's IP address, and the Oblivious Relay Resource never sees plaintext HTTP content ๐Ÿซถ Also, connections can be reused between the Client and the relay, and between the relay and the gateway, so this is faster than opening one connection per request ๐Ÿ”ฅ The relay is only trusted so much (see Section 6), but Clients still get the assurance that their requests aren't uniquely tied to them or linked to other requests ๐Ÿ‘‘

2. Overview ๐ŸŽ€

An Oblivious HTTP Client needs to know three things up front:

  • The identity of an Oblivious Gateway Resource. This might include some info about which Target Resources the gateway supports.
  • The details of an HPKE public key for the Oblivious Gateway Resource, including a key identifier and the HPKE algorithms used with that key.
  • The identity of an Oblivious Relay Resource that accepts relay requests carrying an Encapsulated Request as content, and forwards that content to one particular Oblivious Gateway Resource. Oblivious HTTP uses a one-to-one mapping between Oblivious Relay and Gateway Resources; see Section 8.2 for more details.

With that info, the Client can send HTTP requests to the Oblivious Gateway Resource to be forwarded to a Target Resource ๐Ÿ’– The gateway never learns the Client's IP address or any other identifying info from the transport layer, and it also can't tell which of its requests came from the same Client ๐Ÿ™ˆ

                                    .------------------------------.
+---------+       +----------+     |  +----------+    +----------+  |
| Client  |       | Relay    |     |  | Gateway  |    | Target   |  |
|         |       | Resource |     |  | Resource |    | Resource |  |
+----+----+       +----+-----+     |  +-----+----+    +----+-----+  |
     |                 |            `-------|--------------|-------'
     | Relay           |                    |              |
     | Request         |                    |              |
     | [+ Encapsulated |                    |              |
     |    Request ]    |                    |              |
     +---------------->| Gateway            |              |
     |                 | Request            |              |
     |                 | [+ Encapsulated    |              |
     |                 |    Request ]       |              |
     |                 +------------------->| Request      |
     |                 |                    +------------->|
     |                 |                    |              |
     |                 |                    |     Response |
     |                 |            Gateway |<-------------+
     |                 |           Response |              |
     |                 |    [+ Encapsulated |              |
     |                 |         Response ] |              |
     |           Relay |<-------------------+              |
     |        Response |                    |              |
     | [+ Encapsulated |                    |              |
     |      Response ] |                    |              |
     |<----------------+                    |              |
     |                 |                    |              |
                Figure 1: Overview of Oblivious HTTP

Here's how a request for a Target Resource gets sent to the Oblivious Gateway Resource, as shown in Figure 1 ๐Ÿฆ‹

  1. The Client builds an HTTP request for a Target Resource.
  2. The Client encodes that HTTP request in a binary HTTP message, then encapsulates it using HPKE and the process from Section 4.3.
  3. The Client sends a POST request to the Oblivious Relay Resource, with the Encapsulated Request as the content of that message.
  4. The Oblivious Relay Resource forwards this request to the Oblivious Gateway Resource.
  5. The Oblivious Gateway Resource receives the request and removes the HPKE protection to get back an HTTP request.

The Oblivious Gateway Resource then handles that HTTP request ๐Ÿ’… This usually means making an HTTP request using the content of the Encapsulated Request. Once the gateway has an HTTP response, these steps send it back to the Client:

  1. The Oblivious Gateway Resource encapsulates the HTTP response following the process in Section 4.4, and sends it in response to the request from the Oblivious Relay Resource.
  2. The Oblivious Relay Resource forwards this response to the Client.
  3. The Client removes the encapsulation to get the response to the original request.

Okay, real talk โš ๏ธ This interaction gives authentication and confidentiality between the Client and the Oblivious Gateway, but not between the Client and the Target Resource. The Target Resource is a separate HTTP resource from the gateway, but they're both logically under the gateway's control, since the Oblivious Gateway Resource can make up whatever responses come back from the Target Resource to the Client ๐Ÿ˜ญ That's the setup shown in Figure 1 ๐ŸŽ€

Part 1 of 9 done! ๐Ÿซถ More parts are coming, so stay tuned bestie ๐Ÿ’œ

2.1. Applicability

okay so Oblivious HTTP is NOT for everything ๐Ÿ™ˆ it needs explicit support from a willing Oblivious Relay Resource AND an Oblivious Gateway Resource, so it's kinda picky about who it hangs out with. that really limits it for generic apps, see Section 6.3 for the deets ๐Ÿ’…

a lot of HTTP stuff depends on remembering things between requests, like cookies [COOKIES], authentication (Section 11 of [HTTP]), or even alternative services [RFC7838]. Oblivious HTTP cuts the linkage at the transport layer, which only helps apps that DON'T carry state between requests. so if your app is keeping a memory of you, this isn't the drip for it ๐Ÿ’€

it's mostly useful when the privacy risk of a server treating your requests statefully is big enough to justify deploying it. it's simpler and cheaper than heavier systems like Prio [PRIO] or Tor [DMS2004], which can give stronger guarantees but cost more to run ๐Ÿฆ‹

and ngl, it's more expensive than just connecting straight to a server. some costs, like connection setup, can get amortized, but there are a few ways it's pricier than a direct request:

  • Each request requires at least two regular HTTP requests, which could increase latency.
  • Each request is expanded in size with additional HTTP fields, encryption-related metadata, and Authenticated Encryption with Associated Data (AEAD) expansion.
  • Deriving cryptographic keys and applying them for request and response protection takes non-negligible computational resources.

so when is the extra cost worth it? when preventing requests from being linked is the whole point. some examples:

DNS queries: DNS queries made to a recursive resolver reveal information about the requester, especially if they get linked to other queries. big mood ๐Ÿ‘€

Telemetry submission: Applications that submit reports about their usage to their developers might use Oblivious HTTP for some types of moderately sensitive data. โœจ

these are all requests where there's info that, if it got tied to your identity, could tell a server something about you even if your identity were only pseudonymous. other examples are submitting anonymous surveys, making search queries, or requesting location-specific content (like grabbing tiles for a map display) ๐Ÿ—บ๏ธ

โš ๏ธ on top of all this, Section 6 covers the operational constraints you NEED to follow to actually get the goals of the protocol. no skipping!

2.2. Conventions and Definitions

okay the BCP 14 key words. the words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are only read as BCP 14 [RFC2119] [RFC8174] when they show up in ALL CAPS like that. lowercase ones are just regular words ๐Ÿงš

this doc uses terminology from [HTTP] and defines a few terms too:

Client: A Client originates Oblivious HTTP requests. A Client is also an HTTP client in two ways: for the Target Resource and for the Oblivious Relay Resource. but when we mean the HTTP definition of client (Section 3.3 of [HTTP]), we say "HTTP client" instead; see Section 5. ๐Ÿ’œ

Encapsulated Request: An HTTP request that is encapsulated in an HPKE-encrypted message; see Section 4.3. ๐ŸŽ€

Encapsulated Response: An HTTP response that is encapsulated in an HPKE-encrypted message; see Section 4.4. ๐ŸŽ€

Oblivious Relay Resource: An intermediary that forwards Encapsulated Requests and Responses between Clients and a single Oblivious Gateway Resource. in context, you can just call it a "relay" ๐Ÿฆ‹

Oblivious Gateway Resource: A resource that can receive an Encapsulated Request, extract the contents of that request, forward it to a Target Resource, receive a response, encapsulate that response, and then return the resulting Encapsulated Response. in context, just say "gateway" ๐ŸŒธ

Target Resource: The resource that is the target of an Encapsulated Request. this resource logically handles only regular HTTP requests and responses, so it might be totally unaware that Oblivious HTTP is being used to reach it ๐Ÿซถ

this document includes pseudocode that uses the functions and conventions defined in [HPKE].

encoding an integer into a sequence of bytes in network byte order is described with the function encode(n, v), where n is the number of bytes and v is the integer value. ASCII [ASCII] encoding of a string s is indicated with the function encode_str(s). ๐Ÿ”ฅ

formats are described using notation from Section 1.3 of [QUIC]. there's also an extension to that notation that expresses the number of bits in a field using a simple mathematical function. math girl era ๐Ÿงฎ

3. Key Configuration โœจ

okay so a Client needs to get info about the key configuration of the Oblivious Gateway Resource before it can send Encapsulated Requests. and to make sure Clients don't encapsulate messages that other entities can intercept, the key configuration MUST be authenticated and have integrity protection. non-negotiable bestie ๐Ÿ”

this doc doesn't define HOW that acquisition happens. BUT to help with interop, it DOES specify a format for the keys. that way different Client implementations can be configured the same way, and key configurations can be advertised in a consistent format ๐Ÿ’… this format might be used, for example, with HTTPS, as part of a system for configuring or discovering key configurations. just note that such a system needs to think about the potential for key configuration to be used to compromise Client privacy; see Section 7. ๐Ÿ‘€

a Client might have multiple key configurations to pick from when encapsulating a request. Clients are responsible for picking a preferred key configuration from the ones they support. and they need to consider both the Key Encapsulation Method (KEM) and the combinations of the Key Derivation Function (KDF) and AEAD in that decision ๐Ÿง 

3.1. Key Configuration Encoding

a single key configuration has a key identifier, a public key, an identifier for the KEM that the public key uses, and a set of HPKE symmetric algorithms. each symmetric algorithm is an identifier for a KDF plus an identifier for an AEAD. โœจ

here's Figure 2 showing a single key configuration:

HPKE Symmetric Algorithms {
  HPKE KDF ID (16),
  HPKE AEAD ID (16),
}

Key Config {
  Key Identifier (8),
  HPKE KEM ID (16),
  HPKE Public Key (Npk * 8),
  HPKE Symmetric Algorithms Length (16) = 4..65532,
  HPKE Symmetric Algorithms (32) ...,
}

Figure 2: A Single Key Configuration

so a key configuration has these fields:

  • Key Identifier: An 8-bit value that identifies the key used by the Oblivious Gateway Resource. ๐Ÿ”‘
  • HPKE KEM ID: A 16-bit value that identifies the KEM used for the identified key as defined in Section 7.1 of [HPKE] or the "HPKE KEM Identifiers" registry https://www.iana.org/assignments/hpke.
  • HPKE Public Key: The public key used by the gateway. The length of the public key is Npk, which is determined by the choice of HPKE KEM as defined in Section 4 of [HPKE]. ๐ŸŒˆ
  • HPKE Symmetric Algorithms Length: A 16-bit integer in network byte order that encodes the length, in bytes, of the HPKE Symmetric Algorithms field that follows. ๐Ÿ“
  • HPKE Symmetric Algorithms: One or more pairs of identifiers for the different combinations of HPKE KDF and AEAD that the Oblivious Gateway Resource supports:

3.2. Key Configuration Media Type

the "application/ohttp-keys" format is a media type that identifies a serialized collection of key configurations. its content is one or more key configuration encodings (see Section 3.1). each encoded configuration gets prefixed with a 2-byte integer in network byte order that says how long the key configuration is, in bytes. the length-prefixed encodings are concatenated into a list. see Section 9.1 for the definition of the media type ๐ŸŽ€

evolution of the key configuration format works by defining NEW formats, identified by NEW media types. very future-proof, very iconic ๐Ÿš€

now here's the part to really hear: a Client that receives an "application/ohttp-keys" object with encoding errors might still be able to recover one or more key configurations. BUT differences in how key configurations get recovered might be exploited to segregate Clients, so Clients MUST discard incorrectly encoded key configuration collections. no recovering, no cap ๐Ÿšซ

4. HPKE Encapsulation ๐Ÿฆ‹

this doc defines how a binary-encoded HTTP message [BINARY] gets encapsulated using HPKE [HPKE]. there are separate media types to tell request and response messages apart:

  • An Encapsulated Request format defined in Section 4.1 is identified by the "message/ohttp-req" media type (Section 9.2).
  • An Encapsulated Response format defined in Section 4.2 is identified by the "message/ohttp-res" media type (Section 9.3).

alternative encapsulations or message formats are indicated using the media type; see Sections 4.5 and 4.6. ๐ŸŒธ

4.1. Request Format

a message in "message/ohttp-req" format protects a binary HTTP request message; see Figure 3. ๐Ÿ”

here's the plaintext Request structure, Figure 3:

Request {
  Binary HTTP Message (..),
}

Figure 3: Plaintext Request Structure

this plaintext Request structure gets encapsulated into a "message/ohttp-req" message by generating an Encapsulated Request. an Encapsulated Request is made of a key identifier, HPKE parameters for the chosen KEM, KDF, and AEAD, the encapsulated KEM shared secret (or enc), and an HPKE-protected binary HTTP request message. โœจ

Figure 4 shows an Encapsulated Request, and Section 4.3 walks through how to construct and process one:

Encapsulated Request {
  Key Identifier (8),
  HPKE KEM ID (16),
  HPKE KDF ID (16),
  HPKE AEAD ID (16),
  Encapsulated KEM Shared Secret (8 * Nenc),
  HPKE-Protected Request (..),
}

Figure 4: Encapsulated Request

so an Encapsulated Request has a Key Identifier, an HPKE KEM ID, an HPKE KDF ID, an HPKE AEAD ID, an Encapsulated KEM Shared Secret, and an HPKE-Protected Request. the Key Identifier, HPKE KEM ID, HPKE KDF ID, and HPKE AEAD ID fields are defined in Section 3.1. the Encapsulated KEM Shared Secret is the output of the Encap() function for the KEM, which is Nenc bytes long, as defined in Section 4 of [HPKE]. ๐Ÿงš๐Ÿ’œ

4.2. Response Format ๐ŸŽ€

Okay so a message in message/ohttp-res format is what protects a binary HTTP response message ๐Ÿ’Œ (see Figure 5). The plaintext response is just a Binary HTTP Message wrapped up nice and neat:

Response {
  Binary HTTP Message (..),
}

Figure 5: Plaintext Response Structure

Next, we take that plaintext Response structure and encapsulate it into a message/ohttp-res message by generating an Encapsulated Response ๐Ÿฆ‹. An Encapsulated Response has two parts: a nonce and the AEAD-protected binary HTTP response message. It's giving layers, bestie ๐Ÿง…

Figure 6 shows what that looks like. Section 4.4 walks you through how to build and process an Encapsulated Response ๐Ÿงš

Encapsulated Response {
  Nonce (8 * max(Nn, Nk)),
  AEAD-Protected Response (..),
}

Figure 6: Encapsulated Response

So yeah, an Encapsulated Response contains a Nonce and an AEAD-Protected Response ๐Ÿ’œ The Nonce field is either Nn or Nk bytes long, whichever one is bigger (main character energy for the bigger number ๐Ÿ‘‘). Nn and Nk come from the parameters of the AEAD used in HPKE. You can find that in Section 7.3 of [HPKE] or in the "HPKE AEAD Identifiers" IANA registry at <https://www.iana.org/assignments/hpke>. And just so we're clear, Nn is the size of the AEAD nonce in bytes and Nk is the size of the AEAD key in bytes ๐Ÿ“

4.3. Encapsulation of Requests ๐Ÿ’…

Clients encapsulate a request, which we call request, using values from a key configuration ๐ŸŽ€ They grab:

  • the key identifier from the configuration (key_id) along with the corresponding KEM identified by kem_id,
  • the public key from the configuration (pkR), and
  • a combo of KDF (identified by kdf_id) and AEAD (identified by aead_id) that the Client picks from the ones in the key configuration.

Then the Client builds an Encapsulated Request, enc_request, from a binary-encoded HTTP request [BINARY] (request), like this:

  1. Build a message header (hdr) by concatenating the values of key_id, kem_id, kdf_id, and aead_id. That's one 8-bit integer and three 16-bit integers, in that order, each in network byte order ๐Ÿงฉ
  2. Build a sequence of bytes (info) by concatenating the ASCII-encoded string "message/bhttp request", a zero byte, and the header. Heads up: Section 4.6 talks about how other message formats might use a different info value ๐ŸŒธ
  3. Create a sending HPKE context by invoking SetupBaseS() (Section 5.1.1 of [HPKE]) with the receiver's public key pkR and info. This gives you the context sctxt and an encapsulation key enc ๐Ÿ”
  4. Encrypt request by invoking the Seal() method on sctxt (Section 5.2 of [HPKE]) with empty associated data aad. That gives you the ciphertext ct โœจ
  5. Concatenate hdr, enc, and ct. Ta-da, that's your Encapsulated Request (enc_request) ๐ŸŽ‰

Note that enc is a fixed length, so there's zero ambiguity when parsing this structure. No cap ๐Ÿง 

Here's the same procedure in pseudocode:

hdr = concat(encode(1, key_id),
             encode(2, kem_id),
             encode(2, kdf_id),
             encode(2, aead_id))
info = concat(encode_str("message/bhttp request"),
              encode(1, 0),
              hdr)
enc, sctxt = SetupBaseS(pkR, info)
ct = sctxt.Seal("", request)
enc_request = concat(hdr, enc, ct)

Now, an Oblivious Gateway Resource undoes all of this to decrypt an Encapsulated Request ๐Ÿ”“ To decapsulate an Encapsulated Request, enc_request, it does the following:

  1. Parse enc_request into key_id, kem_id, kdf_id, aead_id, enc, and ct (the pseudocode calls this function parse()). Then the Oblivious Gateway Resource can find the HPKE private key, skR, that matches key_id ๐Ÿ•ต๏ธโ€โ™€๏ธ
    • a. If key_id doesn't identify a key that matches the type of kem_id, the Oblivious Gateway Resource returns an error. Rude ๐Ÿ™…โ€โ™€๏ธ
    • b. If kdf_id and aead_id identify a combo of KDF and AEAD that the Oblivious Gateway Resource isn't willing to use with skR, the Oblivious Gateway Resource returns an error.
  2. Build a sequence of bytes (info) by concatenating the ASCII-encoded string "message/bhttp request", a zero byte, key_id as an 8-bit integer, plus kem_id, kdf_id, and aead_id as three 16-bit integers ๐Ÿง
  3. Create a receiving HPKE context, rctxt, by invoking SetupBaseR() (Section 5.1.1 of [HPKE]) with skR, enc, and info ๐ŸŒท
  4. Decrypt ct by invoking the Open() method on rctxt (Section 5.2 of [HPKE]) with an empty associated data aad. That gives you request, or an error if decryption fails. If decryption fails, the Oblivious Gateway Resource returns an error ๐Ÿ’€

And the pseudocode for that:

key_id, kem_id, kdf_id, aead_id, enc, ct = parse(enc_request)
info = concat(encode_str("message/bhttp request"),
              encode(1, 0),
              encode(1, key_id),
              encode(2, kem_id),
              encode(2, kdf_id),
              encode(2, aead_id))
rctxt = SetupBaseR(enc, skR, info)
request, error = rctxt.Open("", ct)

One more thing: the Oblivious Gateway Resource keeps the HPKE context, rctxt, around so it can encapsulate a response later ๐Ÿฆ‹

4.4. Encapsulation of Responses ๐ŸŒˆ

Oblivious Gateway Resources make an Encapsulated Response, enc_response, from a binary-encoded HTTP response [BINARY] (response). They use the HPKE receiver context (rctxt) as the HPKE context (context), and here's how:

  1. Export a secret (secret) from context, using the string "message/bhttp response" as the exporter_context parameter to context.Export. See Section 5.3 of [HPKE]. The length of this secret is max(Nn, Nk), where Nn and Nk are the lengths of the AEAD key and nonce tied to context. Note: Section 4.6 talks about how other message formats might use a different context value ๐Ÿซถ
  2. Generate a random value of max(Nn, Nk) bytes, called response_nonce ๐ŸŽฒ
  3. Extract a pseudorandom key (prk) using the Extract function from the KDF algorithm associated with context. The ikm input is secret, and the salt input is enc (from enc_request) concatenated with response_nonce ๐Ÿง‚
  4. Use the Expand function from that same KDF to create an AEAD key, key, of length Nk (the key length used by the AEAD associated with context). Making aead_key uses the label "key" ๐Ÿ”‘
  5. Use the same Expand function to create a nonce, nonce, of length Nn (the nonce length used by the AEAD). Making aead_nonce uses the label "nonce" ๐Ÿช„
  6. Encrypt response by passing aead_key, aead_nonce, an empty aad, and a pt input of response to the AEAD function Seal. That gives you ct ๐Ÿ”
  7. Concatenate response_nonce and ct to get the Encapsulated Response, enc_response. response_nonce is a fixed length too, so there's no ambiguity parsing either response_nonce or ct ๐Ÿ’–

Pseudocode, because you know I love it ๐Ÿค“:

secret = context.Export("message/bhttp response", max(Nn, Nk))
response_nonce = random(max(Nn, Nk))
salt = concat(enc, response_nonce)
prk = Extract(salt, secret)
aead_key = Expand(prk, "key", Nk)
aead_nonce = Expand(prk, "nonce", Nn)
ct = Seal(aead_key, aead_nonce, "", response)
enc_response = concat(response_nonce, ct)

Clients decrypt an Encapsulated Response by running the process backwards ๐Ÿ”„ They first parse enc_response into response_nonce and ct. Then they follow the same steps to derive aead_key and aead_nonce, using their sending HPKE context, sctxt, as the HPKE context, context.

The Client uses those values to decrypt ct with the AEAD function Open. Decrypting might fail, which looks like this:

response, error = Open(aead_key, aead_nonce, "", ct)

4.5. Request and Response Media Types ๐Ÿ“ฌ

Media types are how we identify Encapsulated Requests and Responses. Check out Sections 9.2 and 9.3 for the definitions of these media types ๐Ÿ’Œ

Evolving the format of Encapsulated Requests and Responses is totally supported. You just define new formats and give them new media types ๐ŸŒฑ New media types might use a similar encapsulation with a different HTTP message format than the one in [BINARY]. For guidance on reusing this encapsulation method, see Section 4.6. Or, if you're feeling extra, you can define a brand new encapsulation method altogether โœจ

4.6. Repurposing the Encapsulation Format ๐Ÿ”

The encrypted payload of an Oblivious HTTP request and response is a binary HTTP message [BINARY]. The Client and Oblivious Gateway Resource agree on this payload type by putting the media type "message/bhttp" in the HPKE info string for requests and in the HPKE export context string for responses ๐Ÿค

Future specifications can repurpose the encapsulation mechanism described in this document ๐Ÿฆ‹ But they need to define a new media type to do it. The encapsulation process for that content type can follow the same steps, just using new constant strings for the HPKE info and exporter context inputs ๐Ÿงƒ

For example, a future spec might encapsulate DNS messages, which use the "application/dns-message" media type [RFC8484]. To make a new encrypted media type, a spec could use the string "application/dns-message request" (plus a zero byte and the header for the full value) for request encryption, and the string "application/dns-message response" for response encryption ๐Ÿ’œ

5. HTTP Usage โœจ

Okay so a Client talks to the Oblivious Relay Resource by building an Encapsulated Request ๐Ÿ’Œ and putting it in the content of a POST request to that Oblivious Relay Resource. The request only needs the stuff that actually carries the Encapsulated Request: a method of POST, a target URI of the Oblivious Relay Resource, a header field with the content type (see Section 9.2), and the Encapsulated Request as the request content. Clients MAY add extra fields to that request to the Oblivious Relay Resource ๐ŸŽ€. BUT those extra fields MUST be totally independent of the Encapsulated Request, and they MUST be fields the Oblivious Relay Resource will strip off before it forwards the Encapsulated Request on to the target, like the Connection or Proxy-Authorization header fields [HTTP]. No sneaking stuff in, periodt ๐Ÿ™…โ€โ™€๏ธ.

The Client role here acts as an HTTP client with respect to both the Oblivious Relay Resource and the Target Resource ๐Ÿ’…. The request the Client makes to the Target Resource is kinda different from the usual HTTP vibes about connections (see Section 3.3 of [HTTP]), because the request and response are encrypted instead of just sent over a connection ๐Ÿ”. The Oblivious Relay Resource and the Oblivious Gateway Resource also act as HTTP clients, toward the Oblivious Gateway Resource and the Target Resource respectively ๐Ÿฆ‹.

To hit the privacy and security goals of the protocol, a Client also has to follow the guidance in Section 6.1 ๐Ÿซถ.

The Oblivious Relay Resource talks to the Oblivious Gateway Resource as an HTTP client too. It builds a request with the same restrictions as the Client request, except the target URI is now the Oblivious Gateway Resource ๐ŸŒธ. The content gets copied straight over from the Client. An Oblivious Relay Resource MAY reject requests that are obviously invalid, like a request with no content ๐Ÿ™ˆ. It MUST NOT add information to the request unless the Client is aware of the type of information that might get added. See Section 6.2 for more on relay responsibilities ๐Ÿ’œ.

When a response comes back from the Oblivious Gateway Resource, the Oblivious Relay Resource forwards it following the rules of an HTTP proxy; see Section 7.6 of [HTTP] โœจ. If there's a timeout or an error, the Oblivious Relay Resource can generate a response with an appropriate status code ๐Ÿ“.

To hit the privacy and security goals, the Oblivious Relay Resource also needs to follow the guidance in Section 6.2 ๐Ÿ”ฅ.

An Oblivious Gateway Resource acts as a gateway for requests to the Target Resource (see Section 7.6 of [HTTP]) ๐Ÿงš. The one exception is that anything it forwards in a response MUST be encapsulated, unless it's responding to errors that don't relate to processing the contents of the Encapsulated Request. See Section 5.2 for those ๐Ÿ‘‘.

If an Oblivious Gateway Resource gets any response from the Target Resource, it sends back a single 200 response containing the Encapsulated Response ๐ŸŽ. Just like the Client's request, this response MUST only contain the fields needed to carry the Encapsulated Response: a 200 status code, a header field indicating the content type, and the Encapsulated Response as the response content ๐Ÿ’–. Same as with requests, additional fields MAY be used to convey information that doesn't reveal anything about the Encapsulated Response ๐ŸŒˆ.

An Oblivious Gateway Resource that doesn't get a response can generate its own response with an appropriate error status code (like 504 (Gateway Timeout); see Section 15.6.5 of [HTTP]) ๐Ÿ˜ญ. That response then gets encapsulated the same way as a successful one โœจ.

To hit the privacy and security goals, the Oblivious Gateway Resource also needs to follow the guidance in Section 6.3 ๐Ÿ’….

5.1. Informational Responses

Okay, real talk: this encapsulation does NOT allow progressive processing of responses ๐Ÿ™…โ€โ™€๏ธ. Even though the binary HTTP response format can include informational (1xx) status codes, the AEAD encapsulation can't be removed until the entire message has arrived ๐Ÿ”’.

This means the Expect header field with 100-continue (see Section 10.1.1 of [HTTP]) can't be used ๐Ÿ’€. Clients MUST NOT construct a request that includes a 100-continue expectation, and the Oblivious Gateway Resource MUST generate an error if it does receive a 100-continue expectation ๐Ÿšซ.

5.2. Errors

Here's the rule: a server that receives an invalid message for any reason MUST generate an HTTP response with a 4xx status code ๐Ÿ™…โ€โ™€๏ธ.

Errors detected by the Oblivious Relay Resource, and errors the Oblivious Gateway Resource detects before it removes protection (including when it can't remove the encapsulation for any reason), result in the status code being sent unprotected, as the response to the POST request made to that resource ๐Ÿซฃ.

Errors the Oblivious Gateway Resource detects after it has successfully removed the encapsulation, and errors detected by the Target Resource, MUST be sent in an Encapsulated Response ๐ŸŽ€. This might happen because the Encapsulated Request is malformed, or because the Target Resource doesn't produce a response ๐Ÿ™ƒ. Either way, the Oblivious Gateway Resource can generate a response with an appropriate error status code (like 400 (Bad Request) or 504 (Gateway Timeout); see Sections 15.5.1 and 15.6.5 of [HTTP], respectively) ๐Ÿ“. That response gets encapsulated the same way as a successful response ๐Ÿ’œ.

Errors in the encapsulation of requests mean responses can't be encapsulated ๐Ÿ˜ฌ. This includes cases where the key configuration is incorrect or outdated ๐Ÿ”‘. The Oblivious Gateway Resource can generate and send a response with a 4xx status code to the Oblivious Relay Resource. That response MAY be forwarded to the Client, or the Oblivious Relay Resource can treat it as a failure ๐ŸŒธ. If a Client gets a response that is not an Encapsulated Response, it could mean the Client configuration used to build the request is incorrect or out of date โญ.

5.3. Signaling Key Configuration Problems

The problem type [PROBLEM] of https://iana.org/assignments/http-problem-types#ohttp-key is defined in this section ๐Ÿงš. An Oblivious Gateway Resource MAY use this problem type in a response to say that an Encapsulated Request used an outdated or incorrect key configuration ๐ŸŽ€.

Figure 7 shows an example response in HTTP/1.1 format ๐Ÿ‘€:

HTTP/1.1 400 Bad Request
Date: Mon, 07 Feb 2022 00:28:05 GMT
Content-Type: application/problem+json
Content-Length: 106

{"type":"https://iana.org/assignments/http-problem-types#ohttp-key",
"title": "key identifier unknown"}

Figure 7: Example Rejection of Key Configuration

Heads up ๐Ÿšจ: this response can't be encrypted, so it might not reach the Client. A Client can't rely on the Oblivious Gateway Resource using this problem type ๐Ÿ˜ญ. A Client might also be configured to ignore responses that aren't encapsulated, since they might be watched or changed by an Oblivious Relay Resource ๐Ÿ‘๏ธ. One way a Client might manage the risk of an outdated key configuration is a heuristic: it periodically refreshes its key configuration if it gets a response with an error status code that wasn't encapsulated ๐Ÿ”„.


6. Security Considerations ๐Ÿ”

Alright, the vibe here: a Client wants to make a request to an Oblivious Gateway Resource that gets forwarded to a Target Resource ๐Ÿฆ‹. The Client wants to do this without linking that request to either of the following:

  • The identity at the network and transport layer of the Client (that is, the Client IP address and TCP or UDP port number the Client uses to create a connection) ๐ŸŒ.
  • Any other request the Client might have made in the past or might make in the future ๐Ÿ•ฐ๏ธ.

To make sure of that, the Client picks a relay (the one serving the Oblivious Relay Resource) that it trusts to protect this information by forwarding the Encapsulated Request and Response without passing it to the server (the one serving the Oblivious Gateway Resource) ๐Ÿ›ก๏ธ.

In this section, we're looking at a deployment with three entities ๐Ÿ‘ฏ:

  • A Client makes requests and receives responses ๐Ÿ’Œ.
  • A relay operates the Oblivious Relay Resource ๐Ÿ”.
  • A server operates both the Oblivious Gateway Resource and the Target Resource ๐Ÿ–ฅ๏ธ.

Section 6.10 talks about the security implications when different servers operate the Oblivious Gateway Resource and the Target Resource ๐Ÿ‘€.

Requests from the Client to the Oblivious Relay Resource, and from the Oblivious Relay Resource to the Oblivious Gateway Resource, MUST use HTTPS so that unlinkability holds even when a network observer is watching ๐Ÿ”’.

To hit the stated privacy goals, the Oblivious Relay Resource can't be run by the same entity as the Oblivious Gateway Resource ๐Ÿ™…โ€โ™€๏ธ. That said, colocating the Oblivious Gateway Resource and the Target Resource makes the interactions between them simpler, and it doesn't affect Client privacy ๐Ÿ’–.

Because of this setup, Oblivious HTTP prevents the linkability described above. Informally, that means ๐Ÿ’œ:

  1. Requests and responses are known only to Clients and Oblivious Gateway Resources. In particular, the Oblivious Relay Resource knows the origin and destination of an Encapsulated Request and Response, yet it doesn't know the decrypted contents ๐Ÿค. Likewise, Oblivious Gateway Resources learn only the Oblivious Relay Resource and the decrypted request. No entity other than the Client can see the plaintext request and response and attribute them to the Client ๐Ÿซข.
  2. Oblivious Gateway Resources, and therefore Target Resources, can't link requests from the same Client unless there are unique per-Client keys ๐Ÿ”‘.

Traffic analysis that could affect these properties is outside the scope of this document; see Section 6.2.3 ๐Ÿ‘€.

A formal analysis of Oblivious HTTP is in [OHTTP-ANALYSIS] ๐ŸŽ“โœจ.

6.1. Client Responsibilities โœจ

okay so, Clients don't authenticate the Target Resource when they use Oblivious HTTP ๐Ÿ™ˆ which means Clients MUST have some mechanism to authorize an Oblivious Gateway Resource for use with a Target Resource. One possible means of authorization is an allowlist. This makes sure Oblivious Gateway Resources are not misused to forward traffic to arbitrary Target Resources ๐Ÿ’…. Section 6.3 describes similar responsibilities that apply to Oblivious Gateway Resources too.

Clients MUST ensure that the key configuration they select for generating Encapsulated Requests is integrity protected and authenticated, so that it can be attributed to the Oblivious Gateway Resource ๐Ÿ”. See Section 3.

Since Clients connect directly to the Oblivious Relay Resource instead of the Target Resource, app configs where Clients make policy decisions about target connections (like certificate pinning) are incompatible with Oblivious HTTP ๐Ÿ˜ญ. In those cases, alternative technologies such as HTTP CONNECT (Section 9.3.6 of [HTTP]) can be used. Applications could implement related policies on key configurations and relay connections, though these might not provide the same properties as policies enforced directly on target connections. Instead, when this difference matters, applications can connect directly to the target, at the cost of either privacy or performance.

Clients can't carry connection-level state between requests, since they only establish direct connections to the relay responsible for the Oblivious Relay Resource ๐Ÿฆ‹. However, the content of requests might be used by a server to correlate requests. Cookies [COOKIES] are the most obvious feature that could be used to correlate requests, but any identity information and authentication credentials might have the same effect. Clients also need to treat information learned from responses with similar care when constructing subsequent requests, which includes the identity of resources.

Clients MUST generate a new HPKE context for every request, using a good source of entropy [RANDOM] for generating keys ๐ŸŒˆ. Key reuse not only risks requests being linked, but could also expose request and response contents to the relay. Not it, bestie ๐Ÿ™…โ€โ™€๏ธ.

The request the Client sends to the Oblivious Relay Resource only requires minimal information; see Section 5. The request that carries the Encapsulated Request and is sent to the Oblivious Relay Resource MUST NOT include identifying information, unless the Client can trust that this information is removed by the relay. A Client MAY include information only for the Oblivious Relay Resource in header fields identified by the Connection header field, if it trusts the relay to remove these, as required by Section 7.6.1 of [HTTP]. The Client needs to trust that the relay does not replicate the source addressing information in the request it forwards ๐ŸŽ€.

Clients rely on the Oblivious Relay Resource to forward Encapsulated Requests and Responses. However, the relay can only refuse to forward messages. It can't inspect or modify the contents of Encapsulated Requests or Responses ๐Ÿ’œ.

6.2. Relay Responsibilities ๐ŸŒธ

The relay that serves the Oblivious Relay Resource has a very simple job. For each request it receives, it makes a request of the Oblivious Gateway Resource that includes the same content. When it receives a response, it sends a response to the Client that includes the content of the response from the Oblivious Gateway Resource.

When forwarding a request, the relay MUST follow the forwarding rules in Section 7.6 of [HTTP]. A generic HTTP intermediary implementation is suitable for serving an Oblivious Relay Resource, but extra care is needed to make sure Client privacy is maintained ๐Ÿงš.

Firstly, a generic implementation will forward unknown fields. For Oblivious HTTP, an Oblivious Relay Resource SHOULD NOT forward unknown fields. Though Clients aren't expected to include fields that might contain identifying information, removing unknown fields removes this privacy risk ๐Ÿซถ.

Secondly, generic implementations are often configured to augment requests with information about the Client, such as the Via field or the Forwarded field [FORWARDED]. A relay MUST NOT add information when forwarding requests that might be used to identify Clients, except for information that a Client is aware of; see Section 6.2.1 ๐Ÿ‘€.

Finally, a relay can also generate responses. It's assumed not to be able to examine the content of a request (other than to observe the choice of key identifier, KDF, and AEAD), so it's also assumed that it cannot generate an Encapsulated Response ๐Ÿ™ƒ.

6.2.1. Differential Treatment ๐Ÿ“

A relay MAY add information to requests if the Client is aware of the nature of the information that could be added. Any addition MUST NOT include information that uniquely and permanently identifies the Client, including any pseudonymous identifier. Information added by the relay -- beyond what is already revealed through Encapsulated Requests from Clients -- can reduce the size of the anonymity set of Clients at a gateway.

A Client doesn't need to know the exact value added for each request, but it does need to know the range of possible values the relay might use. How a Client might learn about added information is not defined in this document.

Moreover, relays MAY apply differential treatment to Clients that engage in abusive behavior, e.g., by sending too many requests compared to other Clients, or in response to rate limits signaled from the gateway. Any such differential treatment can reveal information to the gateway that would not be revealed otherwise, and therefore reduce the size of the anonymity set of Clients using a gateway. For example, if a relay chooses to rate limit or block an abusive Client, that means any Client requests that aren't treated this way are known to be non-abusive by the gateway. Clients need to consider the likelihood of such differential treatment and the privacy risks when using a relay ๐Ÿšจ.

Some patterns of abuse can't be detected without access to the request that is made to the target. This means only the gateway or the target is in a position to identify abuse. A gateway MAY send signals toward the relay to provide feedback about specific requests. For example, a gateway could respond differently to requests it cannot decapsulate, as mentioned in Section 5.2. A relay that acts on this feedback could -- either inadvertently or by design -- lead to Client deanonymization ๐Ÿ˜ฑ.

6.2.2. Denial of Service ๐Ÿ›ก๏ธ

As there are privacy benefits from having a large rate of requests forwarded by the same relay (see Section 6.2.3), servers that operate the Oblivious Gateway Resource might need an arrangement with Oblivious Relay Resources. This arrangement might be necessary to prevent the large volume of requests from being classified as an attack by the server.

If a server accepts a larger volume of requests from a relay, it needs to trust that the relay does not allow abusive levels of request volumes from Clients. That is, if a server allows requests from the relay to be exempt from rate limits, the server might want to ensure that the relay applies a rate-limiting policy that is acceptable to the server ๐Ÿ’….

Servers that enter into an agreement with a relay that enables a higher request rate might choose to authenticate the relay to enable the higher rate ๐ŸŽ€.

6.2.3. Traffic Analysis ๐Ÿ•ต๏ธโ€โ™€๏ธ

Using HTTPS protects information about which resources are the subject of a request, and it prevents a network observer from being able to trivially correlate messages on either side of a relay. However, using HTTPS does not prevent traffic analysis by such network observers ๐Ÿ‘๏ธ.

The time at which Encapsulated Request or Response messages are sent can reveal information to a network observer. Though messages exchanged between the Oblivious Relay Resource and the Oblivious Gateway Resource might be sent over a single connection, traffic analysis could still be used to match messages that are forwarded by the relay ๐Ÿง.

A relay could, as part of its function, delay requests before forwarding them. Delays might increase the anonymity set into which each request is attributed. Any delay also increases the time a Client waits for a response, so delays SHOULD only be added with the consent -- or at least awareness -- of Clients โณ.

A relay that forwards large volumes of exchanges can provide better privacy by providing larger sets of messages that need to be matched ๐ŸŒˆ.

Traffic analysis isn't restricted to network observers. A malicious Oblivious Relay Resource could use traffic analysis to learn information about otherwise encrypted requests and responses relayed between Clients and gateways. An Oblivious Relay Resource terminates TLS connections from Clients, so it sees message boundaries. This privileged position allows for richer feature extraction from encrypted data, which might improve traffic analysis. Yikes ๐Ÿ’€.

Clients and Oblivious Gateway Resources can use padding to reduce the effectiveness of traffic analysis. Padding is a capability provided by binary HTTP messages; see Section 3.8 of [BINARY]. If the encapsulation method described in this document is used to protect a different message type (see Section 4.6), that message format might need to include padding support. Oblivious Relay Resources can also use padding for the same reason, but they need to operate at the HTTP layer since they cannot manipulate binary HTTP messages; for example, see Section 10.7 of [HTTP/2] or Section 10.7 of [HTTP/3]).

6.3. Server Responsibilities ๐Ÿฆข

The Oblivious Gateway Resource can be operated by a different entity than the Target Resource. However, this means the Client needs to trust the Oblivious Gateway Resource not to modify requests or responses. This analysis concerns a deployment scenario where a single server provides both the Oblivious Gateway Resource and the Target Resource ๐Ÿ‘‘.

A server that operates both Oblivious Gateway and Target Resources is responsible for removing request encryption, generating a response to the Encapsulated Request, and encrypting the response ๐Ÿ”ฅ.

Servers should account for traffic analysis based on response size or generation time. Techniques such as padding or timing delays can help protect against such attacks; see Section 6.2.3 ๐Ÿ™.

If separate entities provide the Oblivious Gateway Resource and the Target Resource, these entities might need an arrangement similar to the one between server and relay for managing denial of service; see Section 6.2.2. Moreover, the Oblivious Gateway Resource SHOULD have some mechanism to ensure it is not misused as a relay for HTTP messages to an arbitrary Target Resource, such as an allowlist ๐ŸŒท.

Non-secure requests -- such as those with the "http" scheme as opposed to the "https" scheme -- SHOULD NOT be used if the Oblivious Gateway and Target Resources are not on the same origin. If messages are forwarded between these resources without the protections afforded by HTTPS, they could be inspected or modified by a network attacker ๐Ÿšซ. Note that a request could be forwarded without protection if the two resources share an origin.

6.4. Key Management โœจ

okay so bestie, the Oblivious Gateway Resource has to have a plan for swapping out its keys ๐Ÿ”‘๐Ÿ’…. This might mean regularly replacing keys, and each new one can get its own new key identifier ๐Ÿฆ‹. If an Oblivious Gateway Resource gets a request with a key identifier it doesn't understand, or one that matches a key that's already been replaced, the server can respond with an HTTP 422 (Unprocessable Content) status code ๐Ÿ™ˆ.

A server can also use a 422 status code if it does have a key matching the key identifier, but the Encapsulated Request just can't be decrypted with that key ๐Ÿ’€.

A server MUST ensure that the HPKE keys it uses are not valid for any other protocol that uses HPKE with the "message/bhttp request" label. ๐Ÿšจ Like, this is a big deal, no cap. Designers of protocols that reuse this encryption format, especially new versions of this protocol, can get key diversity by picking a different label when they use HPKE ๐ŸŒˆ. The "message/bhttp response" label was chosen just for symmetry ๐Ÿ’œ. It only gives key diversity inside the HPKE context made with the "message/bhttp request" label, see Section 4.6 ๐ŸŽ€.


6.5. Replay Attacks ๐Ÿ”

A server is responsible for either rejecting replayed requests or making sure that replays don't adversely affect Clients or resources. Main character responsibility energy ๐Ÿ‘‘.

Encapsulated Requests can be copied and replayed by the Oblivious Relay Resource ๐Ÿ˜ญ. The threat model for Oblivious HTTP allows for the possibility that an Oblivious Relay Resource might replay requests. Furthermore, if a Client sends an Encapsulated Request in TLS early data (see Section 8 of [TLS] and [RFC8470]), a network-based adversary might be able to cause the request to be replayed ๐Ÿ‘€. In both cases, the effect of a replay attack and the mitigations that might be used are similar to TLS early data ๐Ÿฆ‹.

It is the responsibility of the application that uses Oblivious HTTP to either reject replayed requests or make sure replayed requests have no adverse effect on its operation โœจ. This section describes some approaches that are universally applicable, plus suggestions for more targeted techniques ๐Ÿซถ.

A Client or Oblivious Relay Resource MUST NOT automatically attempt to retry a failed request unless it receives a positive signal indicating that the request was not processed or forwarded ๐Ÿ›‘. The HTTP/2 REFUSED_STREAM error code (Section 8.1.4 of [HTTP/2]), the HTTP/3 H3_REQUEST_REJECTED error code (Section 8.1 of [HTTP/3]), or a GOAWAY frame with a low enough identifier (in either protocol version) are all sufficient signals that no processing occurred ๐ŸŒธ. HTTP/1.1 [HTTP/1.1] provides no equivalent signal, sadly ๐Ÿฅบ. Connection failures or interruptions are NOT sufficient signals that no processing occurred, so don't be fooled ๐Ÿ’….

The anti-replay mechanisms described in Section 8 of [TLS] are generally applicable to Oblivious HTTP requests ๐Ÿงš. The encapsulated keying material (or enc) can be used in place of a nonce to uniquely identify a request ๐Ÿ’œ. This value is high-entropy and freshly generated for every request, so two valid requests will have different values with overwhelming probability ๐ŸŽ€.

The mechanism TLS uses for managing differences in Client and server clocks cannot be used, because it depends on being able to observe previous interactions ๐Ÿ™ˆ. Oblivious HTTP explicitly prevents that kind of linkability, so it's not an option here ๐Ÿšซ.

The considerations in [RFC8470] as they relate to managing the risk of replay also apply, though there is no option to delay the processing of a request โญ.

Limiting requests to those with safe methods might not be satisfactory for some applications, especially ones that submit data to a server ๐Ÿ“. Idempotent methods might help a bit with managing replay risk, but it's important to recognize that different idempotent requests can be combined to become not idempotent ๐Ÿ’€.

Even without replay prevention, the server-chosen response_nonce field makes sure responses have unique AEAD keys and nonces even when requests get replayed ๐Ÿ”๐ŸŒˆ.

6.5.1. Use of Date for Anti-replay ๐Ÿ“…

Clients SHOULD include a Date header field in Encapsulated Requests, unless the Client has prior knowledge indicating that the Oblivious Gateway Resource does not use Date for anti-replay purposes โฐ.

Even though HTTP requests often don't include a Date header field, a server can use that value to limit how many requests it needs to track if it's trying to prevent replay attacks ๐Ÿง .

An Oblivious Gateway Resource can keep state for requests over a small window of time in which it wants to accept requests ๐ŸชŸ. It can store all requests it processes within this window. Storing just the enc field of a request, which should be unique to each request, is enough ๐Ÿ’–. The Oblivious Gateway Resource can reject any request that matches one previously answered within that window, or if the Date header field from the decrypted request is outside the current time window ๐Ÿšซ.

Oblivious Gateway Resources might need to allow for the time it takes requests to arrive from the Client, so the window has to be big enough to cover clock differences ๐Ÿ•ฐ๏ธ. Not allowing enough tolerance for time differences could get valid requests unnecessarily rejected ๐Ÿ˜ค. Beyond allowing for multiple round-trip times (to account for retransmission), network delays are unlikely to decide the size of the window, unless all potential Clients are known to have excellent timekeeping โฑ๏ธ. A specific window size might need to be figured out experimentally ๐Ÿงช.

Oblivious Gateway Resources MUST NOT treat the time window as secret information ๐Ÿซฃ. An attacker can actively probe with different values for the Date field to find out the time window over which the server will accept responses ๐Ÿ‘€.

6.5.2. Correcting Clock Differences โณ

An Oblivious Gateway Resource can reject requests that contain a Date value outside its active window with a 400 series status code ๐Ÿ™…. The problem type [PROBLEM] of "https://iana.org/assignments/http-problem-types#date" exists so the server can signal that the Date value in the request was unacceptable ๐Ÿšฉ.

Figure 8 shows an example response in HTTP/1.1 format ๐Ÿ“œ.

HTTP/1.1 400 Bad Request
Date: Mon, 07 Feb 2022 00:28:05 GMT
Content-Type: application/problem+json
Content-Length: 128

{"type":"https://iana.org/assignments/http-problem-types#date",
"title": "date field in request outside of acceptable range"}

          Figure 8: Example Rejection of Request Date Field

Clock disagreements are unlikely if both Client and Oblivious Gateway Resource have a good source of time; see [NTP] ๐Ÿ•. However, clock differences are known to be super commonplace, see Section 7.1 of [CLOCKSKEW] ๐Ÿ™ƒ.

Including a Date header field in the response lets the Client fix clock errors by retrying the same request using the Date value the Oblivious Gateway Resource provided ๐Ÿ”ง. The value of the Date field can be copied if the response is fresh, or adjusted based on the Age field otherwise; see Section 4.2 of [HTTP-CACHING] ๐Ÿงฎ. When retrying a request, the Client MUST create a fresh encryption of the modified request, using a new HPKE context ๐Ÿ”’.

+---------+       +-------------------+      +----------+
| Client  |       | Relay and Gateway |      | Target   |
|         |       |     Resources     |      | Resource |
+----+----+       +----+-----------+--+      +----+-----+
     |                 |           |              |
     |                 |           |              |
     |  Request        |           |              |
     +============================>+------------->|
     |                 |           |              |
     |                 |           | 400 Response |
     |                 |           |       + Date |
     |<============================+<-------------+
     |                 |           |              |
     |  Request        |           |              |
     |  + Updated Date |           |              |
     +============================>+------------->|
     |                 |           |              |

           Figure 9: Retrying with an Updated Date Field

Retrying immediately lets the Oblivious Gateway Resource measure the round-trip time to the Client โฒ๏ธ. That observed delay might reveal something about where the Client is ๐Ÿ“. Clients could delay retries to add some uncertainty to any observed delay ๐ŸŒ€.

Intermediaries can sometimes rewrite the Date field when forwarding responses ๐Ÿ”€. This might cause problems if the Oblivious Gateway Resource and intermediary clocks differ enough to get the retry rejected ๐Ÿ˜ฌ. So, Clients MUST NOT retry a request with an adjusted date more than once ๐Ÿ›‘.

Oblivious Gateway Resources that condition their responses on the Date header field SHOULD either make sure intermediaries don't cache responses (by including a Cache-Control directive of no-store), or designate the response as conditional on the value of the Date request header field (by including the token "date" in a Vary header field) ๐Ÿ—‚๏ธ.

Clients MUST NOT use the date provided by the Oblivious Gateway Resource for any other purpose, including future requests to any resource ๐Ÿ™…โ€โ™€๏ธ. Any request that uses information provided by the Oblivious Gateway Resource might be correlated using that information ๐Ÿ•ต๏ธ.


6.6. Forward Secrecy ๐Ÿ—๏ธ

This document does not provide forward secrecy for either requests or responses during the lifetime of the key configuration ๐Ÿ˜ข. A measure of forward secrecy can be provided by generating a new key configuration and then deleting the old keys after a suitable period ๐Ÿ‚.


6.7. Post-Compromise Security ๐Ÿ›ก๏ธ

This design does not provide post-compromise security for responses ๐Ÿฅ€.

A Client only needs to retain keying material that might be used to compromise the confidentiality and integrity of a response until that response is consumed, so there's negligible risk associated with a Client compromise ๐Ÿ’.

A server keeps a secret key that might be used to remove protection from messages over much longer periods โณ. A server compromise that gave access to the Oblivious Gateway Resource secret key could let an attacker recover the plaintext of all requests sent toward affected keys, and all of the responses that were generated ๐Ÿ˜ฑ.

Even if server keys are compromised, an adversary cannot access messages exchanged by the Client with the Oblivious Relay Resource, because those messages are protected by TLS ๐Ÿ”. Using a compromised key also requires that the Oblivious Relay Resource cooperate with the attacker, or that the attacker can compromise these TLS connections ๐Ÿ•ธ๏ธ.

The total number of messages affected by server key compromise can be limited by regularly rotating server keys ๐Ÿ”„.


6.8. Client Clock Exposure ๐Ÿ•ฐ๏ธ๐Ÿ‘€

Including a Date field in requests reveals some information about the Client's clock โฐ. This might be used to fingerprint Clients [UWT] or to identify Clients that are vulnerable to attacks that depend on incorrect clocks ๐ŸŽฏ.

Clients can randomize the value they provide for Date to hide the true value of their clock and reduce the chance of linking requests over time ๐ŸŽฒ. However, this increases the risk that their request is rejected as outside the acceptable window ๐Ÿ˜ตโ€๐Ÿ’ซ.

6.9. Media Type Security

The key configuration media type defined in Section 3.2 represents keying material. The content of this media type is not active (see Section 4.6 of [RFC6838]), but it governs how a Client might interact with an Oblivious Gateway Resource. The security implications of processing it are described in Section 6.1; privacy implications are described in Section 7.

The security implications of handling the message media types defined in Section 4.5 is covered in other parts of this section in more detail. However, these message media types are also encrypted encapsulations of HTTP requests and responses.

HTTP messages contain content, which can use any media type. In particular, requests are processed by an Oblivious Target Resource, which -- as an HTTP resource -- defines how content is processed; see Section 3.1 of [HTTP]. HTTP clients can also use resource identity and response content to determine how content is processed. Consequently, the security considerations of Section 17 of [HTTP] also apply to the handling of the content of these media types.

6.10. Separate Gateway and Target

This document generally assumes that the same entity operates the Oblivious Gateway Resource and the Target Resource. However, as the Oblivious Gateway Resource performs generic HTTP processing, the use of forwarding cannot be completely precluded.

The scheme specified in the Encapsulated Request determines the security requirements for any protocol that is used between the Oblivious Gateway and Target Resources. Using HTTPS is RECOMMENDED; see Section 6.3.

A Target Resource that is operated on a different server from the Oblivious Gateway Resource is an ordinary HTTP resource. A Target Resource can privilege requests that are forwarded by a given Oblivious Gateway Resource if it trusts the operator of the Oblivious Gateway Resource to only forward requests that meet the expectations of the Target Resource. Otherwise, the Target Resource treats requests from an Oblivious Gateway Resource no differently than any other HTTP client.

For instance, an Oblivious Gateway Resource might -- possibly with the help of Oblivious Relay Resources -- be trusted not to forward an excessive volume of requests. This might allow the Target Resource to accept a greater volume of requests from that Oblivious Gateway Resource relative to other HTTP clients.

An Oblivious Gateway Resource could implement policies that improve the ability of the Target Resource to implement policy exemptions, such as only forwarding requests toward specific Target Resources according to an allowlist; see Section 6.3.

7. Privacy Considerations โœจ

One goal of this design is that independent Client requests are only linkable by their content. ๐Ÿฆ‹ However, the choice of Client configuration might be used to correlate requests. A Client configuration includes the Oblivious Relay Resource URI, the Oblivious Gateway key configuration, and the Oblivious Gateway Resource URI. A configuration is active if Clients can successfully use it for interacting with a target.

Oblivious Relay and Gateway Resources can identify when requests use the same configuration by matching the key identifier from the key configuration or the Oblivious Gateway Resource URI. The Oblivious Gateway Resource might use the source address of requests to correlate requests that use an Oblivious Relay Resource run by the same operator. If the Oblivious Gateway Resource is willing to use trial decryption, requests can be further separated into smaller groupings based on active configurations that clients use. ๐Ÿ•ต๏ธโ€โ™€๏ธ

Each active Client configuration partitions the Client anonymity set. In practice, it is infeasible to reduce the number of active configurations to one. Enabling diversity in choice of Oblivious Relay Resource naturally increases the number of active configurations. More than one configuration might need to be active to allow for key rotation and server maintenance. ๐ŸŒˆ

Client privacy depends on having each configuration used by many other Clients. It is critical to prevent the use of unique Client configurations, which might be used to track individual Clients, but it is also important to avoid creating small groupings of Clients that might weaken privacy protections. ๐Ÿ’œ

A specific method for a Client to acquire configurations is not included in this specification. Applications using this design MUST provide accommodations to mitigate tracking using Client configurations. [CONSISTENCY] provides options for ensuring that Client configurations are consistent between Clients.

The content of requests or responses, if used in forming new requests, can be used to correlate requests. This includes obvious methods of linking requests, like cookies [COOKIES], but it also includes any information in either message that might affect how subsequent requests are formulated. For example, [FIELDING] describes how interactions that are individually stateless can be used to build a stateful system when a Client acts on the content of a response. ๐Ÿ“

8. Operational and Deployment Considerations ๐ŸŽ€

This section discusses various operational and deployment considerations.

8.1. Performance Overhead โฑ๏ธ

Using Oblivious HTTP adds both cryptographic overhead and latency to requests relative to a simple HTTP request-response exchange. Deploying relay services that are on path between Clients and servers avoids adding significant additional delay due to network topology. A study of a similar system [ODOH-PETS] found that deploying proxies close to servers was most effective in minimizing additional latency. ๐Ÿš€

8.2. Resource Mappings ๐Ÿ—บ๏ธ

This protocol assumes a fixed, one-to-one mapping between the Oblivious Relay Resource and the Oblivious Gateway Resource. This means that any Encapsulated Request sent to the Oblivious Relay Resource will always be forwarded to the Oblivious Gateway Resource. This constraint was imposed to simplify relay configuration and mitigate against the Oblivious Relay Resource being used as a generic relay for unknown Oblivious Gateway Resources. The relay will only forward for Oblivious Gateway Resources that it has explicitly configured and allowed.

It is possible for a server to be configured with multiple Oblivious Relay Resources, each for a different Oblivious Gateway Resource as needed. If the goal is to support a large number of Oblivious Gateway Resources, Clients might be provided with a URI template [TEMPLATE], from which multiple Oblivious Relay Resources could be constructed.

8.3. Network Management ๐Ÿ›œ

Oblivious HTTP might be incompatible with network interception regimes, such as those that rely on configuring Clients with trust anchors and intercepting TLS connections. While TLS might be intercepted successfully, interception middlebox devices might not receive updates that would allow Oblivious HTTP to be correctly identified using the media types defined in Sections 9.2 and 9.3.

Oblivious HTTP has a simple key management design that is not trivially altered to enable interception by intermediaries. Clients that are configured to enable interception might choose to disable Oblivious HTTP in order to ensure that content is accessible to middleboxes. ๐Ÿ™ˆ

9. IANA Considerations ๐Ÿ“‹

IANA has registered the following media types in the "Media Types" registry at https://iana.org/assignments/media-types, following the procedures of [RFC6838]: "application/ohttp-keys" (Section 9.1), "message/ohttp-req" (Section 9.2), and "message/ohttp-res" (Section 9.3).

IANA has added the following types to the "HTTP Problem Types" registry at https://iana.org/assignments/http-problem-types: "date" (Section 9.4) and "ohttp-key" (Section 9.5).

9.1. application/ohttp-keys Media Type ๐ŸŽ€

The "application/ohttp-keys" media type identifies a key configuration used by Oblivious HTTP.

Type name:  application
Subtype name:  ohttp-keys
Required parameters:  N/A
Optional parameters:  N/A
Encoding considerations:  "binary"
Security considerations:  See Section 6.9
Interoperability considerations:  N/A
Published specification:  RFC 9458
Applications that use this media type:  This type identifies a key
   configuration as used by Oblivious HTTP and applications that use
   Oblivious HTTP.
Fragment identifier considerations:  N/A
Additional information:
   Magic number(s):  N/A
   Deprecated alias names for this type:  N/A
   File extension(s):  N/A
   Macintosh file type code(s):  N/A
Person and email address to contact for further information:
   See Authors' Addresses section
Intended usage:  COMMON
Restrictions on usage:  N/A
Author:  See Authors' Addresses section
Change controller:  IETF

9.2. message/ohttp-req Media Type ๐Ÿ’Œ

The "message/ohttp-req" identifies an encrypted binary HTTP request. This is a binary format that is defined in Section 4.3.

Type name:  message
Subtype name:  ohttp-req
Required parameters:  N/A
Optional parameters:  N/A
Encoding considerations:  "binary"
Security considerations:  See Section 6.9
Interoperability considerations:  N/A
Published specification:  RFC 9458
Applications that use this media type:  Oblivious HTTP and
   applications that use Oblivious HTTP use this media type to
   identify encapsulated binary HTTP requests.
Fragment identifier considerations:  N/A
Additional information:
   Magic number(s):  N/A
   Deprecated alias names for this type:  N/A
   File extension(s):  N/A
   Macintosh file type code(s):  N/A
Person and email address to contact for further information:
   See Authors' Addresses section
Intended usage:  COMMON
Restrictions on usage:  N/A
Author:  See Authors' Addresses section
Change controller:  IETF

9.3. message/ohttp-res Media Type ๐Ÿ’Œ

The "message/ohttp-res" identifies an encrypted binary HTTP response. This is a binary format that is defined in Section 4.4.

Type name:  message
Subtype name:  ohttp-res
Required parameters:  N/A
Optional parameters:  N/A
Encoding considerations:  "binary"
Security considerations:  See Section 6.9
Interoperability considerations:  N/A
Published specification:  RFC 9458
Applications that use this media type:  Oblivious HTTP and
   applications that use Oblivious HTTP use this media type to
   identify encapsulated binary HTTP responses.
Fragment identifier considerations:  N/A
Additional information:
   Magic number(s):  N/A
   Deprecated alias names for this type:  N/A
   File extension(s):  N/A
   Macintosh file type code(s):  N/A
Person and email address to contact for further information:
   See Authors' Addresses section
Intended usage:  COMMON
Restrictions on usage:  N/A
Author:  See Authors' Addresses section
Change controller:  IETF

9.4. Registration of "date" Problem Type ๐Ÿ“…

IANA has added a new entry in the "HTTP Problem Types" registry established by [PROBLEM].

Type URI:  https://iana.org/assignments/http-problem-types#date
Title:  Date Not Acceptable
Recommended HTTP Status Code:  400
Reference:  Section 6.5.2 of RFC 9458

9.5. Registration of "ohttp-key" Problem Type ๐Ÿ”‘

IANA has added a new entry in the "HTTP Problem Types" registry established by [PROBLEM].

Type URI:  https://iana.org/assignments/http-problem-types#ohttp-key
Title:  Oblivious HTTP key configuration not acceptable
Recommended HTTP Status Code:  400
Reference:  Section 5.3 of RFC 9458

10. References ๐Ÿ“š

10.1. Normative References ๐Ÿ’–

These are the must-reads, the foundations this whole spec stands on ๐Ÿงฑโœจ

[ASCII] Cerf, V., "ASCII format for network interchange", STD 80, RFC 20, DOI 10.17487/RFC0020, October 1969, https://www.rfc-editor.org/info/rfc20.

[BINARY] Thomson, M. and C. A. Wood, "Binary Representation of HTTP Messages", RFC 9292, DOI 10.17487/RFC9292, August 2022, https://www.rfc-editor.org/info/rfc9292.

[HPKE] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, February 2022, https://www.rfc-editor.org/info/rfc9180.

[HTTP] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, DOI 10.17487/RFC9110, June 2022, https://www.rfc-editor.org/info/rfc9110.

[HTTP-CACHING] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Caching", STD 98, RFC 9111, DOI 10.17487/RFC9111, June 2022, https://www.rfc-editor.org/info/rfc9111.

[PROBLEM] Nottingham, M., Wilde, E., and S. Dalal, "Problem Details for HTTP APIs", RFC 9457, DOI 10.17487/RFC9457, July 2023, https://www.rfc-editor.org/info/rfc9457.

[QUIC] Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based Multiplexed and Secure Transport", RFC 9000, DOI 10.17487/RFC9000, May 2021, https://www.rfc-editor.org/info/rfc9000.

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, https://www.rfc-editor.org/info/rfc2119.

[RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, DOI 10.17487/RFC6838, January 2013, https://www.rfc-editor.org/info/rfc6838.

[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, https://www.rfc-editor.org/info/rfc8174.

[RFC8470] Thomson, M., Nottingham, M., and W. Tarreau, "Using Early Data in HTTP", RFC 8470, DOI 10.17487/RFC8470, September 2018, https://www.rfc-editor.org/info/rfc8470.

[TLS] Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018, https://www.rfc-editor.org/info/rfc8446.


10.2. Informative References ๐Ÿงš

These ones are the extra reading for the curious ๐Ÿค“๐Ÿ“š They're not strictly required, but they're seriously worth a peek if you want the whole story ๐Ÿ’…

[CLOCKSKEW] Acer, M., Stark, E., Felt, A., Fahl, S., Bhargava, R., Dev, B., Braithwaite, M., Sleevi, R., and P. Tabriz, "Where the Wild Warnings Are: Root Causes of Chrome HTTPS Certificate Errors", Proceedings of the 2017 ACM SIGSAC Conference on Computer and Communications Security, DOI 10.1145/3133956.3134007, October 2017, https://doi.org/10.1145/3133956.3134007.

[CONSISTENCY] Davidson, A., Finkel, M., Thomson, M., and C. A. Wood, "Key Consistency and Discovery", Work in Progress, Internet-Draft, draft-ietf-privacypass-key-consistency-01, 10 July 2023, https://datatracker.ietf.org/doc/html/draft-ietf-privacypass-key-consistency-01.

[COOKIES] Barth, A., "HTTP State Management Mechanism", RFC 6265, DOI 10.17487/RFC6265, April 2011, https://www.rfc-editor.org/info/rfc6265.

[DMS2004] Dingledine, R., Mathewson, N., and P. Syverson, "Tor: The Second-Generation Onion Router", May 2004, https://svn.torproject.org/svn/projects/design-paper/tor-design.html.

[FIELDING] Fielding, R. T., "Architectural Styles and the Design of Network-based Software Architectures", January 2000, https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf.

[FORWARDED] Petersson, A. and M. Nilsson, "Forwarded HTTP Extension", RFC 7239, DOI 10.17487/RFC7239, June 2014, https://www.rfc-editor.org/info/rfc7239.

[HTTP/1.1] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP/1.1", STD 99, RFC 9112, DOI 10.17487/RFC9112, June 2022, https://www.rfc-editor.org/info/rfc9112.

[HTTP/2] Thomson, M., Ed. and C. Benfield, Ed., "HTTP/2", RFC 9113, DOI 10.17487/RFC9113, June 2022, https://www.rfc-editor.org/info/rfc9113.

[HTTP/3] Bishop, M., Ed., "HTTP/3", RFC 9114, DOI 10.17487/RFC9114, June 2022, https://www.rfc-editor.org/info/rfc9114.

[NTP] Mills, D., Martin, J., Ed., Burbank, J., and W. Kasch, "Network Time Protocol Version 4: Protocol and Algorithms Specification", RFC 5905, DOI 10.17487/RFC5905, June 2010, https://www.rfc-editor.org/info/rfc5905.

[ODOH] Kinnear, E., McManus, P., Pauly, T., Verma, T., and C.A. Wood, "Oblivious DNS over HTTPS", RFC 9230, DOI 10.17487/RFC9230, June 2022, https://www.rfc-editor.org/info/rfc9230.

[ODOH-PETS] Singanamalla, S., Chunhapanya, S., Hoyland, J., Vavruลกa, M., Verma, T., Wu, P., Fayed, M., Heimerl, K., Sullivan, N., and C. A. Wood, "Oblivious DNS over HTTPS (ODoH): A Practical Privacy Enhancement to DNS", PoPETS Proceedings Volume 2021, Issue 4, pp. 575-592, DOI 10.2478/popets-2021-0085, January 2021, https://www.petsymposium.org/2021/files/papers/issue4/popets-2021-0085.pdf.

[OHTTP-ANALYSIS] Hoyland, J., "Tamarin Model of Oblivious HTTP", commit 6824eee, October 2022, https://github.com/cloudflare/ohttp-analysis.

[PRIO] Corrigan-Gibbs, H. and D. Boneh, "Prio: Private, Robust, and Scalable Computation of Aggregate Statistics", March 2017, https://crypto.stanford.edu/prio/paper.pdf.

[RANDOM] Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005, https://www.rfc-editor.org/info/rfc4086.

[RFC7838] Nottingham, M., McManus, P., and J. Reschke, "HTTP Alternative Services", RFC 7838, DOI 10.17487/RFC7838, April 2016, https://www.rfc-editor.org/info/rfc7838.

[RFC8484] Hoffman, P. and P. McManus, "DNS Queries over HTTPS (DoH)", RFC 8484, DOI 10.17487/RFC8484, October 2018, https://www.rfc-editor.org/info/rfc8484.

[TEMPLATE] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., and D. Orchard, "URI Template", RFC 6570, DOI 10.17487/RFC6570, March 2012, https://www.rfc-editor.org/info/rfc6570.

[UWT] Nottingham, M., "Unsanctioned Web Tracking", W3C TAG Finding, July 2015, https://www.w3.org/2001/tag/doc/unsanctioned-tracking/.

[X25519] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves for Security", RFC 7748, DOI 10.17487/RFC7748, January 2016, https://www.rfc-editor.org/info/rfc7748.

Appendix A. Complete Example of a Request and Response ๐ŸŽ€

okay so this is the fun part, bestie ๐Ÿ’… we get one full request and response exchange, start to finish, in hex. the request and response are super minimal on purpose, because the whole point is to show you the crypto steps happening ๐Ÿ”โœจ binary stuff (key config, secret keys, message contents, and the in-between values) is shown in hexadecimal.

the setup: the Client is configured with the Oblivious Relay Resource URI https://proxy.example.org/request.example.net/proxy, and the proxy is set up to map requests to that URI over to the Oblivious Gateway Resource URI https://example.com/oblivious/request. the Target Resource URI (the thing the Client actually wants to query) is https://example.com ๐Ÿฆ‹

first up, the Oblivious Gateway Resource generates a key pair. the server picks DHKEM(X25519, HKDF-SHA256) and makes an X25519 key pair [X25519] ๐ŸŒธ the X25519 secret key is:

3c168975674b2fa8e465970b79c8dcf09f1c741626480bd4c6162fc5b6a98e1a

then the Oblivious Gateway Resource builds a key configuration with the matching public key in it:

01002031e1f05a740102115220e9af918f738674aec95f54db6e04eb705aae8e
79815500080001000100010003

the Client somehow gets this key configuration (the spec doesn't say how, it's just "somehow" lol ๐Ÿ™ˆ). then, when the Client wants to send an HTTP GET to https://example.com, it builds this binary HTTP message:

00034745540568747470730b6578616d706c652e636f6d012f

next, the Client reads the key configuration and picks a KDF and AEAD that both sides support. here it goes with HKDF-SHA256 and AES-128-GCM ๐Ÿ’– it makes an HPKE sending context using the server's public key, built from this ephemeral secret key:

bc51d5e930bda26589890ac7032f70ad12e4ecb37abb1b65b1256c9c48999c73

the matching public key is:

4b28f881333e7c164ffc499ad9796f877f4e1051ee6d31bad19dec96c208b472

the context gets created with this info parameter:

6d6573736167652f626874747020726571756573740001002000010001

now the Client runs the Seal operation from that HPKE context to get an encrypted message, and that lets it build this Encapsulated Request ๐Ÿ”ฅ

010020000100014b28f881333e7c164ffc499ad9796f877f4e1051ee6d31bad1
9dec96c208b4726374e469135906992e1268c594d2a10c695d858c40a026e796
5e7d86b83dd440b2c0185204b4d63525

the Client sends that to the Oblivious Relay Resource in a POST, and it might look something like this:

POST /request.example.net/proxy HTTP/1.1
Host: proxy.example.org
Content-Type: message/ohttp-req
Content-Length: 78

<content is the Encapsulated Request above>

the Oblivious Relay Resource gets that and forwards it to the Oblivious Gateway Resource, which might look like:

POST /oblivious/request HTTP/1.1
Host: example.com
Content-Type: message/ohttp-req
Content-Length: 78

<content is the Encapsulated Request above>

the Oblivious Gateway Resource receives it, grabs the key it made earlier using the key identifier from the message, and decrypts it ๐Ÿงš since this request is headed to the same server, the Oblivious Gateway Resource doesn't have to start its own HTTP request to the Target Resource. the Target Resource can serve the request directly, and it makes a tiny response (just a 200 status code) like this:

0140c8
the response gets built by exporting a secret from the HPKE context. ngl this is the part where the crypto gets really spicy ๐ŸŒถ๏ธ

the exported secret is:

62d87a6ba569ee81014c2641f52bea36

the key derivation for the Encapsulated Response uses both the encapsulated KEM key from the request and a randomly picked nonce ๐ŸŽฒ that makes a salt of:

4b28f881333e7c164ffc499ad9796f877f4e1051ee6d31bad19dec96c208b472
c789e7151fcba46158ca84b04464910d

then the salt and secret both go into the Extract function of the chosen KDF (HKDF-SHA256), which gives a pseudorandom key of:

979aaeae066cf211ab407b31ae49767f344e1501e475c84e8aff547cc5a683db

the pseudorandom key goes with the Expand function of the KDF and an info field of "key", which makes a 16-byte key for the chosen AEAD (AES-128-GCM):

5d0172a080e428b16d298c4ea0db620d

using that same KDF and pseudorandom key, but with an info field of "nonce", it makes a 12-byte nonce:

f6bf1aeb88d6df87007fa263

now the AEAD Seal() function encrypts the response, and the result gets added to the randomized nonce value to make the Encapsulated Response ๐Ÿ’œ

c789e7151fcba46158ca84b04464910d86f9013e404feea014e7be4a441f234f
857fbd

the Oblivious Gateway Resource builds a response with this same content:

HTTP/1.1 200 OK
Date: Wed, 27 Jan 2021 04:45:07 GMT
Cache-Control: private, no-store
Content-Type: message/ohttp-res
Content-Length: 38

<content is the Encapsulated Response>

the same response might then come out of the Oblivious Relay Resource too, and the only thing that might change is the Date header โœจ the Client can then use the HPKE context it made, plus the nonce from the Encapsulated Response, to build the AEAD key and nonce and decrypt the response. and that's the whole loop, periodt ๐ŸŽ€๐Ÿ‘‘


Acknowledgments

this design is based on a design for Oblivious DNS (queries) over HTTPS (DoH), described in [ODOH] ๐ŸŒˆ David Benjamin, Mark Nottingham, and Eric Rescorla made technical contributions. the authors also thank Ralph Giles, Lucas Pardue, and Tommy Pauly for invaluable assistance ๐Ÿซถ

Authors' Addresses

Martin Thomson Mozilla Email: mt@lowentropy.net

Christopher A. Wood Cloudflare Email: caw@heapingbits.net

๐Ÿ“ฆ where this page comes from