Original text:
Encoding an integer to a sequence of bytes in network byte order is described using the function "encode(n, v)", where "n" is the number <del> of bytes and "v" is the integer value.  The function "len()" returns the length of a sequence of bytes. </del>  Formats are described using notation from QUIC. The client then constructs an Encapsulated Request, "enc_request", from a binary encoded HTTP request, "request", as follows: <del> Compute an HPKE context using "pkR" and a label of "message/bhttp request", yielding "context" and encapsulation key "enc". Construct associated data, "aad", by concatenating the values of </del>  "keyID", "kemID", "kdfID", and "aeadID", as one 8-bit integer and three 16-bit integers, respectively, each in network byte order. <del> Encrypt (seal) "request" with "aad" as associated data using "context", yielding ciphertext "ct". </del>  <del> Concatenate the values of "aad", "enc", and "ct", yielding an </del>  Encrypted Request "enc_request". Note that "enc" is of fixed-length, so there is no ambiguity in that the server is unwilling to use with "skR", the server returns an error. <del> Compute an HPKE context using "skR", a label of "message/bhttp request", and the encapsulated key "enc", yielding "context". </del>  <del> Construct additional associated data, "aad", from "keyID", "kemID", "kdfID", and "aeadID" or as the first seven bytes of "enc_request". </del>  <del> Decrypt "ct" using "aad" as associated data, yielding "request" or an error on failure.  If decryption fails, the server returns an </del>  error. In pseudocode, this procedure is as follows:

Reviewer comments:
We previously put the details from the key configuration (key id, KEM, KDF, AEAD) in the AAD for the request. But NAME noted that this meant that this wasn't bound to the response. Analysis shows that this isn't a security problem, but while that depends on some very reasonable assumptions about how keys are used, there is no reason not to do better. Performance-wise, this change is a wash. The AAD input is now empty, which should make this a tiny bit faster to execute. The downside is that the info is no longer static, which means that it is a tiny bit slower, especially on the server side where it is harder to just pass a constant value into SetupBaseR and the bits from the header into the AAD. But those are extraordinarily trivial differences. The spec doesn't really change much either, though I've taken this chance to be a bit more explicit about what functions in HPKE are run and what their inputs are. I will do examples in a follow-up so that you can see the effect on those (the numbers change, that's all). Code changes are fairly simple also, see URL for an example. Note that the biggest part of this is down to a structural choice, the actual changes are minor. Response handling doesn't change at all.
Section 4.1 seems to incorporates a bunch of information into the computation: However, since that context is in the parameter of , and not the parameter to , that same information doesn't figure into : This is a bit odd because , etc., also apply to the response. Should those be moved to the parameter instead? (Although, of those four fields, I think only authenticating does anything. The three algorithm IDs already figure into the HPKE suite ID. Was the intention to authenticate the instead?)
The intent was to belt and braces this, but yeah, it seems like we missed the braces part. A zero-length AAD is somewhat appealing when we already need to provide an info parameter, this would just make it a configurable one. I'm thinking "message/bhttp request" (the current info string), a 0 byte (to follow patterns elsewhere), and then the current AAD. Then make the AAD zero length. The only thing that gives me pause is that we already have an analysis that supports the current design. That suggests that a tighter design might work, but NAME might know how hard it would be to re-tool for a tweak.
I don't think moving the current AAD to the info parameter has a meaningful improvement beyond aesthetics, so I would be fine with this change. Updating the models should be pretty straightforward. (There is a ProVerif one not yet on GitHub, but I hope it will be soon.) As David points out, the HPKE parameters are already implicitly authenticated by way of agreement on the HPKE context. I would claim the keyID is also implicitly authenticated in this way, since the keyID determines the private key used to produce the shared HPKE context. The exception here being the case where there are two distinct keyID values that correspond to the same private key. In that case, if the response was generated without examining the request, then it would be possible for client and gateway to disagree on the keyID value used. Would this actually happen in practice? Doubtful, but fixing by moving the AAD around does the trick.
Verified .

Revised text:
Encoding an integer to a sequence of bytes in network byte order is described using the function "encode(n, v)", where "n" is the number  <ins> of bytes and "v" is the integer value.  ASCII ASCII encoding of a string "s" is indicated using the function "encode_str(s)".  The function "len()" returns the length of a sequence of bytes. </ins> Formats are described using notation from QUIC. The client then constructs an Encapsulated Request, "enc_request", from a binary encoded HTTP request, "request", as follows:  <ins> Construct a message header, "hdr", by concatenating the values of </ins> "keyID", "kemID", "kdfID", and "aeadID", as one 8-bit integer and three 16-bit integers, respectively, each in network byte order.  <ins> Build "info" by concatenating the ASCII-encoded string "message/ bhttp request", a zero byte, and the header. Create a sending HPKE context by invoking "SetupBaseS()" (HPKE) with the public key of the receiver "pkR" and "info".  This yields the context "sctxt" and an encapsulation key "enc". Encrypt "request" by invoking the "Seal()" method on "sctxt" (HPKE) with empty associated data "aad", yielding ciphertext "ct". </ins>  <ins> Concatenate the values of "hdr", "enc", and "ct", yielding an </ins> Encrypted Request "enc_request". Note that "enc" is of fixed-length, so there is no ambiguity in that the server is unwilling to use with "skR", the server returns an error.  <ins> Build "info" by concatenating the ASCII-encoded string "message/ bhttp request", a zero byte, "keyID" as an 8-bit integer, plus "kemID", "kdfID", and "aeadID" as three 16-bit integers. </ins>  <ins> Create a receiving HPKE context by invoking "SetupBaseR()" (HPKE) with "skR", "enc", and "info".  This produces a context "rctxt". </ins>  <ins> Decrypt "ct" by invoking the "Open()" method on "rctxt" (HPKE), with an empty associated data "aad", yielding "request" or an error on failure.  If decryption fails, the server returns an </ins> error. In pseudocode, this procedure is as follows: