diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index d020d61e14c9c3d27712ed05bacf03d16ca1c5b6..c12a085ecedf3e50c4523c9b06f90e35b68e02ed 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -367,6 +367,7 @@ test { _ = ff; _ = errors; _ = tls; + _ = tls.Client; _ = Certificate; _ = codecs; } diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index 93251e5137dd6a74c582e90b7a82409154363d0e..4c547964efa57416e6f834244488e6d88b08a0f2 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -382,7 +382,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch return error.TlsBadRecordMac; // TODO use scalar, non-slice version - cleartext_fragment_end += mem.trimEnd(u8, cleartext, "\x00").len; + const trimmed_len = mem.trimEnd(u8, cleartext, "\x00").len; + if (trimmed_len == 0) return error.TlsDecodeError; + cleartext_fragment_end += trimmed_len; }, } read_seq += 1; @@ -1176,6 +1178,7 @@ fn readIndirect(c: *Client) Reader.Error!usize { .tls_1_3 => { const pv = &p.tls_1_3; const P = @TypeOf(p.*); + if (record_len < P.AEAD.tag_length) return failRead(c, error.TlsRecordOverflow); const ad = input.take(tls.record_header_len) catch unreachable; // already peeked const ciphertext_len = record_len - P.AEAD.tag_length; const ciphertext = input.take(ciphertext_len) catch unreachable; // already peeked @@ -1192,6 +1195,7 @@ fn readIndirect(c: *Client) Reader.Error!usize { return failRead(c, error.TlsBadRecordMac); // TODO use scalar, non-slice version const msg = mem.trimEnd(u8, cleartext, "\x00"); + if (msg.len == 0) return failRead(c, error.TlsDecodeError); break :cleartext .{ msg.len - 1, @enumFromInt(msg[msg.len - 1]) }; }, .tls_1_2 => { @@ -1668,3 +1672,74 @@ else .AES_256_GCM_SHA384, .ECDHE_RSA_WITH_AES_256_GCM_SHA384, }); + +fn testReadError(input_buf: []const u8, cipher: tls.ApplicationCipher) ReadError { + var input_reader: Reader = .fixed(input_buf); + var read_buf: [tls.max_ciphertext_record_len]u8 = undefined; + var c: Client = .{ + .input = &input_reader, + .reader = .{ + .buffer = &read_buf, + .vtable = &.{ .stream = stream, .readVec = readVec }, + .seek = 0, + .end = 0, + }, + .output = undefined, + .writer = undefined, + .tls_version = .tls_1_3, + .read_seq = 0, + .write_seq = 0, + .received_close_notify = false, + .allow_truncation_attacks = false, + .application_cipher = cipher, + .ssl_key_log = null, + }; + var w: Writer = .failing; + std.testing.expectError(error.ReadFailed, c.reader.stream(&w, .unlimited)) catch + @panic("expected ReadFailed"); + return c.read_err.?; +} + +test "empty inner plaintext" { + const AEAD = crypto.aead.chacha_poly.ChaCha20Poly1305; + const key: [AEAD.key_length]u8 = @splat(0); + const iv: [AEAD.nonce_length]u8 = @splat(0); + + const plaintext = [1]u8{0x00}; + var ciphertext: [plaintext.len]u8 = undefined; + var tag: [AEAD.tag_length]u8 = undefined; + const content_len: u16 = plaintext.len + AEAD.tag_length; + const record_header = [_]u8{ 0x17, 0x03, 0x03 } ++ mem.toBytes(big(content_len)); + AEAD.encrypt(&ciphertext, &tag, &plaintext, &record_header, iv, key); + + try std.testing.expectEqual(error.TlsDecodeError, testReadError( + &record_header ++ ciphertext ++ tag, + .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{ + .server_key = key, + .server_iv = iv, + .client_secret = undefined, + .server_secret = undefined, + .client_key = undefined, + .client_iv = undefined, + } } }, + )); +} + +test "record shorter than tag" { + const AEAD = crypto.aead.chacha_poly.ChaCha20Poly1305; + const record_len: u16 = AEAD.tag_length - 1; + const header = [_]u8{ 0x17, 0x03, 0x03 } ++ mem.toBytes(big(record_len)); + const wire = header ++ @as([record_len]u8, @splat(0)); + + try std.testing.expectEqual(error.TlsRecordOverflow, testReadError( + &wire, + .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{ + .server_key = undefined, + .server_iv = undefined, + .client_secret = undefined, + .server_secret = undefined, + .client_key = undefined, + .client_iv = undefined, + } } }, + )); +}