This privileged position allows for richer feature extraction from encrypted data, which might improve traffic analysis. <del> can use padding to reduce the effectiveness of traffic analysis. </del>  Padding is a capability provided by binary HTTP messages; see BINARY. If the encapsulation method described in this document is used to protect a different message type (see repurposing), that message

Comment by NAME How would that work? How would the client consent? Or show awareness? Or become aware ? Can't the Target Resource also not use padding in the response? But I guess they wouldn't know about being connected to obliviously or regularly, unless the Client tells the server within its encrypted payload request? Eg if a regular https server sees a certain field, it would/could know to do some padding? (one would assume ohttp relays are fairly well known on static IPs anyway, so the Client wouldn't be revealing much if it could ask the http server itself for random padding
There are two questions here, delays and padding. Both are areas in which the specification is not prescriptive. This is deliberate, mostly because these are areas where a single strategy would be inadvisable. Application needs vary. Well, in any number of ways. A telemetry submission service (see also STAR) might provide greater privacy benefit from batching requests that are forwarded from the client to gateway. In that model, the client might tolerate arbitrary delay (see URL for example). In other cases, the client still cares about a response, but it might be willing to hang around for some time (10s, 30s) for an answer. How the client consents to delays can also vary. It could be part of terms of service that are negotiated when the use of the relay is arranged. That sort of arrangement might cover all these points. If you are looking for protocol mechanisms, we've discussed explicit signaling as an extension. A client could use something like (see ) to indicate that within the protocol[^1]. An explicit signal demonstrates awareness for those cases where the delay might be conditional. [^1]: Note also that this sort of signaling might imply a need for the relay to remove that field, though perhaps all clients add it and so it doesn't reveal anything if forwarded. Yes. Section 6.3 references back to this text, but it's worth mentioning the gateway too. There are many things that might feed into a padding strategy. Sometimes this is something the gateway will know because they are configured for a particular application and can look at the payload to determine how to best pad. Clients can probably signal (within encapsulation) what they might prefer, if that would help. Again, it's too application-specific to say much of use.
Martin, The comment it isn’t my comment. Maybe there is another Pwouters in github Kind regards Paul Verstuurd vanaf mijn iPad
My apologies. I definitely got the wrong person there.
thanks
Flipping to approve, with or without my suggestion. Sorry again for my confusion.

This privileged position allows for richer feature extraction from encrypted data, which might improve traffic analysis.  <ins> and can use padding to reduce the effectiveness of traffic analysis. </ins> Padding is a capability provided by binary HTTP messages; see BINARY. If the encapsulation method described in this document is used to protect a different message type (see repurposing), that message


The request the sends to the only requires minimal information; see http-usage.  The request that carries the and is sent to the MUST NOT <del> include identifying information unless the ensures that this </del>  information is removed by the relay.  A MAY include information only for the 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

The client relies on a very specific form of trust here, though maybe they have other means to ensure compliance, it's trust that operates here.
Comment by NAME How could a client "ensure" that? Couldn't this at best be "unless the Client can trust that [...]"
(OK, edited this so that it only includes one comment, not two mangled together.) The answer here is down to trust, so this comment is right. Will fix.
looks good.

The request the sends to the only requires minimal information; see http-usage.  The request that carries the and is sent to the MUST NOT  <ins> include identifying information unless the can trust that this </ins> information is removed by the relay.  A MAY include information only for the 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


protection for the unprotected requests and responses, plus protections for traffic analysis; see ta.  8. Words...

If the key identifier isn't known to the oblivious request resource, or the message can't be decrypted, it probably wants to send a clear signal. The signal is definitely not encapsulated at that point, so clients have limited options available to them. As a first-order suggestion, a 4xx status code generally indicates to the client that it might want to review the information it used. However, more specific error codes might help in specific cases that we deem important or common. Top of the list: when the identified key has been decommissioned.
How does sound in terms of saying "the content was unusable". We could go further and recommend the use of if you think more detail is needed.

protection for the unprotected requests and responses, plus protections for traffic analysis; see ta. <ins> An oblivious request resource needs to have a plan for replacing keys.  This might include regular replacement of keys, which can be assigned new key identifiers.  If an oblivious request resource receives a request that contains a key identifier that it does not understand or that corresponds to a key that has been replaced, the server can respond with an HTTP 422 (Unprocessable Content) status code. A server can also use a 422 status code if the server has a key that corresponds to the key identifier, but the encapsulated request cannot be successfully decrypted using the key. </ins> 8. Words...


3.1. A single consists of a key identifier, a public key, an identifier <del> for the KEM that the public key uses, and a set HPKE symmetric </del>  algorithms.  Each symmetric algorithm consists of an identifier for a KDF and an identifier for an AEAD.

"and a set HPKE" => "and a set of HPKE"
Thanks for the quick review and approval, NAME I haven't used the GitHub UI for submitting PRs in a long time so please pardon my lack of understanding here but I still don't see a way to merge the PR. Does it also need a review from NAME ?
NAME it doesn't technically need a review from Martin. The pattern we seem to have landed on is that one of us merges once both approve, but this is simple enough that I just merged it now. Thanks for sending the PR!

3.1. A single consists of a key identifier, a public key, an identifier  <ins> for the KEM that the public key uses, and a set of HPKE symmetric </ins> algorithms.  Each symmetric algorithm consists of an identifier for a KDF and an identifier for an AEAD.


between the and the . While the is a distinct HTTP resource from the , they are both logically under the control of the Oblivious Gateway, since the can unilaterally dictate the responses returned from the to <del> the . This arrangement is shown in fig-overview.  See security for more information about and Oblivious Relay, and Oblivous Gateway resources in running this protocol. </del>  2.1. location-specific content (such as retrieving tiles of a map display).  2.2. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", A server operates both the and the  Requests from the to and from to MUST use HTTPS in order to provide <del> unlinkability in the presence of a network observer.  The scheme of the determines what is used between the Oblivious Gateway and Target Resources, though using HTTPS is RECOMMENDED; see server- responsibilities. </del>  To achieve the stated privacy goals, the cannot be operated by the same entity as the . However, colocation of the and simplifies the considerations of HTTP also apply to the handling of the content of these media types.  7. One goal of this design is that independent requests are only

This talks about the odd case where target and gateway are separate. The primary insight here is that the target is just a regular HTTP resource and the requests from the gateway aren't special. Problems only occur if the gateway is privileged in some way. If abuse comes via the gateway, then the target would ordinary block the client, which tends to affect a bunch of bystanders in this model. So there is pressure to do something special for the gateway. In that case, the target is trusting the gateway in some way. Explain that. The heading here is the only case where we use the short names of the entities, but I can't stand short headings.
The changes are a big step forward in the right direction, but the flow seems a little weird as the text in the first part of the security section is about co-located gateway/target. Strongly suggest to move the added new text at the end of the whole section and add a forward reference in the text: (from the intro in the security section)
And, just to be clear, the PR text addresses my concern. My comment about the flow is just editorial.
I've taken the suggestion to move the text (with forward references) and made a few tweaks: Rebased. Moved and rewritten some text from the top of the security considerations that better fit the new section. Added a forward reference to this section. Moved and rewritten text added in to the applicability section. (Originally this was just to correct a misspelling, but I think this makes more sense in the new home.) NAME can you double-check my work. With this, I think we're ready to revise the draft and ask the IESG to finalize their ballot.

between the and the . While the is a distinct HTTP resource from the , they are both logically under the control of the Oblivious Gateway, since the can unilaterally dictate the responses returned from the to  <ins> the . This arrangement is shown in fig-overview. </ins> 2.1. location-specific content (such as retrieving tiles of a map display). <ins> In addition to these limitations, security describes operational constraints that are necessary to realize the goals of the protocol. </ins> 2.2. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", A server operates both the and the <ins> separate-target discusses the security implications for a case where different servers operate the and . </ins> Requests from the to and from to MUST use HTTPS in order to provide  <ins> unlinkability in the presence of a network observer. </ins> To achieve the stated privacy goals, the cannot be operated by the same entity as the . However, colocation of the and simplifies the considerations of HTTP also apply to the handling of the content of these media types. <ins> 6.10. This document generally assumes that the same entity operates the and the .  However, as the performs generic HTTP processing, the use of forwarding cannot be completely precluded. The scheme specified in the determines the security requirements for any protocol that is used between the Oblivious Gateway and Target Resources.  Using HTTPS is RECOMMENDED; see server-responsibilities. A that is operated on a different server from the is an ordinary HTTP resource.  A can privilege requests that are forwarded by a given if it trusts the operator of the to only forward requests that meet the expectations of the .  Otherwise, the treats requests from an no differently than any other HTTP client. For instance, an might - possibly with the help of - be trusted not to forward an excessive volume of requests.  This might allow the to accept a greater volume of requests from that relative to other HTTP clients. An could implement policies that improve the ability of the to implement policy exemptions, such as only forwarding requests toward specific Target Resources according to an allowlist; see server- responsibilities. </ins> 7. One goal of this design is that independent requests are only


minimum), additional data exchanged, and additional CPU cost of cryptographic computations. <del> To overcome these limitations, this document defines how binary HTTP messages BINARY can be encapsulated using Hybrid Public Key Encryption (HPKE; HPKE) to protect their contents.  exchange these messages with an , which is responsible for forwarding decapsulated requests to the original and encapsulating the corresponding responses and sending them back to the client.  Critically, encapsulated messages are sent through a separate to avoid exposing the client's IP address or allowing the connection to be used as a correlator between its requests. Because it allows connection reuse between the client and , as well as between that relay and the , this scheme represents a performance improvement over using just one request in each connection.  With limited trust placed in the (see security), are assured that requests are not uniquely attributed to them or linked to other requests. </del>  2. particular .  Oblivious HTTP uses a one-to-one mapping between ; see proxy-state for more details. <del> This information allows the to make a request of a with that resource having only a limited ability to correlate that request with the IP or other requests that the might make to that server. </del>  <del> In order to make a request to a , the following steps occur, as shown in fig-overview: </del>  The constructs an HTTP request for a . The receives this request and removes the HPKE protection to obtain an HTTP request. <del> The makes an HTTP request that includes the target URI, method, fields, and content of the request it acquires. The answers this HTTP request with an HTTP response. </del>  The encapsulates the HTTP response following the process in response and sends this in response to the request from the . The removes the encapsulation to obtain the response to the original request.  2.1. <del> Oblivious HTTP has limited applicability.  Many uses of HTTP benefit from being able to carry state between requests, such as with cookies (COOKIES), authentication (HTTP), or even alternative services (RFC7838).  Oblivious HTTP removes linkage at the transport layer, which is only useful for an application that does not carry state between requests. </del>  Oblivious HTTP is primarily useful where privacy risks associated with possible stateful treatment of requests are sufficiently large 6. <del> In this design, a wishes to make a request of a server that is authoritative for a . The wishes to make this request without linking that request with either: </del>  The identity at the network and transport layer of the (that is, the IP address and TCP or UDP port number the uses to create a As a consequence of this configuration, Oblivious HTTP prevents linkability described above.  Informally, this means: <del> Requests and responses are known only to and Target Resources, plus that possess the corresponding response encapsulation key and HPKE keying material.  In particular, the knows the origin and destination of an and Response, yet does not know the decrypted contents.  Likewise, learn only the and the decrypted request.  No entity other than the can see the plaintext request and response and can attribute them to the . </del>  <del> Targets cannot link requests from the same in the absence of unique per- keys. </del>  Traffic analysis that might affect these properties are outside the scope of this document; see ta. 6.1.  MUST ensure that the they select for generating Encapsulated Requests is integrity protected and authenticated so that it can be attributed to the ; see key-configuration. If separate entities provide the and , these entities might need an arrangement similar to that between server and relay for managing <del> denial of service; see dos. </del>  Non-secure requests - such as those with the "http" scheme as opposed to the "https" scheme - SHOULD NOT be used if the Oblivious Gateway

