|
//! Plaintext: //! * type: ContentType //! * legacy_record_version: u16 = 0x0303, //! * length: u16, //! - The length (in bytes) of the following TLSPlaintext.fragment. The //! length MUST NOT exceed 2^14 bytes. //! * fragment: opaque //! - the data being transmitted //! //! Ciphertext //! * ContentType opaque_type = application_data; /* 23 */ //! * ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */ //! * uint16 length; //! * opaque encrypted_record[TLSCiphertext.length]; //! //! Handshake: //! * type: HandshakeType //! * length: u24 //! * data: opaque //! //! ServerHello: //! * ProtocolVersion legacy_version = 0x0303; //! * Random random; //! * opaque legacy_session_id_echo<0..32>; //! * CipherSuite cipher_suite; //! * uint8 legacy_compression_method = 0; //! * Extension extensions<6..2^16-1>; //! //! Extension: //! * ExtensionType extension_type; //! * opaque extension_data<0..2^16-1>; |
Clienttls/Client.zigRFC 6066 |
const std = @import("../std.zig"); const Tls = @This(); const net = std.net; const mem = std.mem; const crypto = std.crypto; const assert = std.debug.assert; |
record_header_lenRFC 6066 |
pub const Client = @import("tls/Client.zig"); |
max_ciphertext_inner_record_lenRFC 6066 |
pub const record_header_len = 5; pub const max_ciphertext_inner_record_len = 1 << 14; |
max_ciphertext_lenRFC 8422, 7919 |
pub const max_ciphertext_len = max_ciphertext_inner_record_len + 256; |
max_ciphertext_record_lenRFC 8446 |
pub const max_ciphertext_record_len = max_ciphertext_len + record_header_len; |
hello_retry_request_sequenceRFC 5764 |
pub const hello_retry_request_sequence = [32]u8{ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, }; |
close_notify_alertRFC 6520 |
pub const close_notify_alert = [_]u8{ @intFromEnum(Alert.Level.warning), @intFromEnum(Alert.Description.close_notify), }; |
ProtocolVersionRFC 7301 |
pub const ProtocolVersion = enum(u16) { tls_1_0 = 0x0301, tls_1_1 = 0x0302, tls_1_2 = 0x0303, tls_1_3 = 0x0304, _, }; |
ContentTypeRFC 6962 |
pub const ContentType = enum(u8) { invalid = 0, change_cipher_spec = 20, alert = 21, handshake = 22, application_data = 23, _, }; |
HandshakeTypeRFC 7250 |
pub const HandshakeType = enum(u8) { hello_request = 0, client_hello = 1, server_hello = 2, new_session_ticket = 4, end_of_early_data = 5, encrypted_extensions = 8, certificate = 11, server_key_exchange = 12, certificate_request = 13, server_hello_done = 14, certificate_verify = 15, client_key_exchange = 16, finished = 20, key_update = 24, message_hash = 254, _, }; |
ExtensionTypeRFC 7250 |
pub const ExtensionType = enum(u16) { /// RFC 6066 server_name = 0, /// RFC 6066 max_fragment_length = 1, /// RFC 6066 status_request = 5, /// RFC 8422, 7919 supported_groups = 10, /// RFC 8446 signature_algorithms = 13, /// RFC 5764 use_srtp = 14, /// RFC 6520 heartbeat = 15, /// RFC 7301 application_layer_protocol_negotiation = 16, /// RFC 6962 signed_certificate_timestamp = 18, /// RFC 7250 client_certificate_type = 19, /// RFC 7250 server_certificate_type = 20, /// RFC 7685 padding = 21, /// RFC 8446 pre_shared_key = 41, /// RFC 8446 early_data = 42, /// RFC 8446 supported_versions = 43, /// RFC 8446 cookie = 44, /// RFC 8446 psk_key_exchange_modes = 45, /// RFC 8446 certificate_authorities = 47, /// RFC 8446 oid_filters = 48, /// RFC 8446 post_handshake_auth = 49, /// RFC 8446 signature_algorithms_cert = 50, /// RFC 8446 key_share = 51, |
AlertRFC 7685 |
_, }; |
LevelRFC 8446 |
pub const Alert = struct { level: Level, description: Description, |
DescriptionRFC 8446 |
pub const Level = enum(u8) { warning = 1, fatal = 2, _, }; |
ErrorRFC 8446 |
pub const Description = enum(u8) { pub const Error = error{ TlsAlertUnexpectedMessage, TlsAlertBadRecordMac, TlsAlertRecordOverflow, TlsAlertHandshakeFailure, TlsAlertBadCertificate, TlsAlertUnsupportedCertificate, TlsAlertCertificateRevoked, TlsAlertCertificateExpired, TlsAlertCertificateUnknown, TlsAlertIllegalParameter, TlsAlertUnknownCa, TlsAlertAccessDenied, TlsAlertDecodeError, TlsAlertDecryptError, TlsAlertProtocolVersion, TlsAlertInsufficientSecurity, TlsAlertInternalError, TlsAlertInappropriateFallback, TlsAlertMissingExtension, TlsAlertUnsupportedExtension, TlsAlertUnrecognizedName, TlsAlertBadCertificateStatusResponse, TlsAlertUnknownPskIdentity, TlsAlertCertificateRequired, TlsAlertNoApplicationProtocol, TlsAlertUnknown, }; |
toError()RFC 8446 |
close_notify = 0, unexpected_message = 10, bad_record_mac = 20, record_overflow = 22, handshake_failure = 40, bad_certificate = 42, unsupported_certificate = 43, certificate_revoked = 44, certificate_expired = 45, certificate_unknown = 46, illegal_parameter = 47, unknown_ca = 48, access_denied = 49, decode_error = 50, decrypt_error = 51, protocol_version = 70, insufficient_security = 71, internal_error = 80, inappropriate_fallback = 86, user_canceled = 90, missing_extension = 109, unsupported_extension = 110, unrecognized_name = 112, bad_certificate_status_response = 113, unknown_psk_identity = 115, certificate_required = 116, no_application_protocol = 120, _, |
SignatureSchemeRFC 8446 |
pub fn toError(description: Description) Error!void { switch (description) { .close_notify => {}, // not an error .unexpected_message => return error.TlsAlertUnexpectedMessage, .bad_record_mac => return error.TlsAlertBadRecordMac, .record_overflow => return error.TlsAlertRecordOverflow, .handshake_failure => return error.TlsAlertHandshakeFailure, .bad_certificate => return error.TlsAlertBadCertificate, .unsupported_certificate => return error.TlsAlertUnsupportedCertificate, .certificate_revoked => return error.TlsAlertCertificateRevoked, .certificate_expired => return error.TlsAlertCertificateExpired, .certificate_unknown => return error.TlsAlertCertificateUnknown, .illegal_parameter => return error.TlsAlertIllegalParameter, .unknown_ca => return error.TlsAlertUnknownCa, .access_denied => return error.TlsAlertAccessDenied, .decode_error => return error.TlsAlertDecodeError, .decrypt_error => return error.TlsAlertDecryptError, .protocol_version => return error.TlsAlertProtocolVersion, .insufficient_security => return error.TlsAlertInsufficientSecurity, .internal_error => return error.TlsAlertInternalError, .inappropriate_fallback => return error.TlsAlertInappropriateFallback, .user_canceled => {}, // not an error .missing_extension => return error.TlsAlertMissingExtension, .unsupported_extension => return error.TlsAlertUnsupportedExtension, .unrecognized_name => return error.TlsAlertUnrecognizedName, .bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse, .unknown_psk_identity => return error.TlsAlertUnknownPskIdentity, .certificate_required => return error.TlsAlertCertificateRequired, .no_application_protocol => return error.TlsAlertNoApplicationProtocol, _ => return error.TlsAlertUnknown, } } }; }; |
NamedGroupRFC 8446 |
pub const SignatureScheme = enum(u16) { // RSASSA-PKCS1-v1_5 algorithms rsa_pkcs1_sha256 = 0x0401, rsa_pkcs1_sha384 = 0x0501, rsa_pkcs1_sha512 = 0x0601, |
PskKeyExchangeModeRFC 8446 |
// ECDSA algorithms ecdsa_secp256r1_sha256 = 0x0403, ecdsa_secp384r1_sha384 = 0x0503, ecdsa_secp521r1_sha512 = 0x0603, |
CipherSuiteRFC 8446 |
// RSASSA-PSS algorithms with public key OID rsaEncryption rsa_pss_rsae_sha256 = 0x0804, rsa_pss_rsae_sha384 = 0x0805, rsa_pss_rsae_sha512 = 0x0806, |
WithRFC 8446 |
// EdDSA algorithms ed25519 = 0x0807, ed448 = 0x0808, |
with()RFC 8446 |
// RSASSA-PSS algorithms with public key OID RSASSA-PSS rsa_pss_pss_sha256 = 0x0809, rsa_pss_pss_sha384 = 0x080a, rsa_pss_pss_sha512 = 0x080b, |
CompressionMethodEncryption parameters for application traffic. |
// Legacy algorithms rsa_pkcs1_sha1 = 0x0201, ecdsa_sha1 = 0x0203, |
CertificateTypeAn abstraction to ensure that protocol-parsing code does not perform an out-of-bounds read. |
ecdsa_brainpoolP256r1tls13_sha256 = 0x081a, ecdsa_brainpoolP384r1tls13_sha384 = 0x081b, ecdsa_brainpoolP512r1tls13_sha512 = 0x081c, |
KeyUpdateRequestPoints to the next byte in buffer that will be decoded. |
rsa_sha224 = 0x0301, dsa_sha224 = 0x0302, ecdsa_sha224 = 0x0303, dsa_sha256 = 0x0402, dsa_sha384 = 0x0502, dsa_sha512 = 0x0602, |
ChangeCipherSpecTypeUp to this point in |
_, }; |
HandshakeCipherT()Beyond this point in |
pub const NamedGroup = enum(u16) { // Elliptic Curve Groups (ECDHE) secp256r1 = 0x0017, secp384r1 = 0x0018, secp521r1 = 0x0019, x25519 = 0x001D, x448 = 0x001E, |
APoints to the end within buffer that has been filled. Beyond this point in buf is undefined bytes. |
// Finite Field Groups (DHE) ffdhe2048 = 0x0100, ffdhe3072 = 0x0101, ffdhe4096 = 0x0102, ffdhe6144 = 0x0103, ffdhe8192 = 0x0104, |
HandshakeCipherDebug helper to prevent illegal calls to read functions. |
// Hybrid post-quantum key agreements secp256r1_ml_kem256 = 0x11EB, x25519_ml_kem768 = 0x11EC, |
ApplicationCipherT()Use this function to increase |
_, }; |
AEADSame as |
pub const PskKeyExchangeMode = enum(u8) { psk_ke = 0, psk_dhe_ke = 1, _, }; |
HashUse this function to increase |
pub const CipherSuite = enum(u16) { RSA_WITH_AES_128_CBC_SHA = 0x002F, DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033, RSA_WITH_AES_256_CBC_SHA = 0x0035, DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039, RSA_WITH_AES_128_CBC_SHA256 = 0x003C, RSA_WITH_AES_256_CBC_SHA256 = 0x003D, DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067, DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, RSA_WITH_AES_128_GCM_SHA256 = 0x009C, RSA_WITH_AES_256_GCM_SHA384 = 0x009D, DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, |
HmacUse this function to increase |
AES_128_GCM_SHA256 = 0x1301, AES_256_GCM_SHA384 = 0x1302, CHACHA20_POLY1305_SHA256 = 0x1303, AES_128_CCM_SHA256 = 0x1304, AES_128_CCM_8_SHA256 = 0x1305, AEGIS_256_SHA512 = 0x1306, AEGIS_128L_SHA256 = 0x1307, |
HkdfUse this function to increase |
ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C, ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, |
enc_key_lengthUse this function to increase |
ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8, ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9, DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA, |
fixed_iv_lengthUse this function to increase |
_, |
record_iv_lengthProvide the length they claim, and receive a sub-decoder specific to that slice. The parent decoder is advanced to the end. |
pub const With = enum { AES_128_CBC_SHA, AES_256_CBC_SHA, AES_128_CBC_SHA256, AES_256_CBC_SHA256, AES_256_CBC_SHA384, |
mac_length |
AES_128_GCM_SHA256, AES_256_GCM_SHA384, |
mac_key_length |
CHACHA20_POLY1305_SHA256, |
verify_data_length |
AES_128_CCM_SHA256, AES_128_CCM_8_SHA256, |
Tls_1_2 |
AEGIS_256_SHA512, AEGIS_128L_SHA256, }; |
Tls_1_3 |
pub fn with(cipher_suite: CipherSuite) With { return switch (cipher_suite) { .RSA_WITH_AES_128_CBC_SHA, .DHE_RSA_WITH_AES_128_CBC_SHA, .ECDHE_ECDSA_WITH_AES_128_CBC_SHA, .ECDHE_RSA_WITH_AES_128_CBC_SHA, => .AES_128_CBC_SHA, .RSA_WITH_AES_256_CBC_SHA, .DHE_RSA_WITH_AES_256_CBC_SHA, .ECDHE_ECDSA_WITH_AES_256_CBC_SHA, .ECDHE_RSA_WITH_AES_256_CBC_SHA, => .AES_256_CBC_SHA, .RSA_WITH_AES_128_CBC_SHA256, .DHE_RSA_WITH_AES_128_CBC_SHA256, .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, .ECDHE_RSA_WITH_AES_128_CBC_SHA256, => .AES_128_CBC_SHA256, .RSA_WITH_AES_256_CBC_SHA256, .DHE_RSA_WITH_AES_256_CBC_SHA256, => .AES_256_CBC_SHA256, .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, .ECDHE_RSA_WITH_AES_256_CBC_SHA384, => .AES_256_CBC_SHA384, |
ApplicationCipher |
.RSA_WITH_AES_128_GCM_SHA256, .DHE_RSA_WITH_AES_128_GCM_SHA256, .AES_128_GCM_SHA256, .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, .ECDHE_RSA_WITH_AES_128_GCM_SHA256, => .AES_128_GCM_SHA256, .RSA_WITH_AES_256_GCM_SHA384, .DHE_RSA_WITH_AES_256_GCM_SHA384, .AES_256_GCM_SHA384, .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, .ECDHE_RSA_WITH_AES_256_GCM_SHA384, => .AES_256_GCM_SHA384, |
hmacExpandLabel() |
.CHACHA20_POLY1305_SHA256, .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, .DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, => .CHACHA20_POLY1305_SHA256, |
hkdfExpandLabel() |
.AES_128_CCM_SHA256 => .AES_128_CCM_SHA256, .AES_128_CCM_8_SHA256 => .AES_128_CCM_8_SHA256, |
emptyHash() |
.AEGIS_256_SHA512 => .AEGIS_256_SHA512, .AEGIS_128L_SHA256 => .AEGIS_128L_SHA256, |
hmac() |
.EMPTY_RENEGOTIATION_INFO_SCSV => unreachable, _ => unreachable, }; } }; |
extension() |
pub const CompressionMethod = enum(u8) { null = 0, _, }; |
array() |
pub const CertificateType = enum(u8) { X509 = 0, RawPublicKey = 2, _, }; |
int() |
pub const KeyUpdateRequest = enum(u8) { update_not_requested = 0, update_requested = 1, _, }; |
Decoder |
pub const ChangeCipherSpecType = enum(u8) { change_cipher_spec = 1, _, }; |
fromTheirSlice() |
pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type { return struct { pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length); |
readAtLeast() |
transcript_hash: A.Hash, version: union { tls_1_2: struct { expected_server_verify_data: [A.verify_data_length]u8, app_cipher: A.Tls_1_2, }, tls_1_3: struct { handshake_secret: [A.Hkdf.prk_length]u8, master_secret: [A.Hkdf.prk_length]u8, client_handshake_key: [A.AEAD.key_length]u8, server_handshake_key: [A.AEAD.key_length]u8, client_finished_key: [A.Hmac.key_length]u8, server_finished_key: [A.Hmac.key_length]u8, client_handshake_iv: [A.AEAD.nonce_length]u8, server_handshake_iv: [A.AEAD.nonce_length]u8, }, }, }; } |
readAtLeastOurAmt() |
pub const HandshakeCipher = union(enum) { AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8), AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8), CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0), AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0), AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0), }; |
ensure() |
pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type { return union { pub const AEAD = AeadType; pub const Hash = HashType; pub const Hmac = crypto.auth.hmac.Hmac(Hash); pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac); |
decode() |
pub const enc_key_length = AEAD.key_length; pub const fixed_iv_length = AEAD.nonce_length - explicit_iv_length; pub const record_iv_length = explicit_iv_length; pub const mac_length = AEAD.tag_length; pub const mac_key_length = Hmac.key_length_min; pub const verify_data_length = 12; |
array() |
tls_1_2: Tls_1_2, tls_1_3: Tls_1_3, |
slice() |
pub const Tls_1_2 = extern struct { client_write_MAC_key: [mac_key_length]u8, server_write_MAC_key: [mac_key_length]u8, client_write_key: [enc_key_length]u8, server_write_key: [enc_key_length]u8, client_write_IV: [fixed_iv_length]u8, server_write_IV: [fixed_iv_length]u8, // non-standard entropy client_salt: [record_iv_length]u8, }; |
skip() |
pub const Tls_1_3 = struct { client_secret: [Hash.digest_length]u8, server_secret: [Hash.digest_length]u8, client_key: [AEAD.key_length]u8, server_key: [AEAD.key_length]u8, client_iv: [AEAD.nonce_length]u8, server_iv: [AEAD.nonce_length]u8, }; }; } |
eof() |
/// Encryption parameters for application traffic. pub const ApplicationCipher = union(enum) { AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8), AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8), CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0), AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0), AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0), }; |
sub() |
pub fn hmacExpandLabel( comptime Hmac: type, secret: []const u8, label_then_seed: []const []const u8, comptime len: usize, ) [len]u8 { const initial_hmac: Hmac = .init(secret); var a: [Hmac.mac_length]u8 = undefined; var result: [std.mem.alignForwardAnyAlign(usize, len, Hmac.mac_length)]u8 = undefined; var index: usize = 0; while (index < result.len) : (index += Hmac.mac_length) { var a_hmac = initial_hmac; if (index > 0) a_hmac.update(&a) else for (label_then_seed) |part| a_hmac.update(part); a_hmac.final(&a); |
rest() |
var result_hmac = initial_hmac; result_hmac.update(&a); for (label_then_seed) |part| result_hmac.update(part); result_hmac.final(result[index..][0..Hmac.mac_length]); } return result[0..len].*; } pub fn hkdfExpandLabel( comptime Hkdf: type, key: [Hkdf.prk_length]u8, label: []const u8, context: []const u8, comptime len: usize, ) [len]u8 { const max_label_len = 255; const max_context_len = 255; const tls13 = "tls13 "; var buf: [2 + 1 + tls13.len + max_label_len + 1 + max_context_len]u8 = undefined; mem.writeInt(u16, buf[0..2], len, .big); buf[2] = @as(u8, @intCast(tls13.len + label.len)); buf[3..][0..tls13.len].* = tls13.*; var i: usize = 3 + tls13.len; @memcpy(buf[i..][0..label.len], label); i += label.len; buf[i] = @as(u8, @intCast(context.len)); i += 1; @memcpy(buf[i..][0..context.len], context); i += context.len; var result: [len]u8 = undefined; Hkdf.expand(&result, buf[0..i], key); return result; } pub fn emptyHash(comptime Hash: type) [Hash.digest_length]u8 { var result: [Hash.digest_length]u8 = undefined; Hash.hash(&.{}, &result, .{}); return result; } pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8) [Hmac.mac_length]u8 { var result: [Hmac.mac_length]u8 = undefined; Hmac.create(&result, message, &key); return result; } pub fn extension(et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 { return int(u16, @intFromEnum(et)) ++ array(u16, u8, bytes); } pub fn array( comptime Len: type, comptime Elem: type, elems: anytype, ) [@divExact(@bitSizeOf(Len), 8) + @divExact(@bitSizeOf(Elem), 8) * elems.len]u8 { const len_size = @divExact(@bitSizeOf(Len), 8); const elem_size = @divExact(@bitSizeOf(Elem), 8); var arr: [len_size + elem_size * elems.len]u8 = undefined; std.mem.writeInt(Len, arr[0..len_size], @intCast(elem_size * elems.len), .big); const ElemInt = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(Elem) } }); for (0.., @as([elems.len]Elem, elems)) |index, elem| { std.mem.writeInt( ElemInt, arr[len_size + elem_size * index ..][0..elem_size], switch (@typeInfo(Elem)) { .int => @as(Elem, elem), .@"enum" => @intFromEnum(@as(Elem, elem)), else => @bitCast(@as(Elem, elem)), }, .big, ); } return arr; } pub fn int(comptime Int: type, val: Int) [@divExact(@bitSizeOf(Int), 8)]u8 { var arr: [@divExact(@bitSizeOf(Int), 8)]u8 = undefined; std.mem.writeInt(Int, &arr, val, .big); return arr; } /// An abstraction to ensure that protocol-parsing code does not perform an /// out-of-bounds read. pub const Decoder = struct { buf: []u8, /// Points to the next byte in buffer that will be decoded. idx: usize = 0, /// Up to this point in `buf` we have already checked that `cap` is greater than it. our_end: usize = 0, /// Beyond this point in `buf` is extra tag-along bytes beyond the amount we /// requested with `readAtLeast`. their_end: usize = 0, /// Points to the end within buffer that has been filled. Beyond this point /// in buf is undefined bytes. cap: usize = 0, /// Debug helper to prevent illegal calls to read functions. disable_reads: bool = false, pub fn fromTheirSlice(buf: []u8) Decoder { return .{ .buf = buf, .their_end = buf.len, .cap = buf.len, .disable_reads = true, }; } /// Use this function to increase `their_end`. pub fn readAtLeast(d: *Decoder, stream: *std.io.Reader, their_amt: usize) !void { assert(!d.disable_reads); const existing_amt = d.cap - d.idx; d.their_end = d.idx + their_amt; if (their_amt <= existing_amt) return; const request_amt = their_amt - existing_amt; const dest = d.buf[d.cap..]; if (request_amt > dest.len) return error.TlsRecordOverflow; stream.readSlice(dest[0..request_amt]) catch |err| switch (err) { error.EndOfStream => return error.TlsConnectionTruncated, error.ReadFailed => return error.ReadFailed, }; d.cap += request_amt; } /// Same as `readAtLeast` but also increases `our_end` by exactly `our_amt`. /// Use when `our_amt` is calculated by us, not by them. pub fn readAtLeastOurAmt(d: *Decoder, stream: *std.io.Reader, our_amt: usize) !void { assert(!d.disable_reads); try readAtLeast(d, stream, our_amt); d.our_end = d.idx + our_amt; } /// Use this function to increase `our_end`. /// This should always be called with an amount provided by us, not them. pub fn ensure(d: *Decoder, amt: usize) !void { d.our_end = @max(d.idx + amt, d.our_end); if (d.our_end > d.their_end) return error.TlsDecodeError; } /// Use this function to increase `idx`. pub fn decode(d: *Decoder, comptime T: type) T { switch (@typeInfo(T)) { .int => |info| switch (info.bits) { 8 => { skip(d, 1); return d.buf[d.idx - 1]; }, 16 => { skip(d, 2); const b0: u16 = d.buf[d.idx - 2]; const b1: u16 = d.buf[d.idx - 1]; return (b0 << 8) | b1; }, 24 => { skip(d, 3); const b0: u24 = d.buf[d.idx - 3]; const b1: u24 = d.buf[d.idx - 2]; const b2: u24 = d.buf[d.idx - 1]; return (b0 << 16) | (b1 << 8) | b2; }, else => @compileError("unsupported int type: " ++ @typeName(T)), }, .@"enum" => |info| { if (info.is_exhaustive) @compileError("exhaustive enum cannot be used"); return @enumFromInt(d.decode(info.tag_type)); }, else => @compileError("unsupported type: " ++ @typeName(T)), } } /// Use this function to increase `idx`. pub fn array(d: *Decoder, comptime len: usize) *[len]u8 { skip(d, len); return d.buf[d.idx - len ..][0..len]; } /// Use this function to increase `idx`. pub fn slice(d: *Decoder, len: usize) []u8 { skip(d, len); return d.buf[d.idx - len ..][0..len]; } /// Use this function to increase `idx`. pub fn skip(d: *Decoder, amt: usize) void { d.idx += amt; assert(d.idx <= d.our_end); // insufficient ensured bytes } pub fn eof(d: Decoder) bool { assert(d.our_end <= d.their_end); assert(d.idx <= d.our_end); return d.idx == d.their_end; } /// Provide the length they claim, and receive a sub-decoder specific to that slice. /// The parent decoder is advanced to the end. pub fn sub(d: *Decoder, their_len: usize) !Decoder { const end = d.idx + their_len; if (end > d.their_end) return error.TlsDecodeError; const sub_buf = d.buf[d.idx..end]; d.idx = end; d.our_end = end; return fromTheirSlice(sub_buf); } pub fn rest(d: Decoder) []u8 { return d.buf[d.idx..d.cap]; } }; |
Generated by zstd-live on 2025-08-10 02:45:58 UTC. |