authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-05-31 09:36:25+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-31 09:36:25+02:00
log99c2792b5e52996c72f7689c347a394bb4a827f3
tree61d68406c973dec2e437da4d3fa2a591f5445b5f
parent9f0a38ac815d4d38a3afec6628827f79fdf43b98

tls: reject undersized TLS 1.2 AEAD records in readIndirect (#31973)

Don't crash when a peer send a short record (the handshake is fine) Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31973 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

1 files changed, 17 insertions(+), 2 deletions(-)

lib/std/crypto/tls/Client.zig+17-2
......@@ -1202,6 +1202,7 @@ fn readIndirect(c: *Client) Reader.Error!usize {
12021202 .tls_1_2 => {
12031203 const pv = &p.tls_1_2;
12041204 const P = @TypeOf(p.*);
1205 if (record_len < P.record_iv_length + P.mac_length) return failRead(c, error.TlsRecordOverflow);
12051206 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
12061207 const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked
12071208 const ad = mem.toBytes(big(c.read_seq)) ++
......@@ -1674,7 +1675,7 @@ else
16741675 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
16751676 });
16761677
1677fn testReadError(input_buf: []const u8, cipher: tls.ApplicationCipher) ReadError {
1678fn testReadError(input_buf: []const u8, tls_version: tls.ProtocolVersion, cipher: tls.ApplicationCipher) ReadError {
16781679 var input_reader: Reader = .fixed(input_buf);
16791680 var read_buf: [tls.max_ciphertext_record_len]u8 = undefined;
16801681 var c: Client = .{
......@@ -1687,7 +1688,7 @@ fn testReadError(input_buf: []const u8, cipher: tls.ApplicationCipher) ReadError
16871688 },
16881689 .output = undefined,
16891690 .writer = undefined,
1690 .tls_version = .tls_1_3,
1691 .tls_version = tls_version,
16911692 .read_seq = 0,
16921693 .write_seq = 0,
16931694 .received_close_notify = false,
......@@ -1715,6 +1716,7 @@ test "empty inner plaintext" {
17151716
17161717 try std.testing.expectEqual(error.TlsDecodeError, testReadError(
17171718 &record_header ++ ciphertext ++ tag,
1719 .tls_1_3,
17181720 .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{
17191721 .server_key = key,
17201722 .server_iv = iv,
......@@ -1734,6 +1736,7 @@ test "record shorter than tag" {
17341736
17351737 try std.testing.expectEqual(error.TlsRecordOverflow, testReadError(
17361738 &wire,
1739 .tls_1_3,
17371740 .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{
17381741 .server_key = undefined,
17391742 .server_iv = undefined,
......@@ -1744,3 +1747,15 @@ test "record shorter than tag" {
17441747 } } },
17451748 ));
17461749}
1750
1751test "TLS 1.2 record shorter than IV plus tag" {
1752 const P = tls.ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8);
1753 const record_len: u16 = P.record_iv_length + P.mac_length - 1;
1754 const header = [_]u8{ 0x17, 0x03, 0x03 } ++ mem.toBytes(big(record_len));
1755
1756 try std.testing.expectEqual(error.TlsRecordOverflow, testReadError(
1757 &(header ++ @as([record_len]u8, @splat(0))),
1758 .tls_1_2,
1759 .{ .AES_128_GCM_SHA256 = .{ .tls_1_2 = mem.zeroes(P.Tls_1_2) } },
1760 ));
1761}