This change tries to emphasize that OHTTP is a protocol between client, relay, and gateway, and that the target is sort of ancillary to the whole thing. It further clarifies that the relay should not act as an open proxy when it's operated by an entity that is separate from the target server.
This works for me. We can merge this and then I want to take another pass at the other PR.Thanks NAME -- couple nits, otherwise lgtm.LGTM

minimum), additional data exchanged, and additional CPU cost of cryptographic computations.  <ins> To overcome these limitations, this document defines Oblivious HTTP, a protocol for encrypting and sending HTTP messages from a client to a gateway through a trusted relay service.  In particular, the protocol in this document describes: an algorithm for encapsulating binary HTTP messages BINARY using Hybrid Public Key Encryption (HPKE; HPKE) to protect their contents, a method for forwarding these encapsulated messages between clients and an through a trusted using HTTP, and requirements for how the handles encapsulated HTTP messages and produces encapsulated responses for the client. The combination of encapsulation and relaying ensures that never sees the client's IP address and the never sees plaintext HTTP message content. Oblivious HTTP allows connection reuse between the client and , as well as between that relay and the , so this scheme represents a performance improvement over using just one request in each connection.  With limited trust placed in the (see security), are assured that requests are not uniquely attributed to them or linked to other requests. </ins> 2. particular .  Oblivious HTTP uses a one-to-one mapping between ; see proxy-state for more details.  <ins> This information allows the to send HTTP requests to the for forwarding to a .  The does not learn the client's IP address or any other identifying information that might be revealed from the client at the transport layer, nor does the learn which of the requests it receives are from the same . </ins>  <ins> In order to forward a request for a to the , the following steps occur, as shown in fig-overview: </ins> The constructs an HTTP request for a . The receives this request and removes the HPKE protection to obtain an HTTP request.  <ins> The then handles the HTTP request.  This typically involves making an HTTP request using the content of the . Once the has an HTTP response for this request, the following steps occur to return this response to the client: </ins> The encapsulates the HTTP response following the process in response and sends this in response to the request from the . The removes the encapsulation to obtain the response to the original request. <ins> This interaction provides authentication and confidentiality protection between the and the Oblivious Gateway, but importantly not between the and the . While the is a distinct HTTP resource from the , they are both logically under the control of the Oblivious Gateway, since the can unilaterally dictate the responses returned from the to the . This arrangement is shown in fig-overview.  See security for more information about and Oblivious Relay, and Oblivous Gateway resources in running this protocol. </ins> 2.1.  <ins> Oblivious HTTP has limited applicability.  Imporantly, it requires expicit support from a willing and , thereby limiting the use of Oblivious HTTP for generic applications; see server-responsibilities for more information. Many uses of HTTP benefit from being able to carry state between requests, such as with cookies (COOKIES), authentication (HTTP), or even alternative services (RFC7838).  Oblivious HTTP removes linkage at the transport layer, which is only useful for an application that does not carry state between requests. </ins> Oblivious HTTP is primarily useful where privacy risks associated with possible stateful treatment of requests are sufficiently large 6.  <ins> In this design, a wishes to make a request to an that is forwarded to a . The wishes to make this request without linking that request with either: </ins> The identity at the network and transport layer of the (that is, the IP address and TCP or UDP port number the uses to create a As a consequence of this configuration, Oblivious HTTP prevents linkability described above.  Informally, this means:  <ins> Requests and responses are known only to and .  In particular, the knows the origin and destination of an and Response, yet does not know the decrypted contents.  Likewise, learn only the and the decrypted request.  No entity other than the can see the plaintext request and response and can attribute them to the . </ins>  <ins> Oblivous Gateway Resources, and therefore Target Resources, cannot link requests from the same in the absence of unique per- keys. </ins> Traffic analysis that might affect these properties are outside the scope of this document; see ta. 6.1. <ins> Because do not authenticate the when using Oblivious HTTP, MUST maintain an explicit allowlist of target resources that the trusts an Oblivious Gateway to respond for, and check requests against this allowlist before sending them.  This ensures that are not abused to forward traffic to arbitrary Target Resources.  See server- responsibilities for similar responsibilities that apply to . </ins> MUST ensure that the they select for generating Encapsulated Requests is integrity protected and authenticated so that it can be attributed to the ; see key-configuration. If separate entities provide the and , these entities might need an arrangement similar to that between server and relay for managing  <ins> denial of service; see dos.  Moreover, the SHOULD apply some form of allowlist to ensure that the is not misused as a relay for HTTP messages to an arbitrary . </ins> Non-secure requests - such as those with the "http" scheme as opposed to the "https" scheme - SHOULD NOT be used if the Oblivious Gateway


1. <del> Words... </del>  2. BCP 14 RFC2119 RFC8174 when, and only when, they appear in all capitals, as shown here.  3. <del> Words... </del>  4. <del> TODO: Define a media type here. </del>

This is fully-featured, which is a little awkward. We can probably trim this down a little once we have a better handle on the requirements.
NAME is the idea here that padding might just be a separate field for each (un)known-length message?
Not sure if you meant to ask this on instead. The idea I had for padding is that the format as described is self-delimiting. (Actually, it was accidental, and I only realized that it worked that way after coming back and thinking about padding.) Therefore, you can just add padding at the end. We can mandate the use of zero bytes, which works for the truncated message syntax I suggest here.
The format I've sketched out for binary messages can be padded with zeros without needing a separate padding field. That's probably a feature worth mentioning in both drafts.
Padding is in the binary HTTP draft now.

1.  <ins> This document defines a simple format for representing an HTTP message (HTTP), either request or response.  This allows for the encoding of HTTP messages that can be conveyed outside of an HTTP protocol.  This enables the transformation of entire messages, including the application of authenticated encryption. This format is informed by the framing structure of HTTP/2 (H2) and HTTP/3 (H3).  In comparison, this format simpler by virtue of not including either header compression (HPACK, QPACK) or a generic framing layer. This format provides an alternative to the "message/http" content type defined in MESSAGING.  A binary format permits more efficient encoding and processing of messages.  A binary format also reduces exposure to security problems related to processing of HTTP messages. Two modes for encoding are described: a known-length encoding includes length prefixes for all major message components; and an indefinite-length encoding enables efficient generation of messages where lengths are not known when encoding starts. </ins> 2. BCP 14 RFC2119 RFC8174 when, and only when, they appear in all capitals, as shown here. <ins> This document uses terminology from HTTP (HTTP) and notation from QUIC (QUIC). </ins> 3.  <ins> An HTTP message is split into five sections, following the structure defined in Section 6 of HTTP: Framing indicator.  This format uses a single integer to describe framing, which describes whether the message is a request or response and how subsequent sections are formatted; see Control data.  For a request, this contains the request method and target.  For a response, this contains the status code. Header section.  This contains zero or more header fields. Content.  This is a sequence of zero or more bytes. Trailer section.  This contains zero or more trailer fields. A response message can include multiple informational responses, which causes the control data and header section to be repeated. All lengths and numeric values are encoded using the variable-length integer encoding from QUIC. 3.1. A message that has a known length at the time of construction uses the format shown in format-known-length. That is, a known-length message consists of a framing indicator, a block of control data that is formatted according to the value of the framing indicator, a header section with a length prefix, binary content with a length prefix, and a trailer section with a length prefix. Response messages that contain informational status codes result in a different structure; see informational. Fields in the header and trailer sections consist of a length- prefixed name and length-prefixed value.  Both name and value are sequences of bytes that cannot be zero length. The format allows for the message to be truncated before any of the length prefixes that precede the field sections or content.  This reduces the overall message size.  A message that is truncated at any other point is invalid; see invalid. The variable-length integer encoding means that there is a limit of 2^62-1 bytes for each field section and the message content. 3.2. A message that is constructed without encoding a known length for each section uses the format shown in format-indeterminate-length: That is, an indeterminate length consists of a framing indicator, a block of control data that is formatted according to the value of the framing indicator, a header section that is terminated by a zero value, any number of non-zero-length chunks of binary content, a zero value, and a trailer section that is terminated by a zero value. Response messages that contain informational status codes result in a different structure; see informational. Indeterminate-length messages can be truncated in a similar way as known-length messages.  Truncation occurs after the control data, or after the Content Terminator field that ends a field section or sequence of content chunks.  A message that is truncated at any other point is invalid; see invalid. Indeterminate-length messages use the same encoding for field lines as known-length messages; see fields. 3.3. The start of each is a framing indicator that is a single integer that describes the structure of the subsequent sections.  The framing indicator can take just four values: A value of 0 describes a request of known length. A value of 1 describes a response of known length. A value of 2 describes a request of indeterminate length. A value of 3 describes a response of indeterminate length. Other values cause the message to be invalid; see invalid. 3.4. The control data for a request message includes four values that correspond to the values of the ":method", ":scheme", ":authority", and ":path" pseudo-header fields described in HTTP/2 (Section 8.1.2.3 of H2).  These fields are encoded, each with a length prefix, in the order listed. The rules in Section 8.1.2.3 of H2 for constructing pseudo-header fields apply to the construction of these values.  However, where the ":authority" pseudo-header field might be omitted in HTTP/2, a zero- length value is encoded instead. The format of request control data is shown in format-request- control-data. 3.5. The control data for a request message includes a single field that corresponds to the ":status" pseudo-header field in HTTP/2 H2.  This field is encoded as a single variable length integer, not a decimal string. The format of final response control data is shown in format- response-control-data. 3.5.1. This format supports informational status codes (see Section 15.2 of HTTP).  Responses that include information status codes are encoded by repeating the response control data and associated header section until the final status code is encoded. The format of the informational response control data is shown in format-informational. A response message can include any number of informational responses. If the response control data includes an informational status code (that is, a value between 100 and 199 inclusive), the control data is followed by a header section (encoded with known- or indeterminate- length according to the framing indicator).  After the header section, another response control data block follows. 3.6. Header and trailer sections consist of zero or more field lines; see Section 5 of HTTP.  The format of a field section depends on whether the message is known- or intermediate-length. Each field line includes a name and a value.  Both the name and value are non-zero length sequences of bytes.  The format of a field line is shown in format-field-line. For field names, byte values that are not permitted in an HTTP field name cause the message to be invalid; see Section 5.1 of HTTP for a definition of what is valid and invalid for handling of invalid messages. In addition, values from the ASCII uppercase range (0x41-0x5a inclusive) MUST be translated to lowercase values (0x61-0x7a) when generating or forwarding messages.  A recipient MUST treat a message containing field names with bytes in the range 0x41-0x5a as invalid; see invalid. For field values, byte values that are not permitted in an HTTP field value cause the message to be invalid; see Section 5.5 of HTTP for a definition of valid values. The same field name can be repeated in multiple field lines; see Section 5.2 of HTTP for the semantics of repeated field names and rules for combining values. Like HTTP/2, this format has an exception for the combination of multiple instances of the "Cookie" field.  Instances of fields with the ASCII-encoded value of "cookie" are combined using a semicolon octet (0x3b) rather than a comma; see Section 8.1.2.5 of H2. This format provides fixed locations for content that would be carried in HTTP/2 pseudo-fields.  Therefore, there is no need to include field lines containing a name of ":method", ":scheme", ":authority", ":path", or ":status".  Fields that contain one of these names cause the message to be invalid; see invalid.  Pseudo- fields that are defined by protocol extensions MAY be included, however field lines containing pseudo-fields MUST precede other field lines. 3.7. The content of messages is a sequence of bytes of any length.  Though a known-length message has a limit, this limit is large enough that it is unlikely to be a practical limitation.  There is there is no limit to an indeterminate length message. Omitting content by truncating a message is only possible if the content is zero-length. </ins> 4.  <ins> This document describes a number of ways that a message can be invalid.  Invalid messages MUST NOT be processed except to log an error and produce an error response. The format is designed to allow incremental processing. Implementations need to be aware of the possibility that an error might be detected after performing incremental processing. 5. This section includes example requests and responses encoded in both known-length and indefinite-length forms. 5.1. The example HTTP/1.1 message in ex-request shows the content of a "message/http". Valid HTTP/1.1 messages require lines terminated with CRLF (the two bytes 0x0a and 0x0d).  For simplicity and consistenct, the content of these examples is limited to text, which also uses CRLF for line endings. This can be expressed as a binary message (type "message/bhttp") using a known-length encoding as shown in hexadecimal in ex-bink- request.  ex-bink-request view includes some of the text alongside to show that most of the content is not modified. This example shows that the Host header field is not replicated in the :authority field, as is required for ensuring that the request is reproduced accurately; see Section 8.1.2.3 of H2. The same message can be truncated with no effect on interpretation. In this case, the last two bytes - corresponding to content and a trailer section - can each be removed without altering the semantics of the message. The same message, encoded using an indefinite-length encoding is shown in ex-bini-request.  As the content of this message is empty, the difference in formats is negligible. This indefinite-length encoding can be truncated by two bytes in the same way. 5.2. Response messages can contain interim (1xx) status codes as the message in ex-response shows. ex-response includes examples of informational status codes defined in RFC2518 and RFC8297. As this is a longer example, only the indefinite-length encoding is shown in ex-bini-response.  Note here that the specific text used in the reason phrase is not retained by this encoding. A response that uses the chunked encoding (Section 7.1 of MESSAGING) as shown for ex-chunked can be encoded by preserving chunk boundaries using indefinite-length encoding, which minimizes buffering needed to translate into the binary format.  However, these boundaries do not need to be retained and any chunk extensions cannot be conveyed using the binary format. ex-bink-chunked shows this message using the known-length coding. Note that the transfer-encoding header field is removed. 6. The message/http media type can be used to enclose a single HTTP request or response message, provided that it obeys the MIME restrictions for all "message" types regarding line length and encodings. message bhttp N/A None only "8bit" or "binary" is permitted see N/A this specification N/A N/A see Authors' Addresses section COMMON N/A see Authors' Addresses section IESG 7. Many of the considerations that apply to HTTP message handling apply to this format; see Section 17 of HTTP and Section 11 of MESSAGING for common issues in handling HTTP messages. Strict parsing of the format with no tolerance for errors can help avoid a number of attacks.  However, implementations still need to be aware of the possibility of resource exhaustion attacks that might arise from receiving large messages, particularly those with large numbers of fields. The format is designed to allow for minimal state when translating for use with HTTP proper.  However, producing a combined value for fields, which might be necessary for the "Cookie" field when translating this format (like HTTP/1.1 MESSAGING), can require the commitment of resources.  Implementations need to ensure that they aren't subject to resource exhaustion attack from a maliciously crafted message. 8. Please add the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-type for the media type "message/ bhttp". 9.  References 9.1.  URIs [1] https://www.iana.org/assignments/media-types </ins>


Encrypt (seal) "request" with "aad" as associated data using "context", yielding ciphertext "ct". <del> Concatenate the three values of "aad", "enc", and "ct", yielding an Encapsulated Request "enc_request". </del>  Note that "enc" is of fixed-length, so there is no ambiguity in parsing this structure. In pseudocode, this procedure is as follows: <del> Servers MUST verify that the request padding consists of all zeroes before processing the corresponding Message. </del> 5.2. Given an HPKE context "context", a request message "request", and a "enc_response" as follows: Export a secret "secret" from "context", using the string <del> "response" as a label.  The length of this secret is "Nk" - the length of an AEAD key assocated with "context". </del>  Generate a random value of length "max(Nn, Nk)" bytes, called "response_nonce". The client uses these values to decrypt "ct" using the Open function provided by the AEAD.  Decrypting might produce an error, as follows: <del> 5.3. Plaintext Messages support arbitrary length padding.  Clients and servers MAY pad HTTP messages as needed to hide metadata leakage through ciphertext length. </del> 6. A client interacts with the oblivious proxy resource by constructing

One question we might want to ask (even though I think we have the right answer already) is: should be included in AAD? I also removed the padding text, since we moved that to the bhttp spec.
It's a reasonable question, but I think we can safely assume that a single key can't be used with multiple KEMs.
In principle it might be possible, but I think we should prohibit it.

Encrypt (seal) "request" with "aad" as associated data using "context", yielding ciphertext "ct".  <ins> Concatenate the values of "aad", "enc", and "ct", yielding an Encapsulated Request "enc_request". </ins> Note that "enc" is of fixed-length, so there is no ambiguity in parsing this structure. In pseudocode, this procedure is as follows:  5.2. Given an HPKE context "context", a request message "request", and a "enc_response" as follows: Export a secret "secret" from "context", using the string  <ins> "response" as context.  The length of this secret is "max(Nn, Nk)", where "Nn" and "Nk" are the length of AEAD key and nonce associated with "context". </ins> Generate a random value of length "max(Nn, Nk)" bytes, called "response_nonce". The client uses these values to decrypt "ct" using the Open function provided by the AEAD.  Decrypting might produce an error, as follows:  6. A client interacts with the oblivious proxy resource by constructing


The client removes the encapsulation to obtain the response to the original request.  4. A client needs to acquire information about the key configuration of

This is more background than I would normally like to include, but it seems prudent at this stage. The example usage bit might go eventually, but I can see us keeping some sort of applicability statement.
I recognize that / added some conditions and use cases, but it would help readers like me to have even more. A separate explainer document/readme would be fine too. In the meeting chat today, I heard the suggestion of requesting map tiles as another use case, which makes sense to me: asking for map tiles is itself pretty revealing if connected to the user's identity (it might reveal your current location, or a location where you're going to be) and can be pretty transactional/stateless. Maybe search queries are similar. Safe browsing lookups was also mentioned. Having more example use cases might help in resolving other questions, like whether to support multiple HTTP requests at a time, or non-HTTP message bodies, etc. Ideally, we could explain the conditions under which adoption of ohttp will be useful -- maybe, a privacy need for reducing linkability of multiple requests or of a single request from user identity, and HTTP requests that are typically stateless or single-shot in some way, and frequent or performant-requiring at a certain level.
NAME added some more examples. NAME I think we can safely close this now, so I'm going to go ahead and do that. Please reopen with concrete suggestions if you think the new text is insufficient!
A little more text on the narrow applicability of this would be helpful. Some people originally interpreted this as having broader application than was ever intended.
NAME can we close this with ?
Yep. I forgot to link it.

