Commit aa73bb6bc9

Igor Anić <igor.anic@gmail.com>
2024-07-21 10:19:36
tls.Client: implement record padding (#20558)
On decryption tls client should remove zero byte padding after the content type field. This padding is rarely used, the only site (from the list of top domains) that I found using it is `tutanota.com`. From [RFC](https://datatracker.ietf.org/doc/html/rfc8446#section-5.4): > All encrypted TLS records can be padded. > Padding is a string of zero-valued bytes appended to the ContentType field before encryption. > the receiving implementation scans the field from the end toward the beginning until it finds a non-zero octet. This non-zero octet is the content type of the message. Currently we can't connect to that site: ``` $ zig run main.zig -- tutanota.com error: TlsInitializationFailed /usr/local/zig/zig-linux-x86_64-0.14.0-dev.208+854e86c56/lib/std/crypto/tls/Client.zig:476:45: 0x121fbed in init__anon_10331 (http_get_std) if (inner_ct != .handshake) return error.TlsUnexpectedMessage; ^ /usr/local/zig/zig-linux-x86_64-0.14.0-dev.208+854e86c56/lib/std/http/Client.zig:1357:99: 0x1161f0b in connectTcp (http_get_std) conn.data.tls_client.* = std.crypto.tls.Client.init(stream, client.ca_bundle, host) catch return error.TlsInitializationFailed; ^ /usr/local/zig/zig-linux-x86_64-0.14.0-dev.208+854e86c56/lib/std/http/Client.zig:1492:14: 0x11271e1 in connect (http_get_std) } orelse return client.connectTcp(host, port, protocol); ^ /usr/local/zig/zig-linux-x86_64-0.14.0-dev.208+854e86c56/lib/std/http/Client.zig:1640:9: 0x111a24e in open (http_get_std) try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol); ^ /home/ianic/Code/tls.zig/example/http_get_std.zig:28:19: 0x1118f8c in main (http_get_std) var req = try client.open(.GET, uri, .{ .server_header_buffer = &server_header_buffer }); ^ ``` using this example: ```zig const std = @import("std"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); if (args.len > 1) { const domain = args[1]; var client: std.http.Client = .{ .allocator = allocator }; defer client.deinit(); // Add https:// prefix if needed const url = brk: { const scheme = "https://"; if (domain.len >= scheme.len and std.mem.eql(u8, domain[0..scheme.len], scheme)) break :brk domain; var url_buf: [128]u8 = undefined; break :brk try std.fmt.bufPrint(&url_buf, "https://{s}", .{domain}); }; const uri = try std.Uri.parse(url); var server_header_buffer: [16 * 1024]u8 = undefined; var req = try client.open(.GET, uri, .{ .server_header_buffer = &server_header_buffer }); defer req.deinit(); try req.send(); try req.wait(); } } ```
1 parent fae742a
Changed files (1)
lib
std
crypto
lib/std/crypto/tls/Client.zig
@@ -468,7 +468,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
                         read_seq += 1;
                         P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, p.server_handshake_key) catch
                             return error.TlsBadRecordMac;
-                        break :c cleartext;
+                        break :c @constCast(mem.trimRight(u8, cleartext, "\x00"));
                     },
                 };
 
@@ -1146,7 +1146,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
                         const cleartext = cleartext_buf[0..ciphertext.len];
                         P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_key) catch
                             return error.TlsBadRecordMac;
-                        break :c cleartext;
+                        break :c mem.trimRight(u8, cleartext, "\x00");
                     },
                 };