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    });