lib/quic/src/tls/engine/machine.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const quic = @import("../../root.zig");
3 const tls = @import("../root.zig");
4 const extension = @import("extension.zig");
5 const model = @import("model.zig");
6 const storage_mod = @import("storage.zig");
7
8 const Sha256 = std.crypto.hash.sha2.Sha256;
9 const X25519 = std.crypto.dh.X25519;
10
11 pub const Error = error{ TlsAlert, MessageTooLarge };
12 const InitSpecific = error{
13 EncodingFailed,
14 InvalidConfig,
15 InvalidKey,
16 RandomFailed,
17 StorageMismatch,
18 };
19 pub const InitError = storage_mod.Storage.InitError || InitSpecific;
20
21 const Phase = enum {
22 client_wait_server_hello,
23 client_wait_encrypted_extensions,
24 client_wait_certificate_request,
25 client_wait_certificate,
26 client_wait_certificate_verify,
27 client_wait_finished,
28 client_sending_finished,
29 client_complete,
30 client_confirmed,
31 server_wait_client_hello,
32 server_wait_certificate,
33 server_wait_certificate_verify,
34 server_wait_finished,
35 server_confirmed,
36 alerted,
37 };
38
39 pub const Engine = struct {
40 config: model.Config,
41 limits: storage_mod.Storage.Limits,
42 storage: *storage_mod.Storage,
43 phase: Phase,
44 transcript: Sha256,
45 key_pair: X25519.KeyPair,
46 hello_random: [32]u8,
47 schedule: ?tls.schedule.Schedule,
48 application: ?tls.schedule.Application,
49 receive_length: usize,
50 receive_total: usize,
51 receive_level: ?model.Level,
52 initial_length: usize,
53 initial_offset: usize,
54 handshake_length: usize,
55 handshake_offset: usize,
56 peer_key: tls.PublicKey,
57 peer_key_present: bool,
58 peer_verified: bool,
59 peer_parameters_length: usize,
60 pending_alert: ?tls.Alert,
61 cipher_suite: ?std.crypto.tls.CipherSuite,
62 handshake_secrets_present: bool,
63
64 pub fn init(
65 config: model.Config,
66 limits: storage_mod.Storage.Limits,
67 storage: *storage_mod.Storage,
68 ) InitError!Engine {
69 try validateConfig(config, limits, storage);
70 var random = try config.random.values();
71 defer std.crypto.secureZero(u8, std.mem.asBytes(&random));
72 var key_pair = X25519.KeyPair.generateDeterministic(random.key_exchange) catch {
73 return error.InvalidKey;
74 };
75 defer std.crypto.secureZero(u8, &key_pair.secret_key);
76 var self = Engine{
77 .config = config,
78 .limits = limits,
79 .storage = storage,
80 .phase = if (config.role == .client)
81 .client_wait_server_hello
82 else
83 .server_wait_client_hello,
84 .transcript = Sha256.init(.{}),
85 .key_pair = key_pair,
86 .hello_random = random.hello,
87 .schedule = null,
88 .application = null,
89 .receive_length = 0,
90 .receive_total = 0,
91 .receive_level = null,
92 .initial_length = 0,
93 .initial_offset = 0,
94 .handshake_length = 0,
95 .handshake_offset = 0,
96 .peer_key = undefined,
97 .peer_key_present = false,
98 .peer_verified = false,
99 .peer_parameters_length = 0,
100 .pending_alert = null,
101 .cipher_suite = null,
102 .handshake_secrets_present = false,
103 };
104 errdefer std.crypto.secureZero(u8, std.mem.asBytes(&self));
105 if (config.role == .client) {
106 try self.queueClientHello(random.hello, storage.initialBytesForInitialization());
107 }
108 storage.activate();
109 return self;
110 }
111
112 /// Overwrites every retained handshake secret and every region of the engine's storage with
113 /// zeros so a caller finishing with a connection leaves no key material behind in the block it
114 /// hands back. The four storage regions and the engine value itself are all covered. The call
115 /// frees nothing, because the caller owns the block.
116 pub fn deinit(self: *Engine) void {
117 const storage = self.storage;
118 std.crypto.secureZero(u8, storage.receiveBytes());
119 std.crypto.secureZero(u8, storage.initialBytes());
120 std.crypto.secureZero(u8, storage.handshakeBytes());
121 std.crypto.secureZero(u8, storage.peerBytes());
122 std.crypto.secureZero(u8, std.mem.asBytes(self));
123 }
124
125 pub fn receive(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
126 if (self.pending_alert != null) return error.TlsAlert;
127 if (self.receive_level) |partial_level| {
128 if (partial_level != level) return self.reject(.unexpected_message);
129 }
130 for (bytes) |byte| {
131 const input = self.storage.receiveBytes();
132 if (self.receive_length >= input.len) return self.reject(.decode_error);
133 input[self.receive_length] = byte;
134 self.receive_length += 1;
135 if (self.receive_length == 1) self.receive_level = level;
136 if (self.receive_length == 4) try self.readMessageLength(input[0..4]);
137 if (self.receive_total == 0) continue;
138 if (self.receive_length != self.receive_total) continue;
139 try self.process(level, input[0..self.receive_total]);
140 self.receive_length = 0;
141 self.receive_total = 0;
142 }
143 if (self.receive_length == 0) self.receive_level = null;
144 }
145
146 pub fn emit(self: *Engine, level: model.Level, output: []u8) usize {
147 if (self.pending_alert != null) return 0;
148 const written = switch (level) {
149 .initial => emitQueue(
150 self.storage.initialBytes(),
151 &self.initial_offset,
152 self.initial_length,
153 output,
154 ),
155 .handshake => emitQueue(
156 self.storage.handshakeBytes(),
157 &self.handshake_offset,
158 self.handshake_length,
159 output,
160 ),
161 .one_rtt => 0,
162 };
163 if (self.phase == .client_sending_finished) {
164 if (self.handshake_offset == self.handshake_length) self.phase = .client_complete;
165 }
166 return written;
167 }
168
169 /// Returns the handshake bytes the caller has already taken at one encryption level. The bytes
170 /// stay in the engine's storage, so an endpoint that lost a CRYPTO frame retransmits from them,
171 /// which RFC 9000 section 13.3 asks of a sender. The 1-RTT level gives an empty slice, because
172 /// it carries no handshake bytes. The slice stays valid while the engine does, because it
173 /// points into the engine's storage.
174 pub fn emitted(self: *const Engine, level: model.Level) []const u8 {
175 std.debug.assert(self.initial_offset <= self.initial_length);
176 std.debug.assert(self.handshake_offset <= self.handshake_length);
177 return switch (level) {
178 .initial => self.storage.initialBytes()[0..self.initial_offset],
179 .handshake => self.storage.handshakeBytes()[0..self.handshake_offset],
180 .one_rtt => &.{},
181 };
182 }
183
184 pub fn secret(
185 self: *const Engine,
186 level: model.Level,
187 direction: model.Direction,
188 ) ?model.Secret {
189 if (self.pending_alert != null) return null;
190 return switch (level) {
191 .initial => null,
192 .handshake => self.handshakeSecret(direction),
193 .one_rtt => self.applicationSecret(direction),
194 };
195 }
196
197 pub fn state(self: *const Engine) model.Status {
198 return switch (self.phase) {
199 .client_complete => .handshake_complete,
200 .client_confirmed, .server_confirmed => .handshake_confirmed,
201 else => .handshaking,
202 };
203 }
204
205 pub fn confirm(self: *Engine) void {
206 if (self.phase == .client_complete) {
207 self.eraseHandshakeTrafficSecrets();
208 self.phase = .client_confirmed;
209 }
210 }
211
212 pub fn peer(self: *const Engine) ?model.Peer {
213 if (self.pending_alert != null) return null;
214 if (!self.peer_verified) return null;
215 const peer_bytes = self.storage.peerBytes();
216 return .{
217 .public_key = self.peer_key,
218 .alpn = self.config.alpn,
219 .transport_parameters = peer_bytes[0..self.peer_parameters_length],
220 };
221 }
222
223 pub fn alert(self: *const Engine) ?tls.Alert {
224 return self.pending_alert;
225 }
226
227 pub fn cipherSuite(self: *const Engine) ?std.crypto.tls.CipherSuite {
228 return self.cipher_suite;
229 }
230
231 fn readMessageLength(self: *Engine, header: []const u8) Error!void {
232 std.debug.assert(header.len == 4);
233 const body_length = (@as(usize, header[1]) << 16) |
234 (@as(usize, header[2]) << 8) | header[3];
235 if (body_length > self.limits.max_message) {
236 self.pending_alert = .decode_error;
237 self.phase = .alerted;
238 return error.MessageTooLarge;
239 }
240 self.receive_total = body_length + 4;
241 }
242
243 fn process(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
244 const value = tls.message.decode(bytes) catch return self.reject(.decode_error);
245 if (value.kind == .key_update) return self.reject(.unexpected_message);
246 if (value.kind == .new_session_ticket) {
247 if (level != .one_rtt) return self.reject(.unexpected_message);
248 if (self.config.role == .server) return self.reject(.unexpected_message);
249 _ = tls.message.decodeNewSessionTicket(bytes) catch return self.reject(.decode_error);
250 return;
251 }
252 if (self.config.role == .client) return self.processClient(level, bytes);
253 return self.processServer(level, bytes);
254 }
255
256 fn processClient(
257 self: *Engine,
258 level: model.Level,
259 bytes: []const u8,
260 ) Error!void {
261 return switch (self.phase) {
262 .client_wait_server_hello => self.clientServerHello(level, bytes),
263 .client_wait_encrypted_extensions => self.clientEncrypted(level, bytes),
264 .client_wait_certificate_request => self.clientRequest(level, bytes),
265 .client_wait_certificate => self.receiveCertificate(level, bytes, false),
266 .client_wait_certificate_verify => self.receiveCertificateVerify(level, bytes, false),
267 .client_wait_finished => self.clientFinished(level, bytes),
268 else => self.reject(.unexpected_message),
269 };
270 }
271
272 fn processServer(
273 self: *Engine,
274 level: model.Level,
275 bytes: []const u8,
276 ) Error!void {
277 return switch (self.phase) {
278 .server_wait_client_hello => self.serverClientHello(level, bytes),
279 .server_wait_certificate => self.receiveCertificate(level, bytes, true),
280 .server_wait_certificate_verify => self.receiveCertificateVerify(level, bytes, true),
281 .server_wait_finished => self.serverFinished(level, bytes),
282 else => self.reject(.unexpected_message),
283 };
284 }
285
286 fn clientServerHello(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
287 if (level != .initial) return self.reject(.unexpected_message);
288 const hello = tls.message.decodeServerHello(bytes) catch return self.reject(.decode_error);
289 if (std.mem.eql(u8, &hello.random, &std.crypto.tls.hello_retry_request_sequence)) {
290 return self.reject(.handshake_failure);
291 }
292 const suite = validServerHello(hello) orelse return self.reject(.illegal_parameter);
293 const selected = extension.serverSelection(hello.extensions) catch |err| {
294 return self.reject(extensionAlert(err));
295 };
296 if (!selected.tls_1_3) {
297 if (hasDowngradeSentinel(hello.random)) return self.reject(.illegal_parameter);
298 return self.reject(.protocol_version);
299 }
300 const peer_share = selected.key_share orelse return self.reject(.missing_extension);
301 self.transcript.update(bytes);
302 try self.installSchedule(peer_share);
303 self.cipher_suite = suite;
304 self.phase = .client_wait_encrypted_extensions;
305 }
306
307 fn clientEncrypted(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
308 if (level != .handshake) return self.reject(.unexpected_message);
309 const encrypted = tls.message.decodeEncryptedExtensions(bytes) catch {
310 return self.reject(.decode_error);
311 };
312 const selected = extension.encryptedSelection(
313 encrypted.extensions,
314 self.config.alpn,
315 self.config.server_name != null,
316 ) catch |err| return self.reject(extensionAlert(err));
317 if (selected.alpn == null) return self.reject(.no_application_protocol);
318 const parameters = selected.transport_parameters orelse {
319 return self.reject(.missing_extension);
320 };
321 if (!selected.server_raw_key) return self.reject(.unsupported_certificate);
322 if (!selected.client_raw_key) return self.reject(.unsupported_certificate);
323 try self.retainParameters(parameters);
324 self.transcript.update(bytes);
325 self.phase = .client_wait_certificate_request;
326 }
327
328 fn clientRequest(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
329 if (level != .handshake) return self.reject(.unexpected_message);
330 const request = tls.message.decodeCertificateRequest(bytes) catch {
331 return self.reject(.decode_error);
332 };
333 if (request.context.len != 0) return self.reject(.illegal_parameter);
334 const offered = extension.requestOffersEd25519(request.extensions) catch |err| {
335 return self.reject(extensionAlert(err));
336 };
337 if (!offered) return self.reject(.handshake_failure);
338 self.transcript.update(bytes);
339 self.phase = .client_wait_certificate;
340 }
341
342 fn serverClientHello(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
343 if (level != .initial) return self.reject(.unexpected_message);
344 const hello = tls.message.decodeClientHello(bytes) catch return self.reject(.decode_error);
345 const suite = validClientHello(hello, self.config.cipher_suite) orelse
346 return self.reject(.illegal_parameter);
347 const offer = extension.clientOffer(hello.extensions, self.config.alpn) catch |err| {
348 return self.reject(extensionAlert(err));
349 };
350 try self.validateOffer(offer);
351 const peer_share = offer.key_share orelse return self.reject(.missing_extension);
352 try self.retainParameters(offer.transport_parameters.?);
353 self.transcript.update(bytes);
354 try self.queueServerFlight(peer_share, offer.server_name != null, suite);
355 self.cipher_suite = suite;
356 self.phase = .server_wait_certificate;
357 }
358
359 fn validateOffer(self: *Engine, offer: extension.ClientOffer) Error!void {
360 if (!offer.tls_1_3) return self.reject(.protocol_version);
361 if (!offer.x25519_group) return self.reject(.handshake_failure);
362 if (!offer.ed25519_signature) return self.reject(.handshake_failure);
363 if (offer.alpn == null) return self.reject(.no_application_protocol);
364 if (offer.transport_parameters == null) return self.reject(.missing_extension);
365 if (!offer.server_raw_key) return self.reject(.unsupported_certificate);
366 if (!offer.client_raw_key) return self.reject(.unsupported_certificate);
367 if (self.config.server_name) |wanted| {
368 const received = offer.server_name orelse return self.reject(.unrecognized_name);
369 if (!std.mem.eql(u8, wanted, received)) return self.reject(.unrecognized_name);
370 }
371 }
372
373 fn receiveCertificate(
374 self: *Engine,
375 level: model.Level,
376 bytes: []const u8,
377 client_certificate: bool,
378 ) Error!void {
379 if (level != .handshake) return self.reject(.unexpected_message);
380 const certificate = tls.message.decodeCertificate(bytes) catch {
381 return self.reject(.decode_error);
382 };
383 if (certificate.request_context.len != 0) return self.reject(.illegal_parameter);
384 var entries = certificate.entries.iterator();
385 const entry = entries.next() catch return self.reject(.decode_error);
386 if (entry == null) {
387 if (client_certificate) return self.reject(.certificate_required);
388 return self.reject(.decode_error);
389 }
390 const second = entries.next() catch return self.reject(.decode_error);
391 if (second != null) return self.reject(.unsupported_certificate);
392 var entry_extensions = entry.?.extensions.iterator();
393 if ((entry_extensions.next() catch return self.reject(.decode_error)) != null) {
394 return self.reject(.unsupported_extension);
395 }
396 self.peer_key = tls.decodeSubjectPublicKeyInfo(entry.?.data) catch {
397 return self.reject(.bad_certificate);
398 };
399 self.peer_key_present = true;
400 if (self.config.expected_peer) |expected| {
401 if (!std.crypto.timing_safe.eql(tls.PublicKey, expected, self.peer_key)) {
402 return self.reject(.bad_certificate);
403 }
404 }
405 self.transcript.update(bytes);
406 self.phase = if (client_certificate)
407 .server_wait_certificate_verify
408 else
409 .client_wait_certificate_verify;
410 }
411
412 fn receiveCertificateVerify(
413 self: *Engine,
414 level: model.Level,
415 bytes: []const u8,
416 client_signature: bool,
417 ) Error!void {
418 if (level != .handshake) return self.reject(.unexpected_message);
419 if (!self.peer_key_present) return self.reject(.unexpected_message);
420 const value = tls.message.decodeCertificateVerify(bytes) catch {
421 return self.reject(.decode_error);
422 };
423 if (value.algorithm != .ed25519) return self.reject(.illegal_parameter);
424 if (value.signature.len != 64) return self.reject(.decrypt_error);
425 const hash = self.transcriptHash();
426 const content = certificateVerifyContent(client_signature, hash);
427 tls.verifySignature(
428 self.peer_key,
429 &content,
430 value.signature[0..64].*,
431 ) catch return self.reject(.decrypt_error);
432 self.transcript.update(bytes);
433 self.peer_verified = true;
434 self.phase = if (client_signature) .server_wait_finished else .client_wait_finished;
435 }
436
437 fn clientFinished(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
438 if (level != .handshake) return self.reject(.unexpected_message);
439 try self.verifyFinished(bytes, self.schedule.?.server_handshake);
440 self.transcript.update(bytes);
441 self.application = self.schedule.?.application(self.transcriptHash());
442 self.schedule.?.eraseMasterSecret();
443 try self.queueClientFlight();
444 self.phase = .client_sending_finished;
445 }
446
447 fn serverFinished(self: *Engine, level: model.Level, bytes: []const u8) Error!void {
448 if (level != .handshake) return self.reject(.unexpected_message);
449 try self.verifyFinished(bytes, self.schedule.?.client_handshake);
450 self.transcript.update(bytes);
451 self.eraseHandshakeTrafficSecrets();
452 self.phase = .server_confirmed;
453 }
454
455 fn verifyFinished(
456 self: *Engine,
457 bytes: []const u8,
458 traffic_secret: model.Secret,
459 ) Error!void {
460 const value = tls.message.decodeFinished(bytes) catch return self.reject(.decode_error);
461 if (value.verify_data.len != 32) return self.reject(.decrypt_error);
462 const expected = tls.schedule.finishedVerifyData(traffic_secret, self.transcriptHash());
463 if (!std.crypto.timing_safe.eql(model.Secret, expected, value.verify_data[0..32].*)) {
464 return self.reject(.decrypt_error);
465 }
466 }
467
468 fn installSchedule(self: *Engine, peer_share: [32]u8) Error!void {
469 var shared = X25519.scalarmult(self.key_pair.secret_key, peer_share) catch {
470 std.crypto.secureZero(u8, &self.key_pair.secret_key);
471 return self.reject(.illegal_parameter);
472 };
473 defer std.crypto.secureZero(u8, &shared);
474 std.crypto.secureZero(u8, &self.key_pair.secret_key);
475 var installed = tls.schedule.Schedule.init(shared, self.transcriptHash());
476 installed.eraseDerivationSecrets();
477 self.schedule = installed;
478 self.handshake_secrets_present = true;
479 }
480
481 fn queueClientHello(
482 self: *Engine,
483 random: [32]u8,
484 output_bytes: []u8,
485 ) InitError!void {
486 var output = quic.cursor.Write.init(output_bytes);
487 tls.message.profile.encodeClientHello(.{
488 .random = random,
489 .key_share = self.key_pair.public_key,
490 .alpn = self.config.alpn,
491 .transport_parameters = self.config.transport_parameters,
492 .server_name = self.config.server_name,
493 .cipher_suite = self.config.cipher_suite,
494 }, &output) catch return error.EncodingFailed;
495 self.initial_length = output.index;
496 self.transcript.update(output.written());
497 }
498
499 fn queueServerFlight(
500 self: *Engine,
501 peer_share: [32]u8,
502 acknowledge_name: bool,
503 suite: std.crypto.tls.CipherSuite,
504 ) Error!void {
505 var initial = quic.cursor.Write.init(self.storage.initialBytes());
506 tls.message.profile.encodeServerHello(.{
507 .random = self.hello_random,
508 .key_share = self.key_pair.public_key,
509 .cipher_suite = suite,
510 }, &initial) catch return self.reject(.internal_error);
511 self.initial_length = initial.index;
512 self.transcript.update(initial.written());
513 try self.installSchedule(peer_share);
514 try self.queueServerAuthentication(acknowledge_name);
515 }
516
517 fn queueServerAuthentication(self: *Engine, acknowledge_name: bool) Error!void {
518 var output = quic.cursor.Write.init(self.storage.handshakeBytes());
519 tls.message.profile.encodeEncryptedExtensions(.{
520 .alpn = self.config.alpn,
521 .transport_parameters = self.config.transport_parameters,
522 .acknowledge_server_name = acknowledge_name,
523 }, &output) catch return self.reject(.internal_error);
524 self.hashNew(output.written(), &self.handshake_length);
525 tls.message.profile.encodeCertificateRequest(&output) catch {
526 return self.reject(.internal_error);
527 };
528 self.hashNew(output.written(), &self.handshake_length);
529 tls.message.profile.encodeCertificate(self.config.identity.public(), &output) catch {
530 return self.reject(.internal_error);
531 };
532 self.hashNew(output.written(), &self.handshake_length);
533 try self.queueSignedAndFinished(&output, false);
534 self.application = self.schedule.?.application(self.transcriptHash());
535 self.schedule.?.eraseMasterSecret();
536 }
537
538 fn queueClientFlight(self: *Engine) Error!void {
539 var output = quic.cursor.Write.init(self.storage.handshakeBytes());
540 tls.message.profile.encodeCertificate(
541 self.config.identity.public(),
542 &output,
543 ) catch return self.reject(.internal_error);
544 self.hashNew(output.written(), &self.handshake_length);
545 try self.queueSignedAndFinished(&output, true);
546 }
547
548 fn queueSignedAndFinished(
549 self: *Engine,
550 output: *quic.cursor.Write,
551 client_signature: bool,
552 ) Error!void {
553 const hash = self.transcriptHash();
554 const content = certificateVerifyContent(client_signature, hash);
555 const signature = self.config.identity.sign(&content) catch {
556 return self.reject(.internal_error);
557 };
558 tls.message.profile.encodeCertificateVerify(signature, output) catch {
559 return self.reject(.internal_error);
560 };
561 self.hashNew(output.written(), &self.handshake_length);
562 const traffic = if (client_signature)
563 self.schedule.?.client_handshake
564 else
565 self.schedule.?.server_handshake;
566 const verify_data = tls.schedule.finishedVerifyData(traffic, self.transcriptHash());
567 tls.message.profile.encodeFinished(verify_data, output) catch {
568 return self.reject(.internal_error);
569 };
570 self.hashNew(output.written(), &self.handshake_length);
571 }
572
573 fn hashNew(self: *Engine, bytes: []const u8, previous_length: *usize) void {
574 std.debug.assert(previous_length.* <= bytes.len);
575 self.transcript.update(bytes[previous_length.*..]);
576 previous_length.* = bytes.len;
577 }
578
579 fn retainParameters(self: *Engine, parameters: []const u8) Error!void {
580 const output = self.storage.peerBytes();
581 if (parameters.len > output.len) return self.reject(.internal_error);
582 @memcpy(output[0..parameters.len], parameters);
583 self.peer_parameters_length = parameters.len;
584 }
585
586 fn transcriptHash(self: *const Engine) model.Secret {
587 var transcript = self.transcript;
588 var digest: model.Secret = undefined;
589 transcript.final(&digest);
590 return digest;
591 }
592
593 fn handshakeSecret(self: *const Engine, direction: model.Direction) ?model.Secret {
594 if (!self.handshake_secrets_present) return null;
595 const value = self.schedule orelse return null;
596 return switch (self.config.role) {
597 .client => if (direction == .write) value.client_handshake else value.server_handshake,
598 .server => if (direction == .write) value.server_handshake else value.client_handshake,
599 };
600 }
601
602 fn applicationSecret(self: *const Engine, direction: model.Direction) ?model.Secret {
603 if (self.config.role == .server and direction == .read) {
604 if (self.phase != .server_confirmed) return null;
605 }
606 const value = self.application orelse return null;
607 return switch (self.config.role) {
608 .client => if (direction == .write) value.client else value.server,
609 .server => if (direction == .write) value.server else value.client,
610 };
611 }
612
613 fn eraseHandshakeTrafficSecrets(self: *Engine) void {
614 if (!self.handshake_secrets_present) return;
615 self.schedule.?.eraseHandshakeTrafficSecrets();
616 self.handshake_secrets_present = false;
617 }
618
619 fn reject(self: *Engine, alert_value: tls.Alert) Error {
620 self.pending_alert = alert_value;
621 self.phase = .alerted;
622 return error.TlsAlert;
623 }
624 };
625
626 /// The body length in bytes of the CertificateVerify message this engine sends is 68. RFC 8446
627 /// section 4.4.3 gives the message two bytes of signature scheme, two bytes of length, and the
628 /// signature itself, which for Ed25519 is 64 bytes. Configuration is checked against it, so a
629 /// configured maximum message size below it gives `InvalidConfig` at startup.
630 const ed25519_certificate_verify_body_length: usize = 2 + 2 + 64;
631
632 fn validateConfig(
633 config: model.Config,
634 limits: storage_mod.Storage.Limits,
635 storage: *const storage_mod.Storage,
636 ) InitError!void {
637 const capacity = try storage_mod.Storage.Capacity.derive(limits);
638 if (storage.capacity.storage_bytes != capacity.storage_bytes) return error.StorageMismatch;
639 if (storage.capacity.message_bytes != capacity.message_bytes) return error.StorageMismatch;
640 if (config.alpn.len == 0) return error.InvalidConfig;
641 if (config.alpn.len > std.math.maxInt(u8)) return error.InvalidConfig;
642 if (config.transport_parameters.len > limits.max_message) return error.InvalidConfig;
643 switch (config.cipher_suite) {
644 .AES_128_GCM_SHA256, .CHACHA20_POLY1305_SHA256 => {},
645 else => return error.InvalidConfig,
646 }
647 const certificate_body: usize = 1 + 3 + 3 + tls.spki_length + 2;
648 if (certificate_body > limits.max_message) return error.InvalidConfig;
649 if (ed25519_certificate_verify_body_length > limits.max_message) {
650 return error.InvalidConfig;
651 }
652 if (tls.message.profile.serverHelloBodyLength() > limits.max_message) {
653 return error.InvalidConfig;
654 }
655 try validateRoleOutput(config, limits);
656 }
657
658 fn validateRoleOutput(
659 config: model.Config,
660 limits: storage_mod.Storage.Limits,
661 ) InitError!void {
662 if (config.role == .client) {
663 const length = tls.message.profile.clientHelloBodyLength(.{
664 .random = undefined,
665 .key_share = undefined,
666 .alpn = config.alpn,
667 .transport_parameters = config.transport_parameters,
668 .server_name = config.server_name,
669 .cipher_suite = config.cipher_suite,
670 }) catch return error.InvalidConfig;
671 if (length > limits.max_message) return error.InvalidConfig;
672 return;
673 }
674 const length = tls.message.profile.encryptedExtensionsBodyLength(.{
675 .alpn = config.alpn,
676 .transport_parameters = config.transport_parameters,
677 .acknowledge_server_name = true,
678 }) catch return error.InvalidConfig;
679 if (length > limits.max_message) return error.InvalidConfig;
680 }
681
682 fn validClientHello(
683 hello: tls.message.ClientHello,
684 preferred: std.crypto.tls.CipherSuite,
685 ) ?std.crypto.tls.CipherSuite {
686 if (hello.legacy_version != 0x0303) return null;
687 if (hello.legacy_session_id.len != 0) return null;
688 if (!std.mem.eql(u8, hello.compression_methods, &.{0})) return null;
689 var suites = quic.cursor.Read.init(hello.cipher_suites);
690 var aes = false;
691 var chacha = false;
692 for (0..std.math.maxInt(u15) + 1) |_| {
693 if (suites.remaining() == 0) break;
694 const suite = suites.int(u16) catch return null;
695 if (suite == @backingInt(std.crypto.tls.CipherSuite.AES_128_GCM_SHA256)) {
696 aes = true;
697 }
698 if (suite == @backingInt(std.crypto.tls.CipherSuite.CHACHA20_POLY1305_SHA256)) {
699 chacha = true;
700 }
701 }
702 return switch (preferred) {
703 .AES_128_GCM_SHA256 => if (aes)
704 .AES_128_GCM_SHA256
705 else if (chacha)
706 .CHACHA20_POLY1305_SHA256
707 else
708 null,
709 .CHACHA20_POLY1305_SHA256 => if (chacha)
710 .CHACHA20_POLY1305_SHA256
711 else if (aes)
712 .AES_128_GCM_SHA256
713 else
714 null,
715 else => null,
716 };
717 }
718
719 fn validServerHello(hello: tls.message.ServerHello) ?std.crypto.tls.CipherSuite {
720 if (hello.legacy_version != 0x0303) return null;
721 if (hello.legacy_session_id_echo.len != 0) return null;
722 if (hello.compression_method != 0) return null;
723 if (hello.cipher_suite == @backingInt(std.crypto.tls.CipherSuite.AES_128_GCM_SHA256)) {
724 return .AES_128_GCM_SHA256;
725 }
726 if (hello.cipher_suite == @backingInt(
727 std.crypto.tls.CipherSuite.CHACHA20_POLY1305_SHA256,
728 )) return .CHACHA20_POLY1305_SHA256;
729 return null;
730 }
731
732 fn hasDowngradeSentinel(random: [32]u8) bool {
733 const tls_1_2 = "DOWNGRD\x01";
734 const tls_1_1_or_older = "DOWNGRD\x00";
735 const suffix = random[random.len - tls_1_2.len ..];
736 if (std.mem.eql(u8, suffix, tls_1_2)) return true;
737 return std.mem.eql(u8, suffix, tls_1_1_or_older);
738 }
739
740 fn certificateVerifyContent(client: bool, transcript_hash: model.Secret) [130]u8 {
741 const context = if (client)
742 "TLS 1.3, client CertificateVerify"
743 else
744 "TLS 1.3, server CertificateVerify";
745 var content: [130]u8 = undefined;
746 @memset(content[0..64], 0x20);
747 @memcpy(content[64..][0..context.len], context);
748 content[64 + context.len] = 0;
749 @memcpy(content[65 + context.len ..], &transcript_hash);
750 return content;
751 }
752
753 fn emitQueue(bytes: []const u8, offset: *usize, length: usize, output: []u8) usize {
754 std.debug.assert(offset.* <= length);
755 std.debug.assert(length <= bytes.len);
756 const count = @min(output.len, length - offset.*);
757 @memcpy(output[0..count], bytes[offset.*..][0..count]);
758 offset.* += count;
759 return count;
760 }
761
762 fn extensionAlert(err: extension.Error) tls.Alert {
763 return switch (err) {
764 error.IllegalExtension => .illegal_parameter,
765 error.UnsupportedExtension => .unsupported_extension,
766 else => .decode_error,
767 };
768 }