The client removes the encapsulation to obtain the response to the original request. <ins> 3.1. Oblivious HTTP has limited applicability.  Many uses of HTTP benefit from being able to carry state between requests, such as with cookies (RFC6265), authentication (Section 11 of I-D.ietf-httpbis-semantics), or even alternative services (RFC7838).  Oblivious HTTP seeks to prevent this sort of linkage, which requires that applications not carry state between requests. Oblivious HTTP is primarily useful where privacy risks associated with possible stateful treatment of requests are sufficiently negative that the cost of deploying this protocol can be justified. Oblivious HTTP is simpler and less costly than more robust systems, like Prio (PRIO) or Tor (Dingledine2004), which can provide stronger guarantees at higher operational costs. Oblivious HTTP is more costly than a direct connection to a server. Some costs, like those involved with connection setup, can be amortized, but there are several ways in which oblivious HTTP is more expensive than a direct request: Each oblivious request requires at least two regular HTTP requests, which adds latency. Each request is expanded in size with additional HTTP fields, encryption-related metadata, and AEAD expansion. Deriving cryptographic keys and applying them for request and response protection takes non-negligible computational resources. Examples of where preventing the linking of requests might justify these costs include: DNS queries.  DNS queries made to a recursive resolver reveal information about the requester, particularly if linked to other queries. Telemetry submission.  Applications that submit reports about their usage to their developers might use oblivious HTTP for some types of moderately sensitive data. </ins> 4. A client needs to acquire information about the key configuration of


