lib/quic/src/crypto/test.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const quic = @import("../root.zig");
  3 
  4 const crypto = quic.crypto;
  5 const KeyStorage = [crypto.Keys.storage_bytes_max]u8;
  6 
  7 fn hex(comptime text: []const u8) [text.len / 2]u8 {
  8     @setEvalBranchQuota(10_000);
  9     var result: [text.len / 2]u8 = undefined;
 10     _ = std.fmt.hexToBytes(&result, text) catch unreachable;
 11     return result;
 12 }
 13 
 14 fn deriveKeys(
 15     storage: *KeyStorage,
 16     suite: crypto.Suite,
 17     secret: crypto.Secret,
 18 ) !crypto.Keys {
 19     return crypto.Keys.derive(storage, suite, secret);
 20 }
 21 
 22 const dcid = hex("8394c8f03e515708");
 23 const client_header = hex("c300000001088394c8f03e5157080000449e00000002");
 24 const client_crypto = hex(
 25     "060040f1010000ed0303ebf8fa56f12939b9584a3896472ec40bb863cfd3e868" ++
 26         "04fe3a47f06a2b69484c00000413011302010000c000000010000e00000b6578" ++
 27         "616d706c652e636f6dff01000100000a00080006001d00170018001000070005" ++
 28         "04616c706e000500050100000000003300260024001d00209370b2c9caa47fba" ++
 29         "baf4559fedba753de171fa71f50f1ce15d43e994ec74d748002b000302030400" ++
 30         "0d0010000e0403050306030203080408050806002d00020101001c0002400100" ++
 31         "3900320408ffffffffffffffff05048000ffff07048000ffff08011001048000" ++
 32         "75300901100f088394c8f03e51570806048000ffff",
 33 );
 34 const client_protected = hex(
 35     "c000000001088394c8f03e5157080000449e7b9aec34d1b1c98dd7689fb8ec11" ++
 36         "d242b123dc9bd8bab936b47d92ec356c0bab7df5976d27cd449f63300099f399" ++
 37         "1c260ec4c60d17b31f8429157bb35a1282a643a8d2262cad67500cadb8e7378c" ++
 38         "8eb7539ec4d4905fed1bee1fc8aafba17c750e2c7ace01e6005f80fcb7df6212" ++
 39         "30c83711b39343fa028cea7f7fb5ff89eac2308249a02252155e2347b63d58c5" ++
 40         "457afd84d05dfffdb20392844ae812154682e9cf012f9021a6f0be17ddd0c208" ++
 41         "4dce25ff9b06cde535d0f920a2db1bf362c23e596d11a4f5a6cf3948838a3aec" ++
 42         "4e15daf8500a6ef69ec4e3feb6b1d98e610ac8b7ec3faf6ad760b7bad1db4ba3" ++
 43         "485e8a94dc250ae3fdb41ed15fb6a8e5eba0fc3dd60bc8e30c5c4287e53805db" ++
 44         "059ae0648db2f64264ed5e39be2e20d82df566da8dd5998ccabdae053060ae6c" ++
 45         "7b4378e846d29f37ed7b4ea9ec5d82e7961b7f25a9323851f681d582363aa5f8" ++
 46         "9937f5a67258bf63ad6f1a0b1d96dbd4faddfcefc5266ba6611722395c906556" ++
 47         "be52afe3f565636ad1b17d508b73d8743eeb524be22b3dcbc2c7468d54119c74" ++
 48         "68449a13d8e3b95811a198f3491de3e7fe942b330407abf82a4ed7c1b311663a" ++
 49         "c69890f4157015853d91e923037c227a33cdd5ec281ca3f79c44546b9d90ca00" ++
 50         "f064c99e3dd97911d39fe9c5d0b23a229a234cb36186c4819e8b9c5927726632" ++
 51         "291d6a418211cc2962e20fe47feb3edf330f2c603a9d48c0fcb5699dbfe58964" ++
 52         "25c5bac4aee82e57a85aaf4e2513e4f05796b07ba2ee47d80506f8d2c25e50fd" ++
 53         "14de71e6c418559302f939b0e1abd576f279c4b2e0feb85c1f28ff18f58891ff" ++
 54         "ef132eef2fa09346aee33c28eb130ff28f5b766953334113211996d20011a198" ++
 55         "e3fc433f9f2541010ae17c1bf202580f6047472fb36857fe843b19f5984009dd" ++
 56         "c324044e847a4f4a0ab34f719595de37252d6235365e9b84392b061085349d73" ++
 57         "203a4a13e96f5432ec0fd4a1ee65accdd5e3904df54c1da510b0ff20dcc0c77f" ++
 58         "cb2c0e0eb605cb0504db87632cf3d8b4dae6e705769d1de354270123cb11450e" ++
 59         "fc60ac47683d7b8d0f811365565fd98c4c8eb936bcab8d069fc33bd801b03ade" ++
 60         "a2e1fbc5aa463d08ca19896d2bf59a071b851e6c239052172f296bfb5e724047" ++
 61         "90a2181014f3b94a4e97d117b438130368cc39dbb2d198065ae3986547926cd2" ++
 62         "162f40a29f0c3c8745c0f50fba3852e566d44575c29d39a03f0cda721984b6f4" ++
 63         "40591f355e12d439ff150aab7613499dbd49adabc8676eef023b15b65bfc5ca0" ++
 64         "6948109f23f350db82123535eb8a7433bdabcb909271a6ecbcb58b936a88cd4e" ++
 65         "8f2e6ff5800175f113253d8fa9ca8885c2f552e657dc603f252e1a8e308f76f0" ++
 66         "be79e2fb8f5d5fbbe2e30ecadd220723c8c0aea8078cdfcb3868263ff8f09400" ++
 67         "54da48781893a7e49ad5aff4af300cd804a6b6279ab3ff3afb64491c85194aab" ++
 68         "760d58a606654f9f4400e8b38591356fbf6425aca26dc85244259ff2b19c41b9" ++
 69         "f96f3ca9ec1dde434da7d2d392b905ddf3d1f9af93d1af5950bd493f5aa731b4" ++
 70         "056df31bd267b6b90a079831aaf579be0a39013137aac6d404f518cfd4684064" ++
 71         "7e78bfe706ca4cf5e9c5453e9f7cfd2b8b4c8d169a44e55c88d4a9a7f9474241" ++
 72         "e221af44860018ab0856972e194cd934",
 73 );
 74 const client_payload_len: u16 = 1162;
 75 
 76 const server_header = hex("c1000000010008f067a5502a4262b50040750001");
 77 const server_payload = hex(
 78     "02000000000600405a020000560303eefce7f7b37ba1d1632e96677825ddf739" ++
 79         "88cfc79825df566dc5430b9a045a1200130100002e00330024001d00209d3c94" ++
 80         "0d89690b84d08a60993c144eca684d1081287c834d5311bcf32bb9da1a002b00" ++
 81         "020304",
 82 );
 83 const server_protected = hex(
 84     "cf000000010008f067a5502a4262b5004075c0d95a482cd0991cd25b0aac406a" ++
 85         "5816b6394100f37a1c69797554780bb38cc5a99f5ede4cf73c3ec2493a1839b3" ++
 86         "dbcba3f6ea46c5b7684df3548e7ddeb9c3bf9c73cc3f3bded74b562bfb19fb84" ++
 87         "022f8ef4cdd93795d77d06edbb7aaf2f58891850abbdca3d20398c276456cbc4" ++
 88         "2158407dd074ee",
 89 );
 90 
 91 fn openPacket(
 92     keys: *crypto.Keys,
 93     packet: []u8,
 94     pn_offset: usize,
 95     largest_acked: ?u62,
 96 ) crypto.packet.OpenError!crypto.packet.Opened {
 97     std.debug.assert(packet.len <= client_protected.len);
 98     var scratch: [client_protected.len]u8 = undefined;
 99     return crypto.packet.open(keys, packet, &scratch, pn_offset, largest_acked);
100 }
101 
102 fn clientPlain() [client_protected.len]u8 {
103     var result: [client_protected.len]u8 = @splat(0);
104     result[0..client_header.len].* = client_header;
105     result[client_header.len..][0..client_crypto.len].* = client_crypto;
106     return result;
107 }
108 
109 fn serverPlain() [server_protected.len]u8 {
110     var result: [server_protected.len]u8 = @splat(0);
111     result[0..server_header.len].* = server_header;
112     result[server_header.len..][0..server_payload.len].* = server_payload;
113     return result;
114 }
115 
116 test "RFC 9001 Appendix A.1 Initial secrets and packet keys" {
117     comptime {
118         @stardustClaim(
119             @import("alloc_phase").capacity.witness(
120                 crypto.Keys,
121                 "quic_crypto_keys_transitive",
122             ),
123             null,
124             null,
125             null,
126             null,
127             null,
128             null,
129         );
130     }
131     const secrets = crypto.initial.secrets(&dcid);
132     const expected_client = hex(
133         "c00cf151ca5be075ed0ebfb5c80323c4" ++
134             "2d6b7db67881289af4008f1f6c357aea",
135     );
136     const expected_server = hex(
137         "3c199828fd139efd216c155ad844cc81" ++
138             "fb82fa8d7446fa7d78be803acdda951b",
139     );
140     try std.testing.expectEqual(expected_client, secrets.client);
141     try std.testing.expectEqual(expected_server, secrets.server);
142     var client_storage: KeyStorage = undefined;
143     const client = try deriveKeys(&client_storage, .aes_128_gcm_sha256, secrets.client);
144     try std.testing.expectEqualSlices(
145         u8,
146         &hex("1f369613dd76d5467730efcbe3b1a22d"),
147         client.packetKey()[0..16],
148     );
149     try std.testing.expectEqual(
150         hex("fa044b2f42a3fd3b46fb255c"),
151         client.initializationVector(),
152     );
153     try std.testing.expectEqualSlices(
154         u8,
155         &hex("9f50449e04a0e810283a1e9933adedd2"),
156         client.headerKey()[0..16],
157     );
158     var server_storage: KeyStorage = undefined;
159     const server = try deriveKeys(&server_storage, .aes_128_gcm_sha256, secrets.server);
160     try std.testing.expectEqualSlices(
161         u8,
162         &hex("cf3a5331653c364c88f0f379b6067e37"),
163         server.packetKey()[0..16],
164     );
165     try std.testing.expectEqual(
166         hex("0ac1493ca1905853b0bba03e"),
167         server.initializationVector(),
168     );
169     try std.testing.expectEqualSlices(
170         u8,
171         &hex("c206b8d9b9f0f37644430b490eeaa314"),
172         server.headerKey()[0..16],
173     );
174 }
175 
176 test "RFC 9001 section 5.2 the draft salt yields a different client Initial secret" {
177     const Hkdf = std.crypto.kdf.hkdf.HkdfSha256;
178     const draft_salt = hex("afbfec289993d24c9e9786f19c6111e04390a899");
179     const v1_initial = Hkdf.extract(&crypto.initial.v1_salt, &dcid);
180     const draft_initial = Hkdf.extract(&draft_salt, &dcid);
181     try std.testing.expect(!std.mem.eql(u8, &v1_initial, &draft_initial));
182     const draft_client = std.crypto.tls.hkdfExpandLabel(
183         Hkdf,
184         draft_initial,
185         "client in",
186         "",
187         crypto.secret_bytes,
188     );
189     const v1_client = crypto.initial.secrets(&dcid).client;
190     try std.testing.expect(!std.mem.eql(u8, &v1_client, &draft_client));
191 }
192 
193 test "RFC 9001 section 5.1 key storage maximum and maximum plus one" {
194     comptime {
195         @stardustClaim(
196             @import("alloc_phase").capacity.witness(
197                 crypto.Keys,
198                 "quic_crypto_keys_capacity",
199             ),
200             null,
201             null,
202             null,
203             null,
204             null,
205             null,
206         );
207         @stardustClaim(
208             @import("alloc_phase").capacity.witness(
209                 crypto.Keys,
210                 "quic_crypto_keys_overload",
211             ),
212             null,
213             null,
214             null,
215             null,
216             null,
217             null,
218         );
219         @stardustClaim(
220             @import("alloc_phase").capacity.witness(
221                 crypto.Keys,
222                 "quic_crypto_keys_work",
223             ),
224             null,
225             null,
226             null,
227             null,
228             null,
229             null,
230         );
231     }
232     var exact: KeyStorage = undefined;
233     var extra: [crypto.Keys.storage_bytes_max + 1]u8 = undefined;
234     var short: [crypto.Keys.storage_bytes_max - 1]u8 = undefined;
235     var exact_keys = try crypto.Keys.derive(&exact, .aes_128_gcm_sha256, @splat(1));
236     var extra_keys = try crypto.Keys.derive(&extra, .aes_128_gcm_sha256, @splat(1));
237     try std.testing.expectEqual(crypto.Keys.storage_bytes_max, exact_keys.storage.len);
238     try std.testing.expectEqual(crypto.Keys.storage_bytes_max, extra_keys.storage.len);
239     try std.testing.expectError(
240         error.StorageTooShort,
241         crypto.Keys.derive(&short, .aes_128_gcm_sha256, @splat(1)),
242     );
243     _ = exact_keys.deinit();
244     _ = extra_keys.deinit();
245 }
246 
247 test "RFC 9001 Appendix A.2 client Initial packet protection" {
248     const secrets = crypto.initial.secrets(&dcid);
249     var packet = clientPlain();
250     var send_storage: KeyStorage = undefined;
251     var send_keys = try deriveKeys(&send_storage, .aes_128_gcm_sha256, secrets.client);
252     try crypto.packet.seal(&send_keys, 2, &packet, 18, 4, client_payload_len);
253     try std.testing.expectEqual(client_protected, packet);
254 
255     var received = client_protected;
256     var receive_storage: KeyStorage = undefined;
257     var receive_keys = try deriveKeys(
258         &receive_storage,
259         .aes_128_gcm_sha256,
260         secrets.client,
261     );
262     const opened = try openPacket(&receive_keys, &received, 18, null);
263     const expected = clientPlain();
264     try std.testing.expectEqual(@as(u62, 2), opened.packet_number);
265     try std.testing.expectEqualSlices(u8, expected[22..1184], opened.payload);
266 }
267 
268 test "RFC 9001 Appendix A.3 server Initial packet protection" {
269     const secrets = crypto.initial.secrets(&dcid);
270     var packet = serverPlain();
271     var send_storage: KeyStorage = undefined;
272     var send_keys = try deriveKeys(&send_storage, .aes_128_gcm_sha256, secrets.server);
273     try crypto.packet.seal(&send_keys, 1, &packet, 18, 2, server_payload.len);
274     try std.testing.expectEqual(server_protected, packet);
275 
276     var received = server_protected;
277     var receive_storage: KeyStorage = undefined;
278     var receive_keys = try deriveKeys(
279         &receive_storage,
280         .aes_128_gcm_sha256,
281         secrets.server,
282     );
283     const opened = try openPacket(&receive_keys, &received, 18, null);
284     try std.testing.expectEqual(@as(u62, 1), opened.packet_number);
285     try std.testing.expectEqualSlices(u8, &server_payload, opened.payload);
286 }
287 
288 test "RFC 9001 Appendix A.4 Retry integrity tag" {
289     const retry_packet = hex(
290         "ff000000010008f067a5502a4262b574" ++
291             "6f6b656e04a265ba2eff4d829058fb3f" ++
292             "0f2496ba",
293     );
294     try std.testing.expect(crypto.retry.verify(&dcid, &retry_packet));
295     const tag_offset = retry_packet.len - crypto.retry.tag_bytes;
296     const expected: [crypto.retry.tag_bytes]u8 = retry_packet[tag_offset..].*;
297     try std.testing.expectEqual(expected, try crypto.retry.tag(&dcid, retry_packet[0..tag_offset]));
298 }
299 
300 test "RFC 9000 section 18.2 and RFC 9001 section 5.8 Retry maximum plus one" {
301     const odcid: [crypto.retry.connection_id_bytes_max]u8 = @splat(0x31);
302     var packet: [crypto.packet.packet_bytes_max]u8 = @splat(0x42);
303     const tag_offset = packet.len - crypto.retry.tag_bytes;
304     packet[tag_offset..].* = try crypto.retry.tag(&odcid, packet[0..tag_offset]);
305     try std.testing.expect(crypto.retry.verify(&odcid, &packet));
306 
307     const packet_too_long: [crypto.packet.packet_bytes_max + 1]u8 = @splat(0x53);
308     try std.testing.expect(!crypto.retry.verify(&odcid, &packet_too_long));
309     const odcid_too_long: [crypto.retry.connection_id_bytes_max + 1]u8 = @splat(0x64);
310     try std.testing.expect(!crypto.retry.verify(&odcid_too_long, &packet));
311     try std.testing.expectError(
312         error.ConnectionIdTooLong,
313         crypto.retry.tag(&odcid_too_long, packet[0..tag_offset]),
314     );
315     const pseudo_too_long: [crypto.retry.pseudo_bytes_max + 1]u8 = @splat(0x75);
316     try std.testing.expectError(error.PacketTooLarge, crypto.retry.tag(&odcid, &pseudo_too_long));
317 }
318 
319 test "RFC 9001 section 5.4.1 header masks and packet number length bounds" {
320     const protection_mask = [_]u8{ 0xff, 1, 2, 3, 4 };
321     var long: [10]u8 = @splat(0xa5);
322     long[0] = 0xc3;
323     const original_long = long;
324     try crypto.header.protect(protection_mask, &long, 5, 4);
325     try std.testing.expectEqual(@as(u8, 0xcc), long[0]);
326     _ = try crypto.header.unprotect(protection_mask, &long, 5);
327     try std.testing.expectEqual(original_long, long);
328 
329     var short: [10]u8 = @splat(0xa5);
330     short[0] = 0x43;
331     const original_short = short;
332     try crypto.header.protect(protection_mask, &short, 5, 4);
333     try std.testing.expectEqual(@as(u8, 0x5c), short[0]);
334     _ = try crypto.header.unprotect(protection_mask, &short, 5);
335     try std.testing.expectEqual(original_short, short);
336     try std.testing.expectError(
337         error.InvalidPacketNumberLength,
338         crypto.header.protect(protection_mask, &long, 5, 5),
339     );
340     try std.testing.expectError(
341         error.TruncatedPacket,
342         crypto.header.protect(protection_mask, long[0..1], std.math.maxInt(usize), 1),
343     );
344 }
345 
346 test "RFC 9001 Appendix A.5 ChaCha20-Poly1305 packet and key update" {
347     const secret = hex(
348         "9ac312a7f877468ebe69422748ad00a1" ++
349             "5443f18203a07d6060f688f30f21632b",
350     );
351     var storage: KeyStorage = undefined;
352     var keys = try deriveKeys(&storage, .chacha20_poly1305_sha256, secret);
353     try std.testing.expectEqual(
354         hex("c6d98ff3441c3fe1b2182094f69caa2ed4b716b65488960a7a984979fb23e1c8"),
355         keys.packetKey(),
356     );
357     try std.testing.expectEqual(
358         hex("e0459b3474bdd0e44a41c144"),
359         keys.initializationVector(),
360     );
361     try std.testing.expectEqual(
362         hex("25a282b9e82f06f21f488917a4fc8f1b73573685608597d0efcb076b0ab7a7a4"),
363         keys.headerKey(),
364     );
365     try std.testing.expectEqual(
366         hex("1223504755036d556342ee9361d253421a826c9ecdf3c7148684b36b714881f9"),
367         crypto.Keys.next(.chacha20_poly1305_sha256, secret),
368     );
369     const protected = hex("4cfe4189655e5cd55c41f69080575d7999c25a5bfb");
370     var received = protected;
371     const packet_number: u62 = 654_360_564;
372     const opened = try openPacket(&keys, &received, 1, packet_number - 1);
373     try std.testing.expectEqual(packet_number, opened.packet_number);
374     try std.testing.expectEqualSlices(u8, &.{0x01}, opened.payload);
375     var packet: [protected.len]u8 = @splat(0);
376     packet[0..5].* = hex("4200bff4") ++ .{0x01};
377     var send_storage: KeyStorage = undefined;
378     var send_keys = try deriveKeys(&send_storage, .chacha20_poly1305_sha256, secret);
379     try crypto.packet.seal(&send_keys, packet_number, &packet, 1, 3, 1);
380     try std.testing.expectEqual(protected, packet);
381 }
382 
383 fn shortPacket(packet_number: u32, payload: [3]u8) [24]u8 {
384     return shortPacketForm(packet_number, 4, 0, payload);
385 }
386 
387 fn shortPacketForm(packet_number: u62, pn_len: u3, flags: u8, payload: [3]u8) [24]u8 {
388     std.debug.assert(pn_len >= 1);
389     std.debug.assert(pn_len <= 4);
390     var packet: [24]u8 = @splat(0);
391     packet[0] = 0x40 | flags | @as(u8, pn_len - 1);
392     var encoded: [8]u8 = undefined;
393     std.mem.writeInt(u64, &encoded, packet_number, .big);
394     @memcpy(packet[1..][0..pn_len], encoded[encoded.len - pn_len ..]);
395     @memcpy(packet[1 + pn_len ..][0..payload.len], &payload);
396     return packet;
397 }
398 
399 fn shortPacketLength(pn_len: u3) usize {
400     std.debug.assert(pn_len >= 1);
401     std.debug.assert(pn_len <= 4);
402     return 1 + pn_len + 3 + crypto.packet.tag_bytes;
403 }
404 
405 test "RFC 9001 section 6.6 integrity failures survive a key update" {
406     const secret: crypto.Secret = @splat(0x31);
407     const next_secret = crypto.Keys.next(.aes_128_gcm_sha256, secret);
408     var send_storage: KeyStorage = undefined;
409     var receive_storage: KeyStorage = undefined;
410     var send = try deriveKeys(&send_storage, .aes_128_gcm_sha256, secret);
411     var receive = try deriveKeys(&receive_storage, .aes_128_gcm_sha256, secret);
412     receive.failed_opens = receive.integrityLimit() - 2;
413 
414     var before = shortPacket(7, .{ 1, 2, 3 });
415     try crypto.packet.seal(&send, 7, &before, 1, 4, 3);
416     before[before.len - 1] ^= 1;
417     try std.testing.expectError(error.AuthenticationFailed, openPacket(&receive, &before, 1, 6));
418     try std.testing.expectEqual(receive.integrityLimit() - 1, receive.failed_opens);
419 
420     send.update(next_secret);
421     receive.update(next_secret);
422     var after = shortPacket(8, .{ 4, 5, 6 });
423     try crypto.packet.seal(&send, 8, &after, 1, 4, 3);
424     after[after.len - 1] ^= 1;
425     const protected = after;
426     try std.testing.expectError(error.AuthenticationFailed, openPacket(&receive, &after, 1, 7));
427     try std.testing.expect(receive.exhausted());
428     try std.testing.expectEqual(protected, after);
429     try std.testing.expectError(error.IntegrityLimitReached, openPacket(&receive, &after, 1, 7));
430     try std.testing.expectEqual(protected, after);
431 }
432 
433 test "RFC 9001 sections 6.3 to 6.5 failed open preserves packet bytes" {
434     const secret: crypto.Secret = @splat(0x32);
435     const next_secret = crypto.Keys.next(.aes_128_gcm_sha256, secret);
436     var send_storage: KeyStorage = undefined;
437     var send = try deriveKeys(&send_storage, .aes_128_gcm_sha256, secret);
438     send.update(next_secret);
439     var packet = shortPacket(7, .{ 1, 2, 3 });
440     try crypto.packet.seal(&send, 7, &packet, 1, 4, 3);
441     const protected = packet;
442 
443     var wrong_storage: KeyStorage = undefined;
444     var wrong = try deriveKeys(&wrong_storage, .aes_128_gcm_sha256, secret);
445     try std.testing.expectError(
446         error.AuthenticationFailed,
447         openPacket(&wrong, &packet, 1, 6),
448     );
449     try std.testing.expectEqual(protected, packet);
450 
451     var right_storage: KeyStorage = undefined;
452     var right = try deriveKeys(&right_storage, .aes_128_gcm_sha256, secret);
453     right.update(next_secret);
454     const opened = try openPacket(&right, &packet, 1, 6);
455     try std.testing.expectEqual(@as(u62, 7), opened.packet_number);
456     try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, opened.payload);
457 }
458 
459 test "RFC 9001 section 6.2 exposes key phase before payload decryption" {
460     const secret: crypto.Secret = @splat(0x33);
461     const next_secret = crypto.Keys.next(.aes_128_gcm_sha256, secret);
462     const packet_len = shortPacketLength(2);
463     var packet = shortPacketForm(7, 2, 0x04, .{ 1, 2, 3 });
464     var send_storage: KeyStorage = undefined;
465     var send = try deriveKeys(&send_storage, .aes_128_gcm_sha256, secret);
466     send.update(next_secret);
467     try crypto.packet.seal(&send, 7, packet[0..packet_len], 1, 2, 3);
468 
469     var header_storage: KeyStorage = undefined;
470     var header_keys = try deriveKeys(&header_storage, .aes_128_gcm_sha256, secret);
471     const parsed = try crypto.packet.unprotect(&header_keys, packet[0..packet_len], 1, 6);
472     try std.testing.expectEqual(@as(?bool, true), parsed.key_phase);
473     try std.testing.expectEqual(@as(u8, 0x45), parsed.first_byte);
474     try std.testing.expectEqual(@as(u62, 7), parsed.packet_number);
475     const unprotected = packet;
476     var empty_scratch: [0]u8 = .{};
477     try std.testing.expectError(
478         error.ScratchTooShort,
479         crypto.packet.openPayload(
480             &header_keys,
481             packet[0..packet_len],
482             parsed,
483             &empty_scratch,
484         ),
485     );
486     try std.testing.expectEqual(@as(u64, 0), header_keys.failed_opens);
487     try std.testing.expectEqual(unprotected, packet);
488     var wrong_scratch: [3]u8 = undefined;
489     try std.testing.expectError(
490         error.AuthenticationFailed,
491         crypto.packet.openPayload(
492             &header_keys,
493             packet[0..packet_len],
494             parsed,
495             &wrong_scratch,
496         ),
497     );
498     try std.testing.expectEqual(unprotected, packet);
499 
500     var payload_storage: KeyStorage = undefined;
501     var payload_keys = try deriveKeys(&payload_storage, .aes_128_gcm_sha256, secret);
502     payload_keys.update(next_secret);
503     var scratch: [3]u8 = undefined;
504     const opened = try crypto.packet.openPayload(
505         &payload_keys,
506         packet[0..packet_len],
507         parsed,
508         &scratch,
509     );
510     try std.testing.expectEqual(@as(?bool, true), opened.key_phase);
511     try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, opened.payload);
512 }
513 
514 test "RFC 9001 section 6.1 key update preserves header protection" {
515     const secret: crypto.Secret = @splat(0x41);
516     const next_secret = crypto.Keys.next(.aes_128_gcm_sha256, secret);
517     var old_storage: KeyStorage = undefined;
518     var new_storage: KeyStorage = undefined;
519     var old_send = try deriveKeys(&old_storage, .aes_128_gcm_sha256, secret);
520     var new_send = try deriveKeys(&new_storage, .aes_128_gcm_sha256, secret);
521     new_send.sealed_packets = 1;
522     new_send.failed_opens = 1;
523     new_send.update(next_secret);
524     try std.testing.expectEqual(old_send.headerKey(), new_send.headerKey());
525     try std.testing.expectEqual(@as(u64, 0), new_send.sealed_packets);
526     try std.testing.expectEqual(@as(u64, 1), new_send.failed_opens);
527     var old_packet = shortPacket(7, .{ 1, 2, 3 });
528     var new_packet = shortPacket(8, .{ 4, 5, 6 });
529     try crypto.packet.seal(&old_send, 7, &old_packet, 1, 4, 3);
530     try crypto.packet.seal(&new_send, 8, &new_packet, 1, 4, 3);
531 
532     var receive_storage: KeyStorage = undefined;
533     var new_receive = try deriveKeys(&receive_storage, .aes_128_gcm_sha256, secret);
534     new_receive.update(next_secret);
535     const opened = try openPacket(&new_receive, &new_packet, 1, 7);
536     try std.testing.expectEqual(@as(u62, 8), opened.packet_number);
537     try std.testing.expectEqualSlices(u8, &.{ 4, 5, 6 }, opened.payload);
538     var stale = old_packet;
539     var reject_storage: KeyStorage = undefined;
540     var new_reject = try deriveKeys(&reject_storage, .aes_128_gcm_sha256, secret);
541     new_reject.update(next_secret);
542     try std.testing.expectError(
543         error.AuthenticationFailed,
544         openPacket(&new_reject, &stale, 1, 6),
545     );
546 }
547 
548 test "RFC 9001 section 6.6 AEAD usage limits include maximum plus one" {
549     var storage: KeyStorage = undefined;
550     var keys = try deriveKeys(&storage, .aes_128_gcm_sha256, @splat(0x52));
551     keys.sealed_packets = crypto.aes_confidentiality_limit - crypto.aes_update_margin - 1;
552     try std.testing.expect(!keys.needsUpdate());
553     keys.sealed_packets += 1;
554     try std.testing.expect(keys.needsUpdate());
555     var packet = shortPacket(9, .{ 1, 2, 3 });
556     try crypto.packet.seal(&keys, 9, &packet, 1, 4, 3);
557     try std.testing.expect(keys.confidentialityExhausted());
558     var beyond = shortPacket(10, .{ 4, 5, 6 });
559     try std.testing.expectError(
560         error.ConfidentialityLimitReached,
561         crypto.packet.seal(&keys, 10, &beyond, 1, 4, 3),
562     );
563 
564     var chacha_storage: KeyStorage = undefined;
565     var chacha = try deriveKeys(
566         &chacha_storage,
567         .chacha20_poly1305_sha256,
568         @splat(0x63),
569     );
570     chacha.sealed_packets = std.math.maxInt(u64);
571     try std.testing.expect(!chacha.needsUpdate());
572     try std.testing.expect(!chacha.confidentialityExhausted());
573 }
574 
575 test "RFC 9001 section 6.6 integrity maximum and maximum plus one" {
576     const suites = [_]crypto.Suite{
577         .aes_128_gcm_sha256,
578         .chacha20_poly1305_sha256,
579     };
580     for (suites) |suite| {
581         var send_storage: KeyStorage = undefined;
582         var receive_storage: KeyStorage = undefined;
583         var send = try deriveKeys(&send_storage, suite, @splat(0x67));
584         var receive = try deriveKeys(&receive_storage, suite, @splat(0x67));
585         receive.failed_opens = receive.integrityLimit() - 1;
586         var packet = shortPacket(11, .{ 1, 2, 3 });
587         try crypto.packet.seal(&send, 11, &packet, 1, 4, 3);
588         packet[packet.len - 1] ^= 1;
589         const protected = packet;
590         try std.testing.expectError(
591             error.AuthenticationFailed,
592             openPacket(&receive, &packet, 1, 10),
593         );
594         try std.testing.expect(receive.exhausted());
595         try std.testing.expectEqual(protected, packet);
596         try std.testing.expectError(
597             error.IntegrityLimitReached,
598             openPacket(&receive, &packet, 1, 10),
599         );
600         try std.testing.expectEqual(protected, packet);
601     }
602 }
603 
604 test "RFC 9000 Appendix A.3 packet number expansion wrap branches and maximum guard" {
605     const Case = struct { packet_number: u62, largest_acked: u62 };
606     const cases = [_]Case{
607         .{ .packet_number = 255, .largest_acked = 255 },
608         .{ .packet_number = 256, .largest_acked = 200 },
609     };
610     const secret: crypto.Secret = @splat(0x64);
611     var send_storage: KeyStorage = undefined;
612     var send = try deriveKeys(&send_storage, .aes_128_gcm_sha256, secret);
613     for (cases) |case| {
614         var packet = shortPacketForm(case.packet_number, 1, 0, .{ 1, 2, 3 });
615         const packet_len = shortPacketLength(1);
616         try crypto.packet.seal(&send, case.packet_number, packet[0..packet_len], 1, 1, 3);
617         var receive_storage: KeyStorage = undefined;
618         var receive = try deriveKeys(&receive_storage, .aes_128_gcm_sha256, secret);
619         const opened = try openPacket(
620             &receive,
621             packet[0..packet_len],
622             1,
623             case.largest_acked,
624         );
625         try std.testing.expectEqual(case.packet_number, opened.packet_number);
626     }
627 
628     var guarded = shortPacketForm(257, 1, 0, .{ 4, 5, 6 });
629     const guarded_len = shortPacketLength(1);
630     try crypto.packet.seal(&send, 257, guarded[0..guarded_len], 1, 1, 3);
631     const protected = guarded;
632     var guard_storage: KeyStorage = undefined;
633     var guard = try deriveKeys(&guard_storage, .aes_128_gcm_sha256, secret);
634     try std.testing.expectError(
635         error.InvalidPacketNumber,
636         openPacket(&guard, guarded[0..guarded_len], 1, std.math.maxInt(u62)),
637     );
638     try std.testing.expectEqualSlices(u8, protected[0..guarded_len], guarded[0..guarded_len]);
639     try std.testing.expectEqual(@as(u64, 0), guard.failed_opens);
640 }
641 
642 test "RFC 9000 sections 17.2 and 17.3 reject authenticated reserved bits" {
643     const secrets = crypto.initial.secrets(&dcid);
644     var long = clientPlain();
645     long[0] |= 0x0c;
646     var long_send_storage: KeyStorage = undefined;
647     var long_send = try deriveKeys(&long_send_storage, .aes_128_gcm_sha256, secrets.client);
648     try crypto.packet.seal(&long_send, 2, &long, 18, 4, client_payload_len);
649     const protected_long = long;
650     var long_receive_storage: KeyStorage = undefined;
651     var long_receive = try deriveKeys(
652         &long_receive_storage,
653         .aes_128_gcm_sha256,
654         secrets.client,
655     );
656     try std.testing.expectError(error.ReservedBits, openPacket(&long_receive, &long, 18, null));
657     try std.testing.expectEqual(protected_long, long);
658     try std.testing.expectEqual(@as(u64, 0), long_receive.failed_opens);
659 
660     const secret: crypto.Secret = @splat(0x65);
661     var short = shortPacketForm(7, 4, 0x18, .{ 1, 2, 3 });
662     var short_send_storage: KeyStorage = undefined;
663     var short_send = try deriveKeys(&short_send_storage, .aes_128_gcm_sha256, secret);
664     try crypto.packet.seal(&short_send, 7, &short, 1, 4, 3);
665     const protected_short = short;
666     var short_receive_storage: KeyStorage = undefined;
667     var short_receive = try deriveKeys(&short_receive_storage, .aes_128_gcm_sha256, secret);
668     try std.testing.expectError(error.ReservedBits, openPacket(&short_receive, &short, 1, 6));
669     try std.testing.expectEqual(protected_short, short);
670     try std.testing.expectEqual(@as(u64, 0), short_receive.failed_opens);
671 }
672 
673 test "RFC 9000 section 18.2 packet maximum plus one rejects before authentication" {
674     var packet: [crypto.packet.packet_bytes_max + 1]u8 = @splat(0);
675     packet[0] = 0x40;
676     const payload_len = packet.len - 1 - 1 - crypto.packet.tag_bytes;
677     var send_storage: KeyStorage = undefined;
678     var send = try deriveKeys(&send_storage, .aes_128_gcm_sha256, @splat(0x66));
679     try std.testing.expectError(
680         error.PacketTooLarge,
681         crypto.packet.seal(&send, 0, &packet, 1, 1, @intCast(payload_len)),
682     );
683 
684     var receive_storage: KeyStorage = undefined;
685     var receive = try deriveKeys(&receive_storage, .aes_128_gcm_sha256, @splat(0x66));
686     receive.failed_opens = 7;
687     var scratch: [0]u8 = .{};
688     try std.testing.expectError(
689         error.PacketTooLarge,
690         crypto.packet.open(&receive, &packet, &scratch, 1, null),
691     );
692     try std.testing.expectEqual(@as(u64, 7), receive.failed_opens);
693     var invalid = [_]u8{0};
694     try std.testing.expectError(
695         error.InvalidHeader,
696         crypto.packet.open(&receive, &invalid, &scratch, 1, null),
697     );
698     try std.testing.expectEqual(@as(u64, 7), receive.failed_opens);
699 }
700 
701 test "RFC 9001 section 5.4.2 rejects a long packet beyond its buffer" {
702     const secrets = crypto.initial.secrets(&dcid);
703     var packet = client_protected;
704     packet[17] = 0xff;
705     const protected = packet;
706     var storage: KeyStorage = undefined;
707     var keys = try deriveKeys(&storage, .aes_128_gcm_sha256, secrets.client);
708     try std.testing.expectError(
709         error.InvalidLength,
710         openPacket(&keys, &packet, 18, null),
711     );
712     try std.testing.expectEqual(protected, packet);
713     try std.testing.expectEqual(@as(u64, 0), keys.failed_opens);
714 }