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