The server-chosen "response_nonce" field ensures that responses have unique AEAD keys and nonces even when requests are replayed.  9. Please update the "Media Types" registry at

And recommend key rotation as a mitigation strategy.
Compromise of the client after it sends the request can be used to recover the keys used to decrypt (or falsify) the response.
NAME suggests that we need to consider how to time-bound server keys to limit the risk of secret key exposure.

The server-chosen "response_nonce" field ensures that responses have unique AEAD keys and nonces even when requests are replayed. <ins> 8.5. This design does not provide post-compromise security for responses. A client only needs to retain keying material that might be used compromise the confidentiality and integrity of a response until that response is consumed, so there is negligible risk associated with a client compromise. A server retains a secret key that might be used to remove protection from messages over much longer periods.  A server compromise that provided access to the oblivious request resource secret key could allow an attacker to recover the plaintext of all requests sent toward affected keys and all of the responses that were generated. Accessing requests and responses also requires access to requests and responses, which implies either compromise of TLS connections or collusion with the oblivious proxy resource. The total number of affected messages affected by server key compromise can be limited by regular rotation of server keys. </ins> 9. Please update the "Media Types" registry at


9.  Please update the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-types for the media types "message/ ohttp-req", "message/ohttp-res", and "application/ohttp-keys". <del> 10.  References </del>  <del> 10.1.  URIs </del>  [1] https://www.iana.org/assignments/media-types

A server that wants to undermine this process can ensure that clients are provided with individual keys. Though there is a limited number of key identifiers (though see also ), a server could extend the number of identifiers with a little bit of trial decryption. Or the server could just use this ability selectively. Though the draft deliberately avoids talking about how clients learn about keys, we might talk about the implications of acquiring keys from servers. If the server is responsible for issuing keys, then the server can target individuals with specific keys. NAME suggests that maybe we could have the proxy provide keys. This is not a bad way to deal with this problem, though it places more trust in the proxy as the proxy now needs to be trusted not to provide a key for which it has the secret key. That might still work if the key could be further authenticated by the server, but that's a rabbit hole of its own.

9. <ins> One goal of this design is that independent client requests are only linkable by the chosen KeyConfig.  The oblivious proxy and request resources can link requests using the same KeyConfig by matching KeyConfig.key_id.  The oblivious proxy can also link requests using the public key corresponding to KeyConfig.key_id.  The proxy cannot determine precisely which public key was used to encapsulate a request, given that multiple public keys may have matching key identifiers. Request resources are capable of linking requests depending on how KeyConfigs are produced by servers and discovered by clients. Specifically, servers can maliciously construct KeyConfigs to track individual clients.  While KeyConfig discovery is outside the scope of this specification, clients should consider this tracking vector in deployments.  Applications using this design should provide accommodations to mitigation this tracking vector. 10. </ins> Please update the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-types for the media types "message/ ohttp-req", "message/ohttp-res", and "application/ohttp-keys".  <ins> 11.  References </ins>  <ins> 11.1.  URIs </ins> [1] https://www.iana.org/assignments/media-types


In order to ensure that clients do not encapsulate messages that other entities can intercept, the key configuration MUST be <del> authenticated and have integrity protection.  One way to ensure integrity for key configuration is for the oblivious request resource to serve content to the client directly, using HTTPS and the "application/ohttp-keys" media type; see ohttp-keys. </del>  Specifying a format for expressing the information a client needs to construct an encapsulated request ensures that different client 9. One goal of this design is that independent client requests are only <del> linkable by the chosen KeyConfig.  The oblivious proxy and request resources can link requests using the same KeyConfig by matching KeyConfig.key_id.  The oblivious proxy can also link requests using the public key corresponding to KeyConfig.key_id.  The proxy cannot determine precisely which public key was used to encapsulate a request, given that multiple public keys may have matching key identifiers. </del>  Request resources are capable of linking requests depending on how KeyConfigs are produced by servers and discovered by clients. <del> Specifically, servers can maliciously construct KeyConfigs to track individual clients.  While KeyConfig discovery is outside the scope of this specification, clients should consider this tracking vector in deployments.  Applications using this design should provide accommodations to mitigation this tracking vector. </del>  10.

The whole mess that is key configuration discovery needs real work. There are cases where trust suffices, but we can't rely on that here. What this does is tweak the language to more clearly note the lines of responsibility for this draft. I also noted that we didn't talk about trial decryption and tracking, so I added that. It seemed like we didn't need to mention multiple keys with the same identifier twice after that.

In order to ensure that clients do not encapsulate messages that other entities can intercept, the key configuration MUST be  <ins> authenticated and have integrity protection. This document describes the "application/ohttp-keys" media type; see ohttp-keys.  This media type might be used, for example with HTTPS, as part of a system for configuring or discovering key configurations.  Note however that such a system needs to consider the potential for key configuration to be used to compromise client privacy; see privacy. </ins> Specifying a format for expressing the information a client needs to construct an encapsulated request ensures that different client 9. One goal of this design is that independent client requests are only  <ins> linkable by the chosen key configuration.  The oblivious proxy and request resources can link requests using the same key configuration by matching KeyConfig.key_id, or, if the oblivious target resource is willing to use trial decryption, a limited set of key configurations that share an identifier.  An oblivious proxy can link requests using the public key corresponding to KeyConfig.key_id. </ins> Request resources are capable of linking requests depending on how KeyConfigs are produced by servers and discovered by clients.  <ins> Specifically, servers can maliciously construct key configurations to track individual clients.  A specific method for a client to acquire key configurations is not included in this specification.  Clients need to consider these tracking vectors when choosing a discovery method.  Applications using this design should provide accommodations to mitigate tracking. </ins> 10.


A proxy MUST NOT add information about the client identity when forwarding requests.  This includes the Via field, the Forwarded <del> field FORWARDED, and any similar information. </del>  8.2.1.

