lib/quic/src/crypto/initial.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const quic = @import("../root.zig");
3
4 const Hkdf = std.crypto.kdf.hkdf.HkdfSha256;
5 const Secret = quic.crypto.Secret;
6 const tls = std.crypto.tls;
7
8 /// The 20-byte Initial salt RFC 9001 section 5.2 fixes for QUIC version 1 serves a caller deriving
9 /// Initial keys by hand so both endpoints use the same salt. This value acts as the salt of the
10 /// extraction that turns a client's destination connection ID into the Initial secret, from which
11 /// the client and server traffic secrets follow. A version other than 1 uses a different salt, so
12 /// this value belongs to version 1 alone.
13 pub const v1_salt = [_]u8{
14 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
15 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
16 };
17
18 pub const Secrets = struct {
19 client: Secret,
20 server: Secret,
21 };
22
23 fn extracted(dcid: []const u8) Secret {
24 return Hkdf.extract(&v1_salt, dcid);
25 }
26
27 pub fn secrets(dcid: []const u8) Secrets {
28 const initial_secret = extracted(dcid);
29 return .{
30 .client = tls.hkdfExpandLabel(
31 Hkdf,
32 initial_secret,
33 "client in",
34 "",
35 quic.crypto.secret_bytes,
36 ),
37 .server = tls.hkdfExpandLabel(
38 Hkdf,
39 initial_secret,
40 "server in",
41 "",
42 quic.crypto.secret_bytes,
43 ),
44 };
45 }
46
47 fn hex(comptime text: []const u8) [text.len / 2]u8 {
48 var result: [text.len / 2]u8 = undefined;
49 _ = std.fmt.hexToBytes(&result, text) catch unreachable;
50 return result;
51 }
52
53 test "RFC 9001 Appendix A.1 version 1 initial secret" {
54 const dcid = hex("8394c8f03e515708");
55 const expected = hex(
56 "7db5df06e7a69e432496adedb0085192" ++
57 "3595221596ae2ae9fb8115c1e9ed0a44",
58 );
59 try std.testing.expectEqual(expected, extracted(&dcid));
60 }