Change:
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.

Discussion:
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.