But they should. Consider the case where clients have equal-sized messages, but are making requests using different key identifiers or different request targets. If that information could be linked to certain clients, encryption would increase the size of the anonymity set from the perspective of a passive observer for little extra cost.
Done. PTAL.
Clients and proxies create independent TLS connections to the upstream node in the OHTTP chain. This raises some interesting questions: What if the trust stores between client and proxy are disjoint? What if the proxy completely disables server authentication in its connection to the target? The does not assume a secure connection between proxy and target, so I don't think this has an effect on the security properties of OHTTP. That said, it may be worth noting explicitly, if we don't already do so.
Yeah, the same applies here. We rely on the proxy not leaking certain information, but that does not necessarily extend to a reliance on TLS for that hop. Worth noting.

A proxy MUST NOT add information about the client identity when forwarding requests.  This includes the Via field, the Forwarded  <ins> field FORWARDED, and any similar information.  A client does not depend on the proxy using an authenticated and encrypted connection to the oblivious request resource, only that information about the client not be attached to forwarded requests. </ins> 8.2.1.


10.  Please update the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-types for the media types "message/ ohttp-req", "message/ohttp-res", and "application/ohttp-keys". <del> 11.  References </del>  <del> 11.1.  URIs </del>  [1] https://www.iana.org/assignments/media-types

I wanted to add more then just interception text here. So I added a little about performance and the ODoH study made some of those costs clear enough to cite. I anticipate this section growing over time. Some of the material we might add is already in other sections of the doc (DoS management at the proxy, for example), but we might move that around some. I also expect the working group to focus rather narrowly on this topic. Let's leave this open so that we have something to point to for IETF discussions.

10. <ins> Using Oblivious HTTP adds both cryptographic and latency to requests relative to a simple HTTP request-response exchange.  Deploying proxy 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 found that deploying proxies close to servers was most effective in minimizing additional latency. 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 middleboxes devices might not receive updates that would allow Oblivious HTTP to be correctly identified using the media types defined in media-types. 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. 11. </ins> Please update the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-types for the media types "message/ ohttp-req", "message/ohttp-res", and "application/ohttp-keys".  <ins> 12.  References </ins>  <ins> 12.1.  URIs </ins> [1] https://www.iana.org/assignments/media-types


authenticated so that it can be attributed to the oblivious request resource; see key-configuration.  Clients MUST NOT include identifying information in the request that is encapsulated.  Identifying information includes cookies COOKIES, authentication credentials or tokens, and any information that might

This may not be obvious, so perhaps good to note.
I'll move most of the text to the client section, leaving a pointer in the proxy section.
It probably depends. One could fetch the HPKE key and apply policy for each OHTTP message, which would be equivalent to applying policy for each HTTP connection if you were not using OHTTP, or one could fetch and apply policy to the HPKE key for many OHTTP messages. The results are different.
NAME this should be good to go.
Thanks for changing it up. I'm not 100% on the structure of this yet, but it is more important to get the info in place.

authenticated so that it can be attributed to the oblivious request resource; see key-configuration. <ins> Since clients connect directly to the proxy instead of the target, application configurations wherein clients make policy decisions about target connections, e.g., to apply certificate pinning, are incompatible with Oblivious HTTP.  In such 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 proxy connections, though these might not provide the same properties as policies enforced directly on target connections.  When this difference is relevant, applications can instead connect directly to the target at the cost of either privacy or performance. </ins> Clients MUST NOT include identifying information in the request that is encapsulated.  Identifying information includes cookies COOKIES, authentication credentials or tokens, and any information that might


This is mostly a port from ODoH to start. We can modify/tweak as needed going forward.
In our discussion of , NAME and I explored the ways in which nonce reuse might occur. The problem arises if the client reuses . This leads to nonce reuse on the request for sure. However, if the request is the same, the problem shifts to the response. Unless the response is also identical, we get nonce reuse on the response instead. We then discussed options for safeguarding against this. As the client is expected to include randomness in its derivation of its key, it seems like the failing is not something that adding extra steps at the client will address. So I don't think that adding anything to the request really helps. Separately, HPKE code is supposed to be stateful. Reuse of a context should result in the sequence number increasing, leading to a nonce that the server cannot reconstruct (unless it also counts and does trial decryption, but that's two bad actors). That should ensure that the client doesn't use the same nonce twice. You really have to have bad randomness at the client to get into a deliberate nonce reuse scenario if you trust that the HPKE implementation is properly stateful. However, a response that is generated in response to a replayed request is something I am concerned about. We know that HTTP clients routinely do, even if the request is marked POST. So some protection here is warranted. For this, I'm going to suggest that we change from salting based on the request to salting based on . Hashing the entire message is likely to be very expensive, both in terms of cycles, but also in terms of state. The public key should be enough to ensure the binding in the case where a client doesn't reuse the context, and the new randomness should help in case of reuse. I think that it's OK to use 128 bits of extra randomness here, but we don't have a handy constant to hang that on. Nn is 96, Nk could be 128 or 256 depending on the AEAD. What we really want is the block size, but that isn't a property that is exposed anywhere.
I think this is the right design on balance. If we were more conservative, we could include the entire client request (including ), but that doesn't really hedge against the misuse scenario. I'll propose text in .
Fixed in .
The format I've sketched out for binary messages can be padded with zeros without needing a separate padding field. That's probably a feature worth mentioning in both drafts.
Padding is in the binary HTTP draft now.

4. <ins> HTTP message encapsulation uses HPKE for request and response encryption.  An encapsulated HTTP message includes the following values: A binary-encoded HTTP message [CITEME]. Padding of arbitrary length which MUST contain all zeroes. The encoding of an HTTP message is as follows: This structure is then encrypted under a key with a specific identity, forming an encapsulated HTTP message, with the following structure: The encapsulated message Key ID, as well as the encryption mechanics, are different for requests and responses, as described below. </ins> 4.1. <ins> Clients encapsulate a request Plaintext Message "msg" with an HPKE public key "pkR", whose Key Identifier is "keyID" as follows: Compute an HPKE context using "pkR", yielding "context" and encapsulation key "enc" Encrypt (seal) "msg" with "keyID" as associated data using "context", yielding ciphertext "ct" Concatenate "enc" and "ct", yielding an Encrypted Message "encrypted_msg" In pseudocode, this procedure is as follows: Clients construct the Encapsulated Message "req" using "keyID" and "encrypted_msg". Servers decrypt an Encapsulated Message by reversing this process. Given an Encapsulated Message "req" request with Key Identifier "keyID" corresponding to an existing HPKE private key "skR", servers decapsulate the Message as follows: Parse the "req" Encrypted Message as the concatenation of "enc" and "encrypted_message" Compute an HPKE context using "skR" and the encapsulated key "enc" from "req", yielding "context" Decrypt "encrypted_message" with "keyID" as associated data, yielding "msg" or an error on failure In pseudocode, this procedure is as follows: Servers MUST verify that the Plaintext Message padding consists of all zeroes before processing the corresponding Message. </ins> 4.2. <ins> Given an HPKE context "context" and a response Plaintext Message "resp" sent in response to a Plaintext Message "req", servers encrypt the data as follows: Derive a symmetric key and nonce from "context" Encrypt "resp" with empty Key Identifier "emptyKeyID" as associated data, yielding "encrypted_msg" In pseudocode, this procedure is as follows: Extract and Expand are functions corresponding to the HPKE context's KDF algorithm, and Seal, Nk, and Nn correspond to the HPKE context's AEAD algorithm. Clients decrypt an Encapsulated Message by reversing this process. Namely, they derive the necessary AEAD parameters from an existing HPKE context and then decrypt (open) the Encapsulated Message encrypted message. 4.3. Plaintext Messages support arbitrary length padding.  Clients and servers MAY pad HTTP messages as needed to hide metadata leakage through ciphertext length. </ins> 5. 5.1.


5. HTTP message encapsulation uses HPKE for request and response <del> encryption.  An encapsulated HTTP message includes the following values: A binary-encoded HTTP message; see BINARY. Padding of arbitrary length which MUST contain all zeroes. The encoding of an HTTP message is as follows: </del>  An Encapsulated Request is comprised of a length-prefixed key identifier and a HPKE-protected request message.  HPKE protection response, which might not be what some clients want. A proxy can use padding to reduce the effectiveness of traffic <del> analysis. </del>  A proxy that forwards large volumes of exchanges can provide better privacy by providing larger sets of messages that need to be matched.

See discussion on-list.
See also URL
If we're pinning the contents of the encapsulation to message/bhttp, it seems like we also ought to change the HPKE info strings to match ("request" -> "message/bhttp-request" or whatever). When someone tries to reuse this layer of encapsulation, I think they ought to change the info string accordingly to match whatever the underlying type is. And it might not hurt to add guidance to such people in the security considerations. Can we do this in a followup PR?
I agree that we should think about content types a bit more, but that seems appropriate for another PR.
With , we're enforcing a 1:1 mapping between message/ohttp-req and message/bhttp, but we don't have any cryptographic enforcement of this pairing. If someone were to reuse the encapsulation mechanism in this document with another message type, say, to encapsulate application/dns-message values with a application/oblivious-dns-message wrapper, then we'd probably want to ensure that targets can't get confused between these two types. We could use the HPKE info string to separate these two protocols. (This is related to .)
The format I've sketched out for binary messages can be padded with zeros without needing a separate padding field. That's probably a feature worth mentioning in both drafts.
Padding is in the binary HTTP draft now.

