master
1const builtin = @import("builtin");
2const native_endian = builtin.cpu.arch.endian();
3
4const std = @import("../../std.zig");
5const tls = std.crypto.tls;
6const Client = @This();
7const mem = std.mem;
8const crypto = std.crypto;
9const assert = std.debug.assert;
10const Certificate = std.crypto.Certificate;
11const Reader = std.Io.Reader;
12const Writer = std.Io.Writer;
13
14const max_ciphertext_len = tls.max_ciphertext_len;
15const hmacExpandLabel = tls.hmacExpandLabel;
16const hkdfExpandLabel = tls.hkdfExpandLabel;
17const int = tls.int;
18const array = tls.array;
19
20/// The encrypted stream from the server to the client. Bytes are pulled from
21/// here via `reader`.
22///
23/// The buffer is asserted to have capacity at least `min_buffer_len`.
24input: *Reader,
25/// Decrypted stream from the server to the client.
26reader: Reader,
27
28/// The encrypted stream from the client to the server. Bytes are pushed here
29/// via `writer`.
30///
31/// The buffer is asserted to have capacity at least `min_buffer_len`.
32output: *Writer,
33/// The plaintext stream from the client to the server.
34writer: Writer,
35
36/// Populated when `error.TlsAlert` is returned.
37alert: ?tls.Alert = null,
38read_err: ?ReadError = null,
39tls_version: tls.ProtocolVersion,
40read_seq: u64,
41write_seq: u64,
42/// When this is true, the stream may still not be at the end because there
43/// may be data in the input buffer.
44received_close_notify: bool,
45allow_truncation_attacks: bool,
46application_cipher: tls.ApplicationCipher,
47
48/// If non-null, ssl secrets are logged to a stream. Creating such a log file
49/// allows other programs with access to that file to decrypt all traffic over
50/// this connection.
51ssl_key_log: ?*SslKeyLog,
52
53pub const ReadError = error{
54 /// The alert description will be stored in `alert`.
55 TlsAlert,
56 TlsBadLength,
57 TlsBadRecordMac,
58 TlsConnectionTruncated,
59 TlsDecodeError,
60 TlsRecordOverflow,
61 TlsUnexpectedMessage,
62 TlsIllegalParameter,
63 TlsSequenceOverflow,
64};
65
66pub const SslKeyLog = struct {
67 client_key_seq: u64,
68 server_key_seq: u64,
69 client_random: [32]u8,
70 writer: *Writer,
71
72 fn clientCounter(key_log: *@This()) u64 {
73 defer key_log.client_key_seq += 1;
74 return key_log.client_key_seq;
75 }
76
77 fn serverCounter(key_log: *@This()) u64 {
78 defer key_log.server_key_seq += 1;
79 return key_log.server_key_seq;
80 }
81};
82
83/// The `Reader` supplied to `init` requires a buffer capacity
84/// at least this amount.
85pub const min_buffer_len = tls.max_ciphertext_record_len;
86
87pub const Options = struct {
88 /// How to perform host verification of server certificates.
89 host: union(enum) {
90 /// No host verification is performed, which prevents a trusted connection from
91 /// being established.
92 no_verification,
93 /// Verify that the server certificate was issued for a given host.
94 explicit: []const u8,
95 },
96 /// How to verify the authenticity of server certificates.
97 ca: union(enum) {
98 /// No ca verification is performed, which prevents a trusted connection from
99 /// being established.
100 no_verification,
101 /// Verify that the server certificate is a valid self-signed certificate.
102 /// This provides no authorization guarantees, as anyone can create a
103 /// self-signed certificate.
104 self_signed,
105 /// Verify that the server certificate is authorized by a given ca bundle.
106 bundle: Certificate.Bundle,
107 },
108 write_buffer: []u8,
109 read_buffer: []u8,
110 /// Cryptographically secure random bytes. The pointer is not captured; data is only
111 /// read during `init`.
112 entropy: *const [176]u8,
113 /// Current time according to the wall clock / calendar, in seconds.
114 realtime_now_seconds: i64,
115
116 /// If non-null, ssl secrets are logged to this stream. Creating such a log file allows
117 /// other programs with access to that file to decrypt all traffic over this connection.
118 ///
119 /// Only the `writer` field is observed during the handshake (`init`).
120 /// After that, the other fields are populated.
121 ssl_key_log: ?*SslKeyLog = null,
122 /// By default, reaching the end-of-stream when reading from the server will
123 /// cause `error.TlsConnectionTruncated` to be returned, unless a close_notify
124 /// message has been received. By setting this flag to `true`, instead, the
125 /// end-of-stream will be forwarded to the application layer above TLS.
126 ///
127 /// This makes the application vulnerable to truncation attacks unless the
128 /// application layer itself verifies that the amount of data received equals
129 /// the amount of data expected, such as HTTP with the Content-Length header.
130 allow_truncation_attacks: bool = false,
131 /// Populated when `error.TlsAlert` is returned from `init`.
132 alert: ?*tls.Alert = null,
133};
134
135const InitError = error{
136 WriteFailed,
137 ReadFailed,
138 InsufficientEntropy,
139 DiskQuota,
140 LockViolation,
141 NotOpenForWriting,
142 /// The alert description will be stored in `alert`.
143 TlsAlert,
144 TlsUnexpectedMessage,
145 TlsIllegalParameter,
146 TlsDecryptFailure,
147 TlsRecordOverflow,
148 TlsBadRecordMac,
149 CertificateFieldHasInvalidLength,
150 CertificateHostMismatch,
151 CertificatePublicKeyInvalid,
152 CertificateExpired,
153 CertificateFieldHasWrongDataType,
154 CertificateIssuerMismatch,
155 CertificateNotYetValid,
156 CertificateSignatureAlgorithmMismatch,
157 CertificateSignatureAlgorithmUnsupported,
158 CertificateSignatureInvalid,
159 CertificateSignatureInvalidLength,
160 CertificateSignatureNamedCurveUnsupported,
161 CertificateSignatureUnsupportedBitCount,
162 TlsCertificateNotVerified,
163 TlsBadSignatureScheme,
164 TlsBadRsaSignatureBitCount,
165 InvalidEncoding,
166 IdentityElement,
167 SignatureVerificationFailed,
168 TlsDecryptError,
169 TlsConnectionTruncated,
170 TlsDecodeError,
171 UnsupportedCertificateVersion,
172 CertificateTimeInvalid,
173 CertificateHasUnrecognizedObjectId,
174 CertificateHasInvalidBitString,
175 MessageTooLong,
176 NegativeIntoUnsigned,
177 TargetTooSmall,
178 BufferTooSmall,
179 InvalidSignature,
180 NotSquare,
181 NonCanonical,
182 WeakPublicKey,
183};
184
185/// Initiates a TLS handshake and establishes a TLSv1.2 or TLSv1.3 session.
186///
187/// `host` is only borrowed during this function call.
188///
189/// `input` is asserted to have buffer capacity at least `min_buffer_len`.
190pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client {
191 assert(input.buffer.len >= min_buffer_len);
192 const host = switch (options.host) {
193 .no_verification => "",
194 .explicit => |host| host,
195 };
196 const host_len: u16 = @intCast(host.len);
197
198 const client_hello_rand = options.entropy[0..32].*;
199 var key_seq: u64 = 0;
200 var server_hello_rand: [32]u8 = undefined;
201 const legacy_session_id = options.entropy[32..64].*;
202
203 var key_share = KeyShare.init(options.entropy[64..176].*) catch |err| switch (err) {
204 // Only possible to happen if the seed is all zeroes.
205 error.IdentityElement => return error.InsufficientEntropy,
206 };
207
208 const extensions_payload = tls.extension(.supported_versions, array(u8, tls.ProtocolVersion, .{
209 .tls_1_3,
210 .tls_1_2,
211 })) ++ tls.extension(.signature_algorithms, array(u16, tls.SignatureScheme, .{
212 .ecdsa_secp256r1_sha256,
213 .ecdsa_secp384r1_sha384,
214 .rsa_pkcs1_sha256,
215 .rsa_pkcs1_sha384,
216 .rsa_pkcs1_sha512,
217 .rsa_pss_rsae_sha256,
218 .rsa_pss_rsae_sha384,
219 .rsa_pss_rsae_sha512,
220 .rsa_pss_pss_sha256,
221 .rsa_pss_pss_sha384,
222 .rsa_pss_pss_sha512,
223 .rsa_pkcs1_sha1,
224 .ed25519,
225 })) ++ tls.extension(.supported_groups, array(u16, tls.NamedGroup, .{
226 .x25519_ml_kem768,
227 .secp256r1,
228 .secp384r1,
229 .x25519,
230 })) ++ tls.extension(.psk_key_exchange_modes, array(u8, tls.PskKeyExchangeMode, .{
231 .psk_dhe_ke,
232 })) ++ tls.extension(.key_share, array(
233 u16,
234 u8,
235 int(u16, @intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++
236 array(u16, u8, key_share.ml_kem768_kp.public_key.toBytes() ++ key_share.x25519_kp.public_key) ++
237 int(u16, @intFromEnum(tls.NamedGroup.secp256r1)) ++
238 array(u16, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++
239 int(u16, @intFromEnum(tls.NamedGroup.secp384r1)) ++
240 array(u16, u8, key_share.secp384r1_kp.public_key.toUncompressedSec1()) ++
241 int(u16, @intFromEnum(tls.NamedGroup.x25519)) ++
242 array(u16, u8, key_share.x25519_kp.public_key),
243 ));
244 const server_name_extension = int(u16, @intFromEnum(tls.ExtensionType.server_name)) ++
245 int(u16, 2 + 1 + 2 + host_len) ++ // byte length of this extension payload
246 int(u16, 1 + 2 + host_len) ++ // server_name_list byte count
247 .{0x00} ++ // name_type
248 int(u16, host_len);
249 const server_name_extension_len = switch (options.host) {
250 .no_verification => 0,
251 .explicit => server_name_extension.len + host_len,
252 };
253
254 const extensions_header =
255 int(u16, @intCast(extensions_payload.len + server_name_extension_len)) ++
256 extensions_payload ++
257 server_name_extension;
258
259 const client_hello =
260 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
261 client_hello_rand ++
262 [1]u8{32} ++ legacy_session_id ++
263 cipher_suites ++
264 array(u8, tls.CompressionMethod, .{.null}) ++
265 extensions_header;
266
267 const out_handshake = .{@intFromEnum(tls.HandshakeType.client_hello)} ++
268 int(u24, @intCast(client_hello.len - server_name_extension.len + server_name_extension_len)) ++
269 client_hello;
270
271 const cleartext_header_buf = .{@intFromEnum(tls.ContentType.handshake)} ++
272 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_0)) ++
273 int(u16, @intCast(out_handshake.len - server_name_extension.len + server_name_extension_len)) ++
274 out_handshake;
275 const cleartext_header = switch (options.host) {
276 .no_verification => cleartext_header_buf[0 .. cleartext_header_buf.len - server_name_extension.len],
277 .explicit => &cleartext_header_buf,
278 };
279
280 {
281 var iovecs: [2][]const u8 = .{ cleartext_header, host };
282 try output.writeVecAll(iovecs[0..if (host.len == 0) 1 else 2]);
283 try output.flush();
284 }
285
286 var tls_version: tls.ProtocolVersion = undefined;
287 // These are used for two purposes:
288 // * Detect whether a certificate is the first one presented, in which case
289 // we need to verify the host name.
290 var cert_index: usize = 0;
291 // * Flip back and forth between the two cleartext buffers in order to keep
292 // the previous certificate in memory so that it can be verified by the
293 // next one.
294 var cert_buf_index: usize = 0;
295 var write_seq: u64 = 0;
296 var read_seq: u64 = 0;
297 var prev_cert: Certificate.Parsed = undefined;
298 const CipherState = enum {
299 /// No cipher is in use
300 cleartext,
301 /// Handshake cipher is in use
302 handshake,
303 /// Application cipher is in use
304 application,
305 };
306 var pending_cipher_state: CipherState = .cleartext;
307 var cipher_state = pending_cipher_state;
308 const HandshakeState = enum {
309 /// In this state we expect only a server hello message.
310 hello,
311 /// In this state we expect only an encrypted_extensions message.
312 encrypted_extensions,
313 /// In this state we expect certificate handshake messages.
314 certificate,
315 /// In this state we expect certificate or certificate_verify messages.
316 /// certificate messages are ignored since the trust chain is already
317 /// established.
318 trust_chain_established,
319 /// In this state, we expect only the server_hello_done handshake message.
320 server_hello_done,
321 /// In this state, we expect only the finished handshake message.
322 finished,
323 };
324 var handshake_state: HandshakeState = .hello;
325 var handshake_cipher: tls.HandshakeCipher = undefined;
326 var main_cert_pub_key: CertificatePublicKey = undefined;
327 var tls12_negotiated_group: ?tls.NamedGroup = null;
328 const now_sec = options.realtime_now_seconds;
329
330 var cleartext_fragment_start: usize = 0;
331 var cleartext_fragment_end: usize = 0;
332 var cleartext_bufs: [2][tls.max_ciphertext_inner_record_len]u8 = undefined;
333 fragment: while (true) {
334 // Ensure the input buffer pointer is stable in this scope.
335 input.rebase(tls.max_ciphertext_record_len) catch |err| switch (err) {
336 error.EndOfStream => {}, // We have assurance the remainder of stream can be buffered.
337 };
338 const record_header = input.peek(tls.record_header_len) catch |err| switch (err) {
339 error.EndOfStream => return error.TlsConnectionTruncated,
340 error.ReadFailed => return error.ReadFailed,
341 };
342 const record_ct = input.takeEnumNonexhaustive(tls.ContentType, .big) catch unreachable; // already peeked
343 input.toss(2); // legacy_version
344 const record_len = input.takeInt(u16, .big) catch unreachable; // already peeked
345 if (record_len > tls.max_ciphertext_len) return error.TlsRecordOverflow;
346 const record_buffer = input.take(record_len) catch |err| switch (err) {
347 error.EndOfStream => return error.TlsConnectionTruncated,
348 error.ReadFailed => return error.ReadFailed,
349 };
350 var record_decoder: tls.Decoder = .fromTheirSlice(record_buffer);
351 var ctd, const ct = content: switch (cipher_state) {
352 .cleartext => .{ record_decoder, record_ct },
353 .handshake => {
354 assert(tls_version == .tls_1_3);
355 if (record_ct != .application_data) return error.TlsUnexpectedMessage;
356 try record_decoder.ensure(record_len);
357 const cleartext_buf = &cleartext_bufs[cert_buf_index % 2];
358 switch (handshake_cipher) {
359 inline else => |*p| {
360 const pv = &p.version.tls_1_3;
361 const P = @TypeOf(p.*).A;
362 if (record_len < P.AEAD.tag_length) return error.TlsRecordOverflow;
363 const ciphertext = record_decoder.slice(record_len - P.AEAD.tag_length);
364 const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..];
365 if (ciphertext.len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
366 const cleartext = cleartext_fragment_buf[0..ciphertext.len];
367 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;
368 const nonce = nonce: {
369 const V = @Vector(P.AEAD.nonce_length, u8);
370 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
371 const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq)));
372 break :nonce @as(V, pv.server_handshake_iv) ^ operand;
373 };
374 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch
375 return error.TlsBadRecordMac;
376 // TODO use scalar, non-slice version
377 cleartext_fragment_end += mem.trimEnd(u8, cleartext, "\x00").len;
378 },
379 }
380 read_seq += 1;
381 cleartext_fragment_end -= 1;
382 const ct: tls.ContentType = @enumFromInt(cleartext_buf[cleartext_fragment_end]);
383 if (ct != .handshake) return error.TlsUnexpectedMessage;
384 break :content .{ tls.Decoder.fromTheirSlice(@constCast(cleartext_buf[cleartext_fragment_start..cleartext_fragment_end])), ct };
385 },
386 .application => {
387 assert(tls_version == .tls_1_2);
388 if (record_ct != .handshake) return error.TlsUnexpectedMessage;
389 try record_decoder.ensure(record_len);
390 const cleartext_buf = &cleartext_bufs[cert_buf_index % 2];
391 switch (handshake_cipher) {
392 inline else => |*p| {
393 const pv = &p.version.tls_1_2;
394 const P = @TypeOf(p.*).A;
395 if (record_len < P.record_iv_length + P.mac_length) return error.TlsRecordOverflow;
396 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
397 const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..];
398 if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
399 const cleartext = cleartext_fragment_buf[0..message_len];
400 const ad = mem.toBytes(big(read_seq)) ++
401 record_header[0 .. 1 + 2] ++
402 mem.toBytes(big(message_len));
403 const record_iv = record_decoder.array(P.record_iv_length).*;
404 const masked_read_seq = read_seq &
405 comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
406 const nonce: [P.AEAD.nonce_length]u8 = nonce: {
407 const V = @Vector(P.AEAD.nonce_length, u8);
408 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
409 const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
410 break :nonce @as(V, pv.app_cipher.server_write_IV ++ record_iv) ^ operand;
411 };
412 const ciphertext = record_decoder.slice(message_len);
413 const auth_tag = record_decoder.array(P.mac_length);
414 P.AEAD.decrypt(cleartext, ciphertext, auth_tag.*, ad, nonce, pv.app_cipher.server_write_key) catch return error.TlsBadRecordMac;
415 cleartext_fragment_end += message_len;
416 },
417 }
418 read_seq += 1;
419 break :content .{ tls.Decoder.fromTheirSlice(cleartext_buf[cleartext_fragment_start..cleartext_fragment_end]), record_ct };
420 },
421 };
422 switch (ct) {
423 .alert => {
424 ctd.ensure(2) catch continue :fragment;
425 if (options.alert) |a| a.* = .{
426 .level = ctd.decode(tls.Alert.Level),
427 .description = ctd.decode(tls.Alert.Description),
428 };
429 return error.TlsAlert;
430 },
431 .change_cipher_spec => {
432 ctd.ensure(1) catch continue :fragment;
433 if (ctd.decode(tls.ChangeCipherSpecType) != .change_cipher_spec) return error.TlsIllegalParameter;
434 cipher_state = pending_cipher_state;
435 },
436 .handshake => while (true) {
437 ctd.ensure(4) catch continue :fragment;
438 const handshake_type = ctd.decode(tls.HandshakeType);
439 const handshake_len = ctd.decode(u24);
440 var hsd = ctd.sub(handshake_len) catch continue :fragment;
441 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];
442 switch (handshake_type) {
443 .server_hello => {
444 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
445 if (handshake_state != .hello) return error.TlsUnexpectedMessage;
446 try hsd.ensure(2 + 32 + 1);
447 const legacy_version = hsd.decode(u16);
448 @memcpy(&server_hello_rand, hsd.array(32));
449 if (mem.eql(u8, &server_hello_rand, &tls.hello_retry_request_sequence)) {
450 // This is a HelloRetryRequest message. This client implementation
451 // does not expect to get one.
452 return error.TlsUnexpectedMessage;
453 }
454 const legacy_session_id_echo_len = hsd.decode(u8);
455 try hsd.ensure(legacy_session_id_echo_len + 2 + 1);
456 const legacy_session_id_echo = hsd.slice(legacy_session_id_echo_len);
457 const cipher_suite_tag = hsd.decode(tls.CipherSuite);
458 hsd.skip(1); // legacy_compression_method
459 var supported_version: ?u16 = null;
460 if (!hsd.eof()) {
461 try hsd.ensure(2);
462 const extensions_size = hsd.decode(u16);
463 var all_extd = try hsd.sub(extensions_size);
464 while (!all_extd.eof()) {
465 try all_extd.ensure(2 + 2);
466 const et = all_extd.decode(tls.ExtensionType);
467 const ext_size = all_extd.decode(u16);
468 var extd = try all_extd.sub(ext_size);
469 switch (et) {
470 .supported_versions => {
471 if (supported_version) |_| return error.TlsIllegalParameter;
472 try extd.ensure(2);
473 supported_version = extd.decode(u16);
474 },
475 .key_share => {
476 if (key_share.getSharedSecret()) |_| return error.TlsIllegalParameter;
477 try extd.ensure(4);
478 const named_group = extd.decode(tls.NamedGroup);
479 const key_size = extd.decode(u16);
480 try extd.ensure(key_size);
481 try key_share.exchange(named_group, extd.slice(key_size));
482 },
483 else => {},
484 }
485 }
486 }
487
488 tls_version = @enumFromInt(supported_version orelse legacy_version);
489 switch (tls_version) {
490 .tls_1_3 => if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id)) return error.TlsIllegalParameter,
491 .tls_1_2 => if (mem.eql(u8, server_hello_rand[24..31], "DOWNGRD") and
492 server_hello_rand[31] >> 1 == 0x00) return error.TlsIllegalParameter,
493 else => return error.TlsIllegalParameter,
494 }
495
496 switch (cipher_suite_tag) {
497 inline .AES_128_GCM_SHA256,
498 .AES_256_GCM_SHA384,
499 .CHACHA20_POLY1305_SHA256,
500 .AEGIS_256_SHA512,
501 .AEGIS_128L_SHA256,
502
503 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
504 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
505 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
506 => |tag| {
507 handshake_cipher = @unionInit(tls.HandshakeCipher, @tagName(tag.with()), .{
508 .transcript_hash = .init(.{}),
509 .version = undefined,
510 });
511 const p = &@field(handshake_cipher, @tagName(tag.with()));
512 p.transcript_hash.update(cleartext_header[tls.record_header_len..]); // Client Hello part 1
513 p.transcript_hash.update(host); // Client Hello part 2
514 p.transcript_hash.update(wrapped_handshake);
515 },
516
517 else => return error.TlsIllegalParameter,
518 }
519 switch (tls_version) {
520 .tls_1_3 => {
521 switch (cipher_suite_tag) {
522 inline .AES_128_GCM_SHA256,
523 .AES_256_GCM_SHA384,
524 .CHACHA20_POLY1305_SHA256,
525 .AEGIS_256_SHA512,
526 .AEGIS_128L_SHA256,
527 => |tag| {
528 const sk = key_share.getSharedSecret() orelse return error.TlsIllegalParameter;
529 const p = &@field(handshake_cipher, @tagName(tag.with()));
530 const P = @TypeOf(p.*).A;
531 const hello_hash = p.transcript_hash.peek();
532 const zeroes = [1]u8{0} ** P.Hash.digest_length;
533 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
534 const empty_hash = tls.emptyHash(P.Hash);
535 p.version = .{ .tls_1_3 = undefined };
536 const pv = &p.version.tls_1_3;
537 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);
538 pv.handshake_secret = P.Hkdf.extract(&hs_derived_secret, sk);
539 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);
540 pv.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);
541 const client_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);
542 const server_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length);
543 if (options.ssl_key_log) |key_log| logSecrets(key_log.writer, .{
544 .client_random = &client_hello_rand,
545 }, .{
546 .SERVER_HANDSHAKE_TRAFFIC_SECRET = &server_secret,
547 .CLIENT_HANDSHAKE_TRAFFIC_SECRET = &client_secret,
548 });
549 pv.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length);
550 pv.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length);
551 pv.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
552 pv.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
553 pv.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
554 pv.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
555 },
556 else => return error.TlsIllegalParameter,
557 }
558 pending_cipher_state = .handshake;
559 handshake_state = .encrypted_extensions;
560 },
561 .tls_1_2 => switch (cipher_suite_tag) {
562 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
563 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
564 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
565 => handshake_state = .certificate,
566 else => return error.TlsIllegalParameter,
567 },
568 else => return error.TlsIllegalParameter,
569 }
570 },
571 .encrypted_extensions => {
572 if (tls_version != .tls_1_3) return error.TlsUnexpectedMessage;
573 if (cipher_state != .handshake) return error.TlsUnexpectedMessage;
574 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;
575 switch (handshake_cipher) {
576 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
577 }
578 try hsd.ensure(2);
579 const total_ext_size = hsd.decode(u16);
580 var all_extd = try hsd.sub(total_ext_size);
581 while (!all_extd.eof()) {
582 try all_extd.ensure(4);
583 const et = all_extd.decode(tls.ExtensionType);
584 const ext_size = all_extd.decode(u16);
585 const extd = try all_extd.sub(ext_size);
586 _ = extd;
587 switch (et) {
588 .server_name => {},
589 else => {},
590 }
591 }
592 handshake_state = .certificate;
593 },
594 .certificate => cert: {
595 if (cipher_state == .application) return error.TlsUnexpectedMessage;
596 switch (handshake_state) {
597 .certificate => {},
598 .trust_chain_established => break :cert,
599 else => return error.TlsUnexpectedMessage,
600 }
601 switch (handshake_cipher) {
602 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
603 }
604
605 switch (tls_version) {
606 .tls_1_3 => {
607 try hsd.ensure(1 + 3);
608 const cert_req_ctx_len = hsd.decode(u8);
609 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
610 },
611 .tls_1_2 => try hsd.ensure(3),
612 else => unreachable,
613 }
614 const certs_size = hsd.decode(u24);
615 var certs_decoder = try hsd.sub(certs_size);
616 while (!certs_decoder.eof()) {
617 try certs_decoder.ensure(3);
618 const cert_size = certs_decoder.decode(u24);
619 const certd = try certs_decoder.sub(cert_size);
620
621 if (tls_version == .tls_1_3) {
622 try certs_decoder.ensure(2);
623 const total_ext_size = certs_decoder.decode(u16);
624 const all_extd = try certs_decoder.sub(total_ext_size);
625 _ = all_extd;
626 }
627
628 const subject_cert: Certificate = .{
629 .buffer = certd.buf,
630 .index = @intCast(certd.idx),
631 };
632 const subject = try subject_cert.parse();
633 if (cert_index == 0) {
634 // Verify the host on the first certificate.
635 switch (options.host) {
636 .no_verification => {},
637 .explicit => try subject.verifyHostName(host),
638 }
639
640 // Keep track of the public key for the
641 // certificate_verify message later.
642 try main_cert_pub_key.init(subject.pub_key_algo, subject.pubKey());
643 } else {
644 try prev_cert.verify(subject, now_sec);
645 }
646
647 switch (options.ca) {
648 .no_verification => {
649 handshake_state = .trust_chain_established;
650 break :cert;
651 },
652 .self_signed => {
653 try subject.verify(subject, now_sec);
654 handshake_state = .trust_chain_established;
655 break :cert;
656 },
657 .bundle => |ca_bundle| if (ca_bundle.verify(subject, now_sec)) |_| {
658 handshake_state = .trust_chain_established;
659 break :cert;
660 } else |err| switch (err) {
661 error.CertificateIssuerNotFound => {},
662 else => |e| return e,
663 },
664 }
665
666 prev_cert = subject;
667 cert_index += 1;
668 }
669 cert_buf_index += 1;
670 },
671 .server_key_exchange => {
672 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
673 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
674 switch (handshake_state) {
675 .trust_chain_established => {},
676 .certificate => return error.TlsCertificateNotVerified,
677 else => return error.TlsUnexpectedMessage,
678 }
679
680 switch (handshake_cipher) {
681 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
682 }
683 try hsd.ensure(1 + 2 + 1);
684 const curve_type = hsd.decode(u8);
685 if (curve_type != 0x03) return error.TlsIllegalParameter; // named_curve
686 const named_group = hsd.decode(tls.NamedGroup);
687 tls12_negotiated_group = named_group;
688 const key_size = hsd.decode(u8);
689 try hsd.ensure(key_size);
690 const server_pub_key = hsd.slice(key_size);
691 try main_cert_pub_key.verifySignature(&hsd, &.{ &client_hello_rand, &server_hello_rand, hsd.buf[0..hsd.idx] });
692 try key_share.exchange(named_group, server_pub_key);
693 handshake_state = .server_hello_done;
694 },
695 .server_hello_done => {
696 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
697 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
698 if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage;
699
700 const public_key_bytes: []const u8 = switch (tls12_negotiated_group orelse .secp256r1) {
701 .secp256r1 => &key_share.secp256r1_kp.public_key.toUncompressedSec1(),
702 .secp384r1 => &key_share.secp384r1_kp.public_key.toUncompressedSec1(),
703 .x25519 => &key_share.x25519_kp.public_key,
704 else => return error.TlsIllegalParameter,
705 };
706
707 const client_key_exchange_prefix = .{@intFromEnum(tls.ContentType.handshake)} ++
708 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
709 int(u16, @intCast(public_key_bytes.len + 5)) ++ // record length
710 .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++
711 int(u24, @intCast(public_key_bytes.len + 1)) ++ // handshake message length
712 .{@as(u8, @intCast(public_key_bytes.len))}; // public key length
713 const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
714 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
715 array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
716 const pre_master_secret = key_share.getSharedSecret().?;
717 switch (handshake_cipher) {
718 inline else => |*p| {
719 const P = @TypeOf(p.*).A;
720 p.transcript_hash.update(wrapped_handshake);
721 p.transcript_hash.update(client_key_exchange_prefix[tls.record_header_len..]);
722 p.transcript_hash.update(public_key_bytes);
723 const master_secret = hmacExpandLabel(P.Hmac, pre_master_secret, &.{
724 "master secret",
725 &client_hello_rand,
726 &server_hello_rand,
727 }, 48);
728 if (options.ssl_key_log) |key_log| logSecrets(key_log.writer, .{
729 .client_random = &client_hello_rand,
730 }, .{
731 .CLIENT_RANDOM = &master_secret,
732 });
733 const key_block = hmacExpandLabel(
734 P.Hmac,
735 &master_secret,
736 &.{ "key expansion", &server_hello_rand, &client_hello_rand },
737 @sizeOf(P.Tls_1_2),
738 );
739 const client_verify_cleartext = .{@intFromEnum(tls.HandshakeType.finished)} ++
740 array(u24, u8, hmacExpandLabel(
741 P.Hmac,
742 &master_secret,
743 &.{ "client finished", &p.transcript_hash.peek() },
744 P.verify_data_length,
745 ));
746 p.transcript_hash.update(&client_verify_cleartext);
747 p.version = .{ .tls_1_2 = .{
748 .expected_server_verify_data = hmacExpandLabel(
749 P.Hmac,
750 &master_secret,
751 &.{ "server finished", &p.transcript_hash.finalResult() },
752 P.verify_data_length,
753 ),
754 .app_cipher = mem.bytesToValue(P.Tls_1_2, &key_block),
755 } };
756 const pv = &p.version.tls_1_2;
757 const nonce: [P.AEAD.nonce_length]u8 = nonce: {
758 const V = @Vector(P.AEAD.nonce_length, u8);
759 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
760 const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq)));
761 break :nonce @as(V, pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt) ^ operand;
762 };
763 var client_verify_msg = .{@intFromEnum(tls.ContentType.handshake)} ++
764 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
765 array(u16, u8, nonce[P.fixed_iv_length..].* ++
766 @as([client_verify_cleartext.len + P.mac_length]u8, undefined));
767 P.AEAD.encrypt(
768 client_verify_msg[client_verify_msg.len - P.mac_length -
769 client_verify_cleartext.len ..][0..client_verify_cleartext.len],
770 client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length],
771 &client_verify_cleartext,
772 mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len),
773 nonce,
774 pv.app_cipher.client_write_key,
775 );
776 var all_msgs_vec: [4][]const u8 = .{
777 &client_key_exchange_prefix,
778 public_key_bytes,
779 &client_change_cipher_spec_msg,
780 &client_verify_msg,
781 };
782 try output.writeVecAll(&all_msgs_vec);
783 try output.flush();
784 },
785 }
786 write_seq += 1;
787 pending_cipher_state = .application;
788 handshake_state = .finished;
789 },
790 .certificate_verify => {
791 if (tls_version != .tls_1_3) return error.TlsUnexpectedMessage;
792 if (cipher_state != .handshake) return error.TlsUnexpectedMessage;
793 switch (handshake_state) {
794 .trust_chain_established => {},
795 .certificate => return error.TlsCertificateNotVerified,
796 else => return error.TlsUnexpectedMessage,
797 }
798 switch (handshake_cipher) {
799 inline else => |*p| {
800 try main_cert_pub_key.verifySignature(&hsd, &.{
801 " " ** 64 ++ "TLS 1.3, server CertificateVerify\x00",
802 &p.transcript_hash.peek(),
803 });
804 p.transcript_hash.update(wrapped_handshake);
805 },
806 }
807 handshake_state = .finished;
808 },
809 .finished => {
810 if (cipher_state == .cleartext) return error.TlsUnexpectedMessage;
811 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
812 // This message is to trick buggy proxies into behaving correctly.
813 const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
814 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
815 array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
816 const app_cipher = app_cipher: switch (handshake_cipher) {
817 inline else => |*p, tag| switch (tls_version) {
818 .tls_1_3 => {
819 const pv = &p.version.tls_1_3;
820 const P = @TypeOf(p.*).A;
821 try hsd.ensure(P.Hmac.mac_length);
822 const finished_digest = p.transcript_hash.peek();
823 p.transcript_hash.update(wrapped_handshake);
824 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, pv.server_finished_key);
825 if (!std.crypto.timing_safe.eql([P.Hmac.mac_length]u8, expected_server_verify_data, hsd.array(P.Hmac.mac_length).*)) return error.TlsDecryptError;
826 const handshake_hash = p.transcript_hash.finalResult();
827 const verify_data = tls.hmac(P.Hmac, &handshake_hash, pv.client_finished_key);
828 const out_cleartext = .{@intFromEnum(tls.HandshakeType.finished)} ++
829 array(u24, u8, verify_data) ++
830 .{@intFromEnum(tls.ContentType.handshake)};
831
832 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
833
834 var finished_msg = .{@intFromEnum(tls.ContentType.application_data)} ++
835 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
836 array(u16, u8, @as([wrapped_len]u8, undefined));
837
838 const ad = finished_msg[0..tls.record_header_len];
839 const ciphertext = finished_msg[tls.record_header_len..][0..out_cleartext.len];
840 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
841 const nonce = pv.client_handshake_iv;
842 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, pv.client_handshake_key);
843
844 var all_msgs_vec: [2][]const u8 = .{
845 &client_change_cipher_spec_msg,
846 &finished_msg,
847 };
848 try output.writeVecAll(&all_msgs_vec);
849 try output.flush();
850
851 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
852 const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
853 if (options.ssl_key_log) |key_log| logSecrets(key_log.writer, .{
854 .counter = key_seq,
855 .client_random = &client_hello_rand,
856 }, .{
857 .SERVER_TRAFFIC_SECRET = &server_secret,
858 .CLIENT_TRAFFIC_SECRET = &client_secret,
859 });
860 key_seq += 1;
861 break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_3 = .{
862 .client_secret = client_secret,
863 .server_secret = server_secret,
864 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),
865 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),
866 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),
867 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
868 } });
869 },
870 .tls_1_2 => {
871 const pv = &p.version.tls_1_2;
872 const P = @TypeOf(p.*).A;
873 try hsd.ensure(P.verify_data_length);
874 if (!std.crypto.timing_safe.eql([P.verify_data_length]u8, pv.expected_server_verify_data, hsd.array(P.verify_data_length).*)) return error.TlsDecryptError;
875 break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_2 = pv.app_cipher });
876 },
877 else => unreachable,
878 },
879 };
880 if (options.ssl_key_log) |ssl_key_log| ssl_key_log.* = .{
881 .client_key_seq = key_seq,
882 .server_key_seq = key_seq,
883 .client_random = client_hello_rand,
884 .writer = ssl_key_log.writer,
885 };
886 return .{
887 .input = input,
888 .reader = .{
889 .buffer = options.read_buffer,
890 .vtable = &.{
891 .stream = stream,
892 .readVec = readVec,
893 },
894 .seek = 0,
895 .end = 0,
896 },
897 .output = output,
898 .writer = .{
899 .buffer = options.write_buffer,
900 .vtable = &.{
901 .drain = drain,
902 .flush = flush,
903 },
904 },
905 .tls_version = tls_version,
906 .read_seq = switch (tls_version) {
907 .tls_1_3 => 0,
908 .tls_1_2 => read_seq,
909 else => unreachable,
910 },
911 .write_seq = switch (tls_version) {
912 .tls_1_3 => 0,
913 .tls_1_2 => write_seq,
914 else => unreachable,
915 },
916 .received_close_notify = false,
917 .allow_truncation_attacks = options.allow_truncation_attacks,
918 .application_cipher = app_cipher,
919 .ssl_key_log = options.ssl_key_log,
920 };
921 },
922 else => return error.TlsUnexpectedMessage,
923 }
924 if (ctd.eof()) break;
925 cleartext_fragment_start = ctd.idx;
926 },
927 else => return error.TlsUnexpectedMessage,
928 }
929 cleartext_fragment_start = 0;
930 cleartext_fragment_end = 0;
931 }
932}
933
934fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
935 const c: *Client = @alignCast(@fieldParentPtr("writer", w));
936 const output = c.output;
937 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
938 var ciphertext_end: usize = 0;
939 var total_clear: usize = 0;
940 done: {
941 {
942 const buf = w.buffered();
943 const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data);
944 total_clear += prepared.cleartext_len;
945 ciphertext_end += prepared.ciphertext_end;
946 if (prepared.cleartext_len < buf.len) break :done;
947 }
948 for (data[0 .. data.len - 1]) |buf| {
949 const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data);
950 total_clear += prepared.cleartext_len;
951 ciphertext_end += prepared.ciphertext_end;
952 if (prepared.cleartext_len < buf.len) break :done;
953 }
954 const buf = data[data.len - 1];
955 for (0..splat) |_| {
956 const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data);
957 total_clear += prepared.cleartext_len;
958 ciphertext_end += prepared.ciphertext_end;
959 if (prepared.cleartext_len < buf.len) break :done;
960 }
961 }
962 output.advance(ciphertext_end);
963 return w.consume(total_clear);
964}
965
966fn flush(w: *Writer) Writer.Error!void {
967 const c: *Client = @alignCast(@fieldParentPtr("writer", w));
968 const output = c.output;
969 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
970 const prepared = prepareCiphertextRecord(c, ciphertext_buf, w.buffered(), .application_data);
971 output.advance(prepared.ciphertext_end);
972 w.end = 0;
973}
974
975/// Sends a `close_notify` alert, which is necessary for the server to
976/// distinguish between a properly finished TLS session, or a truncation
977/// attack.
978pub fn end(c: *Client) Writer.Error!void {
979 try flush(&c.writer);
980 const output = c.output;
981 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
982 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);
983 output.advance(prepared.ciphertext_end);
984}
985
986fn prepareCiphertextRecord(
987 c: *Client,
988 ciphertext_buf: []u8,
989 bytes: []const u8,
990 inner_content_type: tls.ContentType,
991) struct {
992 ciphertext_end: usize,
993 cleartext_len: usize,
994} {
995 // Due to the trailing inner content type byte in the ciphertext, we need
996 // an additional buffer for storing the cleartext into before encrypting.
997 var cleartext_buf: [max_ciphertext_len]u8 = undefined;
998 var ciphertext_end: usize = 0;
999 var bytes_i: usize = 0;
1000 switch (c.application_cipher) {
1001 inline else => |*p| switch (c.tls_version) {
1002 .tls_1_3 => {
1003 const pv = &p.tls_1_3;
1004 const P = @TypeOf(p.*);
1005 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;
1006 while (true) {
1007 const encrypted_content_len: u16 = @min(
1008 bytes.len - bytes_i,
1009 tls.max_ciphertext_inner_record_len,
1010 ciphertext_buf.len -| (overhead_len + ciphertext_end),
1011 );
1012 if (encrypted_content_len == 0) return .{
1013 .ciphertext_end = ciphertext_end,
1014 .cleartext_len = bytes_i,
1015 };
1016
1017 @memcpy(cleartext_buf[0..encrypted_content_len], bytes[bytes_i..][0..encrypted_content_len]);
1018 cleartext_buf[encrypted_content_len] = @intFromEnum(inner_content_type);
1019 bytes_i += encrypted_content_len;
1020 const ciphertext_len = encrypted_content_len + 1;
1021 const cleartext = cleartext_buf[0..ciphertext_len];
1022
1023 const ad = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
1024 ad.* = .{@intFromEnum(tls.ContentType.application_data)} ++
1025 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
1026 int(u16, ciphertext_len + P.AEAD.tag_length);
1027 ciphertext_end += ad.len;
1028 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];
1029 ciphertext_end += ciphertext_len;
1030 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];
1031 ciphertext_end += auth_tag.len;
1032 const nonce = nonce: {
1033 const V = @Vector(P.AEAD.nonce_length, u8);
1034 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1035 const operand: V = pad ++ mem.toBytes(big(c.write_seq));
1036 break :nonce @as(V, pv.client_iv) ^ operand;
1037 };
1038 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key);
1039 c.write_seq += 1; // TODO send key_update on overflow
1040 }
1041 },
1042 .tls_1_2 => {
1043 const pv = &p.tls_1_2;
1044 const P = @TypeOf(p.*);
1045 const overhead_len = tls.record_header_len + P.record_iv_length + P.mac_length;
1046 while (true) {
1047 const message_len: u16 = @min(
1048 bytes.len - bytes_i,
1049 tls.max_ciphertext_inner_record_len,
1050 ciphertext_buf.len -| (overhead_len + ciphertext_end),
1051 );
1052 if (message_len == 0) return .{
1053 .ciphertext_end = ciphertext_end,
1054 .cleartext_len = bytes_i,
1055 };
1056
1057 @memcpy(cleartext_buf[0..message_len], bytes[bytes_i..][0..message_len]);
1058 bytes_i += message_len;
1059 const cleartext = cleartext_buf[0..message_len];
1060
1061 const record_header = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
1062 ciphertext_end += tls.record_header_len;
1063 record_header.* = .{@intFromEnum(inner_content_type)} ++
1064 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
1065 int(u16, P.record_iv_length + message_len + P.mac_length);
1066 const ad = mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len);
1067 const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length];
1068 ciphertext_end += P.record_iv_length;
1069 const nonce: [P.AEAD.nonce_length]u8 = nonce: {
1070 const V = @Vector(P.AEAD.nonce_length, u8);
1071 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1072 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq)));
1073 break :nonce @as(V, pv.client_write_IV ++ pv.client_salt) ^ operand;
1074 };
1075 record_iv.* = nonce[P.fixed_iv_length..].*;
1076 const ciphertext = ciphertext_buf[ciphertext_end..][0..message_len];
1077 ciphertext_end += message_len;
1078 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.mac_length];
1079 ciphertext_end += P.mac_length;
1080 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_write_key);
1081 c.write_seq += 1; // TODO send key_update on overflow
1082 }
1083 },
1084 else => unreachable,
1085 },
1086 }
1087}
1088
1089pub fn eof(c: Client) bool {
1090 return c.received_close_notify;
1091}
1092
1093fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
1094 // This function writes exclusively to the buffer.
1095 _ = w;
1096 _ = limit;
1097 const c: *Client = @alignCast(@fieldParentPtr("reader", r));
1098 return readIndirect(c);
1099}
1100
1101fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize {
1102 // This function writes exclusively to the buffer.
1103 _ = data;
1104 const c: *Client = @alignCast(@fieldParentPtr("reader", r));
1105 return readIndirect(c);
1106}
1107
1108fn readIndirect(c: *Client) Reader.Error!usize {
1109 const r = &c.reader;
1110 if (c.eof()) return error.EndOfStream;
1111 const input = c.input;
1112 // If at least one full encrypted record is not buffered, read once.
1113 const record_header = input.peek(tls.record_header_len) catch |err| switch (err) {
1114 error.EndOfStream => {
1115 // This is either a truncation attack, a bug in the server, or an
1116 // intentional omission of the close_notify message due to truncation
1117 // detection handled above the TLS layer.
1118 if (c.allow_truncation_attacks) {
1119 c.received_close_notify = true;
1120 return error.EndOfStream;
1121 } else {
1122 return failRead(c, error.TlsConnectionTruncated);
1123 }
1124 },
1125 error.ReadFailed => return error.ReadFailed,
1126 };
1127 const ct: tls.ContentType = @enumFromInt(record_header[0]);
1128 const legacy_version = mem.readInt(u16, record_header[1..][0..2], .big);
1129 _ = legacy_version;
1130 const record_len = mem.readInt(u16, record_header[3..][0..2], .big);
1131 if (record_len > max_ciphertext_len) return failRead(c, error.TlsRecordOverflow);
1132 const record_end = 5 + record_len;
1133 if (record_end > input.buffered().len) {
1134 input.fillMore() catch |err| switch (err) {
1135 error.EndOfStream => return failRead(c, error.TlsConnectionTruncated),
1136 error.ReadFailed => return error.ReadFailed,
1137 };
1138 if (record_end > input.buffered().len) return 0;
1139 }
1140
1141 const cleartext_len, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) {
1142 inline else => |*p| switch (c.tls_version) {
1143 .tls_1_3 => {
1144 const pv = &p.tls_1_3;
1145 const P = @TypeOf(p.*);
1146 const ad = input.take(tls.record_header_len) catch unreachable; // already peeked
1147 const ciphertext_len = record_len - P.AEAD.tag_length;
1148 const ciphertext = input.take(ciphertext_len) catch unreachable; // already peeked
1149 const auth_tag = (input.takeArray(P.AEAD.tag_length) catch unreachable).*; // already peeked
1150 const nonce = nonce: {
1151 const V = @Vector(P.AEAD.nonce_length, u8);
1152 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1153 const operand: V = pad ++ mem.toBytes(big(c.read_seq));
1154 break :nonce @as(V, pv.server_iv) ^ operand;
1155 };
1156 rebase(r, ciphertext.len);
1157 const cleartext = r.buffer[r.end..][0..ciphertext.len];
1158 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch
1159 return failRead(c, error.TlsBadRecordMac);
1160 // TODO use scalar, non-slice version
1161 const msg = mem.trimRight(u8, cleartext, "\x00");
1162 break :cleartext .{ msg.len - 1, @enumFromInt(msg[msg.len - 1]) };
1163 },
1164 .tls_1_2 => {
1165 const pv = &p.tls_1_2;
1166 const P = @TypeOf(p.*);
1167 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
1168 const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked
1169 const ad = mem.toBytes(big(c.read_seq)) ++
1170 ad_header[0 .. 1 + 2] ++
1171 mem.toBytes(big(message_len));
1172 const record_iv = (input.takeArray(P.record_iv_length) catch unreachable).*; // already peeked
1173 const masked_read_seq = c.read_seq &
1174 comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
1175 const nonce: [P.AEAD.nonce_length]u8 = nonce: {
1176 const V = @Vector(P.AEAD.nonce_length, u8);
1177 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1178 const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
1179 break :nonce @as(V, pv.server_write_IV ++ record_iv) ^ operand;
1180 };
1181 const ciphertext = input.take(message_len) catch unreachable; // already peeked
1182 const auth_tag = (input.takeArray(P.mac_length) catch unreachable).*; // already peeked
1183 rebase(r, ciphertext.len);
1184 const cleartext = r.buffer[r.end..][0..ciphertext.len];
1185 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch
1186 return failRead(c, error.TlsBadRecordMac);
1187 break :cleartext .{ cleartext.len, ct };
1188 },
1189 else => unreachable,
1190 },
1191 };
1192 const cleartext = r.buffer[r.end..][0..cleartext_len];
1193 c.read_seq = std.math.add(u64, c.read_seq, 1) catch return failRead(c, error.TlsSequenceOverflow);
1194 switch (inner_ct) {
1195 .alert => {
1196 if (cleartext.len != 2) return failRead(c, error.TlsDecodeError);
1197 const alert: tls.Alert = .{
1198 .level = @enumFromInt(cleartext[0]),
1199 .description = @enumFromInt(cleartext[1]),
1200 };
1201 switch (alert.description) {
1202 .close_notify => {
1203 c.received_close_notify = true;
1204 return 0;
1205 },
1206 .user_canceled => {
1207 // TODO: handle server-side closures
1208 return failRead(c, error.TlsUnexpectedMessage);
1209 },
1210 else => {
1211 c.alert = alert;
1212 return failRead(c, error.TlsAlert);
1213 },
1214 }
1215 },
1216 .handshake => {
1217 var ct_i: usize = 0;
1218 while (true) {
1219 const handshake_type: tls.HandshakeType = @enumFromInt(cleartext[ct_i]);
1220 ct_i += 1;
1221 const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big);
1222 ct_i += 3;
1223 const next_handshake_i = ct_i + handshake_len;
1224 if (next_handshake_i > cleartext.len) return failRead(c, error.TlsBadLength);
1225 const handshake = cleartext[ct_i..next_handshake_i];
1226 switch (handshake_type) {
1227 .new_session_ticket => {
1228 // This client implementation ignores new session tickets.
1229 },
1230 .key_update => {
1231 switch (c.application_cipher) {
1232 inline else => |*p| {
1233 const pv = &p.tls_1_3;
1234 const P = @TypeOf(p.*);
1235 const server_secret = hkdfExpandLabel(P.Hkdf, pv.server_secret, "traffic upd", "", P.Hash.digest_length);
1236 if (c.ssl_key_log) |key_log| logSecrets(key_log.writer, .{
1237 .counter = key_log.serverCounter(),
1238 .client_random = &key_log.client_random,
1239 }, .{
1240 .SERVER_TRAFFIC_SECRET = &server_secret,
1241 });
1242 pv.server_secret = server_secret;
1243 pv.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
1244 pv.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
1245 },
1246 }
1247 c.read_seq = 0;
1248
1249 switch (@as(tls.KeyUpdateRequest, @enumFromInt(handshake[0]))) {
1250 .update_requested => {
1251 switch (c.application_cipher) {
1252 inline else => |*p| {
1253 const pv = &p.tls_1_3;
1254 const P = @TypeOf(p.*);
1255 const client_secret = hkdfExpandLabel(P.Hkdf, pv.client_secret, "traffic upd", "", P.Hash.digest_length);
1256 if (c.ssl_key_log) |key_log| logSecrets(key_log.writer, .{
1257 .counter = key_log.clientCounter(),
1258 .client_random = &key_log.client_random,
1259 }, .{
1260 .CLIENT_TRAFFIC_SECRET = &client_secret,
1261 });
1262 pv.client_secret = client_secret;
1263 pv.client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
1264 pv.client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
1265 },
1266 }
1267 c.write_seq = 0;
1268 },
1269 .update_not_requested => {},
1270 _ => return failRead(c, error.TlsIllegalParameter),
1271 }
1272 },
1273 else => return failRead(c, error.TlsUnexpectedMessage),
1274 }
1275 ct_i = next_handshake_i;
1276 if (ct_i >= cleartext.len) break;
1277 }
1278 return 0;
1279 },
1280 .application_data => {
1281 r.end += cleartext.len;
1282 return 0;
1283 },
1284 else => return failRead(c, error.TlsUnexpectedMessage),
1285 }
1286}
1287
1288fn rebase(r: *Reader, capacity: usize) void {
1289 if (r.buffer.len - r.end >= capacity) return;
1290 const data = r.buffer[r.seek..r.end];
1291 @memmove(r.buffer[0..data.len], data);
1292 r.seek = 0;
1293 r.end = data.len;
1294 assert(r.buffer.len - r.end >= capacity);
1295}
1296
1297fn failRead(c: *Client, err: ReadError) error{ReadFailed} {
1298 c.read_err = err;
1299 return error.ReadFailed;
1300}
1301
1302fn logSecrets(w: *Writer, context: anytype, secrets: anytype) void {
1303 inline for (@typeInfo(@TypeOf(secrets)).@"struct".fields) |field| w.print("{s}" ++
1304 (if (@hasField(@TypeOf(context), "counter")) "_{d}" else "") ++ " {x} {x}\n", .{field.name} ++
1305 (if (@hasField(@TypeOf(context), "counter")) .{context.counter} else .{}) ++ .{
1306 context.client_random,
1307 @field(secrets, field.name),
1308 }) catch {};
1309}
1310
1311fn big(x: anytype) @TypeOf(x) {
1312 return switch (native_endian) {
1313 .big => x,
1314 .little => @byteSwap(x),
1315 };
1316}
1317
1318const KeyShare = struct {
1319 ml_kem768_kp: crypto.kem.ml_kem.MLKem768.KeyPair,
1320 secp256r1_kp: crypto.sign.ecdsa.EcdsaP256Sha256.KeyPair,
1321 secp384r1_kp: crypto.sign.ecdsa.EcdsaP384Sha384.KeyPair,
1322 x25519_kp: crypto.dh.X25519.KeyPair,
1323 sk_buf: [sk_max_len]u8,
1324 sk_len: std.math.IntFittingRange(0, sk_max_len),
1325
1326 const sk_max_len = @max(
1327 crypto.dh.X25519.shared_length + crypto.kem.ml_kem.MLKem768.shared_length,
1328 crypto.ecc.P256.scalar.encoded_length,
1329 crypto.ecc.P384.scalar.encoded_length,
1330 crypto.dh.X25519.shared_length,
1331 );
1332
1333 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {
1334 return .{
1335 .ml_kem768_kp = .generate(),
1336 .secp256r1_kp = try .generateDeterministic(seed[0..32].*),
1337 .secp384r1_kp = try .generateDeterministic(seed[32..80].*),
1338 .x25519_kp = try .generateDeterministic(seed[80..112].*),
1339 .sk_buf = undefined,
1340 .sk_len = 0,
1341 };
1342 }
1343
1344 fn exchange(
1345 ks: *KeyShare,
1346 named_group: tls.NamedGroup,
1347 server_pub_key: []const u8,
1348 ) error{ TlsIllegalParameter, TlsDecryptFailure }!void {
1349 switch (named_group) {
1350 .x25519_ml_kem768 => {
1351 const hksl = crypto.kem.ml_kem.MLKem768.ciphertext_length;
1352 const xksl = hksl + crypto.dh.X25519.public_length;
1353 if (server_pub_key.len != xksl) return error.TlsIllegalParameter;
1354
1355 const hsk = ks.ml_kem768_kp.secret_key.decaps(server_pub_key[0..hksl]) catch
1356 return error.TlsDecryptFailure;
1357 const xsk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[hksl..xksl].*) catch
1358 return error.TlsDecryptFailure;
1359 @memcpy(ks.sk_buf[0..hsk.len], &hsk);
1360 @memcpy(ks.sk_buf[hsk.len..][0..xsk.len], &xsk);
1361 ks.sk_len = hsk.len + xsk.len;
1362 },
1363 .secp256r1 => {
1364 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
1365 const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
1366 const mul = pk.p.mulPublic(ks.secp256r1_kp.secret_key.bytes, .big) catch
1367 return error.TlsDecryptFailure;
1368 const sk = mul.affineCoordinates().x.toBytes(.big);
1369 @memcpy(ks.sk_buf[0..sk.len], &sk);
1370 ks.sk_len = sk.len;
1371 },
1372 .secp384r1 => {
1373 const PublicKey = crypto.sign.ecdsa.EcdsaP384Sha384.PublicKey;
1374 const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
1375 const mul = pk.p.mulPublic(ks.secp384r1_kp.secret_key.bytes, .big) catch
1376 return error.TlsDecryptFailure;
1377 const sk = mul.affineCoordinates().x.toBytes(.big);
1378 @memcpy(ks.sk_buf[0..sk.len], &sk);
1379 ks.sk_len = sk.len;
1380 },
1381 .x25519 => {
1382 const ksl = crypto.dh.X25519.public_length;
1383 if (server_pub_key.len != ksl) return error.TlsIllegalParameter;
1384 const sk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..ksl].*) catch
1385 return error.TlsDecryptFailure;
1386 @memcpy(ks.sk_buf[0..sk.len], &sk);
1387 ks.sk_len = sk.len;
1388 },
1389 else => return error.TlsIllegalParameter,
1390 }
1391 }
1392
1393 fn getSharedSecret(ks: *const KeyShare) ?[]const u8 {
1394 return if (ks.sk_len > 0) ks.sk_buf[0..ks.sk_len] else null;
1395 }
1396};
1397
1398fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {
1399 return switch (scheme) {
1400 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,
1401 .ecdsa_secp384r1_sha384 => crypto.sign.ecdsa.EcdsaP384Sha384,
1402 else => @compileError("bad scheme"),
1403 };
1404}
1405
1406fn SchemeRsa(comptime scheme: tls.SignatureScheme) type {
1407 return switch (scheme) {
1408 .rsa_pkcs1_sha256,
1409 .rsa_pkcs1_sha384,
1410 .rsa_pkcs1_sha512,
1411 .rsa_pkcs1_sha1,
1412 => Certificate.rsa.PKCS1v1_5Signature,
1413 .rsa_pss_rsae_sha256,
1414 .rsa_pss_rsae_sha384,
1415 .rsa_pss_rsae_sha512,
1416 .rsa_pss_pss_sha256,
1417 .rsa_pss_pss_sha384,
1418 .rsa_pss_pss_sha512,
1419 => Certificate.rsa.PSSSignature,
1420 else => @compileError("bad scheme"),
1421 };
1422}
1423
1424fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {
1425 return switch (scheme) {
1426 .ed25519 => crypto.sign.Ed25519,
1427 else => @compileError("bad scheme"),
1428 };
1429}
1430
1431fn SchemeHash(comptime scheme: tls.SignatureScheme) type {
1432 return switch (scheme) {
1433 .rsa_pkcs1_sha256,
1434 .ecdsa_secp256r1_sha256,
1435 .rsa_pss_rsae_sha256,
1436 .rsa_pss_pss_sha256,
1437 => crypto.hash.sha2.Sha256,
1438 .rsa_pkcs1_sha384,
1439 .ecdsa_secp384r1_sha384,
1440 .rsa_pss_rsae_sha384,
1441 .rsa_pss_pss_sha384,
1442 => crypto.hash.sha2.Sha384,
1443 .rsa_pkcs1_sha512,
1444 .ecdsa_secp521r1_sha512,
1445 .rsa_pss_rsae_sha512,
1446 .rsa_pss_pss_sha512,
1447 => crypto.hash.sha2.Sha512,
1448 .rsa_pkcs1_sha1,
1449 .ecdsa_sha1,
1450 => crypto.hash.Sha1,
1451 else => @compileError("bad scheme"),
1452 };
1453}
1454
1455const CertificatePublicKey = struct {
1456 algo: Certificate.AlgorithmCategory,
1457 buf: [600]u8,
1458 len: u16,
1459
1460 fn init(
1461 cert_pub_key: *CertificatePublicKey,
1462 algo: Certificate.AlgorithmCategory,
1463 pub_key: []const u8,
1464 ) error{CertificatePublicKeyInvalid}!void {
1465 if (pub_key.len > cert_pub_key.buf.len) return error.CertificatePublicKeyInvalid;
1466 cert_pub_key.algo = algo;
1467 @memcpy(cert_pub_key.buf[0..pub_key.len], pub_key);
1468 cert_pub_key.len = @intCast(pub_key.len);
1469 }
1470
1471 const VerifyError = error{ TlsDecodeError, TlsBadSignatureScheme, InvalidEncoding } ||
1472 // ecdsa
1473 crypto.errors.EncodingError ||
1474 crypto.errors.NotSquareError ||
1475 crypto.errors.NonCanonicalError ||
1476 SchemeEcdsa(.ecdsa_secp256r1_sha256).Signature.VerifyError ||
1477 SchemeEcdsa(.ecdsa_secp384r1_sha384).Signature.VerifyError ||
1478 // rsa
1479 error{TlsBadRsaSignatureBitCount} ||
1480 Certificate.rsa.PublicKey.ParseDerError ||
1481 Certificate.rsa.PublicKey.FromBytesError ||
1482 Certificate.rsa.PSSSignature.VerifyError ||
1483 Certificate.rsa.PKCS1v1_5Signature.VerifyError ||
1484 // eddsa
1485 SchemeEddsa(.ed25519).Signature.VerifyError;
1486
1487 fn verifySignature(
1488 cert_pub_key: *const CertificatePublicKey,
1489 sigd: *tls.Decoder,
1490 msg: []const []const u8,
1491 ) VerifyError!void {
1492 const pub_key = cert_pub_key.buf[0..cert_pub_key.len];
1493
1494 try sigd.ensure(2 + 2);
1495 const scheme = sigd.decode(tls.SignatureScheme);
1496 const sig_len = sigd.decode(u16);
1497 try sigd.ensure(sig_len);
1498 const encoded_sig = sigd.slice(sig_len);
1499
1500 if (cert_pub_key.algo != @as(Certificate.AlgorithmCategory, switch (scheme) {
1501 .ecdsa_secp256r1_sha256,
1502 .ecdsa_secp384r1_sha384,
1503 => .X9_62_id_ecPublicKey,
1504 .rsa_pkcs1_sha256,
1505 .rsa_pkcs1_sha384,
1506 .rsa_pkcs1_sha512,
1507 .rsa_pss_rsae_sha256,
1508 .rsa_pss_rsae_sha384,
1509 .rsa_pss_rsae_sha512,
1510 .rsa_pkcs1_sha1,
1511 => .rsaEncryption,
1512 .rsa_pss_pss_sha256,
1513 .rsa_pss_pss_sha384,
1514 .rsa_pss_pss_sha512,
1515 => .rsassa_pss,
1516 else => return error.TlsBadSignatureScheme,
1517 })) return error.TlsBadSignatureScheme;
1518
1519 switch (scheme) {
1520 inline .ecdsa_secp256r1_sha256,
1521 .ecdsa_secp384r1_sha384,
1522 => |comptime_scheme| {
1523 const Ecdsa = SchemeEcdsa(comptime_scheme);
1524 const sig = try Ecdsa.Signature.fromDer(encoded_sig);
1525 const key = try Ecdsa.PublicKey.fromSec1(pub_key);
1526 var ver = try sig.verifier(key);
1527 for (msg) |part| ver.update(part);
1528 try ver.verify();
1529 },
1530 inline .rsa_pkcs1_sha256,
1531 .rsa_pkcs1_sha384,
1532 .rsa_pkcs1_sha512,
1533 .rsa_pss_rsae_sha256,
1534 .rsa_pss_rsae_sha384,
1535 .rsa_pss_rsae_sha512,
1536 .rsa_pss_pss_sha256,
1537 .rsa_pss_pss_sha384,
1538 .rsa_pss_pss_sha512,
1539 .rsa_pkcs1_sha1,
1540 => |comptime_scheme| {
1541 const RsaSignature = SchemeRsa(comptime_scheme);
1542 const Hash = SchemeHash(comptime_scheme);
1543 const PublicKey = Certificate.rsa.PublicKey;
1544 const components = try PublicKey.parseDer(pub_key);
1545 const exponent = components.exponent;
1546 const modulus = components.modulus;
1547 switch (modulus.len) {
1548 inline 128, 256, 384, 512 => |modulus_len| {
1549 const key: PublicKey = try .fromBytes(exponent, modulus);
1550 const sig = RsaSignature.fromBytes(modulus_len, encoded_sig);
1551 try RsaSignature.concatVerify(modulus_len, sig, msg, key, Hash);
1552 },
1553 else => return error.TlsBadRsaSignatureBitCount,
1554 }
1555 },
1556 inline .ed25519 => |comptime_scheme| {
1557 const Eddsa = SchemeEddsa(comptime_scheme);
1558 if (encoded_sig.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;
1559 const sig = Eddsa.Signature.fromBytes(encoded_sig[0..Eddsa.Signature.encoded_length].*);
1560 if (pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;
1561 const key = try Eddsa.PublicKey.fromBytes(pub_key[0..Eddsa.PublicKey.encoded_length].*);
1562 var ver = try sig.verifier(key);
1563 for (msg) |part| ver.update(part);
1564 try ver.verify();
1565 },
1566 else => unreachable,
1567 }
1568 }
1569};
1570
1571/// The priority order here is chosen based on what crypto algorithms Zig has
1572/// available in the standard library as well as what is faster. Following are
1573/// a few data points on the relative performance of these algorithms.
1574///
1575/// Measurement taken with 0.11.0-dev.810+c2f5848fe
1576/// on x86_64-linux Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz:
1577/// zig run .lib/std/crypto/benchmark.zig -OReleaseFast
1578/// aegis-128l: 15382 MiB/s
1579/// aegis-256: 9553 MiB/s
1580/// aes128-gcm: 3721 MiB/s
1581/// aes256-gcm: 3010 MiB/s
1582/// chacha20Poly1305: 597 MiB/s
1583///
1584/// Measurement taken with 0.11.0-dev.810+c2f5848fe
1585/// on x86_64-linux Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz:
1586/// zig run .lib/std/crypto/benchmark.zig -OReleaseFast -mcpu=baseline
1587/// aegis-128l: 629 MiB/s
1588/// chacha20Poly1305: 529 MiB/s
1589/// aegis-256: 461 MiB/s
1590/// aes128-gcm: 138 MiB/s
1591/// aes256-gcm: 120 MiB/s
1592const cipher_suites = if (crypto.core.aes.has_hardware_support)
1593 array(u16, tls.CipherSuite, .{
1594 .AEGIS_128L_SHA256,
1595 .AEGIS_256_SHA512,
1596 .AES_128_GCM_SHA256,
1597 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
1598 .AES_256_GCM_SHA384,
1599 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1600 .CHACHA20_POLY1305_SHA256,
1601 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1602 })
1603else
1604 array(u16, tls.CipherSuite, .{
1605 .CHACHA20_POLY1305_SHA256,
1606 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1607 .AEGIS_128L_SHA256,
1608 .AEGIS_256_SHA512,
1609 .AES_128_GCM_SHA256,
1610 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
1611 .AES_256_GCM_SHA384,
1612 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1613 });