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