5. HTTP message encapsulation uses HPKE for request and response  <ins> encryption.  An encapsulated HTTP message includes a binary-encoded HTTP message and no other content; see BINARY. </ins> An Encapsulated Request is comprised of a length-prefixed key identifier and a HPKE-protected request message.  HPKE protection response, which might not be what some clients want. A proxy can use padding to reduce the effectiveness of traffic  <ins> analysis.  Padding is a capability provided by binary HTTP messages; see Section 3.8 of BINARY. </ins> A proxy that forwards large volumes of exchanges can provide better privacy by providing larger sets of messages that need to be matched.


The client then constructs an encapsulated request, "enc_request", as follows: <del> Compute an HPKE context using "pkR", yielding "context" and encapsulation key "enc". </del>  Construct associated data, "aad", by concatenating the values of "keyID", "kemID", "kdfID", and "aeadID", as one 8-bit integer and that the server is unwilling to use with "skR", the server returns an error. <del> Compute an HPKE context using "skR" and the encapsulated key "enc", yielding "context". </del>  Construct additional associated data, "aad", from "keyID", "kemID", "kdfID", and "aeadID" or as the first seven bytes of response "response", servers generate an Encapsulated Response "enc_response" as follows: <del> Export a secret "secret" from "context", using the string "response" as context.  The length of this secret is "max(Nn, Nk)", where "Nn" and "Nk" are the length of AEAD key and nonce </del>  associated with "context". Generate a random value of length "max(Nn, Nk)" bytes, called corresponds to the key identifier, but the encapsulated request cannot be successfully decrypted using the key.  8.4. Encapsulated requests can be copied and replayed by the oblivious

This is a breaking change, but I think that it's worthwhile. "request" is an extraordinary generic string and the risk of it being used in another context is too high.
We can address test vectors in a followup PR.

The client then constructs an encapsulated request, "enc_request", as follows:  <ins> Compute an HPKE context using "pkR" and a label of "ohttp request", yielding "context" and encapsulation key "enc". </ins> Construct associated data, "aad", by concatenating the values of "keyID", "kemID", "kdfID", and "aeadID", as one 8-bit integer and that the server is unwilling to use with "skR", the server returns an error.  <ins> Compute an HPKE context using "skR", a label of "ohttp request", and the encapsulated key "enc", yielding "context". </ins> Construct additional associated data, "aad", from "keyID", "kemID", "kdfID", and "aeadID" or as the first seven bytes of response "response", servers generate an Encapsulated Response "enc_response" as follows:  <ins> Export a secret "secret" from "context", using the string "ohttp response" as context.  The length of this secret is "max(Nn, Nk)", where "Nn" and "Nk" are the length of AEAD key and nonce </ins> associated with "context". Generate a random value of length "max(Nn, Nk)" bytes, called corresponds to the key identifier, but the encapsulated request cannot be successfully decrypted using the key. <ins> A server MUST ensure that the HPKE keys it uses are not valid for any other protocol that uses HPKE with the "ohttp request" label. Designers of other protocols, especially new versions of this protocol, can ensure key diversity by choosing a different label in their use of HPKE.  The "ohttp response" label was chosen for symmetry only as it provides key diversity only within the HPKE context created using the "ohttp request" label. </ins> 8.4. Encapsulated requests can be copied and replayed by the oblivious


responses and so might be ignorant of the use of oblivious HTTP to reach it.  3. A client learns the following: encryption.  An encapsulated HTTP message includes the following values: <del> A binary-encoded HTTP message [CITEME]. </del>  Padding of arbitrary length which MUST contain all zeroes. The encoding of an HTTP message is as follows: <del> This structure is then encrypted under a key with a specific identity, forming an encapsulated HTTP message, with the following structure: </del>  <del> The encapsulated message Key ID, as well as the encryption mechanics, are different for requests and responses, as described below. </del>  4.1. <del> Clients encapsulate a request Plaintext Message "msg" with an HPKE public key "pkR", whose Key Identifier is "keyID" as follows: </del>  Compute an HPKE context using "pkR", yielding "context" and <del> encapsulation key "enc" </del>  <del> Encrypt (seal) "msg" with "keyID" as associated data using "context", yielding ciphertext "ct" </del>  <del> Concatenate "enc" and "ct", yielding an Encrypted Message "encrypted_msg" </del>  In pseudocode, this procedure is as follows: <del> Clients construct the Encapsulated Message "req" using "keyID" and "encrypted_msg". </del>  <del> Servers decrypt an Encapsulated Message by reversing this process. Given an Encapsulated Message "req" request with Key Identifier "keyID" corresponding to an existing HPKE private key "skR", servers decapsulate the Message as follows: </del>  <del> Parse the "req" Encrypted Message as the concatenation of "enc" and "encrypted_message" </del>  <del> Compute an HPKE context using "skR" and the encapsulated key "enc" from "req", yielding "context" </del>  <del> Decrypt "encrypted_message" with "keyID" as associated data, yielding "msg" or an error on failure </del>  In pseudocode, this procedure is as follows: <del> Servers MUST verify that the Plaintext Message padding consists of all zeroes before processing the corresponding Message. </del>  4.2. <del> Given an HPKE context "context" and a response Plaintext Message "resp" sent in response to a Plaintext Message "req", servers encrypt the data as follows: </del>  <del> Derive a symmetric key and nonce from "context" </del>  <del> Encrypt "resp" with empty Key Identifier "emptyKeyID" as associated data, yielding "encrypted_msg" </del>  <del> In pseudocode, this procedure is as follows: </del>  <del> Extract and Expand are functions corresponding to the HPKE context's KDF algorithm, and Seal, Nk, and Nn correspond to the HPKE context's AEAD algorithm. </del>  <del> Clients decrypt an Encapsulated Message by reversing this process. Namely, they derive the necessary AEAD parameters from an existing HPKE context and then decrypt (open) the Encapsulated Message encrypted message. </del>  4.3.

This just expands the text and tries to be explicit about everything. The one change I've included here is a change from 32 to Nsk for the exported secret. I think that's in line with the HPKE intent. Constants make me nervous.
I'm going to merge, and then apply my suggestions and the nonce change in a followup PR.
In our discussion of , NAME and I explored the ways in which nonce reuse might occur. The problem arises if the client reuses . This leads to nonce reuse on the request for sure. However, if the request is the same, the problem shifts to the response. Unless the response is also identical, we get nonce reuse on the response instead. We then discussed options for safeguarding against this. As the client is expected to include randomness in its derivation of its key, it seems like the failing is not something that adding extra steps at the client will address. So I don't think that adding anything to the request really helps. Separately, HPKE code is supposed to be stateful. Reuse of a context should result in the sequence number increasing, leading to a nonce that the server cannot reconstruct (unless it also counts and does trial decryption, but that's two bad actors). That should ensure that the client doesn't use the same nonce twice. You really have to have bad randomness at the client to get into a deliberate nonce reuse scenario if you trust that the HPKE implementation is properly stateful. However, a response that is generated in response to a replayed request is something I am concerned about. We know that HTTP clients routinely do, even if the request is marked POST. So some protection here is warranted. For this, I'm going to suggest that we change from salting based on the request to salting based on . Hashing the entire message is likely to be very expensive, both in terms of cycles, but also in terms of state. The public key should be enough to ensure the binding in the case where a client doesn't reuse the context, and the new randomness should help in case of reuse. I think that it's OK to use 128 bits of extra randomness here, but we don't have a handy constant to hang that on. Nn is 96, Nk could be 128 or 256 depending on the AEAD. What we really want is the block size, but that isn't a property that is exposed anywhere.
I think this is the right design on balance. If we were more conservative, we could include the entire client request (including ), but that doesn't really hedge against the misuse scenario. I'll propose text in .
Fixed in .

