master
  1//! Plaintext:
  2//! * type: ContentType
  3//! * legacy_record_version: u16 = 0x0303,
  4//! * length: u16,
  5//!   - The length (in bytes) of the following TLSPlaintext.fragment.  The
  6//!     length MUST NOT exceed 2^14 bytes.
  7//! * fragment: opaque
  8//!   - the data being transmitted
  9//!
 10//! Ciphertext
 11//! * ContentType opaque_type = application_data; /* 23 */
 12//! * ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
 13//! * uint16 length;
 14//! * opaque encrypted_record[TLSCiphertext.length];
 15//!
 16//! Handshake:
 17//! * type: HandshakeType
 18//! * length: u24
 19//! * data: opaque
 20//!
 21//! ServerHello:
 22//! * ProtocolVersion legacy_version = 0x0303;
 23//! * Random random;
 24//! * opaque legacy_session_id_echo<0..32>;
 25//! * CipherSuite cipher_suite;
 26//! * uint8 legacy_compression_method = 0;
 27//! * Extension extensions<6..2^16-1>;
 28//!
 29//! Extension:
 30//! * ExtensionType extension_type;
 31//! * opaque extension_data<0..2^16-1>;
 32
 33const std = @import("../std.zig");
 34const Tls = @This();
 35const net = std.net;
 36const mem = std.mem;
 37const crypto = std.crypto;
 38const assert = std.debug.assert;
 39
 40pub const Client = @import("tls/Client.zig");
 41
 42pub const record_header_len = 5;
 43pub const max_ciphertext_inner_record_len = 1 << 14;
 44pub const max_ciphertext_len = max_ciphertext_inner_record_len + 256;
 45pub const max_ciphertext_record_len = max_ciphertext_len + record_header_len;
 46pub const hello_retry_request_sequence = [32]u8{
 47    0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
 48    0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
 49};
 50
 51pub const close_notify_alert = [_]u8{
 52    @intFromEnum(Alert.Level.warning),
 53    @intFromEnum(Alert.Description.close_notify),
 54};
 55
 56pub const ProtocolVersion = enum(u16) {
 57    tls_1_0 = 0x0301,
 58    tls_1_1 = 0x0302,
 59    tls_1_2 = 0x0303,
 60    tls_1_3 = 0x0304,
 61    _,
 62};
 63
 64pub const ContentType = enum(u8) {
 65    invalid = 0,
 66    change_cipher_spec = 20,
 67    alert = 21,
 68    handshake = 22,
 69    application_data = 23,
 70    _,
 71};
 72
 73pub const HandshakeType = enum(u8) {
 74    hello_request = 0,
 75    client_hello = 1,
 76    server_hello = 2,
 77    new_session_ticket = 4,
 78    end_of_early_data = 5,
 79    encrypted_extensions = 8,
 80    certificate = 11,
 81    server_key_exchange = 12,
 82    certificate_request = 13,
 83    server_hello_done = 14,
 84    certificate_verify = 15,
 85    client_key_exchange = 16,
 86    finished = 20,
 87    key_update = 24,
 88    message_hash = 254,
 89    _,
 90};
 91
 92pub const ExtensionType = enum(u16) {
 93    /// RFC 6066
 94    server_name = 0,
 95    /// RFC 6066
 96    max_fragment_length = 1,
 97    /// RFC 6066
 98    status_request = 5,
 99    /// RFC 8422, 7919
100    supported_groups = 10,
101    /// RFC 8446
102    signature_algorithms = 13,
103    /// RFC 5764
104    use_srtp = 14,
105    /// RFC 6520
106    heartbeat = 15,
107    /// RFC 7301
108    application_layer_protocol_negotiation = 16,
109    /// RFC 6962
110    signed_certificate_timestamp = 18,
111    /// RFC 7250
112    client_certificate_type = 19,
113    /// RFC 7250
114    server_certificate_type = 20,
115    /// RFC 7685
116    padding = 21,
117    /// RFC 8446
118    pre_shared_key = 41,
119    /// RFC 8446
120    early_data = 42,
121    /// RFC 8446
122    supported_versions = 43,
123    /// RFC 8446
124    cookie = 44,
125    /// RFC 8446
126    psk_key_exchange_modes = 45,
127    /// RFC 8446
128    certificate_authorities = 47,
129    /// RFC 8446
130    oid_filters = 48,
131    /// RFC 8446
132    post_handshake_auth = 49,
133    /// RFC 8446
134    signature_algorithms_cert = 50,
135    /// RFC 8446
136    key_share = 51,
137
138    _,
139};
140
141pub const Alert = struct {
142    level: Level,
143    description: Description,
144
145    pub const Level = enum(u8) {
146        warning = 1,
147        fatal = 2,
148        _,
149    };
150
151    pub const Description = enum(u8) {
152        pub const Error = error{
153            TlsAlertUnexpectedMessage,
154            TlsAlertBadRecordMac,
155            TlsAlertRecordOverflow,
156            TlsAlertHandshakeFailure,
157            TlsAlertBadCertificate,
158            TlsAlertUnsupportedCertificate,
159            TlsAlertCertificateRevoked,
160            TlsAlertCertificateExpired,
161            TlsAlertCertificateUnknown,
162            TlsAlertIllegalParameter,
163            TlsAlertUnknownCa,
164            TlsAlertAccessDenied,
165            TlsAlertDecodeError,
166            TlsAlertDecryptError,
167            TlsAlertProtocolVersion,
168            TlsAlertInsufficientSecurity,
169            TlsAlertInternalError,
170            TlsAlertInappropriateFallback,
171            TlsAlertMissingExtension,
172            TlsAlertUnsupportedExtension,
173            TlsAlertUnrecognizedName,
174            TlsAlertBadCertificateStatusResponse,
175            TlsAlertUnknownPskIdentity,
176            TlsAlertCertificateRequired,
177            TlsAlertNoApplicationProtocol,
178            TlsAlertUnknown,
179        };
180
181        close_notify = 0,
182        unexpected_message = 10,
183        bad_record_mac = 20,
184        record_overflow = 22,
185        handshake_failure = 40,
186        bad_certificate = 42,
187        unsupported_certificate = 43,
188        certificate_revoked = 44,
189        certificate_expired = 45,
190        certificate_unknown = 46,
191        illegal_parameter = 47,
192        unknown_ca = 48,
193        access_denied = 49,
194        decode_error = 50,
195        decrypt_error = 51,
196        protocol_version = 70,
197        insufficient_security = 71,
198        internal_error = 80,
199        inappropriate_fallback = 86,
200        user_canceled = 90,
201        missing_extension = 109,
202        unsupported_extension = 110,
203        unrecognized_name = 112,
204        bad_certificate_status_response = 113,
205        unknown_psk_identity = 115,
206        certificate_required = 116,
207        no_application_protocol = 120,
208        _,
209
210        pub fn toError(description: Description) Error!void {
211            switch (description) {
212                .close_notify => {}, // not an error
213                .unexpected_message => return error.TlsAlertUnexpectedMessage,
214                .bad_record_mac => return error.TlsAlertBadRecordMac,
215                .record_overflow => return error.TlsAlertRecordOverflow,
216                .handshake_failure => return error.TlsAlertHandshakeFailure,
217                .bad_certificate => return error.TlsAlertBadCertificate,
218                .unsupported_certificate => return error.TlsAlertUnsupportedCertificate,
219                .certificate_revoked => return error.TlsAlertCertificateRevoked,
220                .certificate_expired => return error.TlsAlertCertificateExpired,
221                .certificate_unknown => return error.TlsAlertCertificateUnknown,
222                .illegal_parameter => return error.TlsAlertIllegalParameter,
223                .unknown_ca => return error.TlsAlertUnknownCa,
224                .access_denied => return error.TlsAlertAccessDenied,
225                .decode_error => return error.TlsAlertDecodeError,
226                .decrypt_error => return error.TlsAlertDecryptError,
227                .protocol_version => return error.TlsAlertProtocolVersion,
228                .insufficient_security => return error.TlsAlertInsufficientSecurity,
229                .internal_error => return error.TlsAlertInternalError,
230                .inappropriate_fallback => return error.TlsAlertInappropriateFallback,
231                .user_canceled => {}, // not an error
232                .missing_extension => return error.TlsAlertMissingExtension,
233                .unsupported_extension => return error.TlsAlertUnsupportedExtension,
234                .unrecognized_name => return error.TlsAlertUnrecognizedName,
235                .bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse,
236                .unknown_psk_identity => return error.TlsAlertUnknownPskIdentity,
237                .certificate_required => return error.TlsAlertCertificateRequired,
238                .no_application_protocol => return error.TlsAlertNoApplicationProtocol,
239                _ => return error.TlsAlertUnknown,
240            }
241        }
242    };
243};
244
245pub const SignatureScheme = enum(u16) {
246    // RSASSA-PKCS1-v1_5 algorithms
247    rsa_pkcs1_sha256 = 0x0401,
248    rsa_pkcs1_sha384 = 0x0501,
249    rsa_pkcs1_sha512 = 0x0601,
250
251    // ECDSA algorithms
252    ecdsa_secp256r1_sha256 = 0x0403,
253    ecdsa_secp384r1_sha384 = 0x0503,
254    ecdsa_secp521r1_sha512 = 0x0603,
255
256    // RSASSA-PSS algorithms with public key OID rsaEncryption
257    rsa_pss_rsae_sha256 = 0x0804,
258    rsa_pss_rsae_sha384 = 0x0805,
259    rsa_pss_rsae_sha512 = 0x0806,
260
261    // EdDSA algorithms
262    ed25519 = 0x0807,
263    ed448 = 0x0808,
264
265    // RSASSA-PSS algorithms with public key OID RSASSA-PSS
266    rsa_pss_pss_sha256 = 0x0809,
267    rsa_pss_pss_sha384 = 0x080a,
268    rsa_pss_pss_sha512 = 0x080b,
269
270    // Legacy algorithms
271    rsa_pkcs1_sha1 = 0x0201,
272    ecdsa_sha1 = 0x0203,
273
274    ecdsa_brainpoolP256r1tls13_sha256 = 0x081a,
275    ecdsa_brainpoolP384r1tls13_sha384 = 0x081b,
276    ecdsa_brainpoolP512r1tls13_sha512 = 0x081c,
277
278    rsa_sha224 = 0x0301,
279    dsa_sha224 = 0x0302,
280    ecdsa_sha224 = 0x0303,
281    dsa_sha256 = 0x0402,
282    dsa_sha384 = 0x0502,
283    dsa_sha512 = 0x0602,
284
285    _,
286};
287
288pub const NamedGroup = enum(u16) {
289    // Elliptic Curve Groups (ECDHE)
290    secp256r1 = 0x0017,
291    secp384r1 = 0x0018,
292    secp521r1 = 0x0019,
293    x25519 = 0x001D,
294    x448 = 0x001E,
295
296    // Finite Field Groups (DHE)
297    ffdhe2048 = 0x0100,
298    ffdhe3072 = 0x0101,
299    ffdhe4096 = 0x0102,
300    ffdhe6144 = 0x0103,
301    ffdhe8192 = 0x0104,
302
303    // Hybrid post-quantum key agreements
304    secp256r1_ml_kem256 = 0x11EB,
305    x25519_ml_kem768 = 0x11EC,
306
307    _,
308};
309
310pub const PskKeyExchangeMode = enum(u8) {
311    psk_ke = 0,
312    psk_dhe_ke = 1,
313    _,
314};
315
316pub const CipherSuite = enum(u16) {
317    RSA_WITH_AES_128_CBC_SHA = 0x002F,
318    DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
319    RSA_WITH_AES_256_CBC_SHA = 0x0035,
320    DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
321    RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
322    RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
323    DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
324    DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
325    RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
326    RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
327    DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
328    DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
329    EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
330
331    AES_128_GCM_SHA256 = 0x1301,
332    AES_256_GCM_SHA384 = 0x1302,
333    CHACHA20_POLY1305_SHA256 = 0x1303,
334    AES_128_CCM_SHA256 = 0x1304,
335    AES_128_CCM_8_SHA256 = 0x1305,
336    AEGIS_256_SHA512 = 0x1306,
337    AEGIS_128L_SHA256 = 0x1307,
338
339    ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
340    ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
341    ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
342    ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
343    ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
344    ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
345    ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
346    ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
347    ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
348    ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
349    ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
350    ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
351
352    ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8,
353    ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9,
354    DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA,
355
356    _,
357
358    pub const With = enum {
359        AES_128_CBC_SHA,
360        AES_256_CBC_SHA,
361        AES_128_CBC_SHA256,
362        AES_256_CBC_SHA256,
363        AES_256_CBC_SHA384,
364
365        AES_128_GCM_SHA256,
366        AES_256_GCM_SHA384,
367
368        CHACHA20_POLY1305_SHA256,
369
370        AES_128_CCM_SHA256,
371        AES_128_CCM_8_SHA256,
372
373        AEGIS_256_SHA512,
374        AEGIS_128L_SHA256,
375    };
376
377    pub fn with(cipher_suite: CipherSuite) With {
378        return switch (cipher_suite) {
379            .RSA_WITH_AES_128_CBC_SHA,
380            .DHE_RSA_WITH_AES_128_CBC_SHA,
381            .ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
382            .ECDHE_RSA_WITH_AES_128_CBC_SHA,
383            => .AES_128_CBC_SHA,
384            .RSA_WITH_AES_256_CBC_SHA,
385            .DHE_RSA_WITH_AES_256_CBC_SHA,
386            .ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
387            .ECDHE_RSA_WITH_AES_256_CBC_SHA,
388            => .AES_256_CBC_SHA,
389            .RSA_WITH_AES_128_CBC_SHA256,
390            .DHE_RSA_WITH_AES_128_CBC_SHA256,
391            .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
392            .ECDHE_RSA_WITH_AES_128_CBC_SHA256,
393            => .AES_128_CBC_SHA256,
394            .RSA_WITH_AES_256_CBC_SHA256,
395            .DHE_RSA_WITH_AES_256_CBC_SHA256,
396            => .AES_256_CBC_SHA256,
397            .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
398            .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
399            => .AES_256_CBC_SHA384,
400
401            .RSA_WITH_AES_128_GCM_SHA256,
402            .DHE_RSA_WITH_AES_128_GCM_SHA256,
403            .AES_128_GCM_SHA256,
404            .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
405            .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
406            => .AES_128_GCM_SHA256,
407            .RSA_WITH_AES_256_GCM_SHA384,
408            .DHE_RSA_WITH_AES_256_GCM_SHA384,
409            .AES_256_GCM_SHA384,
410            .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
411            .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
412            => .AES_256_GCM_SHA384,
413
414            .CHACHA20_POLY1305_SHA256,
415            .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
416            .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
417            .DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
418            => .CHACHA20_POLY1305_SHA256,
419
420            .AES_128_CCM_SHA256 => .AES_128_CCM_SHA256,
421            .AES_128_CCM_8_SHA256 => .AES_128_CCM_8_SHA256,
422
423            .AEGIS_256_SHA512 => .AEGIS_256_SHA512,
424            .AEGIS_128L_SHA256 => .AEGIS_128L_SHA256,
425
426            .EMPTY_RENEGOTIATION_INFO_SCSV => unreachable,
427            _ => unreachable,
428        };
429    }
430};
431
432pub const CompressionMethod = enum(u8) {
433    null = 0,
434    _,
435};
436
437pub const CertificateType = enum(u8) {
438    X509 = 0,
439    RawPublicKey = 2,
440    _,
441};
442
443pub const KeyUpdateRequest = enum(u8) {
444    update_not_requested = 0,
445    update_requested = 1,
446    _,
447};
448
449pub const ChangeCipherSpecType = enum(u8) {
450    change_cipher_spec = 1,
451    _,
452};
453
454pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
455    return struct {
456        pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length);
457
458        transcript_hash: A.Hash,
459        version: union {
460            tls_1_2: struct {
461                expected_server_verify_data: [A.verify_data_length]u8,
462                app_cipher: A.Tls_1_2,
463            },
464            tls_1_3: struct {
465                handshake_secret: [A.Hkdf.prk_length]u8,
466                master_secret: [A.Hkdf.prk_length]u8,
467                client_handshake_key: [A.AEAD.key_length]u8,
468                server_handshake_key: [A.AEAD.key_length]u8,
469                client_finished_key: [A.Hmac.key_length]u8,
470                server_finished_key: [A.Hmac.key_length]u8,
471                client_handshake_iv: [A.AEAD.nonce_length]u8,
472                server_handshake_iv: [A.AEAD.nonce_length]u8,
473            },
474        },
475    };
476}
477
478pub const HandshakeCipher = union(enum) {
479    AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
480    AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
481    CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
482    AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
483    AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
484};
485
486pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
487    return union {
488        pub const AEAD = AeadType;
489        pub const Hash = HashType;
490        pub const Hmac = crypto.auth.hmac.Hmac(Hash);
491        pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
492
493        pub const enc_key_length = AEAD.key_length;
494        pub const fixed_iv_length = AEAD.nonce_length - explicit_iv_length;
495        pub const record_iv_length = explicit_iv_length;
496        pub const mac_length = AEAD.tag_length;
497        pub const mac_key_length = Hmac.key_length_min;
498        pub const verify_data_length = 12;
499
500        tls_1_2: Tls_1_2,
501        tls_1_3: Tls_1_3,
502
503        pub const Tls_1_2 = extern struct {
504            client_write_MAC_key: [mac_key_length]u8,
505            server_write_MAC_key: [mac_key_length]u8,
506            client_write_key: [enc_key_length]u8,
507            server_write_key: [enc_key_length]u8,
508            client_write_IV: [fixed_iv_length]u8,
509            server_write_IV: [fixed_iv_length]u8,
510            // non-standard entropy
511            client_salt: [record_iv_length]u8,
512        };
513
514        pub const Tls_1_3 = struct {
515            client_secret: [Hash.digest_length]u8,
516            server_secret: [Hash.digest_length]u8,
517            client_key: [AEAD.key_length]u8,
518            server_key: [AEAD.key_length]u8,
519            client_iv: [AEAD.nonce_length]u8,
520            server_iv: [AEAD.nonce_length]u8,
521        };
522    };
523}
524
525/// Encryption parameters for application traffic.
526pub const ApplicationCipher = union(enum) {
527    AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
528    AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
529    CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
530    AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
531    AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
532};
533
534pub fn hmacExpandLabel(
535    comptime Hmac: type,
536    secret: []const u8,
537    label_then_seed: []const []const u8,
538    comptime len: usize,
539) [len]u8 {
540    const initial_hmac: Hmac = .init(secret);
541    var a: [Hmac.mac_length]u8 = undefined;
542    var result: [std.mem.alignForwardAnyAlign(usize, len, Hmac.mac_length)]u8 = undefined;
543    var index: usize = 0;
544    while (index < result.len) : (index += Hmac.mac_length) {
545        var a_hmac = initial_hmac;
546        if (index > 0) a_hmac.update(&a) else for (label_then_seed) |part| a_hmac.update(part);
547        a_hmac.final(&a);
548
549        var result_hmac = initial_hmac;
550        result_hmac.update(&a);
551        for (label_then_seed) |part| result_hmac.update(part);
552        result_hmac.final(result[index..][0..Hmac.mac_length]);
553    }
554    return result[0..len].*;
555}
556
557pub fn hkdfExpandLabel(
558    comptime Hkdf: type,
559    key: [Hkdf.prk_length]u8,
560    label: []const u8,
561    context: []const u8,
562    comptime len: usize,
563) [len]u8 {
564    const max_label_len = 255;
565    const max_context_len = 255;
566    const tls13 = "tls13 ";
567    var buf: [2 + 1 + tls13.len + max_label_len + 1 + max_context_len]u8 = undefined;
568    mem.writeInt(u16, buf[0..2], len, .big);
569    buf[2] = @as(u8, @intCast(tls13.len + label.len));
570    buf[3..][0..tls13.len].* = tls13.*;
571    var i: usize = 3 + tls13.len;
572    @memcpy(buf[i..][0..label.len], label);
573    i += label.len;
574    buf[i] = @as(u8, @intCast(context.len));
575    i += 1;
576    @memcpy(buf[i..][0..context.len], context);
577    i += context.len;
578
579    var result: [len]u8 = undefined;
580    Hkdf.expand(&result, buf[0..i], key);
581    return result;
582}
583
584pub fn emptyHash(comptime Hash: type) [Hash.digest_length]u8 {
585    var result: [Hash.digest_length]u8 = undefined;
586    Hash.hash(&.{}, &result, .{});
587    return result;
588}
589
590pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8) [Hmac.mac_length]u8 {
591    var result: [Hmac.mac_length]u8 = undefined;
592    Hmac.create(&result, message, &key);
593    return result;
594}
595
596pub fn extension(et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 {
597    return int(u16, @intFromEnum(et)) ++ array(u16, u8, bytes);
598}
599
600pub fn array(
601    comptime Len: type,
602    comptime Elem: type,
603    elems: anytype,
604) [@divExact(@bitSizeOf(Len), 8) + @divExact(@bitSizeOf(Elem), 8) * elems.len]u8 {
605    const len_size = @divExact(@bitSizeOf(Len), 8);
606    const elem_size = @divExact(@bitSizeOf(Elem), 8);
607    var arr: [len_size + elem_size * elems.len]u8 = undefined;
608    std.mem.writeInt(Len, arr[0..len_size], @intCast(elem_size * elems.len), .big);
609    const ElemInt = @Int(.unsigned, @bitSizeOf(Elem));
610    for (0.., @as([elems.len]Elem, elems)) |index, elem| {
611        std.mem.writeInt(
612            ElemInt,
613            arr[len_size + elem_size * index ..][0..elem_size],
614            switch (@typeInfo(Elem)) {
615                .int => @as(Elem, elem),
616                .@"enum" => @intFromEnum(@as(Elem, elem)),
617                else => @bitCast(@as(Elem, elem)),
618            },
619            .big,
620        );
621    }
622    return arr;
623}
624
625pub fn int(comptime Int: type, val: Int) [@divExact(@bitSizeOf(Int), 8)]u8 {
626    var arr: [@divExact(@bitSizeOf(Int), 8)]u8 = undefined;
627    std.mem.writeInt(Int, &arr, val, .big);
628    return arr;
629}
630
631/// An abstraction to ensure that protocol-parsing code does not perform an
632/// out-of-bounds read.
633pub const Decoder = struct {
634    buf: []u8,
635    /// Points to the next byte in buffer that will be decoded.
636    idx: usize = 0,
637    /// Up to this point in `buf` we have already checked that `cap` is greater than it.
638    our_end: usize = 0,
639    /// Beyond this point in `buf` is extra tag-along bytes beyond the amount we
640    /// requested with `readAtLeast`.
641    their_end: usize = 0,
642    /// Points to the end within buffer that has been filled. Beyond this point
643    /// in buf is undefined bytes.
644    cap: usize = 0,
645    /// Debug helper to prevent illegal calls to read functions.
646    disable_reads: bool = false,
647
648    pub fn fromTheirSlice(buf: []u8) Decoder {
649        return .{
650            .buf = buf,
651            .their_end = buf.len,
652            .cap = buf.len,
653            .disable_reads = true,
654        };
655    }
656
657    /// Use this function to increase `their_end`.
658    pub fn readAtLeast(d: *Decoder, stream: *std.Io.Reader, their_amt: usize) !void {
659        assert(!d.disable_reads);
660        const existing_amt = d.cap - d.idx;
661        d.their_end = d.idx + their_amt;
662        if (their_amt <= existing_amt) return;
663        const request_amt = their_amt - existing_amt;
664        const dest = d.buf[d.cap..];
665        if (request_amt > dest.len) return error.TlsRecordOverflow;
666        stream.readSlice(dest[0..request_amt]) catch |err| switch (err) {
667            error.EndOfStream => return error.TlsConnectionTruncated,
668            error.ReadFailed => return error.ReadFailed,
669        };
670        d.cap += request_amt;
671    }
672
673    /// Same as `readAtLeast` but also increases `our_end` by exactly `our_amt`.
674    /// Use when `our_amt` is calculated by us, not by them.
675    pub fn readAtLeastOurAmt(d: *Decoder, stream: *std.Io.Reader, our_amt: usize) !void {
676        assert(!d.disable_reads);
677        try readAtLeast(d, stream, our_amt);
678        d.our_end = d.idx + our_amt;
679    }
680
681    /// Use this function to increase `our_end`.
682    /// This should always be called with an amount provided by us, not them.
683    pub fn ensure(d: *Decoder, amt: usize) !void {
684        d.our_end = @max(d.idx + amt, d.our_end);
685        if (d.our_end > d.their_end) return error.TlsDecodeError;
686    }
687
688    /// Use this function to increase `idx`.
689    pub fn decode(d: *Decoder, comptime T: type) T {
690        switch (@typeInfo(T)) {
691            .int => |info| switch (info.bits) {
692                8 => {
693                    skip(d, 1);
694                    return d.buf[d.idx - 1];
695                },
696                16 => {
697                    skip(d, 2);
698                    const b0: u16 = d.buf[d.idx - 2];
699                    const b1: u16 = d.buf[d.idx - 1];
700                    return (b0 << 8) | b1;
701                },
702                24 => {
703                    skip(d, 3);
704                    const b0: u24 = d.buf[d.idx - 3];
705                    const b1: u24 = d.buf[d.idx - 2];
706                    const b2: u24 = d.buf[d.idx - 1];
707                    return (b0 << 16) | (b1 << 8) | b2;
708                },
709                else => @compileError("unsupported int type: " ++ @typeName(T)),
710            },
711            .@"enum" => |info| {
712                if (info.is_exhaustive) @compileError("exhaustive enum cannot be used");
713                return @enumFromInt(d.decode(info.tag_type));
714            },
715            else => @compileError("unsupported type: " ++ @typeName(T)),
716        }
717    }
718
719    /// Use this function to increase `idx`.
720    pub fn array(d: *Decoder, comptime len: usize) *[len]u8 {
721        skip(d, len);
722        return d.buf[d.idx - len ..][0..len];
723    }
724
725    /// Use this function to increase `idx`.
726    pub fn slice(d: *Decoder, len: usize) []u8 {
727        skip(d, len);
728        return d.buf[d.idx - len ..][0..len];
729    }
730
731    /// Use this function to increase `idx`.
732    pub fn skip(d: *Decoder, amt: usize) void {
733        d.idx += amt;
734        assert(d.idx <= d.our_end); // insufficient ensured bytes
735    }
736
737    pub fn eof(d: Decoder) bool {
738        assert(d.our_end <= d.their_end);
739        assert(d.idx <= d.our_end);
740        return d.idx == d.their_end;
741    }
742
743    /// Provide the length they claim, and receive a sub-decoder specific to that slice.
744    /// The parent decoder is advanced to the end.
745    pub fn sub(d: *Decoder, their_len: usize) !Decoder {
746        const end = d.idx + their_len;
747        if (end > d.their_end) return error.TlsDecodeError;
748        const sub_buf = d.buf[d.idx..end];
749        d.idx = end;
750        d.our_end = end;
751        return fromTheirSlice(sub_buf);
752    }
753
754    pub fn rest(d: Decoder) []u8 {
755        return d.buf[d.idx..d.cap];
756    }
757};