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 }