responses and so might be ignorant of the use of oblivious HTTP to reach it. <ins> This draft includes pseudocode that uses the functions and conventions defined in HPKE. This draft uses the variable-length integer encoding from Section 16 of QUIC.  Encoding and decoding variable-length integers to a sequence of bytes are described using the functions "vencode()" and "vdecode()".  The function "len()" takes the length of a sequence of bytes. Formats are described using notation from Section 1.3 of QUIC. </ins> 3. A client learns the following: encryption.  An encapsulated HTTP message includes the following values:  <ins> A binary-encoded HTTP message; see BINARY. </ins> Padding of arbitrary length which MUST contain all zeroes. The encoding of an HTTP message is as follows:  <ins> An Encapsulated Request is comprised of a length-prefixed key identifier and a HPKE-protected request message.  HPKE protection includes an encapsulated KEM shared secret (or "enc"), plus the AEAD- protected request message.  An Encapsulated Request is shown in fig- enc-request. request describes the process for constructing and processing an Encapsulated Request. </ins>  <ins> Responses are bound to responses and so consist only of AEAD- protected content.  response describes the process for constructing and processing an Encapsulated Response. </ins> 4.1.  <ins> Clients encapsulate a request "request" with an HPKE public key "pkR", whose Key Identifier is "keyID" as follows: </ins> Compute an HPKE context using "pkR", yielding "context" and  <ins> encapsulation key "enc". </ins>  <ins> Construct additional associated data, "aad", by prepending a single byte with a value of 0x01 to the key identifier.  The key identifier length is not included in the AAD. </ins>  <ins> Encrypt (seal) "request" with "keyID" as associated data using "context", yielding ciphertext "ct". Concatenate the length of "keyID" as a variable-length integer, "keyID", "enc" and "ct", yielding an Encapsulated Request "enc_request". </ins> In pseudocode, this procedure is as follows:  <ins> Servers decrypt an Encapsulated Request by reversing this process. Given an Encapsulated Request "enc_request", a server: </ins>  <ins> Parses "enc_request" into "keyID", "enc", and "ct" (indicated using the function "parse()" in pseudocode).  The server is then able to find the HPKE private key, "skR", corresponding to "keyID". </ins>  <ins> Compute an HPKE context using "skR" and the encapsulated key "enc", yielding "context". </ins>  <ins> Construct additional associated data, "aad", by prepending a single byte with a value of 0x01 to the key identifier. </ins>  <ins> Decrypt "ct" using "aad" as associated data, yielding "request" or an error on failure. </ins> In pseudocode, this procedure is as follows:  <ins> Servers MUST verify that the request padding consists of all zeroes before processing the corresponding Message. </ins> 4.2.  <ins> Given an HPKE context "context", a request message "request", and a response "response", servers generate an Encapsulated Response "enc_response" as follows: </ins>  <ins> Export a secret "secret" from "context", using the string "response" as a label.  The length of this secret is "Nsk" - the length of the secret assocated with "context". </ins>  <ins> Extract a pseudorandom key "prk" using the "Extract" function provided by the KDF algorithm associated with "context".  The "ikm" input to this function is "secret"; the "salt" input is "request". </ins>  <ins> Use the "Expand" function provided by the same KDF to extract an AEAD key "key", of length "Nk" - the length of the keys used by the AEAD associated with "context".  Generating "key" uses a label of "key". </ins>  <ins> Use the same "Expand" function to extract a nonce "nonce" of length "Nn" - the length of the nonce used by the AEAD. Generating "nonce" uses a label of "nonce". Construct additional associated data "aad", that consists of a single byte with a value of 0x02. Encrypt "response", passing the AEAD function Seal the values of "key", "nonce", "aad", and a "pt" input of "request", which yields "enc_response". In pseudocode, this procedure is as follows: </ins>  <ins> Clients decrypt an Encapsulated Request by reversing this process. That is, clients follow the same process to derive values for "key", "nonce", and "aad".  The client then decrypts the Encapsulated Response using the Open function provided by the AEAD.  Decrypting might produce an error, as shown. </ins> 4.3.


The client then constructs an encapsulated request, "enc_request", as follows: <del> Compute an HPKE context using "pkR" and a label of "ohttp </del>  request", yielding "context" and encapsulation key "enc". Construct associated data, "aad", by concatenating the values of that the server is unwilling to use with "skR", the server returns an error. <del> Compute an HPKE context using "skR", a label of "ohttp request", and the encapsulated key "enc", yielding "context". </del>  Construct additional associated data, "aad", from "keyID", "kemID", "kdfID", and "aeadID" or as the first seven bytes of response "response", servers generate an Encapsulated Response "enc_response" as follows: <del> Export a secret "secret" from "context", using the string "ohttp response" as context.  The length of this secret is "max(Nn, Nk)", where "Nn" and "Nk" are the length of AEAD key and nonce associated with "context". </del>  Generate a random value of length "max(Nn, Nk)" bytes, called "response_nonce". cannot be successfully decrypted using the key. A server MUST ensure that the HPKE keys it uses are not valid for any <del> other protocol that uses HPKE with the "ohttp request" label. </del>  Designers of other protocols, especially new versions of this protocol, can ensure key diversity by choosing a different label in <del> their use of HPKE.  The "ohttp response" label was chosen for </del>  symmetry only as it provides key diversity only within the HPKE <del> context created using the "ohttp request" label. </del>  8.4. 11.  Please update the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-types for the media types "message/ ohttp-req", "message/ohttp-res", and "application/ohttp-keys". <del> 12.  References </del>  <del> 12.1.  URIs </del>  [1] https://www.iana.org/assignments/media-types

Test vectors generated from the .
With , we're enforcing a 1:1 mapping between message/ohttp-req and message/bhttp, but we don't have any cryptographic enforcement of this pairing. If someone were to reuse the encapsulation mechanism in this document with another message type, say, to encapsulate application/dns-message values with a application/oblivious-dns-message wrapper, then we'd probably want to ensure that targets can't get confused between these two types. We could use the HPKE info string to separate these two protocols. (This is related to .)
The encapsulated payload could be something other than HTTP. We should be clear about our decision to do that and how alternative payloads might be carried if people wanted to do that.
The clear example here would be allowing just a DNS message body. As long as the client and target agreed, they could use that without the proxy knowing. We could just loosen the restrictions on what the body is, or we could use different content types (if you want the proxy to know what it is?).
I think that the way you would do that is to define a new media type for DNS and then just go for it. (Of course, you need to debate whether to include a length field and a transaction ID field.) Maybe we don't need to do that in this draft, but we could explain how that might look.
I'd do the following here (in line with ): Replace as the HPKE info string with the content-type of the inner message, be it or . That way, both client and target must agree on the type of encapsulated payload to actually use it. Thoughts?
That might work. We have "ohttp response" as the label for HKDF expansion on the response. What would you include there? Maybe you should just extend with "message/bhttp request" and "message/bhttp response" and then the swapping in the media type becomes trivial.
Yeah, that's what I had in mind.
Sorry about that. Misremembered how this worked; I should have double-checked.

The client then constructs an encapsulated request, "enc_request", as follows:  <ins> Compute an HPKE context using "pkR" and a label of "message/bhttp </ins> request", yielding "context" and encapsulation key "enc". Construct associated data, "aad", by concatenating the values of that the server is unwilling to use with "skR", the server returns an error.  <ins> Compute an HPKE context using "skR", a label of "message/bhttp request", and the encapsulated key "enc", yielding "context". </ins> Construct additional associated data, "aad", from "keyID", "kemID", "kdfID", and "aeadID" or as the first seven bytes of response "response", servers generate an Encapsulated Response "enc_response" as follows:  <ins> Export a secret "secret" from "context", using the string "message/bhttp response" as context.  The length of this secret is "max(Nn, Nk)", where "Nn" and "Nk" are the length of AEAD key and nonce associated with "context". </ins> Generate a random value of length "max(Nn, Nk)" bytes, called "response_nonce". cannot be successfully decrypted using the key. A server MUST ensure that the HPKE keys it uses are not valid for any  <ins> other protocol that uses HPKE with the "message/bhttp request" label. </ins> Designers of other protocols, especially new versions of this protocol, can ensure key diversity by choosing a different label in  <ins> their use of HPKE.  The "message/bhttp response" label was chosen for </ins> symmetry only as it provides key diversity only within the HPKE  <ins> context created using the "message/bhttp request" label. </ins> 8.4. 11. <ins> The encapsulated payload of an OHTTP request and response is a binary HTTP message BINARY.  Client and target agree on this encapsulated payload type by specifying the media type "message/bhttp" in the HPKE encapsulation info string and HPKE export context string for request and response encapsulation, respectively. Future specifications may repurpose the encapsulation mechanism described in hpke-encapsulation, provided that the content type of the encapsulated payload is appropriately reflected in the HPKE info and context strings.  For example, if a future specification were to use the encapsulation mechanism in this specification for DNS messages, identified by the "application/dns-message" media type, then the HPKE info string SHOULD be "application/dns-message request" for request encapsulation, and the HPKE export context string should be "application/dns-message response" for response encapsulation. 12. </ins> Please update the "Media Types" registry at https://www.iana.org/assignments/media-types [1] with the registration information in media-types for the media types "message/ ohttp-req", "message/ohttp-res", and "application/ohttp-keys".  <ins> 13.  References </ins>  <ins> 13.1.  URIs </ins> [1] https://www.iana.org/assignments/media-types


8.4. Encapsulated requests can be copied and replayed by the oblivious <del> proxy resource.  The design of oblivious HTTP does not assume that the oblivious proxy resource will not replay requests.  In addition, 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 employed are similar to TLS early data. </del>  A client or oblivious proxy resource MUST NOT automatically attempt to retry a failed request unless it receives a positive signal Clients SHOULD include a "Date" header field in encapsulated requests.  Though HTTP requests often do not include a "Date" header field, the value of this field might be used by a server to limit the <del> amount of requests it needs to track when preventing replays. </del>  A server can maintain state for requests for a small window of time over which it wishes to accept requests.  The server then rejects

