std.crypto.tls: rework for new std.Io API

This commit is contained in:
Andrew Kelley 2025-08-01 00:13:28 -07:00
parent 02908a2d8c
commit 28190cc404
4 changed files with 502 additions and 949 deletions

View File

@ -1306,31 +1306,6 @@ pub fn defaultRebase(r: *Reader, capacity: usize) RebaseError!void {
r.end = data.len; r.end = data.len;
} }
/// Advances the stream and decreases the size of the storage buffer by `n`,
/// returning the range of bytes no longer accessible by `r`.
///
/// This action can be undone by `restitute`.
///
/// Asserts there are at least `n` buffered bytes already.
///
/// Asserts that `r.seek` is zero, i.e. the buffer is in a rebased state.
pub fn steal(r: *Reader, n: usize) []u8 {
assert(r.seek == 0);
assert(n <= r.end);
const stolen = r.buffer[0..n];
r.buffer = r.buffer[n..];
r.end -= n;
return stolen;
}
/// Expands the storage buffer, undoing the effects of `steal`
/// Assumes that `n` does not exceed the total number of stolen bytes.
pub fn restitute(r: *Reader, n: usize) void {
r.buffer = (r.buffer.ptr - n)[0 .. r.buffer.len + n];
r.end += n;
r.seek += n;
}
test fixed { test fixed {
var r: Reader = .fixed("a\x02"); var r: Reader = .fixed("a\x02");
try testing.expect((try r.takeByte()) == 'a'); try testing.expect((try r.takeByte()) == 'a');

View File

@ -49,8 +49,8 @@ pub const hello_retry_request_sequence = [32]u8{
}; };
pub const close_notify_alert = [_]u8{ pub const close_notify_alert = [_]u8{
@intFromEnum(AlertLevel.warning), @intFromEnum(Alert.Level.warning),
@intFromEnum(AlertDescription.close_notify), @intFromEnum(Alert.Description.close_notify),
}; };
pub const ProtocolVersion = enum(u16) { pub const ProtocolVersion = enum(u16) {
@ -138,103 +138,108 @@ pub const ExtensionType = enum(u16) {
_, _,
}; };
pub const AlertLevel = enum(u8) { pub const Alert = struct {
warning = 1, level: Level,
fatal = 2, description: Description,
_,
};
pub const AlertDescription = enum(u8) { pub const Level = enum(u8) {
pub const Error = error{ warning = 1,
TlsAlertUnexpectedMessage, fatal = 2,
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,
}; };
close_notify = 0, pub const Description = enum(u8) {
unexpected_message = 10, pub const Error = error{
bad_record_mac = 20, TlsAlertUnexpectedMessage,
record_overflow = 22, TlsAlertBadRecordMac,
handshake_failure = 40, TlsAlertRecordOverflow,
bad_certificate = 42, TlsAlertHandshakeFailure,
unsupported_certificate = 43, TlsAlertBadCertificate,
certificate_revoked = 44, TlsAlertUnsupportedCertificate,
certificate_expired = 45, TlsAlertCertificateRevoked,
certificate_unknown = 46, TlsAlertCertificateExpired,
illegal_parameter = 47, TlsAlertCertificateUnknown,
unknown_ca = 48, TlsAlertIllegalParameter,
access_denied = 49, TlsAlertUnknownCa,
decode_error = 50, TlsAlertAccessDenied,
decrypt_error = 51, TlsAlertDecodeError,
protocol_version = 70, TlsAlertDecryptError,
insufficient_security = 71, TlsAlertProtocolVersion,
internal_error = 80, TlsAlertInsufficientSecurity,
inappropriate_fallback = 86, TlsAlertInternalError,
user_canceled = 90, TlsAlertInappropriateFallback,
missing_extension = 109, TlsAlertMissingExtension,
unsupported_extension = 110, TlsAlertUnsupportedExtension,
unrecognized_name = 112, TlsAlertUnrecognizedName,
bad_certificate_status_response = 113, TlsAlertBadCertificateStatusResponse,
unknown_psk_identity = 115, TlsAlertUnknownPskIdentity,
certificate_required = 116, TlsAlertCertificateRequired,
no_application_protocol = 120, TlsAlertNoApplicationProtocol,
_, TlsAlertUnknown,
};
pub fn toError(alert: AlertDescription) Error!void { close_notify = 0,
switch (alert) { unexpected_message = 10,
.close_notify => {}, // not an error bad_record_mac = 20,
.unexpected_message => return error.TlsAlertUnexpectedMessage, record_overflow = 22,
.bad_record_mac => return error.TlsAlertBadRecordMac, handshake_failure = 40,
.record_overflow => return error.TlsAlertRecordOverflow, bad_certificate = 42,
.handshake_failure => return error.TlsAlertHandshakeFailure, unsupported_certificate = 43,
.bad_certificate => return error.TlsAlertBadCertificate, certificate_revoked = 44,
.unsupported_certificate => return error.TlsAlertUnsupportedCertificate, certificate_expired = 45,
.certificate_revoked => return error.TlsAlertCertificateRevoked, certificate_unknown = 46,
.certificate_expired => return error.TlsAlertCertificateExpired, illegal_parameter = 47,
.certificate_unknown => return error.TlsAlertCertificateUnknown, unknown_ca = 48,
.illegal_parameter => return error.TlsAlertIllegalParameter, access_denied = 49,
.unknown_ca => return error.TlsAlertUnknownCa, decode_error = 50,
.access_denied => return error.TlsAlertAccessDenied, decrypt_error = 51,
.decode_error => return error.TlsAlertDecodeError, protocol_version = 70,
.decrypt_error => return error.TlsAlertDecryptError, insufficient_security = 71,
.protocol_version => return error.TlsAlertProtocolVersion, internal_error = 80,
.insufficient_security => return error.TlsAlertInsufficientSecurity, inappropriate_fallback = 86,
.internal_error => return error.TlsAlertInternalError, user_canceled = 90,
.inappropriate_fallback => return error.TlsAlertInappropriateFallback, missing_extension = 109,
.user_canceled => {}, // not an error unsupported_extension = 110,
.missing_extension => return error.TlsAlertMissingExtension, unrecognized_name = 112,
.unsupported_extension => return error.TlsAlertUnsupportedExtension, bad_certificate_status_response = 113,
.unrecognized_name => return error.TlsAlertUnrecognizedName, unknown_psk_identity = 115,
.bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse, certificate_required = 116,
.unknown_psk_identity => return error.TlsAlertUnknownPskIdentity, no_application_protocol = 120,
.certificate_required => return error.TlsAlertCertificateRequired, _,
.no_application_protocol => return error.TlsAlertNoApplicationProtocol,
_ => return error.TlsAlertUnknown, 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,
}
} }
} };
}; };
pub const SignatureScheme = enum(u16) { pub const SignatureScheme = enum(u16) {
@ -650,7 +655,7 @@ pub const Decoder = struct {
} }
/// Use this function to increase `their_end`. /// Use this function to increase `their_end`.
pub fn readAtLeast(d: *Decoder, stream: anytype, their_amt: usize) !void { pub fn readAtLeast(d: *Decoder, stream: *std.io.Reader, their_amt: usize) !void {
assert(!d.disable_reads); assert(!d.disable_reads);
const existing_amt = d.cap - d.idx; const existing_amt = d.cap - d.idx;
d.their_end = d.idx + their_amt; d.their_end = d.idx + their_amt;
@ -658,14 +663,16 @@ pub const Decoder = struct {
const request_amt = their_amt - existing_amt; const request_amt = their_amt - existing_amt;
const dest = d.buf[d.cap..]; const dest = d.buf[d.cap..];
if (request_amt > dest.len) return error.TlsRecordOverflow; if (request_amt > dest.len) return error.TlsRecordOverflow;
const actual_amt = try stream.readAtLeast(dest, request_amt); stream.readSlice(dest[0..request_amt]) catch |err| switch (err) {
if (actual_amt < request_amt) return error.TlsConnectionTruncated; error.EndOfStream => return error.TlsConnectionTruncated,
d.cap += actual_amt; error.ReadFailed => return error.ReadFailed,
};
d.cap += request_amt;
} }
/// Same as `readAtLeast` but also increases `our_end` by exactly `our_amt`. /// Same as `readAtLeast` but also increases `our_end` by exactly `our_amt`.
/// Use when `our_amt` is calculated by us, not by them. /// Use when `our_amt` is calculated by us, not by them.
pub fn readAtLeastOurAmt(d: *Decoder, stream: anytype, our_amt: usize) !void { pub fn readAtLeastOurAmt(d: *Decoder, stream: *std.io.Reader, our_amt: usize) !void {
assert(!d.disable_reads); assert(!d.disable_reads);
try readAtLeast(d, stream, our_amt); try readAtLeast(d, stream, our_amt);
d.our_end = d.idx + our_amt; d.our_end = d.idx + our_amt;

File diff suppressed because it is too large Load Diff

View File

@ -343,10 +343,9 @@ pub const Reader = struct {
/// read from `in`. /// read from `in`.
trailers: []const u8 = &.{}, trailers: []const u8 = &.{},
body_err: ?BodyError = null, body_err: ?BodyError = null,
/// Stolen from `in`. /// Determines at which point `error.HttpHeadersOversize` occurs, as well
head_buffer: []u8 = &.{}, /// as the minimum buffer capacity of `in`.
max_head_len: usize,
pub const max_chunk_header_len = 22;
pub const RemainingChunkLen = enum(u64) { pub const RemainingChunkLen = enum(u64) {
head = 0, head = 0,
@ -398,19 +397,11 @@ pub const Reader = struct {
ReadFailed, ReadFailed,
}; };
pub fn restituteHeadBuffer(reader: *Reader) void { /// Buffers the entire head.
reader.in.restitute(reader.head_buffer.len);
reader.head_buffer.len = 0;
}
/// Buffers the entire head into `head_buffer`, invalidating the previous
/// `head_buffer`, if any.
pub fn receiveHead(reader: *Reader) HeadError!void { pub fn receiveHead(reader: *Reader) HeadError!void {
reader.trailers = &.{}; reader.trailers = &.{};
const in = reader.in; const in = reader.in;
in.restitute(reader.head_buffer.len); try in.rebase(reader.max_head_len);
reader.head_buffer.len = 0;
in.rebase();
var hp: HeadParser = .{}; var hp: HeadParser = .{};
var head_end: usize = 0; var head_end: usize = 0;
while (true) { while (true) {