diff --git a/.lint.py b/.lint.py index 707f8f45b7..0f25fc3e25 100755 --- a/.lint.py +++ b/.lint.py @@ -58,7 +58,11 @@ def err(msg): # Check length length = len(line) - limit = args.maxFigureLineLength if insideFigure else args.maxLineLength + limit = ( + int(args.maxFigureLineLength) + if insideFigure + else int(args.maxLineLength) + ) if length > limit: err("Line is {0} characters; limit is {1}".format(length, limit)) diff --git a/draft-ietf-quic-http.md b/draft-ietf-quic-http.md index 358c1b0799..a5ac472962 100644 --- a/draft-ietf-quic-http.md +++ b/draft-ietf-quic-http.md @@ -386,7 +386,7 @@ While connection-level options pertaining to the core QUIC protocol are set in the initial crypto handshake, HTTP/3-specific settings are conveyed in the SETTINGS frame. After the QUIC connection is established, a SETTINGS frame ({{frame-settings}}) MUST be sent by each endpoint as the initial frame of their -respective HTTP control stream (see {{control-streams}}). +respective HTTP control stream; see {{control-streams}}. ## Connection Reuse @@ -401,9 +401,12 @@ requests with multiple different URI authority components. In general, a server is considered authoritative for all URIs with the "https" scheme for which the hostname in the URI is present in the authenticated certificate provided by the server, either as the CN field of the certificate subject or as a dNSName in the -subjectAltName field of the certificate (see {{!RFC6125}}). For a host that is +subjectAltName field of the certificate; see {{!RFC6125}}. For a host that is an IP address, the client MUST verify that the address appears as an iPAddress -in the subjectAltName field of the certificate. +in the subjectAltName field of the certificate. If the hostname or address is +not present in the certificate, the client MUST NOT consider the server +authoritative for origins containing that hostname or address. See Section 5.4 +of {{!SEMANTICS}} for more detail on authoritative access. Clients SHOULD NOT open more than one HTTP/3 connection to a given host and port pair, where the host is derived from a URI, a selected alternative service @@ -420,8 +423,8 @@ complete or terminate any necessary remaining tasks. A server that does not wish clients to reuse connections for a particular origin can indicate that it is not authoritative for a request by sending a 421 -(Misdirected Request) status code in response to the request (see Section 9.1.2 -of {{?HTTP2}}). +(Misdirected Request) status code in response to the request; see Section 9.1.2 +of {{?HTTP2}}. # HTTP Request Lifecycle @@ -433,8 +436,8 @@ A client MUST send only a single request on a given stream. A server sends zero or more interim HTTP responses on the same stream as the request, followed by a single final HTTP response, as detailed below. -Pushed responses are sent on a server-initiated unidirectional QUIC stream (see -{{push-streams}}). A server sends zero or more interim HTTP responses, followed +Pushed responses are sent on a server-initiated unidirectional QUIC stream; see +{{push-streams}}. A server sends zero or more interim HTTP responses, followed by a single final HTTP response, in the same manner as a standard response. Push is described in more detail in {{server-push}}. @@ -453,8 +456,10 @@ An HTTP message (request or response) consists of: 3. optionally, the trailer field section, if present (see Section 4.6 of {{!SEMANTICS}}), sent as a single HEADERS frame. -Receipt of DATA and HEADERS frames in any other sequence MUST be treated as a -connection error of type H3_FRAME_UNEXPECTED ({{errors}}). +Receipt of an invalid sequence of frames MUST be treated as a connection error +of type H3_FRAME_UNEXPECTED ({{errors}}). In particular, a DATA frame before +any HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame +is considered invalid. A server MAY send one or more PUSH_PROMISE frames (see {{frame-push-promise}}) before, after, or interleaved with the frames of a response message. These @@ -480,12 +485,6 @@ informational responses (1xx; see Section 9.2 of {{!SEMANTICS}}) precede a final response to the same request. Interim responses do not contain a payload body or trailers. -If an endpoint receives an invalid sequence of frames on either a request or -a push stream, it MUST respond with a connection error of type -H3_FRAME_UNEXPECTED ({{errors}}). In particular, a DATA frame before any -HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame is -considered invalid. - An HTTP request/response exchange fully consumes a client-initiated bidirectional QUIC stream. After sending a request, a client MUST close the stream for sending. Unless using the CONNECT method (see {{connect}}), clients @@ -589,8 +588,8 @@ The following pseudo-header fields are defined for requests: : To ensure that the HTTP/1.1 request line can be reproduced accurately, this pseudo-header field MUST be omitted when translating from an HTTP/1.1 - request that has a request target in origin or asterisk form (see Section - 3.2 of {{?HTTP11}}). Clients that generate HTTP/3 requests directly SHOULD + request that has a request target in origin or asterisk form; see Section + 3.2 of {{?HTTP11}}. Clients that generate HTTP/3 requests directly SHOULD use the ":authority" pseudo-header field instead of the Host field. An intermediary that converts an HTTP/3 request to HTTP/1.1 MUST create a Host field if one is not present in a request by copying the value of the @@ -599,19 +598,20 @@ The following pseudo-header fields are defined for requests: ":path": : Contains the path and query parts of the target URI (the "path-absolute" - production and optionally a '?' character followed by the "query" production - (see Sections 3.3 and 3.4 of [RFC3986]). A request in asterisk form - includes the value '*' for the ":path" pseudo-header field. + production and optionally a '?' character followed by the "query" + production; see Sections 3.3 and 3.4 of {{?URI=RFC3986}}. A request in + asterisk form includes the value '*' for the ":path" pseudo-header field. : This pseudo-header field MUST NOT be empty for "http" or "https" URIs; "http" or "https" URIs that do not contain a path component MUST include a value of '/'. The exception to this rule is an OPTIONS request for an "http" or "https" URI that does not include a path component; these MUST - include a ":path" pseudo-header field with a value of '*' (see Section 3.2.4 - of {{?HTTP11}}). + include a ":path" pseudo-header field with a value of '*'; see Section 3.2.4 + of {{?HTTP11}}. All HTTP/3 requests MUST include exactly one value for the ":method", ":scheme", -and ":path" pseudo-header fields, unless it is a CONNECT request ({{connect}}). +and ":path" pseudo-header fields, unless it is a CONNECT request; see +{{connect}}. If the ":scheme" pseudo-header field identifies a scheme which has a mandatory authority component (including "http" and "https"), the request MUST contain @@ -628,7 +628,7 @@ HTTP/3 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line. For responses, a single ":status" pseudo-header field is defined that carries -the HTTP status code (see Section 9 of {{!SEMANTICS}}). This pseudo-header +the HTTP status code; see Section 9 of {{!SEMANTICS}}. This pseudo-header field MUST be included in all responses; otherwise, the response is malformed ({{malformed}}). @@ -660,7 +660,7 @@ on the uncompressed size of fields, including the length of the name and value in bytes plus an overhead of 32 bytes for each field. If an implementation wishes to advise its peer of this limit, it can be conveyed -as a number of bytes in the `SETTINGS_MAX_FIELD_SECTION_SIZE` parameter. An +as a number of bytes in the SETTINGS_MAX_FIELD_SECTION_SIZE parameter. An implementation which has received this parameter SHOULD NOT send an HTTP message header which exceeds the indicated size, as the peer will likely refuse to process it. However, because this limit is applied at each hop, messages below @@ -711,7 +711,7 @@ frames but is invalid due to: - the inclusion of invalid characters in field names or values A request or response that includes a payload body can include a -`content-length` header field. A request or response is also malformed if the +Content-Length header field. A request or response is also malformed if the value of a content-length header field does not equal the sum of the DATA frame payload lengths that form the body. A response that is defined to have no payload, as described in Section 6.3.3 of {{!SEMANTICS}} can have a non-zero @@ -746,12 +746,12 @@ A CONNECT request MUST be constructed as follows: - The ":method" pseudo-header field is set to "CONNECT" - The ":scheme" and ":path" pseudo-header fields are omitted - The ":authority" pseudo-header field contains the host and port to connect to - (equivalent to the authority-form of the request-target of CONNECT requests - (see Section 5.3 of {{?HTTP11}})) + (equivalent to the authority-form of the request-target of CONNECT requests; + see Section 5.3 of {{?HTTP11}}) The request stream remains open at the end of the request to carry the data to be transferred. A CONNECT request that does not conform to these restrictions -is malformed (see {{malformed}}). +is malformed; see {{malformed}}. A proxy that supports CONNECT establishes a TCP connection ({{!RFC0793}}) to the server identified in the ":authority" pseudo-header field. Once this connection @@ -809,7 +809,7 @@ same order, and both the name and the value in each field MUST be exact matches. Server push is only enabled on a connection when a client sends a MAX_PUSH_ID -frame (see {{frame-max-push-id}}). A server cannot use server push until it +frame; see {{frame-max-push-id}}. A server cannot use server push until it receives a MAX_PUSH_ID frame. A client sends additional MAX_PUSH_ID frames to control the number of pushes that a server can promise. A server SHOULD use Push IDs sequentially, starting at 0. A client MUST treat receipt of a push stream @@ -823,8 +823,8 @@ This allows the server push to be associated with a client request. Not all requests can be pushed. A server MAY push requests which have the following properties: -- cacheable (see Section 7.2.3 of {{!SEMANTICS}}) -- safe (see Section 7.2.1 of {{!SEMANTICS}}) +- cacheable; see Section 7.2.3 of {{!SEMANTICS}} +- safe; see Section 7.2.1 of {{!SEMANTICS}} - does not include a request body or trailer section Clients SHOULD send a CANCEL_PUSH frame upon receipt of a PUSH_PROMISE frame @@ -834,7 +834,7 @@ push stream, this MAY be treated as a stream error of type H3_STREAM_CREATION_ERROR. The server MUST include a value in the ":authority" pseudo-header field for -which the server is authoritative (see {{connection-reuse}}). A client SHOULD +which the server is authoritative; see {{connection-reuse}}. A client SHOULD send a CANCEL_PUSH frame upon receipt of a PUSH_PROMISE frame carrying a request for which it does not consider the server authoritative. If the pushed response arrives on a push stream, this MAY be treated as a stream error of type @@ -853,7 +853,7 @@ or DATA frames that reference the promised responses. This reduces the chance that a client requests a resource that will be pushed by the server. When a server later fulfills a promise, the server push response is conveyed on -a push stream (see {{push-streams}}). The push stream identifies the Push ID of +a push stream; see {{push-streams}}. The push stream identifies the Push ID of the promise that it fulfills, then contains a response to the promised request using the same format described for responses in {{request-response}}. @@ -973,7 +973,7 @@ can be cleanly shut down without losing requests. A client has more flexibility in the value it chooses for the Push ID in a GOAWAY that it sends. A value of 2^62 - 1 indicates that the server can continue fulfilling pushes which have already been promised, and the client can -continue granting push credit as needed (see {{frame-max-push-id}}). A smaller +continue granting push credit as needed; see {{frame-max-push-id}}. A smaller value indicates the client will reject pushes with Push IDs greater than or equal to this value. Like the server, the client MAY send subsequent GOAWAY frames so long as the specified Push ID is strictly smaller than all previously @@ -987,7 +987,7 @@ state. Once all accepted requests and pushes have been processed, the endpoint can permit the connection to become idle, or MAY initiate an immediate closure of the connection. An endpoint that completes a graceful shutdown SHOULD use the -HTTP_NO_ERROR code when closing the connection. +H3_NO_ERROR code when closing the connection. If a client has consumed all available bidirectional stream IDs with requests, the server need not send a GOAWAY frame, since the client is unable to make @@ -1112,7 +1112,7 @@ the reception of the unidirectional stream header. ### Control Streams -A control stream is indicated by a stream type of `0x00`. Data on this stream +A control stream is indicated by a stream type of 0x00. Data on this stream consists of HTTP/3 frames, as defined in {{frames}}. Each side MUST initiate a single control stream at the beginning of the @@ -1137,7 +1137,7 @@ Server push is an optional feature introduced in HTTP/2 that allows a server to initiate a response before a request has been made. See {{server-push}} for more details. -A push stream is indicated by a stream type of `0x01`, followed by the Push ID +A push stream is indicated by a stream type of 0x01, followed by the Push ID of the promise that it fulfills, encoded as a variable-length integer. The remaining data on this stream consists of HTTP/3 frames, as defined in {{frames}}, and fulfills a promised server push by zero or more interim HTTP @@ -1162,8 +1162,8 @@ client MUST treat this as a connection error of type H3_ID_ERROR. ### Reserved Stream Types {#stream-grease} -Stream types of the format `0x1f * N + 0x21` for integer values of N are -reserved to exercise the requirement that unknown types be ignored. These +Stream types of the format `0x1f * N + 0x21` for non-negative integer values of +N are reserved to exercise the requirement that unknown types be ignored. These streams have no semantics, and can be sent when application-layer padding is desired. They MAY also be sent on connections where no data is currently being transferred. Endpoints MUST NOT consider these streams to have any meaning upon @@ -1313,8 +1313,8 @@ CANCEL_PUSH Frame { {: #fig-cancel-push title="CANCEL_PUSH Frame"} The CANCEL_PUSH frame carries a Push ID encoded as a variable-length integer. -The Push ID identifies the server push that is being cancelled (see -{{frame-push-promise}}). If a CANCEL_PUSH frame is received which references a +The Push ID identifies the server push that is being cancelled; see +{{frame-push-promise}}. If a CANCEL_PUSH frame is received which references a Push ID greater than currently allowed on the connection, this MUST be treated as a connection error of type H3_ID_ERROR. @@ -1387,11 +1387,11 @@ The following settings are defined in HTTP/3: SETTINGS_MAX_FIELD_SECTION_SIZE (0x6): : The default value is unlimited. See {{header-formatting}} for usage. -Setting identifiers of the format `0x1f * N + 0x21` for integer values of N are -reserved to exercise the requirement that unknown identifiers be ignored. Such -settings have no defined meaning. Endpoints SHOULD include at least one such -setting in their SETTINGS frame. Endpoints MUST NOT consider such settings to -have any meaning upon receipt. +Setting identifiers of the format `0x1f * N + 0x21` for non-negative integer +values of N are reserved to exercise the requirement that unknown identifiers be +ignored. Such settings have no defined meaning. Endpoints SHOULD include at +least one such setting in their SETTINGS frame. Endpoints MUST NOT consider such +settings to have any meaning upon receipt. Because the setting has no defined meaning, the value of the setting can be any value the implementation selects. @@ -1571,19 +1571,19 @@ MAX_PUSH_ID Frame { {: #fig-max-push title="MAX_PUSH_ID Frame Payload"} The MAX_PUSH_ID frame carries a single variable-length integer that identifies -the maximum value for a Push ID that the server can use (see -{{frame-push-promise}}). A MAX_PUSH_ID frame cannot reduce the maximum Push ID; +the maximum value for a Push ID that the server can use; see +{{frame-push-promise}}. A MAX_PUSH_ID frame cannot reduce the maximum Push ID; receipt of a MAX_PUSH_ID that contains a smaller value than previously received MUST be treated as a connection error of type H3_ID_ERROR. ### Reserved Frame Types {#frame-reserved} -Frame types of the format `0x1f * N + 0x21` for integer values of N are reserved -to exercise the requirement that unknown types be ignored ({{extensions}}). -These frames have no semantics, and can be sent on any open stream when -application-layer padding is desired. They MAY also be sent on connections where -no data is currently being transferred. Endpoints MUST NOT consider these frames -to have any meaning upon receipt. +Frame types of the format `0x1f * N + 0x21` for non-negative integer values of N +are reserved to exercise the requirement that unknown types be ignored +({{extensions}}). These frames have no semantics, and can be sent on any open +stream when application-layer padding is desired. They MAY also be sent on +connections where no data is currently being transferred. Endpoints MUST NOT +consider these frames to have any meaning upon receipt. The payload and length of the frames are selected in any manner the implementation chooses. @@ -1607,7 +1607,7 @@ outstanding requests before making this choice. Because new error codes can be defined without negotiation (see {{extensions}}), use of an error code in an unexpected context or receipt of an unknown error code MUST be treated as equivalent to H3_NO_ERROR. However, closing a stream -can have other effects regardless of the error code (see {{request-response}}). +can have other effects regardless of the error code; see {{request-response}}. This section describes HTTP/3-specific error codes which can be used to express the cause of a connection or stream error. @@ -1673,10 +1673,11 @@ H3_VERSION_FALLBACK (0x110): : The requested operation cannot be served over HTTP/3. The peer should retry over HTTP/1.1. -Error codes of the format `0x1f * N + 0x21` for integer values of N are reserved -to exercise the requirement that unknown error codes be treated as equivalent to -H3_NO_ERROR ({{extensions}}). Implementations SHOULD select an error code from -this space with some probability when they would have sent H3_NO_ERROR. +Error codes of the format `0x1f * N + 0x21` for non-negative integer values of N +are reserved to exercise the requirement that unknown error codes be treated as +equivalent to H3_NO_ERROR ({{extensions}}). Implementations SHOULD select an +error code from this space with some probability when they would have sent +H3_NO_ERROR. # Extensions to HTTP/3 {#extensions} @@ -2008,9 +2009,9 @@ The entries in {{iana-frame-table}} are registered by this document. | ---------------- | ------ | -------------------------- | {: #iana-frame-table title="Initial HTTP/3 Frame Types"} -Additionally, each code of the format `0x1f * N + 0x21` for integer values of N -(that is, `0x21`, `0x40`, ..., through `0x3FFFFFFFFFFFFFFE`) MUST NOT be -assigned by IANA. +Additionally, each code of the format `0x1f * N + 0x21` for non-negative integer +values of N (that is, 0x21, 0x40, ..., through 0x3FFFFFFFFFFFFFFE) MUST +NOT be assigned by IANA. ### Settings Parameters {#iana-settings} @@ -2050,9 +2051,9 @@ The entries in {{iana-setting-table}} are registered by this document. | ---------------------------- | ------ | ------------------------- | --------- | {: #iana-setting-table title="Initial HTTP/3 Settings"} -Additionally, each code of the format `0x1f * N + 0x21` for integer values of N -(that is, `0x21`, `0x40`, ..., through `0x3FFFFFFFFFFFFFFE`) MUST NOT be -assigned by IANA. +Additionally, each code of the format `0x1f * N + 0x21` for non-negative integer +values of N (that is, 0x21, 0x40, ..., through 0x3FFFFFFFFFFFFFFE) MUST +NOT be assigned by IANA. ### Error Codes {#iana-error-codes} @@ -2102,9 +2103,9 @@ The entries in the {{iana-error-table}} are registered by this document. | --------------------------------- | ---------- | ---------------------------------------- | ---------------------- | {: #iana-error-table title="Initial HTTP/3 Error Codes"} -Additionally, each code of the format `0x1f * N + 0x21` for integer values of N -(that is, `0x21`, `0x40`, ..., through `0x3FFFFFFFFFFFFFFE`) MUST NOT be -assigned by IANA. +Additionally, each code of the format `0x1f * N + 0x21` for non-negative integer +values of N (that is, 0x21, 0x40, ..., through 0x3FFFFFFFFFFFFFFE) MUST +NOT be assigned by IANA. ### Stream Types {#iana-stream-types} @@ -2138,9 +2139,9 @@ The entries in the following table are registered by this document. | Push Stream | 0x01 | {{server-push}} | Server | | ---------------- | ------ | -------------------------- | ------ | -Additionally, each code of the format `0x1f * N + 0x21` for integer values of N -(that is, `0x21`, `0x40`, ..., through `0x3FFFFFFFFFFFFFFE`) MUST NOT be -assigned by IANA. +Additionally, each code of the format `0x1f * N + 0x21` for non-negative integer +values of N (that is, 0x21, 0x40, ..., through 0x3FFFFFFFFFFFFFFE) MUST +NOT be assigned by IANA. --- back @@ -2355,9 +2356,8 @@ the peers' settings to arrive before responding to other streams. See ## HTTP/2 Error Codes QUIC has the same concepts of "stream" and "connection" errors that HTTP/2 -provides. However, there is no direct portability of HTTP/2 error codes to -HTTP/3 error codes; the values are shifted in order to prevent accidental -or implicit conversion. +provides. However, the differences between HTTP/2 and HTTP/3 mean that error +codes are not directly portable between versions. The HTTP/2 error codes defined in Section 7 of {{?HTTP2}} logically map to the HTTP/3 error codes as follows: @@ -2412,6 +2412,34 @@ H3_1_1_REQUIRED (0xd): Error codes need to be defined for HTTP/2 and HTTP/3 separately. See {{iana-error-codes}}. +### Mapping Between HTTP/2 and HTTP/3 Errors + +An intermediary that converts between HTTP/2 and HTTP/3 may encounter error +conditions from either upstream. It is useful to communicate the occurrence of +error to the downstream but error codes largely reflect connection-local +problems that generally do not make sense to propagate. + +An intermediary that encounters an error from an upstream origin can indicate +this by sending an HTTP status code such as 502, which is suitable for a broad +class of errors. + +There are some rare cases where it is beneficial to propagate the error by +mapping it to the closest matching error type to the receiver. For example, an +intermediary that receives an HTTP/2 stream error of type REFUSED_STREAM from +the origin has a clear signal that the request was not processed and that the +request is safe to retry. Propagating this error condition to the client as an +HTTP/3 stream error of type H3_REQUEST_REJECTED allows the client to take the +action it deems most appropriate. In the reverse direction the intermediary +might deem it beneficial to pass on client request cancellations that are +indicated by terminating a stream with H3_REQUEST_CANCELLED. + +Conversion between errors is described in the logical mapping. The error codes +are defined in non-overlapping spaces in order to protect against accidental +conversion that could result in the use of inappropriate or unknown error codes +for the target version. An intermediary is permitted to promote stream errors to +connection errors but they should be aware of the cost to the connection for +what might be a temporary or intermittent error. + # Change Log > **RFC Editor's Note:** Please remove this section prior to publication of a @@ -2661,5 +2689,35 @@ None. The original authors of this specification were Robbie Shade and Mike Warres. -A substantial portion of Mike's contribution was supported by Microsoft during -his employment there. +The IETF QUIC Working Group received an enormous amount of support from many +people. Among others, the following people provided substantial contributions to +this document: + +- Bence Béky +- Daan De Meyer +- Martin Duke +- Roy Fielding +- Alan Frindell +- Alessandro Ghedini +- Nick Harper +- Ryan Hamilton +- Christian Huitema +- Subodh Iyengar +- Robin Marx +- Patrick McManus +- Luca Nicco +- +- Lucas Pardue +- Roberto Peon +- Julian Reschke +- Eric Rescorla +- Martin Seemann +- Ben Schwartz +- Ian Swett +- Willy Taureau +- Martin Thomson +- Dmitri Tikhonov +- Tatsuhiro Tsujikawa + +A portion of Mike's contribution was supported by Microsoft during his +employment there. diff --git a/draft-ietf-quic-invariants.md b/draft-ietf-quic-invariants.md index 1abd81910e..ec40a0ab10 100644 --- a/draft-ietf-quic-invariants.md +++ b/draft-ietf-quic-invariants.md @@ -129,8 +129,7 @@ version-specific and of arbitrary length. ## Long Header -Long headers take the form described in {{fig-long}}. Bits that have -version-specific semantics are marked with an X. +Long headers take the form described in {{fig-long}}. ~~~ Long Header Packet { @@ -138,9 +137,9 @@ Long Header Packet { Version-Specific Bits (7), Version (32), DCID Len (8), - Destination Connection ID (0..160), + Destination Connection ID (0..2040), SCID Len (8), - Source Connection ID (0..160), + Source Connection ID (0..2040), Version-Specific Data (..), } ~~~ @@ -156,18 +155,17 @@ The next byte contains the length in bytes of the Destination Connection ID (see unsigned integer. The Destination Connection ID field follows the DCID Len field and is between 0 and 255 bytes in length. -The next byte contains the length in bytes -of the Source Connection ID field that follows it. This length is encoded as -a 8-bit unsigned integer. The Source Connection ID field follows the SCID Len -field and is between 0 and 255 bytes in length. +The next byte contains the length in bytes of the Source Connection ID field +that follows it. This length is encoded as a 8-bit unsigned integer. The +Source Connection ID field follows the SCID Len field and is between 0 and 255 +bytes in length. The remainder of the packet contains version-specific content. ## Short Header -Short headers take the form described in {{fig-short}}. Bits that have -version-specific semantics are marked with an X. +Short headers take the form described in {{fig-short}}. ~~~~~ Short Header Packet { @@ -235,9 +233,9 @@ Version Negotiation Packet { Unused (7), Version (32) = 0, DCID Len (8), - Destination Connection ID (0..160), + Destination Connection ID (0..2040), SCID Len (8), - Source Connection ID (0..160), + Source Connection ID (0..2040), Supported Version (32) ..., } ~~~ diff --git a/draft-ietf-quic-qpack.md b/draft-ietf-quic-qpack.md index 035fd4a13f..d90c89ace0 100644 --- a/draft-ietf-quic-qpack.md +++ b/draft-ietf-quic-qpack.md @@ -61,6 +61,30 @@ normative: org: Mozilla role: editor +informative: + + CRIME: + target: http://en.wikipedia.org/w/index.php?title=CRIME&oldid=660948120 + title: "CRIME" + author: + - + org: Wikipedia + date: May, 2015 + + + PETAL: + target: http://www.pdl.cmu.edu/PDL-FTP/associated/CMU-PDL-13-106.pdf + title: "PETAL: Preset Encoding Table Information Leakage" + author: + - + ins: J. Tan + name: Jiaqi Tan + - + ins: J. Nahata + name: Jayvardhan Nahata + date: April, 2013 + + --- abstract @@ -204,7 +228,7 @@ evictable, the encoder MUST NOT insert that entry into the dynamic table (including duplicates of existing entries). In order to avoid this, an encoder that uses the dynamic table has to keep track of each dynamic table entry referenced by each header block until that header block is acknowledged by the -decoder (see {{header-acknowledgement}}). +decoder; see {{header-acknowledgement}}. #### Avoiding Prohibited Insertions @@ -226,14 +250,14 @@ references to those entries will eventually become zero, allowing them to be evicted. ~~~~~~~~~~ drawing - +----------+---------------------------------+--------+ - | Draining | Referenceable | Unused | - | Entries | Entries | Space | - +----------+---------------------------------+--------+ - ^ ^ ^ - | | | - Dropping Draining Index Insertion Point - Point + +--------+---------------------------------+----------+ + | Unused | Referenceable | Draining | + | Space | Entries | Entries | + +--------+---------------------------------+----------+ + ^ ^ ^ + | | | + Insertion Point Draining Index Dropping + Point ~~~~~~~~~~ {:#fig-draining-index title="Draining Dynamic Table Entries"} @@ -625,11 +649,11 @@ and follows the definitions in [RFC7541] without modification. QPACK defines two unidirectional stream types: - - An encoder stream is a unidirectional stream of type `0x02`. + - An encoder stream is a unidirectional stream of type 0x02. It carries an unframed sequence of encoder instructions from encoder to decoder. - - A decoder stream is a unidirectional stream of type `0x03`. + - A decoder stream is a unidirectional stream of type 0x03. It carries an unframed sequence of decoder instructions from decoder to encoder. @@ -933,12 +957,12 @@ value of 4 indicates that the Required Insert Count is 9 for the header block. #### Base {#base} -The `Base` is used to resolve references in the dynamic table as described in +The Base is used to resolve references in the dynamic table as described in {{relative-indexing}}. To save space, the Base is encoded relative to the Required Insert Count using a -one-bit sign ('S') and the `Delta Base` value. A sign bit of 0 indicates that -the Base is greater than or equal to the value of the Required Insert Count; the +one-bit sign ('S') and the Delta Base value. A sign bit of 0 indicates that the +Base is greater than or equal to the value of the Required Insert Count; the decoder adds the value of Delta Base to the Required Insert Count to determine the value of the Base. A sign bit of 1 indicates that the Base is less than the Required Insert Count; the decoder subtracts the value of Delta Base from the @@ -1141,7 +1165,197 @@ QPACK_DECODER_STREAM_ERROR (0x202): # Security Considerations -TBD. Also see Section 7.1 of [RFC7541]. + +This section describes potential areas of security concern with QPACK: + + * Use of compression as a length-based oracle for verifying guesses about + secrets that are compressed into a shared compression context. + * Denial of service resulting from exhausting processing or memory capacity at + a decoder. + +## Probing Dynamic Table State + +QPACK reduces the length of header field encodings by exploiting the redundancy +inherent in protocols like HTTP. The ultimate goal of this is to reduce the +amount of data that is required to send HTTP requests or responses. + +The compression context used to encode header fields can be probed by an +attacker who can both define header fields to be encoded and transmitted and +observe the length of those fields once they are encoded. When an attacker can +do both, they can adaptively modify requests in order to confirm guesses about +the dynamic table state. If a guess is compressed into a shorter length, the +attacker can observe the encoded length and infer that the guess was correct. + +This is possible even over the Transport Layer Security Protocol (TLS, see +{{?RFC5246}}), because while TLS provides confidentiality protection for +content, it only provides a limited amount of protection for the length of that +content. + +Note: + +: Padding schemes only provide limited protection against an attacker with these +capabilities, potentially only forcing an increased number of guesses to learn +the length associated with a given guess. Padding schemes also work directly +against compression by increasing the number of bits that are transmitted. + +Attacks like CRIME [CRIME] demonstrated the existence of these general attacker +capabilities. The specific attack exploited the fact that DEFLATE {{?RFC1951}} +removes redundancy based on prefix matching. This permitted the attacker to +confirm guesses a character at a time, reducing an exponential-time attack into +a linear-time attack. + +## Applicability to QPACK and HTTP + +QPACK mitigates but does not completely prevent attacks modeled on CRIME [CRIME] +by forcing a guess to match an entire header field value, rather than individual +characters. An attacker can only learn whether a guess is correct or not, so is +reduced to a brute force guess for the header field values. + +The viability of recovering specific header field values therefore depends on +the entropy of values. As a result, values with high entropy are unlikely to be +recovered successfully. However, values with low entropy remain vulnerable. + +Attacks of this nature are possible any time that two mutually distrustful +entities control requests or responses that are placed onto a single HTTP/3 +connection. If the shared QPACK compressor permits one entity to add entries to +the dynamic table, and the other to access those entries, then the state of the +table can be learned. + +Having requests or responses from mutually distrustful entities occurs when an +intermediary either: + + * sends requests from multiple clients on a single connection toward an origin + server, or + + * takes responses from multiple origin servers and places them on a shared + connection toward a client. + +Web browsers also need to assume that requests made on the same connection by +different web origins {{?RFC6454}} are made by mutually distrustful entities. + +## Mitigation + +Users of HTTP that require confidentiality for header fields can use values with +entropy sufficient to make guessing infeasible. However, this is impractical as +a general solution because it forces all users of HTTP to take steps to mitigate +attacks. It would impose new constraints on how HTTP is used. + +Rather than impose constraints on users of HTTP, an implementation of QPACK can +instead constrain how compression is applied in order to limit the potential for +dynamic table probing. + +An ideal solution segregates access to the dynamic table based on the entity +that is constructing header fields. Header field values that are added to the +table are attributed to an entity, and only the entity that created a particular +value can extract that value. + +To improve compression performance of this option, certain entries might be +tagged as being public. For example, a web browser might make the values of the +Accept-Encoding header field available in all requests. + +An encoder without good knowledge of the provenance of header fields might +instead introduce a penalty for a header field with many different values, such +that a large number of attempts to guess a header field value results in the +header field not being compared to the dynamic table entries in future messages, +effectively preventing further guesses. + +Note: + +: Simply removing entries corresponding to the header field from the dynamic +table can be ineffectual if the attacker has a reliable way of causing values to +be reinstalled. For example, a request to load an image in a web browser +typically includes the Cookie header field (a potentially highly valued target +for this sort of attack), and web sites can easily force an image to be loaded, +thereby refreshing the entry in the dynamic table. + +This response might be made inversely proportional to the length of the header +field value. Disabling access to the dynamic table for a header field might +occur for shorter values more quickly or with higher probability than for longer +values. + +## Never Indexed Literals + +Implementations can also choose to protect sensitive header fields by not +compressing them and instead encoding their value as literals. + +Refusing to insert a header field into the dynamic table is only +effective if doing so is avoided on all hops. The never indexed literal bit (see +{{literal-name-reference}}) can be used to signal to intermediaries that a +particular value was intentionally sent as a literal. + +An intermediary MUST NOT re-encode a value that uses a literal representation +with the 'N' bit set with another representation that would index it. If QPACK +is used for re-encoding, a literal representation with the 'N' bit set MUST be +used. If HPACK is used for re-encoding, the never indexed literal +representation (see Section 6.2.3 of [RFC7541]) MUST be used. + +The choice to mark that a header field should never be indexed +depends on several factors. Since QPACK doesn't protect against guessing an +entire header field value, short or low-entropy values are more readily +recovered by an adversary. Therefore, an encoder might choose not to index +values with low entropy. + +An encoder might also choose not to index values for header fields that are +considered to be highly valuable or sensitive to recovery, such as the Cookie or +Authorization header fields. + +On the contrary, an encoder might prefer indexing values for header fields that +have little or no value if they were exposed. For instance, a User-Agent header +field does not commonly vary between requests and is sent to any server. In that +case, confirmation that a particular User-Agent value has been used provides +little value. + +Note that these criteria for deciding to use a never indexed literal +representation will evolve over time as new attacks are discovered. + +## Static Huffman Encoding + +There is no currently known attack against a static Huffman encoding. A study +has shown that using a static Huffman encoding table created an information +leakage, however this same study concluded that an attacker could not take +advantage of this information leakage to recover any meaningful amount of +information (see [PETAL]). + +## Memory Consumption + +An attacker can try to cause an endpoint to exhaust its memory. QPACK is +designed to limit both the peak and stable amounts of memory allocated by an +endpoint. + +The amount of memory used by the encoder is limited by the protocol using +QPACK through the definition of the maximum size of the dynamic table, and the +maximum number of blocking streams. In HTTP/3, these values are controlled by +the decoder through the settings parameters SETTINGS_QPACK_MAX_TABLE_CAPACITY +and SETTINGS_QPACK_BLOCKED_STREAMS, respectively (see +{{maximum-dynamic-table-capacity}} and {{blocked-streams}}). The limit on the +size of the dynamic table takes into account the size of the data stored in the +dynamic table, plus a small allowance for overhead. The limit on the number of +blocked streams is only a proxy for the maximum amount of memory required by the +decoder. The actual maximum amount of memory will depend on how much memory the +decoder uses to track each blocked stream. + +A decoder can limit the amount of state memory used for the dynamic table by +setting an appropriate value for the maximum size of the dynamic table. In +HTTP/3, this is realized by setting an appropriate value for the +SETTINGS_QPACK_MAX_TABLE_CAPACITY parameter. An encoder can limit the amount of +state memory it uses by signaling a lower dynamic table size than the decoder +allows (see {{eviction}}). + +A decoder can limit the amount of state memory used for blocked streams by +setting an appropriate value for the maximum number of blocked streams. In +HTTP/3, this is realized by setting an appropriate value for the +QPACK_BLOCKED_STREAMS parameter. An encoder can limit the amount of state +memory by only using as many blocked streams as it wishes to support; no +signaling to the decoder is requred. + +The amount of temporary memory consumed by an encoder or decoder can be limited +by processing header fields sequentially. A decoder implementation does not need +to retain a complete list of header fields while decoding a header block. An +encoder implementation does not need to retain a complete list of header fields +while encoding a header block if it is using a single-pass algorithm. Note +that it might be necessary for an application to retain a complete +header list for other reasons; even if QPACK does not force this to occur, +application constraints might make this necessary. While the negotiated limit on the dynamic table size accounts for much of the memory that can be consumed by a QPACK implementation, data which cannot be @@ -1152,6 +1366,19 @@ to an excess of unsent data might include limiting the ability of the peer to open new streams, reading only from the encoder stream, or closing the connection. + +## Implementation Limits + +An implementation of QPACK needs to ensure that large values for integers, long +encoding for integers, or long string literals do not create security +weaknesses. + +An implementation has to set a limit for the values it accepts for integers, as +well as for the encoded length (see {{prefixed-integers}}). In the same way, it +has to set a limit to the length it accepts for string literals (see +{{string-literals}}). + + # IANA Considerations ## Settings Registration @@ -1196,6 +1423,13 @@ are registered in the "HTTP/3 Error Code" registry established in {{HTTP3}}. # Static Table +This table was generated by analyzing actual internet traffic in 2018 and +including the most common headers, after filtering out some unsupported and +non-standard values. Due to this methodology, some of the entries may be +inconsistent or appear multiple times with similar but not identical values. +The order of the entries is optimized to encode the most common headers with the +smallest number of bytes. + | Index | Name | Value | | ----- | -------------------------------- | ----------------------------------------------------------- | | 0 | :authority | | @@ -1445,22 +1679,29 @@ Editorial changes only # Acknowledgments {:numbered="false"} -This draft draws heavily on the text of {{!RFC7541}}. The indirect input of -those authors is gratefully acknowledged, as well as ideas from: - -* Ryan Hamilton - -* Patrick McManus +The IETF QUIC Working Group received an enormous amount of support from many +people. -* Kazuho Oku +The compression design team did substantial work exploring the problem space and +influencing the initial draft. The contributions of design team members Roberto +Peon, Martin Thomson, and Dmitri Tikhonov is gratefully acknowledged. -* Biren Roy +The following people also provided substantial contributions to this document: -* Ian Swett +- Bence Béky +- Alessandro Ghedini +- Ryan Hamilton +- Robin Marx +- Patrick McManus +- +- Lucas Pardue +- Biren Roy +- Ian Swett -* Dmitri Tikhonov +This draft draws heavily on the text of {{!RFC7541}}. The indirect input of +those authors is also gratefully acknowledged. Buck's contribution was supported by Google during his employment there. -A substantial portion of Mike's contribution was supported by Microsoft during -his employment there. +A portion of Mike's contribution was supported by Microsoft during his +employment there. diff --git a/draft-ietf-quic-recovery.md b/draft-ietf-quic-recovery.md index 1f5f5f38f2..10a42681ba 100644 --- a/draft-ietf-quic-recovery.md +++ b/draft-ietf-quic-recovery.md @@ -218,7 +218,7 @@ forward progress without relying on timeouts. QUIC endpoints measure the delay incurred between when a packet is received and when the corresponding acknowledgment is sent, allowing a peer to maintain a -more accurate round-trip time estimate (see Section 13.2 of {{QUIC-TRANSPORT}}). +more accurate round-trip time estimate; see Section 13.2 of {{QUIC-TRANSPORT}}. ### Probe Timeout Replaces RTO and TLP @@ -245,6 +245,19 @@ QUIC specifies a time-based definition to ensure one or more packets are sent prior to a dramatic decrease in congestion window; see {{persistent-congestion}}. +### The Minimum Congestion Window is Two Packets + +TCP uses a minimum congestion window of one packet. However, loss of +that single packet means that the sender needs to waiting for a PTO +({{pto}}) to recover, which can be much longer than a round-trip time. +Sending a single ack-eliciting packet also increases the chances of incurring +additional latency when a receiver delays its acknowledgement. + +QUIC therefore recommends that the minimum congestion window be two +packets. While this increases network load, it is considered safe, since the +sender will still reduce its sending rate exponentially under persistent +congestion ({{pto}}). + # Estimating the Round-Trip Time {#compute-rtt} @@ -421,8 +434,11 @@ The RECOMMENDED initial value for the packet reordering threshold less than 3, to keep in line with TCP {{?RFC5681}}. Some networks may exhibit higher degrees of reordering, causing a sender to -detect spurious losses. Implementers MAY use algorithms developed for TCP, such -as TCP-NCR {{?RFC4653}}, to improve QUIC's reordering resilience. +detect spurious losses. Algorithms that increase the reordering threshold after +spuriously detecting losses, such as TCP-NCR {{?RFC4653}}, have proven to be +useful in TCP and are expected to at least as useful in QUIC. Re-ordering +could be more common with QUIC than TCP, because network elements cannot observe +and fix the order of out-of-order packets. ### Time Threshold {#time-threshold} @@ -462,8 +478,13 @@ and larger thresholds increase loss detection delay. A Probe Timeout (PTO) triggers sending one or two probe datagrams when ack-eliciting packets are not acknowledged within the expected period of -time or the handshake has not been completed. A PTO enables a connection to -recover from loss of tail packets or acknowledgements. +time or the server may not have validated the client's address. A PTO enables +a connection to recover from loss of tail packets or acknowledgements. + +A PTO timer expiration event does not indicate packet loss and MUST NOT cause +prior unacknowledged packets to be marked as lost. When an acknowledgement +is received that newly acknowledges packets, loss detection proceeds as +dictated by packet and time threshold mechanisms; see {{ack-loss-detection}}. As with loss detection, the probe timeout is per packet number space. The PTO algorithm used in QUIC implements the reliability functions of @@ -533,16 +554,14 @@ previous RTT is available, the initial RTT SHOULD be set to 333ms, resulting in a 1 second initial timeout, as recommended in {{?RFC6298}}. A connection MAY use the delay between sending a PATH_CHALLENGE and receiving a -PATH_RESPONSE to set the initial RTT (see kInitialRtt in {{pto-handshake}}) -for a new path, but the delay SHOULD NOT be considered an RTT sample. +PATH_RESPONSE to set the initial RTT (see kInitialRtt in +{{constants-of-interest}}) for a new path, but the delay SHOULD NOT be +considered an RTT sample. Prior to handshake completion, when few to none RTT samples have been generated, it is possible that the probe timer expiration is due to an incorrect RTT estimate at the client. To allow the client to improve its RTT -estimate, the new packet that it sends MUST be ack-eliciting. If Handshake -keys are available to the client, it MUST send a Handshake packet, and -otherwise it MUST send an Initial packet in a UDP datagram of at least 1200 -bytes. +estimate, the new packet that it sends MUST be ack-eliciting. Initial packets and Handshake packets could be never acknowledged, but they are removed from bytes in flight when the Initial and Handshake keys are discarded, @@ -556,7 +575,7 @@ have been set for a now discarded packet number space. Until the server has validated the client's address on the path, the amount of data it can send is limited to three times the amount of data received, as specified in Section 8.1 of {{QUIC-TRANSPORT}}. If no additional data can be -sent, the server's PTO alarm MUST NOT be armed until datagrams have been +sent, the server's PTO timer MUST NOT be armed until datagrams have been received from the client, because packets sent on PTO count against the anti-amplification limit. Note that the server could fail to validate the client's address even if 0-RTT is accepted. @@ -567,6 +586,14 @@ until it is certain that the server has finished its address validation (see Section 8 of {{QUIC-TRANSPORT}}). That is, the client MUST set the probe timer if the client has not received an acknowledgement for one of its Handshake or 1-RTT packets, and has not received a HANDSHAKE_DONE frame. +If Handshake keys are available to the client, it MUST send a Handshake +packet, and otherwise it MUST send an Initial packet in a UDP datagram of +at least 1200 bytes. + +A client could have received and acknowledged a Handshake packet, causing it to +discard state for the Initial packet number space, but not sent any +ack-eliciting Handshake packets. In this case, the PTO is set from the current +time. ### Speeding Up Handshake Completion @@ -597,7 +624,11 @@ All probe packets sent on a PTO MUST be ack-eliciting. In addition to sending data in the packet number space for which the timer expired, the sender SHOULD send ack-eliciting packets from other packet -number spaces with in-flight data, coalescing packets if possible. +number spaces with in-flight data, coalescing packets if possible. This is +particularly valuable when the server has both Initial and Handshake data +in-flight or the client has both Handshake and ApplicationData in-flight, +because the peer might only have receive keys for one of the two packet number +spaces. If the sender wants to elicit a faster acknowledgement on PTO, it can skip a packet number to eliminate the ack delay. @@ -635,16 +666,6 @@ and ensures the highest priority frames arrive first. Sending different payloads each time reduces the chances of spurious retransmission. -### Loss Detection {#pto-loss} - -Delivery or loss of packets in flight is established when an ACK frame is -received that newly acknowledges one or more packets. - -A PTO timer expiration event does not indicate packet loss and MUST NOT cause -prior unacknowledged packets to be marked as lost. When an acknowledgement -is received that newly acknowledges packets, loss detection proceeds as -dictated by packet and time threshold mechanisms; see {{ack-loss-detection}}. - ## Handling Retry Packets A Retry packet causes a client to send another Initial packet, effectively @@ -672,7 +693,7 @@ all recovery state associated with those packets and MUST remove them from the count of bytes in flight. Endpoints stop sending and receiving Initial packets once they start exchanging -Handshake packets (see Section 17.2.2.1 of {{QUIC-TRANSPORT}}). At this point, +Handshake packets; see Section 17.2.2.1 of {{QUIC-TRANSPORT}}. At this point, recovery state for all in-flight Initial packets is discarded. When 0-RTT is rejected, recovery state for all in-flight 0-RTT packets is @@ -684,8 +705,8 @@ is expected to be infrequent. It is expected that keys are discarded after packets encrypted with them would be acknowledged or declared lost. Initial secrets however might be destroyed -sooner, as soon as handshake keys are available (see Section 4.10.1 of -{{QUIC-TLS}}). +sooner, as soon as handshake keys are available; see Section 4.11.1 of +{{QUIC-TLS}}. # Congestion Control {#congestion-control} @@ -711,7 +732,7 @@ window in bytes. An endpoint MUST NOT send a packet if it would cause bytes_in_flight (see {{vars-of-interest}}) to be larger than the congestion window, unless the packet -is sent on a PTO timer expiration (see {{pto}}). +is sent on a PTO timer expiration; see {{pto}}. ## Explicit Congestion Notification {#congestion-ecn} @@ -729,6 +750,12 @@ twice the maximum datagram size. This follows the analysis and recommendations in {{?RFC6928}}, increasing the byte limit to account for the smaller 8 byte overhead of UDP compared to the 20 byte overhead for TCP. +Prior to validating the client's address, the server can be further limited by +the anti-amplification limit as specified in Section 8.1 of {{QUIC-TRANSPORT}}. +Though the anti-amplification limit can prevent the congestion window from +being fully utilized and therefore slow down the increase in congestion window, +it does not directly affect the congestion window. + The minimum congestion window is the smallest value the congestion window can decrease to as a response to loss, ECN-CE, or persistent congestion. The RECOMMENDED value is 2 * max_datagram_size. @@ -817,22 +844,26 @@ This duration is computed as follows: For example, assume: - smoothed_rtt = 1 - rttvar = 0 - max_ack_delay = 0 - kPersistentCongestionThreshold = 3 +~~~ +smoothed_rtt = 1 +rttvar = 0 +max_ack_delay = 0 +kPersistentCongestionThreshold = 3 +~~~ If an ack-eliciting packet is sent at time t = 0, the following scenario would illustrate persistent congestion: - t=0 | Send Pkt #1 (App Data) - t=1 | Send Pkt #2 (PTO 1) - t=3 | Send Pkt #3 (PTO 2) - t=7 | Send Pkt #4 (PTO 3) - t=8 | Recv ACK of Pkt #4 +| Time | Action | +|:-----|:-----------------------| +| t=0 | Send Pkt #1 (App Data) | +| t=1 | Send Pkt #2 (PTO 1) | +| t=3 | Send Pkt #3 (PTO 2) | +| t=7 | Send Pkt #4 (PTO 3) | +| t=8 | Recv ACK of Pkt #4 | The first three packets are determined to be lost when the acknowledgement of -packet 4 is received at t=8. The congestion period is calculated as the time +packet 4 is received at t = 8. The congestion period is calculated as the time between the oldest and newest lost packets: (3 - 0) = 3. The duration for persistent congestion is equal to: (1 * kPersistentCongestionThreshold) = 3. Because the threshold was reached and because none of the packets between the @@ -866,7 +897,7 @@ Sending multiple packets into the network without any delay between them creates a packet burst that might cause short-term congestion and losses. Implementations MUST either use pacing or limit such bursts to the initial congestion window, which is recommended to be the minimum of -10 * max_datagram_size and max(2* max_datagram_size, 14720)), where +`10 * max_datagram_size` and `max(2 * max_datagram_size, 14720))`, where max_datagram_size is the current maximum size of a datagram for the connection, not including UDP or IP overhead. @@ -882,7 +913,7 @@ the congestion window SHOULD NOT be increased in either slow start or congestion avoidance. This can happen due to insufficient application data or flow control limits. -A sender MAY use the pipeACK method described in section 4.3 of {{?RFC7661}} +A sender MAY use the pipeACK method described in Section 4.3 of {{?RFC7661}} to determine if the congestion window is sufficiently utilized. A sender that paces packets (see {{pacing}}) might delay sending packets @@ -983,7 +1014,7 @@ time_sent: : The time the packet was sent. -## Constants of interest +## Constants of Interest {#constants-of-interest} Constants used in loss recovery are based on a combination of RFCs, papers, and common practice. @@ -1032,7 +1063,7 @@ smoothed_rtt: {{smoothed-rtt}}. rttvar: -: The RTT variation, computed as described in {{smoothed-rtt}} +: The RTT variation, computed as described in {{smoothed-rtt}}. min_rtt: : The minimum RTT seen in the connection, ignoring ack delay, as described @@ -1224,7 +1255,7 @@ SetLossDetectionTimer(): return if (server is at anti-amplification limit): - // The server's alarm is not set if nothing can be sent. + // The server's timer is not set if nothing can be sent. loss_detection_timer.cancel() return @@ -1236,16 +1267,23 @@ SetLossDetectionTimer(): loss_detection_timer.cancel() return + // Determine which PN space to arm PTO for. + sent_time, pn_space = GetEarliestTimeAndSpace( + time_of_last_sent_ack_eliciting_packet) + // Don't arm PTO for ApplicationData until handshake complete. + if (pn_space == ApplicationData && + handshake is not confirmed): + loss_detection_timer.cancel() + return + if (sent_time == 0): + assert(!PeerCompletedAddressValidation()) + sent_time = now() + // Calculate PTO duration timeout = smoothed_rtt + max(4 * rttvar, kGranularity) + max_ack_delay timeout = timeout * (2 ^ pto_count) - sent_time, _ = GetEarliestTimeAndSpace( - time_of_last_sent_ack_eliciting_packet) - if (sent_time == 0) - assert(!PeerCompletedAddressValidation()) - sent_time = now() loss_detection_timer.update(sent_time + timeout) ~~~ diff --git a/draft-ietf-quic-tls.md b/draft-ietf-quic-tls.md index 423aceb61a..ecab0368c0 100644 --- a/draft-ietf-quic-tls.md +++ b/draft-ietf-quic-tls.md @@ -188,10 +188,6 @@ TLS provides two basic handshake modes of interest to QUIC: self-contained trigger for any non-idempotent action. A simplified TLS handshake with 0-RTT application data is shown in {{tls-full}}. -Note that this omits the EndOfEarlyData message, which is not used in QUIC (see -{{remove-eoed}}). Likewise, neither ChangeCipherSpec nor KeyUpdate messages are -used by QUIC; ChangeCipherSpec is redundant in TLS 1.3 and QUIC has defined its -own key update mechanism {{key-update}}. ~~~ Client Server @@ -213,6 +209,11 @@ own key update mechanism {{key-update}}. ~~~ {: #tls-full title="TLS Handshake with 0-RTT"} +{{tls-full}} omits the EndOfEarlyData message, which is not used in QUIC; see +{{remove-eoed}}. Likewise, neither ChangeCipherSpec nor KeyUpdate messages are +used by QUIC. ChangeCipherSpec is redundant in TLS 1.3; see {{compat-mode}}. +QUIC has its own key update mechanism; see {{key-update}}. + Data is protected using a number of encryption levels: - Initial Keys @@ -446,7 +447,7 @@ network, it proceeds as follows: find the proper location in the data sequence. If the result of this process is that new data is available, then it is delivered to TLS in order. -- If the packet is from a previously installed encryption level, it MUST not +- If the packet is from a previously installed encryption level, it MUST NOT contain data which extends past the end of previously received data in that flow. Implementations MUST treat any violations of this requirement as a connection error of type PROTOCOL_VIOLATION. @@ -645,6 +646,31 @@ messages and clients MUST treat receipt of such messages as a connection error of type PROTOCOL_VIOLATION. +## Session Resumption {#resumption} + +QUIC can use the session resumption feature of TLS 1.3. It does this by +carrying NewSessionTicket messages in CRYPTO frames after the handshake is +complete. Session resumption is the basis of 0-RTT, but can be used without +also enabling 0-RTT. + +Endpoints that use session resumption might need to remember some information +about the current connection when creating a resumed connection. TLS requires +that some information be retained; see Section 4.6.1 of {{!TLS13}}. QUIC itself +does not depend on any state being retained when resuming a connection, unless +0-RTT is also used; see {{enable-0rtt}} and Section 7.3.1 of +{{QUIC-TRANSPORT}}. Application protocols could depend on state that is +retained between resumed connections. + +Clients can store any state required for resumption along with the session +ticket. Servers can use the session ticket to help carry state. + +Session resumption allows servers to link activity on the original connection +with the resumed connection, which might be a privacy issue for clients. +Clients can choose not to enable resumption to avoid creating this correlation. +Client SHOULD NOT reuse tickets as that allows entities other than the server +to correlate connections; see Section C.4 of {{!TLS13}}. + + ## Enabling 0-RTT {#enable-0rtt} To communicate their willingness to process 0-RTT data, servers send a @@ -715,19 +741,25 @@ QUIC implementations SHOULD instead use the Retry feature (see Section 8.1 of {{QUIC-TRANSPORT}}). HelloRetryRequest is still used to request key shares. -## TLS Errors +## TLS Errors {#tls-errors} If TLS experiences an error, it generates an appropriate alert as defined in Section 6 of {{!TLS13}}. -A TLS alert is turned into a QUIC connection error by converting the one-byte -alert description into a QUIC error code. The alert description is added to -0x100 to produce a QUIC error code from the range reserved for CRYPTO_ERROR. -The resulting value is sent in a QUIC CONNECTION_CLOSE frame of type 0x1c. +A TLS alert is converted into a QUIC connection error. The alert description is +added to 0x100 to produce a QUIC error code from the range reserved for +CRYPTO_ERROR. The resulting value is sent in a QUIC CONNECTION_CLOSE frame of +type 0x1c. The alert level of all TLS alerts is "fatal"; a TLS stack MUST NOT generate alerts at the "warning" level. +QUIC permits the use of a generic code in place of a specific error code; see +Section 11 of {{QUIC-TRANSPORT}}. For TLS alerts, this includes replacing any +alert with a generic alert, such as handshake_failure (0x128 in QUIC). +Endpoints MAY use a generic error code to avoid possibly exposing confidential +information. + ## Discarding Unused Keys @@ -1529,7 +1561,7 @@ Handshake packets, but because that tampering requires modifying TLS handshake messages, that tampering will cause the TLS handshake to fail. -# QUIC-Specific Additions to the TLS Handshake +# QUIC-Specific Adjustments to the TLS Handshake QUIC uses the TLS handshake for more than just negotiation of cryptographic parameters. The TLS handshake provides preliminary values for QUIC transport @@ -1542,12 +1574,13 @@ QUIC requires that the cryptographic handshake provide authenticated protocol negotiation. TLS uses Application Layer Protocol Negotiation (ALPN) {{!ALPN=RFC7301}} to select an application protocol. Unless another mechanism is used for agreeing on an application protocol, endpoints MUST use ALPN for -this purpose. When using ALPN, endpoints MUST immediately close a connection -(see Section 10.3 in {{QUIC-TRANSPORT}}) if an application protocol is not -negotiated with a no_application_protocol TLS alert (QUIC error code 0x178, see -{{tls-errors}}). While {{!ALPN}} only specifies that servers use this alert, -QUIC clients MUST also use it to terminate a connection when ALPN negotiation -fails. +this purpose. + +When using ALPN, endpoints MUST immediately close a connection (see Section +10.3 of {{QUIC-TRANSPORT}}) with a no_application_protocol TLS alert (QUIC error +code 0x178; see {{tls-errors}}) if an application protocol is not negotiated. +While {{!ALPN}} only specifies that servers use this alert, QUIC clients MUST +use error 0x178 to terminate a connection when ALPN negotiation fails. An application protocol MAY restrict the QUIC versions that it can operate over. Servers MUST select an application protocol compatible with the QUIC version @@ -1572,7 +1605,7 @@ protection for these values. } ExtensionType; ~~~ -The `extension_data` field of the quic_transport_parameters extension contains a +The extension_data field of the quic_transport_parameters extension contains a value that is defined by the version of QUIC that is in use. The quic_transport_parameters extension is carried in the ClientHello and the @@ -1606,13 +1639,36 @@ PROTOCOL_VIOLATION. As a result, EndOfEarlyData does not appear in the TLS handshake transcript. +## Prohibit TLS Middlebox Compatibility Mode {#compat-mode} + +Appendix D.4 of {{!TLS13}} describes an alteration to the TLS 1.3 handshake as +a workaround for bugs in some middleboxes. The TLS 1.3 middlebox compatibility +mode involves setting the legacy_session_id field to a 32-byte value in the +ClientHello and ServerHello, then sending a change_cipher_spec record. Both +field and record carry no semantic content and are ignored. + +This mode has no use in QUIC as it only applies to middleboxes that interfere +with TLS over TCP. QUIC also provides no means to carry a change_cipher_spec +record. A client MUST NOT request the use of the TLS 1.3 compatibility mode. A +server SHOULD treat the receipt of a TLS ClientHello that with a non-empty +legacy_session_id field as a connection error of type PROTOCOL_VIOLATION. + + # Security Considerations -There are likely to be some real clangers here eventually, but the current set -of issues is well captured in the relevant sections of the main text. +All of the security considerations that apply to TLS also apply to the use of +TLS in QUIC. Reading all of {{!TLS13}} and its appendices is the best way to +gain an understanding of the security properties of QUIC. -Never assume that because it isn't in the security considerations section it -doesn't affect security. Most of this document does. +This section summarizes some of the more important security aspects specific to +the TLS integration, though there are many security-relevant details in the +remainder of the document. + + +## Session Linkability + +Use of TLS session tickets allows servers and possibly other entities to +correlate connections made by the same client; see {{resumption}} for details. ## Replay Attacks with 0-RTT @@ -2163,23 +2219,24 @@ No significant changes. The IETF QUIC Working Group received an enormous amount of support from many people. The following people provided substantive contributions to this document: -Adam Langley, -Alessandro Ghedini, -Christian Huitema, -Christopher Wood, -David Schinazi, -Dragana Damjanovic, -Eric Rescorla, -Ian Swett, -Jana Iyengar, , -Marten Seemann, -Martin Duke, -Mike Bishop, , -Nick Banks, -Nick Harper, -Roberto Peon, -Rui Paulo, -Ryan Hamilton, -and Victor Vasiliev. + +- Adam Langley +- Alessandro Ghedini +- Christian Huitema +- Christopher Wood +- David Schinazi +- Dragana Damjanovic +- Eric Rescorla +- Ian Swett +- Jana Iyengar +- +- Marten Seemann +- Martin Duke +- Mike Bishop +- +- Nick Banks +- Nick Harper +- Roberto Peon +- Rui Paulo +- Ryan Hamilton +- Victor Vasiliev diff --git a/draft-ietf-quic-transport.md b/draft-ietf-quic-transport.md index bb453f044f..eeabfef1b7 100644 --- a/draft-ietf-quic-transport.md +++ b/draft-ietf-quic-transport.md @@ -190,8 +190,8 @@ QUIC packet: Ack-eliciting Packet: : A QUIC packet that contains frames other than ACK, PADDING, and - CONNECTION_CLOSE. These cause a recipient to send an acknowledgment (see - {{sending-acknowledgements}}). + CONNECTION_CLOSE. These cause a recipient to send an acknowledgment; see + {{sending-acknowledgements}}. Out-of-order packet: @@ -256,7 +256,7 @@ x (i): x (A..B): : Indicates that x can be any length from A to B; A can be omitted to indicate - a mimumum of zero bits and B can be omitted to indicate no set upper limit; + a minimum of zero bits and B can be omitted to indicate no set upper limit; values in this format always end on an octet boundary x (?) = C: @@ -311,7 +311,7 @@ means of ensuring ordering between bytes on different streams. QUIC allows for an arbitrary number of streams to operate concurrently and for an arbitrary amount of data to be sent on any stream, subject to flow control -constraints (see {{flow-control}}) and stream limits. +constraints and stream limits; see {{flow-control}}. ## Stream Types and Identifiers {#stream-id} @@ -916,8 +916,8 @@ An endpoint MUST NOT send data on a stream at or beyond the final size. Once a final size for a stream is known, it cannot change. If a RESET_STREAM or STREAM frame is received indicating a change in the final size for the stream, -an endpoint SHOULD respond with a FINAL_SIZE_ERROR error (see -{{error-handling}}). A receiver SHOULD treat receipt of data at or beyond the +an endpoint SHOULD respond with a FINAL_SIZE_ERROR error; see +{{error-handling}}. A receiver SHOULD treat receipt of data at or beyond the final size as a FINAL_SIZE_ERROR error, even after a stream is closed. Generating these errors is not mandatory, but only because requiring that an endpoint generate these errors also means that the endpoint needs to maintain @@ -1097,15 +1097,16 @@ field. The endpoint SHOULD continue to accept the previously issued connection IDs until they are retired by the peer. If the endpoint can no longer process the indicated connection IDs, it MAY close the connection. -Upon receipt of an increased Retire Prior To field, the peer MUST stop using the -corresponding connection IDs and retire them with RETIRE_CONNECTION_ID frames -before adding the newly provided connection ID to the set of active connection -IDs. This ordering allows an endpoint that has already supplied its peer with as -many connection IDs as allowed by the active_connection_id_limit transport -parameter to replace those connection IDs with new ones as necessary. Failure -to cease using the connection IDs when requested can result in connection -failures, as the issuing endpoint might be unable to continue using the -connection IDs with the active connection. +Upon receipt of an increased Retire Prior To field, the peer MUST stop using +the corresponding connection IDs and retire them with RETIRE_CONNECTION_ID +frames before adding the newly provided connection ID to the set of active +connection IDs. This ordering allows an endpoint to replace all active +connection IDs without the possibility of a peer having no available connection +IDs and without exceeding the limit the peer sets in the +active_connection_id_limit transport parameter; see +{{transport-parameter-definitions}}. Failure to cease using the connection IDs +when requested can result in connection failures, as the issuing endpoint might +be unable to continue using the connection IDs with the active connection. ## Matching Packets to Connections {#packet-handling} @@ -1194,6 +1195,29 @@ SHOULD ignore any such packets. Servers MUST drop incoming packets under all other circumstances. +### Considerations for Simple Load Balancers + +A server deployment could load balance among servers using only source and +destination IP addresses and ports. Changes to the client's IP address or port +could result in packets being forwarded to the wrong server. Such a server +deployment could use one of the following methods for connection continuity +when a client's address changes. + +* Servers could use an out-of-band mechanism to forward packets to the correct + server based on Connection ID. + +* If servers can use a dedicated server IP address or port, other than the one + that the client initially connects to, they could use the preferred_address + transport parameter to request that clients move connections to that dedicated + address. Note that clients could choose not to use the preferred address. + +A server in a deployment that does not implement a solution to +maintain connection continuity during connection migration +SHOULD disallow migration using the disable_active_migration transport +parameter. + +Server deployments that use this simple form of load balancing MUST avoid the +creation of a stateless reset oracle; see {{reset-oracle}}. ## Life of a QUIC Connection {#connection-lifecycle} @@ -1344,16 +1368,15 @@ MUST NOT be used outside of draft implementations. ## Using Reserved Versions For a server to use a new version in the future, clients need to correctly -handle unsupported versions. To help ensure this, a server SHOULD include a -version that is reserved for forcing version negotiation (0x?a?a?a?a as defined -in {{versions}}) when generating a Version Negotiation packet. +handle unsupported versions. Some version numbers (0x?a?a?a?a as defined in +{{versions}}) are reserved for inclusion in fields that contain version +numbers. -The design of version negotiation permits a server to avoid maintaining state -for packets that it rejects in this fashion. - -A client MAY send a packet using a version that is reserved for forcing version -negotiation. This can be used to solicit a list of supported versions from a -server. +Endpoints MAY add reserved versions to any field where unknown or unsupported +versions are ignored to test that a peer correctly ignores the value. For +instance, an endpoint could include a reserved version in a Version Negotiation +packet; see {{packet-version}}. Endpoints MAY send packets with a reserved +version to test that a peer correctly discards the packet. # Cryptographic and Transport Handshake {#handshake} @@ -1419,9 +1442,9 @@ the first packet is of type Initial, with packet number 0, and contains a CRYPTO frame carrying the ClientHello. Note that multiple QUIC packets -- even of different packet types -- can be -coalesced into a single UDP datagram (see {{packet-coalesce}}), and so this -handshake may consist of as few as 4 UDP datagrams, or any number more. For -instance, the server's first flight contains Initial packets, +coalesced into a single UDP datagram; see {{packet-coalesce}}). As a result, +this handshake may consist of as few as 4 UDP datagrams, or any number more. +For instance, the server's first flight contains Initial packets, Handshake packets, and "0.5-RTT data" in 1-RTT packets with a short header. ~~~~ @@ -1437,8 +1460,8 @@ Initial[1]: ACK[0] Handshake[0]: CRYPTO[FIN], ACK[0] 1-RTT[0]: STREAM[0, "..."], ACK[0] -> - 1-RTT[1]: STREAM[3, "..."], ACK[0] - <- Handshake[1]: ACK[0] + Handshake[1]: ACK[0] + <- 1-RTT[1]: STREAM[3, "..."], ACK[0] ~~~~ {: #tls-1rtt-handshake title="Example 1-RTT Handshake"} @@ -1461,8 +1484,8 @@ Initial[1]: ACK[0] Handshake[0]: CRYPTO[FIN], ACK[0] 1-RTT[1]: STREAM[0, "..."] ACK[0] -> - 1-RTT[1]: STREAM[3, "..."], ACK[1] - <- Handshake[1]: ACK[0] + Handshake[1]: ACK[0] + <- 1-RTT[1]: STREAM[3, "..."], ACK[1] ~~~~ {: #tls-0rtt-handshake title="Example 0-RTT Handshake"} @@ -1867,7 +1890,7 @@ connection properties. Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect against such attacks, servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time. Tokens that are provided in -NEW_TOKEN frames (see {{frame-new-token}}) need to be valid for longer, but +NEW_TOKEN frames ({{frame-new-token}}) need to be valid for longer, but SHOULD NOT be accepted multiple times in a short period. Servers are encouraged to allow tokens to be used only once, if possible. @@ -2023,7 +2046,7 @@ duration of the handshake. An endpoint MUST NOT initiate connection migration before the handshake is confirmed, as defined in section 4.1.2 of {{QUIC-TLS}}. An endpoint also MUST NOT send packets from a different local address, actively -initiating migration, if the peer sent the `disable_active_migration` transport +initiating migration, if the peer sent the disable_active_migration transport parameter during the handshake. An endpoint which has sent this transport parameter, but detects that a peer has nonetheless migrated to a different network MUST either drop the incoming packets on that path without generating a @@ -2147,8 +2170,8 @@ congestion window's worth of data per estimated round-trip time (kMinimumWindow, as defined in {{QUIC-RECOVERY}}). In the absence of this limit, an endpoint risks being used for a denial of service attack against an unsuspecting victim. Note that since the endpoint will not have any round-trip time measurements to -this address, the estimate SHOULD be the default initial value (see -{{QUIC-RECOVERY}}). +this address, the estimate SHOULD be the default initial value; see +{{QUIC-RECOVERY}}. If an endpoint skips validation of a peer address as described in {{migration-response}}, it does not need to limit its sending rate. @@ -2304,6 +2327,16 @@ that packet numbers cannot be used to correlate activity. This does not prevent other properties of packets, such as timing and size, from being used to correlate activity. +An endpoint SHOULD NOT initiate migration with a peer that has requested a +zero-length connection ID, because traffic over the new path might be trivially +linkable to traffic over the old one. If the server is able to route packets +with a zero-length connection ID to the right connection, it means that the +server is using other information to demultiplex packets. For example, a server +might provide a unique address to every client, for instance using HTTP +alternative services {{?ALTSVC=RFC7838}}. Information that might allow correct +routing of packets across multiple network paths will also allow activity on +those paths to be linked by entities other than the peer. + A client might wish to reduce linkability by employing a new connection ID and source UDP port when sending traffic after a period of inactivity. Changing the UDP port from which it sends packets at the same time might cause the packet to @@ -2374,6 +2407,11 @@ begins sending non-probing packets to the client exclusively from its preferred IP address. It SHOULD drop packets for this connection received on the old IP address, but MAY continue to process delayed packets. +The addresses that a server provides in the preferred_address transport +parameter are only valid for the connection in which they are provided. A +client MUST NOT use these for other connections, including connections that are +resumed from the current connection. + ### Interaction of Client Migration and Preferred Address @@ -2402,6 +2440,12 @@ address before path validation is complete. A client that migrates to a new address SHOULD use a preferred address from the same address family for the server. +The connection ID provided in the preferred_address transport parameter is not +specific to the addresses that are provided. This connection ID is provided to +ensure that the client has a connection ID available for migration, but the +client MAY use this connection ID on any path. + + ## Use of IPv6 Flow-Label and Migration {#ipv6-flow-label} Endpoints that send data using IPv6 SHOULD apply an IPv6 flow label @@ -2857,7 +2901,7 @@ are sent; such failures might only be detected by other means, such as timers. An endpoint that detects an error SHOULD signal the existence of that error to its peer. Both transport-level and application-level errors can affect an -entire connection (see {{connection-errors}}), while only application-level +entire connection; see {{connection-errors}}. Only application-level errors can be isolated to a single stream; see {{stream-errors}}. The most appropriate error code ({{error-codes}}) SHOULD be included in the @@ -2924,18 +2968,18 @@ prematurely cancelled by either endpoint. # Packets and Frames {#packets-frames} QUIC endpoints communicate by exchanging packets. Packets have confidentiality -and integrity protection (see {{packet-protected}}) and are carried in UDP -datagrams (see {{packet-coalesce}}). +and integrity protection; see {{packet-protected}}. Packets are carried in UDP +datagrams; see {{packet-coalesce}}. -This version of QUIC uses the long packet header (see {{long-header}}) during -connection establishment. Packets with the long header are Initial +This version of QUIC uses the long packet header during connection +establishment; see {{long-header}}. Packets with the long header are Initial ({{packet-initial}}), 0-RTT ({{packet-0rtt}}), Handshake ({{packet-handshake}}), and Retry ({{packet-retry}}). Version negotiation uses a version-independent -packet with a long header (see {{packet-version}}). +packet with a long header; see {{packet-version}}. -Packets with the short header ({{short-header}}) are designed for minimal -overhead and are used after a connection is established and 1-RTT keys are -available. +Packets with the short header are designed for minimal overhead and are used +after a connection is established and 1-RTT keys are available; see +{{short-header}}. ## Protected Packets {#packet-protected} @@ -3234,7 +3278,7 @@ contract: an endpoint promises to never intentionally delay acknowledgments of an ack-eliciting packet by more than the indicated value. If it does, any excess accrues to the RTT estimate and could result in spurious or delayed retransmissions from the peer. For Initial and Handshake packets, -a max_ack_delay of 0 is used. The sender uses the receiver's `max_ack_delay` +a max_ack_delay of 0 is used. The sender uses the receiver's max_ack_delay value in determining timeouts for timer-based retransmission, as detailed in Section 5.2.1 of {{QUIC-RECOVERY}}. @@ -3339,9 +3383,20 @@ necessary if an ACK frame would be too large to fit in a packet, however receivers MAY also limit ACK frame size further to preserve space for other frames. -When discarding unacknowledged ACK Ranges, a receiver MUST retain the largest -received packet number. A receiver SHOULD retain ACK Ranges containing newly -received packets or higher-numbered packets. +A receiver MUST retain an ACK Range unless it can ensure that it will not +subsequently accept packets with numbers in that range. Maintaining a minimum +packet number that increases as ranges are discarded is one way to achieve this +with minimal state. + +Receivers can discard all ACK Ranges, but they MUST retain the largest packet +number that has been successfully processed as that is used to recover packet +numbers from subsequent packets; see {{packet-encoding}}. + +A receiver SHOULD include an ACK Range containing the largest received packet +number in every ACK frame. The Largest Acknowledged field is used in ECN +validation at a sender and including a lower value than what was included in a +previous ACK frame could cause ECN to be unnecessarily disabled; see +{{ecn-validation}}. A receiver that sends only non-ack-eliciting packets, such as ACK frames, might not receive an acknowledgement for a long period of time. This could cause the @@ -3475,8 +3530,8 @@ containing that information is acknowledged. * The HANDSHAKE_DONE frame MUST be retransmitted until it is acknowledged. Endpoints SHOULD prioritize retransmission of data over sending new data, unless -priorities specified by the application indicate otherwise (see -{{stream-prioritization}}). +priorities specified by the application indicate otherwise; see +{{stream-prioritization}}. Even though a sender is encouraged to assemble frames containing up-to-date information every time it sends a packet, it is not forbidden to retransmit @@ -3639,17 +3694,22 @@ supports a reasonable Maximum Transmission Unit (MTU). Padding datagrams also helps reduce the amplitude of amplification attacks caused by server responses toward an unverified client address; see {{address-validation}}. +Enforcement of the max_udp_payload_size transport parameter +({{transport-parameter-definitions}}) might act as an additional limit on +packet size. Exceeding this limit can be avoided once the value is known. +However, prior to learning the value of the transport parameter, endpoints risk +datagrams being lost if they send packets larger than 1200 bytes. + Datagrams containing Initial packets MAY exceed 1200 bytes if the client -believes that the Path Maximum Transmission Unit (PMTU) supports the size that -it chooses. +believes that the network path and peer both support the size that it chooses. UDP datagrams MUST NOT be fragmented at the IP layer. In IPv4 {{!IPv4=RFC0791}}, the DF bit MUST be set to prevent fragmentation on the path. -A server MUST discard an Initial packet that is carried in a UDP datagram that -is smaller than 1200 bytes. A server MAY also immediately close the connection -by sending a CONNECTION_CLOSE frame with an error code of PROTOCOL_VIOLATION; -see {{immediate-close-hs}}. +A server MUST discard an Initial packet that is carried in a UDP datagram with +a payload that is less than 1200 bytes. A server MAY also immediately close the +connection by sending a CONNECTION_CLOSE frame with an error code of +PROTOCOL_VIOLATION; see {{immediate-close-hs}}. The server MUST also limit the number of bytes it sends before validating the address of the client; see {{address-validation}}. @@ -3657,11 +3717,12 @@ address of the client; see {{address-validation}}. ## Path Maximum Transmission Unit (PMTU) -The PMTU is the maximum size of the entire IP packet including the IP header, -UDP header, and UDP payload. The UDP payload includes the QUIC packet header, -protected payload, and any authentication fields. The PMTU can depend upon the -current path characteristics. Therefore, the current largest UDP payload an -implementation will send is referred to as the QUIC maximum packet size. +The Path Maximum Transmission Unit (PMTU) is the maximum size of the entire IP +packet including the IP header, UDP header, and UDP payload. The UDP payload +includes the QUIC packet header, protected payload, and any authentication +fields. The PMTU can depend on path characteristics, and can therefore change +over time. The largest UDP payload an endpoint sends at any given time is +referred to as the endpoint's maximum packet size. QUIC depends on a PMTU of at least 1280 bytes. This is the IPv6 minimum size {{?RFC8200}} and is also supported by most modern IPv4 networks. All QUIC @@ -3718,20 +3779,12 @@ associate the message with a corresponding transport connection {{!DPLPMTUD}}. ICMP message validation MUST include matching IP addresses and UDP ports {{!RFC8085}} and, when possible, connection IDs to an active QUIC session. -Further validation can also be provided: - -* An IPv4 endpoint could set the Don't Fragment (DF) bit on a small proportion - of packets, so that most invalid ICMP messages arrive when there are no DF - packets outstanding, and can therefore be identified as spurious. - -* An endpoint could store additional information from the IP or UDP headers to - use for validation (for example, the IP ID or UDP checksum). - The endpoint SHOULD ignore all ICMP messages that fail validation. -An endpoint MUST NOT increase PMTU based on ICMP messages. Any reduction in the -QUIC maximum packet size MAY be provisional until QUIC's loss detection -algorithm determines that the quoted packet has actually been lost. +An endpoint MUST NOT increase PMTU based on ICMP messages; see Section 3, clause +6 of {{!DPLPMTUD}}. Any reduction in the QUIC maximum packet size in response +to ICMP messages MAY be provisional until QUIC's loss detection algorithm +determines that the quoted packet has actually been lost. ## Datagram Packetization Layer PMTU Discovery @@ -3757,18 +3810,21 @@ apply if these messages are used by DPLPMTUD. ### PMTU Probes Containing Source Connection ID {#pmtu-probes-src-cid} -Endpoints that rely on the destination connection ID for routing QUIC packets -are likely to require that the connection ID be included in PMTU probe packets -to route any resulting ICMP messages ({{icmp-pmtud}}) back to the correct -endpoint. However, only long header packets ({{long-header}}) contain source -connection IDs, and long header packets are not decrypted or acknowledged by -the peer once the handshake is complete. One way to construct a PMTU probe is -to coalesce (see {{packet-coalesce}}) a Handshake packet ({{packet-handshake}}) -with a short header packet in a single UDP datagram. If the UDP datagram -reaches the endpoint, the Handshake packet will be ignored, but the short header -packet will be acknowledged. If the UDP datagram elicits an ICMP message, that -message will likely contain the source connection ID within the quoted portion -of the UDP datagram. +Endpoints that rely on the destination connection ID for routing incoming QUIC +packets are likely to require that the connection ID be included in PMTU probe +packets to route any resulting ICMP messages ({{icmp-pmtud}}) back to the +correct endpoint. However, only long header packets ({{long-header}}) contain +source connection IDs, and long header packets are not decrypted or acknowledged +by the peer once the handshake is complete. + +One way to construct a probe for the path MTU is to coalesce (see +{{packet-coalesce}}) a Handshake packet ({{packet-handshake}}) with a short +header packet in a single UDP datagram. If the UDP datagram reaches the +endpoint, the Handshake packet will be ignored, but the short header packet will +be acknowledged. If the UDP datagram causes an ICMP message to be sent, the +first part of the datagram will be quoted in that message. If the source +connection ID is within the quoted portion of the UDP datagram, that could be +used for routing. # Versions {#versions} @@ -4002,13 +4058,13 @@ contain these additional fields: Reserved Bits: : Two bits (those with a mask of 0x0c) of byte 0 are reserved across multiple - packet types. These bits are protected using header protection (see Section - 5.4 of {{QUIC-TLS}}). The value included prior to protection MUST be set to 0. + packet types. These bits are protected using header protection; see Section + 5.4 of {{QUIC-TLS}}. The value included prior to protection MUST be set to 0. An endpoint MUST treat receipt of a packet that has a non-zero value for these bits, after removing both packet and header protection, as a connection error of type PROTOCOL_VIOLATION. Discarding such a packet after only removing - header protection can expose the endpoint to attacks (see Section 9.3 of - {{QUIC-TLS}}). + header protection can expose the endpoint to attacks; see Section 9.3 of + {{QUIC-TLS}}. Packet Number Length: @@ -4017,7 +4073,7 @@ Packet Number Length: number, encoded as an unsigned, two-bit integer that is one less than the length of the packet number field in bytes. That is, the length of the packet number field is the value of this field, plus one. These bits are protected - using header protection (see Section 5.4 of {{QUIC-TLS}}). + using header protection; see Section 5.4 of {{QUIC-TLS}}. Length: @@ -4030,7 +4086,7 @@ Packet Number: : The packet number field is 1 to 4 bytes long. The packet number has confidentiality protection separate from packet protection, as described in Section 5.4 of {{QUIC-TLS}}. The length of the packet number field is encoded - in the Packet Number Length bits of byte 0 (see above). + in the Packet Number Length bits of byte 0; see above. ### Version Negotiation Packet {#packet-version} @@ -4049,9 +4105,9 @@ Version Negotiation Packet { Unused (7), Version (32) = 0, DCID Length (8), - Destination Connection ID (0..160), + Destination Connection ID (0..2040), SCID Length (8), - Source Connection ID (0..160), + Source Connection ID (0..2040), Supported Version (32) ..., } ~~~ @@ -4113,6 +4169,7 @@ Initial Packet { Source Connection ID (0..160), Token Length (i), Token (..), + Length (i), Packet Number (8..32), Packet Payload (..), } @@ -4181,7 +4238,7 @@ when it receives its first Handshake packet. Though packets might still be in flight or awaiting acknowledgment, no further Initial packets need to be exchanged beyond this point. Initial packet protection keys are discarded (see Section 4.10.1 of {{QUIC-TLS}}) along with any loss recovery and congestion -control state (see Section 6.5 of {{QUIC-RECOVERY}}). +control state; see Section 6.5 of {{QUIC-RECOVERY}}. Any data in CRYPTO frames is discarded - and no longer retransmitted - when Initial keys are discarded. @@ -4209,6 +4266,7 @@ limitations. Destination Connection ID (0..160), SCID Length (8), Source Connection ID (0..160), + Length (i), Packet Number (8..32), Packet Payload (..), } @@ -4265,8 +4323,7 @@ Handshake Packet { Destination Connection ID (0..160), SCID Length (8), Source Connection ID (0..160), - Token Length (i), - Token (..), + Length (i), Packet Number (8..32), Packet Payload (..), } @@ -4279,8 +4336,8 @@ to the server. The Destination Connection ID field in a Handshake packet contains a connection ID that is chosen by the recipient of the packet; the Source Connection ID -includes the connection ID that the sender of the packet wishes to use (see -{{negotiating-connection-ids}}). +includes the connection ID that the sender of the packet wishes to use; see +{{negotiating-connection-ids}}. Handshake packets are their own packet number space, and thus the first Handshake packet sent by a server contains a packet number of 0. @@ -4305,11 +4362,12 @@ Retry Packet { Header Form (1) = 1, Fixed Bit (1) = 1, Long Packet Type (2) = 3, - Type-Specific Bits (4), + Unused (4), Version (32), DCID Length (8), Destination Connection ID (0..160), SCID Length (8), + Source Connection ID (0..160), Retry Token (..), Retry Integrity Tag (128), } @@ -4329,8 +4387,8 @@ Retry Integrity Tag: : See the Retry Packet Integrity section of {{QUIC-TLS}}. - + +#### Sending a Retry Packet The server populates the Destination Connection ID with the connection ID that the client included in the Source Connection ID of the Initial packet. @@ -4349,6 +4407,9 @@ can send multiple Retry packets as it receives Initial or 0-RTT packets. A server MUST NOT send more than one Retry packet in response to a single UDP datagram. + +#### Handling a Retry Packet + A client MUST accept and process at most one Retry packet for each connection attempt. After the client has received and processed an Initial or Retry packet from the server, it MUST discard any subsequent Retry packets that it receives. @@ -4367,8 +4428,14 @@ value from the Source Connection ID in the Retry packet. Changing Destination Connection ID also results in a change to the keys used to protect the Initial packet. It also sets the Token field to the token provided in the Retry. The client MUST NOT change the Source Connection ID because the server could include -the connection ID as part of its token validation logic (see -{{token-integrity}}). +the connection ID as part of its token validation logic; see +{{token-integrity}}. + +A Retry packet does not include a packet number and cannot be explicitly +acknowledged by a client. + + +#### Continuing a Handshake After Retry The next Initial packet from the client uses the connection ID and token values from the Retry packet; see {{negotiating-connection-ids}}. Aside from this, @@ -4386,8 +4453,8 @@ A client MUST NOT reset the packet number for any packet number space after processing a Retry packet; {{packet-0rtt}} contains more information on this. A server acknowledges the use of a Retry packet for a connection using the -original_connection_id transport parameter (see -{{transport-parameter-definitions}}). If the server sends a Retry packet, it +original_connection_id transport parameter; see +{{transport-parameter-definitions}}. If the server sends a Retry packet, it MUST include the Destination Connection ID field from the client's first Initial packet in the transport parameter. @@ -4396,8 +4463,6 @@ original_connection_id transport parameter is present and correct; otherwise, it MUST validate that the transport parameter is absent. A client MUST treat a failed validation as a connection error of type TRANSPORT_PARAMETER_ERROR. -A Retry packet does not include a packet number and cannot be explicitly -acknowledged by a client. ## Short Header Packets {#short-header} @@ -4439,13 +4504,13 @@ as described in {{spin-bit}}. Reserved Bits: : The next two bits (those with a mask of 0x18) of byte 0 are reserved. These - bits are protected using header protection (see Section 5.4 of - {{QUIC-TLS}}). The value included prior to protection MUST be set to 0. An + bits are protected using header protection; see Section 5.4 of + {{QUIC-TLS}}. The value included prior to protection MUST be set to 0. An endpoint MUST treat receipt of a packet that has a non-zero value for these bits, after removing both packet and header protection, as a connection error of type PROTOCOL_VIOLATION. Discarding such a packet after only removing - header protection can expose the endpoint to attacks (see Section 9.3 of - {{QUIC-TLS}}). + header protection can expose the endpoint to attacks; see Section 9.3 of + {{QUIC-TLS}}. Key Phase: @@ -4460,7 +4525,7 @@ Packet Number Length: the length of the packet number, encoded as an unsigned, two-bit integer that is one less than the length of the packet number field in bytes. That is, the length of the packet number field is the value of this field, plus one. These - bits are protected using header protection (see Section 5.4 of {{QUIC-TLS}}). + bits are protected using header protection; see Section 5.4 of {{QUIC-TLS}}. Destination Connection ID: @@ -4536,7 +4601,7 @@ connection. # Transport Parameter Encoding {#transport-parameter-encoding} -The `extension_data` field of the quic_transport_parameters extension defined in +The extension_data field of the quic_transport_parameters extension defined in {{QUIC-TLS}} contains the QUIC transport parameters. They are encoded as a sequence of transport parameters, as shown in {{transport-parameter-sequence}}: @@ -4580,8 +4645,8 @@ This section details the transport parameters defined in this document. Many transport parameters listed here have integer values. Those transport parameters that are identified as integers use a variable-length integer -encoding (see {{integer-encoding}}) and have a default value of 0 if the -transport parameter is absent, unless otherwise stated. +encoding; see {{integer-encoding}}. Transport parameters have a default value +of 0 if the transport parameter is absent unless otherwise stated. The following transport parameters are defined: @@ -4715,11 +4780,19 @@ preferred_address (0x0d): network byte order. : The Connection ID field and the Stateless Reset Token field contain an - alternative connection ID that has a sequence number of 1 ({{issue-cid}}). + alternative connection ID that has a sequence number of 1; see {{issue-cid}}. Having these values bundled with the preferred address ensures that there will be at least one unused active connection ID when the client initiates migration to the preferred address. +: The Connection ID and Stateless Reset Token fields of a preferred address are + identical in syntax and semantics to the corresponding fields of a + NEW_CONNECTION_ID frame ({{frame-new-connection-id}}). A server that chooses + a zero-length connection ID MUST NOT provide a preferred address. Similarly, + a server MUST NOT include a zero-length connection ID in this transport + parameter. A client MUST treat violation of these requirements as a + connection error of type TRANSPORT_PARAMETER_ERROR. + ~~~ Preferred Address { IPv4 Address (32), @@ -4855,7 +4928,7 @@ ACK Delay: when this ACK was sent and when the largest acknowledged packet, as indicated in the Largest Acknowledged field, was received by this peer. The value of the ACK Delay field is scaled by multiplying the encoded value by 2 to the - power of the value of the `ack_delay_exponent` transport parameter set by the + power of the value of the ack_delay_exponent transport parameter set by the sender of the ACK frame; see {{transport-parameter-definitions}}. Scaling in this fashion allows for a larger range of values with a shorter encoding at the cost of lower resolution. Because the receiver doesn't use the ACK Delay @@ -5200,8 +5273,8 @@ STREAM frames contain the following fields: Stream ID: -: A variable-length integer indicating the stream ID of the stream (see - {{stream-id}}). +: A variable-length integer indicating the stream ID of the stream; see + {{stream-id}}. Offset: @@ -5265,8 +5338,8 @@ the initial limits; see {{zerortt-parameters}}. The MAX_STREAM_DATA frame (type=0x11) is used in flow control to inform a peer of the maximum amount of data that can be sent on a stream. -A MAX_STREAM_DATA frame can be sent for streams in the Recv state (see -{{stream-send-states}}). Receiving a MAX_STREAM_DATA frame for a +A MAX_STREAM_DATA frame can be sent for streams in the Recv state; see +{{stream-send-states}}. Receiving a MAX_STREAM_DATA frame for a locally-initiated stream that has not yet been created MUST be treated as a connection error of type STREAM_STATE_ERROR. An endpoint that receives a MAX_STREAM_DATA frame for a receive-only stream MUST terminate the connection @@ -5353,8 +5426,8 @@ includes streams that have been closed as well as those that are open. ## DATA_BLOCKED Frame {#frame-data-blocked} A sender SHOULD send a DATA_BLOCKED frame (type=0x14) when it wishes to send -data, but is unable to due to connection-level flow control (see -{{flow-control}}). DATA_BLOCKED frames can be used as input to tuning of flow +data, but is unable to due to connection-level flow control; see +{{flow-control}}. DATA_BLOCKED frames can be used as input to tuning of flow control algorithms; see {{fc-credit}}. The DATA_BLOCKED frame is shown in {{fig-data-blocked}}. @@ -5522,6 +5595,7 @@ NEW_CONNECTION_ID frame MUST send a corresponding RETIRE_CONNECTION_ID frame that retires the newly received connection ID, unless it has already done so for that sequence number. + ## RETIRE_CONNECTION_ID Frame {#frame-retire-connection-id} An endpoint sends a RETIRE_CONNECTION_ID frame (type=0x19) to indicate that it @@ -6064,11 +6138,23 @@ considered separately. ### Handshake {#handshake-properties} -The QUIC handshake incorporates the TLS 1.3 handshake and enjoys the -cryptographic properties described in Appendix E.1 of {{?TLS13=RFC8446}}. +The QUIC handshake incorporates the TLS 1.3 handshake and inherits the +cryptographic properties described in Appendix E.1 of {{?TLS13=RFC8446}}. Many +of the security properties of QUIC depend on the TLS handshake providing these +properties. Any attack on the TLS handshake could affect QUIC. + +Any attack on the TLS handshake that compromises the secrecy or uniqueness +of session keys affects other security guarantees provided by QUIC that depends +on these keys. For instance, migration ({{migration}}) depends on the efficacy +of confidentiality protections, both for the negotiation of keys using the TLS +handshake and for QUIC packet protection, to avoid linkability across network +paths. -In addition to those properties, the handshake is intended to provide some -defense against DoS attacks on the handshake, as described below. +An attack on the integrity of the TLS handshake might allow an attacker to +affect the selection of application protocol or QUIC version. + +In addition to the properties provided by TLS, the QUIC handshake provides some +defense against DoS attacks on the handshake. #### Anti-Amplification @@ -6115,9 +6201,9 @@ The entire handshake is cryptographically protected, with the Initial packets being encrypted with per-version keys and the Handshake and later packets being encrypted with keys derived from the TLS key exchange. Further, parameter negotiation is folded into the TLS transcript and thus provides the same -security guarantees as ordinary TLS negotiation. Thus, an attacker can observe +integrity guarantees as ordinary TLS negotiation. An attacker can observe the client's transport parameters (as long as it knows the version-specific -salt) but cannot observe the server's transport parameters and cannot influence +keys) but cannot observe the server's transport parameters and cannot influence parameter negotiation. Connection IDs are unencrypted but integrity protected in all packets. @@ -6504,7 +6590,7 @@ The initial contents of this registry are shown in {{iana-tp-table}}. {: #iana-tp-table title="Initial QUIC Transport Parameters Entries"} Additionally, each value of the format `31 * N + 27` for integer values of N -(that is, `27`, `58`, `89`, ...) are reserved and MUST NOT be assigned by IANA. +(that is, 27, 58, 89, ...) are reserved and MUST NOT be assigned by IANA. ## QUIC Frame Type Registry {#iana-frames} @@ -6529,9 +6615,9 @@ Frame Name: In addition to the advice in {{iana-policy}}, specifications for new permanent registrations SHOULD describe the means by which an endpoint might determine that it can send the identified type of frame. An accompanying transport -parameter registration (see {{iana-transport-parameters}}) is expected for most -registrations. Specifications for permanent registrations also needs to -describe the format and assigned semantics of any fields in the frame. +parameter registration is expected for most registrations; see +{{iana-transport-parameters}}. Specifications for permanent registrations also +needs to describe the format and assigned semantics of any fields in the frame. The initial contents of this registry are tabulated in {{frame-types}}. @@ -7169,34 +7255,35 @@ work by Jim Roskind {{EARLY-DESIGN}}. The IETF QUIC Working Group received an enormous amount of support from many people. The following people provided substantive contributions to this document: -Alessandro Ghedini, -Alyssa Wilk, -Antoine Delignat-Lavaud, -Brian Trammell, -Christian Huitema, -Colin Perkins, -David Schinazi, -Dmitri Tikhonov, -Eric Kinnear, -Eric Rescorla, -Gorry Fairhurst, -Ian Swett, -Igor Lubashev, , -Lucas Pardue, -Magnus Westerlund, -Marten Seemann, -Martin Duke, -Mike Bishop, , , -Nick Banks, -Nick Harper, -Patrick McManus, -Roberto Peon, -Ryan Hamilton, -Subodh Iyengar, -Tatsuhiro Tsujikawa, -Ted Hardie, -Tom Jones, -and Victor Vasiliev. + +- Alessandro Ghedini +- Alyssa Wilk +- Antoine Delignat-Lavaud +- Brian Trammell +- Christian Huitema +- Colin Perkins +- David Schinazi +- Dmitri Tikhonov +- Eric Kinnear +- Eric Rescorla +- Gorry Fairhurst +- Ian Swett +- Igor Lubashev +- +- Lucas Pardue +- Magnus Westerlund +- Marten Seemann +- Martin Duke +- Mike Bishop +- +- +- Nick Banks +- Nick Harper +- Patrick McManus +- Roberto Peon +- Ryan Hamilton +- Subodh Iyengar +- Tatsuhiro Tsujikawa +- Ted Hardie +- Tom Jones +- Victor Vasiliev diff --git a/workflow.xml b/workflow.xml deleted file mode 100644 index aa0c254010..0000000000 --- a/workflow.xml +++ /dev/null @@ -1 +0,0 @@ -7Vxbc5w2FP41O20f7AHEbR/jdZymk3ZS2500Tx0taBc1gChibW9+fSWQWEDYi73c7ElmkiwHxOWc8507LMAqeviQwiT4nfgoXBia/7AAlwvDWOou+5cT9gXBMeyCsE2xX5D0A+EGf0eCqAnqDvuI1g7MCAkznNSJHolj5GU1GkxTcl8/bEPC+lUTuEUK4caDoUr9gv0sKKiufApO/xXhbSCvrNvLYs8aet+2KdnF4noLA2zyP8XuCMpziQelAfTJfYUE3i/AKiUkK35FDysUctZKthXrrh7ZW953iuKsy4LIodruu34d3NFb/Tq5D+Lf/jnTNfE0NNtLjiCfMUhskjQLyJbEMHx/oF7kT434eTW2dTjmEyEJI+qM+C/Ksr2QNtxlhJGCLArFXvSAs7/58nNLbH2t7Ll8EGfON/ZyI87SfWUR3/xa3XdYlm/JdcXz8Yd6lHGCRMku9dAT3FoKpchgukXZk2x1Sgkz4CASIXZLbGWKQpjhu/qtQKHC2/K4gxjZDyHJ50jVMd+6VFHsv+PoZ5teCCnFXkG8wqG8nQ2JsysY4ZCvWTHZYpQynvyB7qfSCseYUivkfd7BcCcuxXlhaB8pZYSmwtTV4T7AGbpJYM6Je+YJ6qLfMLavSEjSfC3wIXI3XCI0S8k3VNljey5ab0oB3KE0Qw9Pi0Dll1hgmsK0CtcDhC27P9hxXVrfoGLDbW0o3OmOwuIURRDHdFI8VtB4wGY7HnvExelGUCz9TDC79kHsy7rYdXNZP0UBRLGqIdLyNk6R8rQ+8zV5TKEDXYyjbk3qMsswbRKh6m9VqOakQtVUe3ybYp4RKN4uINF6R5/t6Vi8b3itns6317Zl9+Tp7IbJs1VX57Z4OncwT2dYryXC7FPxT1bndo9mG41Axhnbo5kKTlhSjrcxX1lEh3bImHWxZhG0vc1Ktr40XuwBE1YDE4YzefhnGq8FFHPwIrrV1YuARzRhJC8ib7SCDuTjjDBPEs4YIMCYHUAMZ2KAVBGinS+fzoX4xmeUYvbwKJ0RcMo66VHgFFXM6WJqXXUrmHo7SjGJVU0IQ5xQLnEawIQTvZDs/JkWH5ruB3RF13AxGVCj3T2iHSzTGBh8aZ2v1blp5y5wn43dPjFod06BAJgWg2rVL0lJQij3XU3N+HlHdzAM+e1B9vfz9S8dtEei1SfeLso5eQyw60KbPq0nRrBdQ7ApkTkdgq1pihDHa+l9QufkaK5bWXBpj5xEAdXb/UBaayRqaebUSAPurLzgueaUhEc7XmxrSJdWOKpOLm3aqp680QrQViSmKKY7Fu1oKxiWaZmvSPkgQ32mxb6mX3KMjn5puLxNdtZ+5G0nAczsDLBpeyGgpRz4dvK2BrqWk0d9pmrPYjKTtO0F/akymIxJjGqRZO+AOhUm7ZGk49TDFVMfOZI0pzW3z5D1YMM7/atJF7trTmt3LdXuMvDDiNvNeE35f0VBp0bqYCmYuczqwk4Rxd/hOj+AiyThypw/kHWxsC4ZBYa8/8MkxBjO3egFN7vYg+E7sSPCvp9rXgjXKLwoxyHrARMfiHzCcIuxTnEni3Jasirop0DyqJ0/0851VzZkBJDPxEVfah/kIWSzoWigJFLtNQSQFowSES64UoPaWXevLbcuBrfN5WotLrckDuBz26DWtLltZkrp0Ry3USpjKg9utTy3pPXtx4yGHytMp+LH1P60+zKHyLgH95XDhJHpfMNguWxIujhjv5CzpCOZRylgXr7T7O473Ul9p6kazgilfNJH+3ytGswTqgC+hVzfbDOarrEGdl8jP6BRzuyapww43uAqLGaZHkWLl/TAXwvfdW16xluqbkte8yF3tmcDBYzt/3b8hZKL+rx7SZaCkYXpsxRBfy9Pxu6uOF+fYSSzfLQwgnqXqDLj9vOCMqXA8fY2N6Zn5lTKYJv1YoFluaou6C26YAynC2o/T45qDZMGtMsvRJvsOdJ76l2MSYLSpmiBZOyR2GxAyart+8qY0RsRbonbbQp9zGR1iVOW//G6IrhEkGZPQlouquy7ury6Wq360QnQaCECS53pcKxRdUJ1uSwdPDskg29FL0bKRB294dy1jqnocCKWYUOLcw90KdVPMENnNxks4tmUeIjSitc+HCiJiSTcBpjmAdqi6DTfYd54xhTmmDM0suHRfcDP++dfH9kzaF9I+o0Jkv36wMK15Ce+MuTXp+L6iby+tiGpDP7oeeV+kmdoY13gomZc1Q5BEvrFCJaibcZjE5WEqcsmzLOlAPs+ipXUrY+QsWE1VJVq9SQ6GE6n1FEERRJHgm9Ik+J16w1+4MyaplvTKAjYLbwFLawdkLPG8JwdJbRtctaZnLNqF6xvzo7jZBqcdTQ1iBiZs2qtE3rC/K/z+ly85zaWnScunYFHomgX42w/o4CiB+GUr7qUFsVRrfXIAYCa3dfFU2QB6ivEr1wQVsMAqYIYOxJTU+u6ILwA4nTeMfcAEHH0lmm9kSUzTUN8xFEGe5h35a1GNdNsymjoUQZHjUT5bEsfeJl5f9o+2p8Gmpz82deuOeP2tN32gYu8gixyQY8PX76+DrXTqPN2dUYDdqjteU0oL460JWf6no7R+eM8A1lg22iolunUT9G5Cd84URkwjWbK2wpVTQ09MjJRUS/NBTUFO7d0cEzJThuB7/C+5KTzGkeddVdVKRfKCK7ZmhxcVcAc47WxPgh2+ne+2sUKGmM4ZaVhNLGqxa8yN2Wen+dJDPU4KnPWygvyavo6/1mAZmW3rNUPMArANg/fgiwkdvjeJnj/Pw== \ No newline at end of file