首页
随机
最近更改
特殊页面
社群首页
参数设置
关于WHY42
免责声明
WHY42
搜索
用户菜单
登录
欢迎来到Riguz的小站!这是一个私人wiki,用来记录一些我的笔记。
查看“︁QUIC”︁的源代码
←
QUIC
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
QUIC is a new multiplexed transport built on top of UDP. HTTP/3 is designed to take advantage of QUIC's features, including lack of Head-Of-Line blocking between streams. The QUIC project started as an alternative to TCP+TLS+HTTP/2, with the goal of improving user experience, particularly page load times. The QUIC working group at the IETF defined a clear boundary between the transport(QUIC) and application(HTTP/3) layers, as well as migrating from QUIC Crypto to TLS 1.3. Because TCP is implemented in operating system kernels and middleboxes, widely deploying significant changes to TCP is next to impossible. However, since QUIC is built on top of UDP and the transport functionality is encrypted, it suffers from no such limitations. Key features of QUIC and HTTP/3 over TCP+TLS and HTTP/2 include<ref>https://www.chromium.org/quic/</ref> * Reduced connection establishment time - 0 round trips in the common case * Improved congestion control feedback * Multiplexing without head of line blocking * Connection migration * Transport extensibility * Optional unreliable delivery = Overview = == Reliablity == While UDP is not a reliable transport, QUIC adds a layer on top of UDP that introduces reliability. It offers re-transmissions of packets, congestion control, pacing and the other features otherwise present in TCP. Data sent over QUIC from one end-point will appear in the other end sooner or later, as long as the connection is maintained. By doing the fragmentation, retransmission and acknowledgements in QUIC.<ref>https://www.quora.com/How-does-QUIC-ensure-reliability-if-it-uses-UDP</ref> === Streams === Similar to SCTP, SSH and HTTP/2, QUIC features separate logical streams within the physical connections. A number of parallel streams that can transfer data simultaneously over a single connection without affecting the other streams. A connection is a negotiated setup between two end-points similar to how a TCP connection works. A QUIC connection is made to a UDP port and IP address, but once established the connection is associated by its "connection ID". Over an established connection, either side can create streams and send data to the other end. '''Streams are delivered in-order and they are reliable, but different streams may be delivered out-of-order.''' [[Image:Quic-stream-order.webp|600px]] QUIC offers flow control on both connection and streams. =Algorithm= == UDP Datagram 1 - Client hello == === Client key change generation=== The connection begins with the client generating a private/public keypair for key exchange. Key exchange is a technique where two parties can agree on the same number without an eavesdropper being able to tell what the number is. The private key is chosen by selecting an integer between 0 and 2^256-1. The client does this by generating 32 bytes (256 bits) of random data. The private key selected is<ref>https://quic.xargs.org/#client-key-exchange-generation</ref>: <syntaxhighlight lang="bash"> 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f </syntaxhighlight> The public key calculated is: <syntaxhighlight lang="bash"> 358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254 </syntaxhighlight> ===Client initialial key calculation === Next, the client continues to prepare for the connection by generating the encryption keys for the Initial packets. Because key exchange between client and server has not taken place there is limited security in these keys - any observer can derive the keys and read the traffic like the server will. Encrypting the Initial packets prevents certain kinds of attacks such as request forgery attacks. The client begins by generating 8 bytes of random data, in this case the bytes: <syntaxhighlight lang="bash"> 0001020304050607 </syntaxhighlight> The client then derives encryption keys using the following process: <syntaxhighlight lang="bash"> initial_salt = 38762cf7f55934b34d179ae6a4c80cadccbb7f0a initial_random = (random bytes from client given above) initial_secret = HKDF-Extract(salt: initial_salt, key: initial_random) client_secret = HKDF-Expand-Label(key: initial_secret, label: "client in", ctx: "", len: 32) server_secret = HKDF-Expand-Label(key: initial_secret, label: "server in", ctx: "", len: 32) client_key = HKDF-Expand-Label(key: client_secret, label: "quic key", ctx: "", len: 16) server_key = HKDF-Expand-Label(key: server_secret, label: "quic key", ctx: "", len: 16) client_iv = HKDF-Expand-Label(key: client_secret, label: "quic iv", ctx: "", len: 12) server_iv = HKDF-Expand-Label(key: server_secret, label: "quic iv", ctx: "", len: 12) client_hp_key = HKDF-Expand-Label(key: client_secret, label: "quic hp", ctx: "", len: 16) server_hp_key = HKDF-Expand-Label(key: server_secret, label: "quic hp", ctx: "", len: 16) </syntaxhighlight> The use of the magic constant "<syntaxhighlight lang="bash" inline>38762cf7f55934b34d179ae6a4c80cadccbb7f0a</syntaxhighlight>" as the initial salt is interesting, as it is not derived from mathematical constants or cryptographic principles. It's the value of the first SHA-1 collision, co-discovered by Google researchers (QUIC itself was initially created, sponsored, and deployed by Google). This has introduced two new cryptographic concepts from TLS 1.3: * HKDF-Extract - given a salt and some bytes of key material create 256 bits (32 bytes) of new key material, with the input key material's entropy evenly distributed in the output. * HKDF-Expand-Label - given the inputs of key material, label, and context data, create a new key of the requested length. <syntaxhighlight lang="bash"> $ init_salt=38762cf7f55934b34d179ae6a4c80cadccbb7f0a $ init_dcid=0001020304050607 $ init_secret=$(./hkdf extract $init_salt $init_dcid) $ csecret=$(./hkdf expandlabel $init_secret "client in" "" 32) $ ssecret=$(./hkdf expandlabel $init_secret "server in" "" 32) $ client_init_key=$(./hkdf expandlabel $csecret "quic key" "" 16) $ server_init_key=$(./hkdf expandlabel $ssecret "quic key" "" 16) $ client_init_iv=$(./hkdf expandlabel $csecret "quic iv" "" 12) $ server_init_iv=$(./hkdf expandlabel $ssecret "quic iv" "" 12) $ client_init_hp=$(./hkdf expandlabel $csecret "quic hp" "" 16) $ server_init_hp=$(./hkdf expandlabel $ssecret "quic hp" "" 16) $ echo ckey: $client_init_key $ echo civ: $client_init_iv $ echo chp: $client_init_hp $ echo skey: $server_init_key $ echo siv: $server_init_iv $ echo shp: $server_init_hp ckey: b14b918124fda5c8d79847602fa3520b civ: ddbc15dea80925a55686a7df chp: 6df4e9d737cdf714711d7c617ee82981 skey: d77fc4056fcfa32bd1302469ee6ebf90 siv: fcb748e37ff79860faa07477 shp: 440b2725e91dc79b370711ef792faa3d </syntaxhighlight> From this we get the following encryption keys and IVs: <syntaxhighlight lang="bash"> client initial key: b14b918124fda5c8d79847602fa3520b client initial IV: ddbc15dea80925a55686a7df server initial key: d77fc4056fcfa32bd1302469ee6ebf90 server initial IV: fcb748e37ff79860faa07477 </syntaxhighlight> We also get the following "header protection keys", which will be explained below: client initial header protection key: 6df4e9d737cdf714711d7c617ee82981 server initial header protection key: 440b2725e91dc79b370711ef792faa3d At this point there is still no data sent over the network. The session begins with the client sending an "Initial" packet. This packet contains the "ClientHello" TLS record, used to begin the TLS 1.3 encrypted session. [[Image:client hello.webp|600px]] [[Image:client hello frame.png|600px]] === Packet header === The packet begins with a header byte, which has header protection applied. Header protection is used to hide packet numbers and other information from outside observers. Header protection is applied by encrypting a sample of each packet's payload with the "header protection key", then XOR'ing certain bits and bytes in each packet with the resulting data. For "long" format packets such as this one, the protected sections are the lower 4 bits of this byte, and the bytes of the Packet Number (seen later). An example of how to compute header protection: <syntaxhighlight lang="bash"> ### "client header protection key" from calc step above $ key=6df4e9d737cdf714711d7c617ee82981 ### sample is taken from 16 bytes of payload starting ### 4 bytes past the first byte of the packet number $ sample=ed78716be9711ba498b7ed868443bb2e $ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p ed9895bb15 ### first byte of result is xor'd into lower 4 bits of this byte, ### remaining bytes are xor'd one-for-one into the bytes of ### the packet number (which in this packet is only one byte) </syntaxhighlight> === dcid === The client has not yet received a connection ID chosen by the server. Instead it uses this field to provide the 8 bytes of random data for deriving Initial encryption keys. === scid === The client uses this field to indicate its chosen connection ID to the server. === Token === The client can use this field in some scenarios to provide a token requested by the server, such as to prove that its connection attempt is not spoofed. In this case, there is no token to provide, and the field is empty. === Packet length === The client indicates how many bytes of encrypted payload are in the packet. This field is a variable length integer - the first two bits of the first byte indicate how many total bytes are in the integer. === Packet number === This byte has header protection applied. === Encrypted data === This data is encrypted with the client "Initial" traffic key. == UDP Datagram 2 - Server hello and handshake == === Server key exchange generation === The private key: <syntaxhighlight lang="bash"> 909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf </syntaxhighlight> The public key calculated is: <syntaxhighlight lang="bash"> 9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615 </syntaxhighlight> === Server initial keys cacl === Next, the server performs its own calculation of the Initial traffic keys. It gets the 8 bytes of random data from the "Destination Connection ID" field from the client's first Initial packet: 0001020304050607 and computes the same keys using the method shown in "Client Initial Keys Calc": <syntaxhighlight lang="bash"> client initial key: b14b918124fda5c8d79847602fa3520b client initial IV: ddbc15dea80925a55686a7df client initial header protection key: 6df4e9d737cdf714711d7c617ee82981 server initial key: d77fc4056fcfa32bd1302469ee6ebf90 server initial IV: fcb748e37ff79860faa07477 server initial header protection key: 440b2725e91dc79b370711ef792faa3d </syntaxhighlight> === Server Hello === The server responds with an "Initial" packet in return. This packet contains the "ServerHello" TLS record, used to continue the TLS 1.3 encrypted session negotiation. [[Image: server hello packet.png|600px]] == UDP Datagram 3 - Server handshake finished == == UDP Datagram 4 - Acks == == UDP Datagram 5 - Client handshake finished, "ping" == == UDP Datagram 6 - "pong" == == UDP Datagram 7 - Acks == == UDP Datagram 8 - Close connection == Ref: * [https://datatracker.ietf.org/doc/html/rfc9000 RFC9000: QUIC: A UDP-Based Multiplexed and Secure Transport] * [https://datatracker.ietf.org/doc/html/rfc9001 RFC9001: Using TLS to Secure QUIC] * [https://datatracker.ietf.org/doc/html/rfc9002 RFC9002: QUIC Loss Detection and Congestion Control] [[Category:Network]] [[Category:RFC]] [[Category:Protocol]] [[Category:HTTP]]
返回
QUIC
。