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