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 }