This recommends the use of in requests as discussed on and . Most of the heavy lifting is outsourced to , so I won't merge this until we have some greater certainty about the destiny of that draft.
This seems like a separate issue, but: it seems really quite problematic if two requests had the same enc value but different values for t, as the client is then either reusing its key share or something else is going on. What my suggestion meant to say is that, if you get a value of t outside of the window, reject it or ask the client to retry with a new value of t. And use the new enc value to enforce your replay check for that given value t. That is, logically: Given (enc, t) and enc has not been seen before in t, accept. Given (enc, t) and enc has been seen in t, reject as replay. Given (enc, t') and t' is not the current time window, reject and ask them to retry
Yeah, that's right. I was interpreting your request as saying that you might need to do something with duplicate enc values even when you forgot the original.
In and , we're talking about adding a timestamp to requests. The problem with that is that - unlike TLS - we don't have any way to correct for gross clock errors. TLS manages this easily by leveraging a previous session to make that correction, reducing any relative clock error to the amount of drift over the week that a ticket can be used for early data. This design has nothing like that it can use. To correct for clock errors, we should think about whether the server should be able to signal that the request was rejected as a result of the time being too far out of synchronization.
The problem with allowing a server to correct your time is that you might be tricked into setting a time that is useful for tracking you. To that end, if we build a mechanism we need either to recommend that clients : seek an independent and trustworthy source of time rather than use any time that the server has provided; that is, the signal is an error code, without a direct means of correcting client time only use any server-provided time correction for an immediate retry; clients cannot remember that time for any requests that they wish to remain unlinked to the original; that is, it might be OK for a retry to be linked to the first attempt, but clients should not allow server-provided information to affect multiple requests
The current design doesn't add any data to requests, so servers are only able to provide anti-replay based on what they see: the encapsulation and the content of the HTTP request. One common factor in anti-replay designs is time. It is easier to apply anti-replay protections for timestamped requests as any state can be bounded in time: requests are rejected as being a replay if they fall outside a small time window. Should we include a timestamp in requests? Or, should we merely insist on the Date header field being present? It is somewhat for Date to be present on requests, but it is permitted. The other thing to consider is clock drift. TLS uses an anti-replay design that uses relative times, which means that only relative clock drift over a short time needs to be accounted for. Here, we have no prior synchronization, so we are forced to use absolute times. That likely means a greater allowance for clock drift. It also likely means that we need to include a new signal to clients for when their time is so badly off that their requests are being rejected. Experience shows that client clocks are terrifically bad, but given prompting they might be able to adjust to match the server clock. We need to be very careful to ensure that this sort of signal is properly authenticated, so that if clients do retry requests, they aren't subject to attacks that might cause them to be complicit in generating a replay attack. We might also consider a nonce or other anti-replay measures, but as clients are required to perform a new encapsulation for every request, the value can be used. (This issue is split from .)
The most obvious mechanism here for protecting against anti-replay is a timestamp in combination with a per-entry nonce. There are a few things to consider when deciding on how to encode the time. The first question is whether to encrypt the timestamp or not. An encrypted timestamp would result in servers spending the cycles necessary to decrypt a request before they can be sure if it is a replay. Each server will maintain nonces over a certain time window and discard requests that are fall outside this span (either earlier or later). An encrypted timestamp means that a server can still use the nonce to determine if a request is a replay before decrypting. However, requests from outside the time window will need to be decrypted to determine that they should be discarded. This is more work. However, we should assume that - under normal operation - replays will be rare. Clock skew is the obvious cause for a request falling outside of the time window that a server is tracking. However, that only really points to a need for having a wider time window or feedback that allows client time to be corrected. It's only the DoS cost that might play into a choice to avoid the extra computation cost. Validation of an HPKE-encrypted message is only possible after performing lots of work. For an attacker looking to exhaust server computation resources, it is possible to just send a server complete garbage. The server only learns that the message was garbage after performing an expensive key exchange and decryption. A timestamp on the outside of the message doesn't save this, as this can be set to whatever value best suits the attacker. That all leads me to suggest that including the timestamp under encryption is probably the best choice. Also, if we want to provide an authenticated signal for clock skew errors, it will be easier to have the timestamp under protection. Then, when the timestamp falls outside of the server time window, the server can generate an authenticated response. Using for the nonce part gives us a very high entropy value with very low collision probability. That allows us to use a timestamp that is quite coarse. The only reason we would want to increase clock resolution is so that servers can manage narrow anti-replay windows. Because we are operating in near-real time, anti-replay windows need to be wide enough to allow for clock skew and not a lot else. How much tolerance for clock skew we need will depend on whether we have good correction mechanisms in place. A coarser clock forces a server to maintain state for a slightly wider window, to allow for the requests within their target window to be adjusted by the coarseness of the clock. For instance, if the desired window is 10 minutes and the clock resolution is 1 day, then the window is expanded to 2 days. Thus, we should not reduce resolution too much. Provided that it is not too coarse, a coarse value reveals less about the clock of the client. Though we cannot stop some information about the client clock from leaking, less information is better. Note that we don't have to worry so much about values crossing thresholds (e.g., from one day to the next if the resolution is 1 day), as the server cannot link requests from the same client to use information about when it crosses that threshold. This isn't that much value as clients are able to randomize their clock to obscure the true value. This is likely to be very effective as a privacy protection as each request is handled in isolation. It must be possible to unambiguously represent any time in the period over which a server public key is valid. If the range of possible timestamp values is shorter than the lifetime of a key, the same timestamp value might identify multiple times in the lifetime of that key. A 32-bit value in seconds covers 136 years. If we take the low 32-bits of the current time in seconds, then a timestamp will be valid for multiple times 136 years apart. That's probably long enough that a server should have replaced its public key. Given what we know about public key usage in other contexts, it's probably wise to cover a period of multiple years. A 32-bit value in seconds is clearly good. Clock skew in the order of a second is extremely common, so this doesn't significantly increase the state a server needs to maintain. If we were to reduce the size to the next threshold and choose 24 bits, seconds doesn't work as that gives a range of only half a year. We can expand that 32 times (i.e., 17 years) by shifting by 5 bits and having a resolution of 32 seconds. That seems like a poor trade. That's enough for anti-replay (17 years is far too long to hold onto the same key), but the shift by 5 is annoying to implement, and the space savings are hardly significant. It also forces servers to keep state for a minimum period of 64 seconds, which is at the point that it could be longer than some servers would otherwise need to hold anti-replay state. The only benefit is the reduction in timer resolution from clients. The 32-bit value seems like the best answer. It's simple and effective. We can pack that in ahead of the request message and encrypt it.
I agree with the conclusion to encrypt the timestamp (proxies don't really need it and it's very little extra work) and on the resolution (clients that want to hide information can mask out the lower bits), but not quite on the range. In particular: given a validity window of length broken up into two windows represented by the same timestamp range, we don't want a replay from the first window to be valid in the second window. This is where I think we diverge. This seems to encourage poor key management practices. I'd prefer the spec take a stricter stance on this, and 24 bits seems like it would be quite reasonable in this regard. Servers can make sensible choices about what state their willing to store based on anticipated clock skew in honest clients, but the spec ultimately needs to be robust against malicious clients replaying messages at arbitrary points in the future within the validity window of a public key. At the end of the day, this seems to boil down to how long we want or expect keys to be valid, and I'm just not comfortable with allowances for extremely long windows.
Before designing a system, I think we should start by asking the threshold question of whether we need anti-replay at all. First, there are a number of systems which simply don't need it: anything that's idempotent on the server doesn't need replay detection. Even systems which are not idempotent quite likely have their own anti-replay mechanisms in order to deal with application-level replay conditions. For instance suppose that you have a telemetry or crash reporting system: you want it to be robust to transport-level failures which means that you keep retrying in the face of those failures. But the HTTP anti-replay mechanisms are at the TLS layer and so you also need application layer anti-replay. Finally, even if we do have an anti-replay mechanism at this layer, it's not going to be effective unless we provide strict application guidance. For instance, suppose that the proxy collects the client's message and then generates a transport error to force a replay at the client. If the client generates a new transaction (not unreasonable) then this won't trigger the anti-replay defenses. And of course if the client just re-sends the same message then the proxy learns information. For these reasons, I think we should consider punting this entirely.
I think it's true that we can't do much about replays of the same application data, since a server can't distinguish between the scenario you describe (proxy forces client to replay data with a new transaction) and a dishonest client that's simply replaying the same application data. But I think we can do something here about replays of the encapsulated requests. Whether or not we do seems to boil down to the threat model and the relative cost of attack. How hard is it for an attacker to trigger these client-side replays? Trivial for the proxy, as noted, and certainly easier than transparently storing and replaying client requests in the future. However, the client does have to actively generate the new transaction, so it has the opportunity to try and detect this sort of misbehavior. (Whether or not it could distinguish between a maliciously truncated transport connection by an on-path attacker, a maliciously truncated connection by the proxy, or a legitimate error seems a separable question.) On balance, I think the added complexity here is palatable for what can be a pretty trivial attack to be carried out by malicious proxies without the client seeing anything.
I certainly agree that the attack is trivial. My point is just that the application layer protocol that cares about replay will need A-R defenses anyway and so won't this be redundant?
Possibly, yeah. We should work through some use cases to see if it's truly needed at this layer.
There's an alternative: we suggest/recommend that clients populate the header field in requests and allow/encourage the anti-replay to occur at the next layer up. Parsing is annoying and error prone, but it would mean that the design is kept simple at this layer.
NAME that seems like a reasonable alternative!
LGTM after some reformatting. Thanks for the updates, NAME

8.4. Encapsulated requests can be copied and replayed by the oblivious  <ins> proxy resource.  The threat model for oblivious HTTP allows the possibility that an oblivious proxy 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 employed are similar to TLS early data. It is the responsibility of the application that uses oblivious HTTP to either reject replayed requests or to ensure that replayed requests have no adverse affects on their operation.  This section describes some approaches that are universally applicable and suggestions for more targeted techniques. </ins> A client or oblivious proxy resource MUST NOT automatically attempt to retry a failed request unless it receives a positive signal Clients SHOULD include a "Date" header field in encapsulated requests.  Though HTTP requests often do not include a "Date" header field, the value of this field might be used by a server to limit the  <ins> amount of requests it needs to track if it needs to prevent replay attacks. </ins> A server can maintain state for requests for a small window of time over which it wishes to accept requests.  The server then rejects