lib/quic/src/tls/message/profile.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const quic = @import("../../root.zig");
3 const message = @import("root.zig");
4
5 const cursor = quic.cursor;
6 const ExtensionType = std.crypto.tls.ExtensionType;
7
8 pub const ClientHello = struct {
9 random: [32]u8,
10 key_share: [32]u8,
11 alpn: []const u8,
12 transport_parameters: []const u8,
13 server_name: ?[]const u8,
14 cipher_suite: std.crypto.tls.CipherSuite = .AES_128_GCM_SHA256,
15 };
16
17 pub const ServerHello = struct {
18 random: [32]u8,
19 key_share: [32]u8,
20 cipher_suite: std.crypto.tls.CipherSuite,
21 };
22
23 pub const EncryptedExtensions = struct {
24 alpn: []const u8,
25 transport_parameters: []const u8,
26 acknowledge_server_name: bool,
27 };
28
29 pub const Error = message.EncodeError;
30
31 pub fn clientHelloBodyLength(value: ClientHello) Error!usize {
32 try validateAlpn(value.alpn);
33 var extensions: usize = 7 + 8 + 8 + 42;
34 extensions = try add(extensions, 7 + value.alpn.len);
35 extensions = try add(extensions, 4 + value.transport_parameters.len);
36 extensions = try add(extensions, 6 + 6);
37 if (value.server_name) |name| extensions = try add(extensions, 9 + name.len);
38 return add(45, extensions);
39 }
40
41 pub fn serverHelloBodyLength() usize {
42 return 86;
43 }
44
45 pub fn encryptedExtensionsBodyLength(value: EncryptedExtensions) Error!usize {
46 try validateAlpn(value.alpn);
47 var extensions = try add(7 + value.alpn.len, 4 + value.transport_parameters.len);
48 extensions = try add(extensions, 5 + 5);
49 if (value.acknowledge_server_name) extensions = try add(extensions, 4);
50 return add(2, extensions);
51 }
52
53 pub fn encodeClientHello(value: ClientHello, output: *cursor.Write) Error!void {
54 const body_length = try clientHelloBodyLength(value);
55 try writeHandshakeHeader(.client_hello, body_length, output);
56 try output.int(u16, 0x0303);
57 try output.put(&value.random);
58 try output.byte(0);
59 try output.int(u16, 4);
60 try output.int(u16, @backingInt(value.cipher_suite));
61 const alternate: std.crypto.tls.CipherSuite = switch (value.cipher_suite) {
62 .AES_128_GCM_SHA256 => .CHACHA20_POLY1305_SHA256,
63 .CHACHA20_POLY1305_SHA256 => .AES_128_GCM_SHA256,
64 else => return error.InvalidLength,
65 };
66 try output.int(u16, @backingInt(alternate));
67 try output.put(&.{ 1, 0 });
68 try output.int(u16, @intCast(body_length - 45));
69 if (value.server_name) |name| try writeServerName(name, output);
70 try writeSupportedVersions(true, output);
71 try writeSupportedGroups(output);
72 try writeSignatureAlgorithms(output);
73 try writeKeyShare(value.key_share, true, output);
74 try writeAlpn(value.alpn, output);
75 try writeExtension(.quic_transport_parameters, value.transport_parameters, output);
76 try writeCertificateType(.server_certificate_type, true, output);
77 try writeCertificateType(.client_certificate_type, true, output);
78 }
79
80 pub fn encodeServerHello(value: ServerHello, output: *cursor.Write) Error!void {
81 try writeHandshakeHeader(.server_hello, serverHelloBodyLength(), output);
82 try output.int(u16, 0x0303);
83 try output.put(&value.random);
84 try output.byte(0);
85 try output.int(u16, @backingInt(value.cipher_suite));
86 try output.byte(0);
87 try output.int(u16, 46);
88 try writeSupportedVersions(false, output);
89 try writeKeyShare(value.key_share, false, output);
90 }
91
92 pub fn encodeEncryptedExtensions(
93 value: EncryptedExtensions,
94 output: *cursor.Write,
95 ) Error!void {
96 const body_length = try encryptedExtensionsBodyLength(value);
97 try writeHandshakeHeader(.encrypted_extensions, body_length, output);
98 try output.int(u16, @intCast(body_length - 2));
99 try writeAlpn(value.alpn, output);
100 try writeExtension(.quic_transport_parameters, value.transport_parameters, output);
101 try writeCertificateType(.server_certificate_type, false, output);
102 try writeCertificateType(.client_certificate_type, false, output);
103 if (value.acknowledge_server_name) try writeExtension(.server_name, &.{}, output);
104 }
105
106 pub fn encodeCertificateRequest(output: *cursor.Write) Error!void {
107 try writeHandshakeHeader(.certificate_request, 11, output);
108 try output.byte(0);
109 try output.int(u16, 8);
110 try writeSignatureAlgorithms(output);
111 }
112
113 pub fn encodeCertificate(public_key: [32]u8, output: *cursor.Write) Error!void {
114 const encoded = quic.tls.encodeSubjectPublicKeyInfo(public_key);
115 try message.encodeSingleCertificate(&.{}, &encoded, output);
116 }
117
118 pub fn encodeCertificateVerify(signature: [64]u8, output: *cursor.Write) Error!void {
119 try message.encodeCertificateVerify(.{
120 .algorithm = .ed25519,
121 .signature = &signature,
122 }, output);
123 }
124
125 pub fn encodeFinished(verify_data: [32]u8, output: *cursor.Write) Error!void {
126 try message.encodeFinished(.{ .verify_data = &verify_data }, output);
127 }
128
129 fn writeSupportedVersions(client: bool, output: *cursor.Write) Error!void {
130 if (client) {
131 try writeExtensionHeader(.supported_versions, 3, output);
132 try output.byte(2);
133 } else {
134 try writeExtensionHeader(.supported_versions, 2, output);
135 }
136 try output.int(u16, 0x0304);
137 }
138
139 fn writeSupportedGroups(output: *cursor.Write) Error!void {
140 try writeExtensionHeader(.supported_groups, 4, output);
141 try output.int(u16, 2);
142 try output.int(u16, @backingInt(std.crypto.tls.NamedGroup.x25519));
143 }
144
145 fn writeSignatureAlgorithms(output: *cursor.Write) Error!void {
146 try writeExtensionHeader(.signature_algorithms, 4, output);
147 try output.int(u16, 2);
148 try output.int(u16, @backingInt(std.crypto.tls.SignatureScheme.ed25519));
149 }
150
151 fn writeKeyShare(key: [32]u8, client: bool, output: *cursor.Write) Error!void {
152 try writeExtensionHeader(.key_share, if (client) 38 else 36, output);
153 if (client) try output.int(u16, 36);
154 try output.int(u16, @backingInt(std.crypto.tls.NamedGroup.x25519));
155 try output.int(u16, 32);
156 try output.put(&key);
157 }
158
159 fn writeAlpn(alpn: []const u8, output: *cursor.Write) Error!void {
160 try validateAlpn(alpn);
161 try writeExtensionHeader(.application_layer_protocol_negotiation, 3 + alpn.len, output);
162 try output.int(u16, @intCast(1 + alpn.len));
163 try output.byte(@intCast(alpn.len));
164 try output.put(alpn);
165 }
166
167 fn writeCertificateType(
168 kind: ExtensionType,
169 client: bool,
170 output: *cursor.Write,
171 ) Error!void {
172 try writeExtensionHeader(kind, if (client) 2 else 1, output);
173 if (client) try output.byte(1);
174 try output.byte(@backingInt(std.crypto.tls.CertificateType.RawPublicKey));
175 }
176
177 fn writeServerName(name: []const u8, output: *cursor.Write) Error!void {
178 const list_length = try add(3, name.len);
179 const data_length = try add(2, list_length);
180 try writeExtensionHeader(.server_name, data_length, output);
181 try writeLength(u16, list_length, output);
182 try output.byte(0);
183 try writeLength(u16, name.len, output);
184 try output.put(name);
185 }
186
187 fn writeExtension(
188 kind: ExtensionType,
189 data: []const u8,
190 output: *cursor.Write,
191 ) Error!void {
192 try writeExtensionHeader(kind, data.len, output);
193 try output.put(data);
194 }
195
196 fn writeExtensionHeader(
197 kind: ExtensionType,
198 length: usize,
199 output: *cursor.Write,
200 ) Error!void {
201 try output.int(u16, @backingInt(kind));
202 try writeLength(u16, length, output);
203 }
204
205 fn writeHandshakeHeader(
206 kind: std.crypto.tls.HandshakeType,
207 length: usize,
208 output: *cursor.Write,
209 ) Error!void {
210 try output.byte(@backingInt(kind));
211 try writeLength(u24, length, output);
212 }
213
214 fn writeLength(comptime T: type, length: usize, output: *cursor.Write) Error!void {
215 if (length > std.math.maxInt(T)) return error.InvalidLength;
216 try output.int(T, @intCast(length));
217 }
218
219 fn validateAlpn(alpn: []const u8) Error!void {
220 if (alpn.len == 0) return error.InvalidLength;
221 if (alpn.len > std.math.maxInt(u8)) return error.InvalidLength;
222 }
223
224 fn add(left: usize, right: usize) Error!usize {
225 return std.math.add(usize, left, right) catch error.InvalidLength;
226 }