authorgravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-01-21 15:47:31-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-22 00:33:29-05:00
logf85c01d4c7520d2242626f4c0684ab97e47af373
tree9385b5c065a3e90449dc1e02883d579799dca1fe
parent562d52e23dce703b0eb45a212522588274afbd38

Implement gzip header CRC check.

From RFC 1952: > If FHCRC is set, a CRC16 for the gzip header is present, > immediately before the compressed data. The CRC16 consists > of the two least significant bytes of the CRC32 for all > bytes of the gzip header up to and not including the CRC16.

2 files changed, 54 insertions(+), 8 deletions(-)

lib/std/compress.zig+30
......@@ -4,6 +4,36 @@ pub const deflate = @import("compress/deflate.zig");
44pub const gzip = @import("compress/gzip.zig");
55pub const zlib = @import("compress/zlib.zig");
66
7pub fn HashedReader(
8 comptime ReaderType: anytype,
9 comptime HasherType: anytype,
10) type {
11 return struct {
12 child_reader: ReaderType,
13 hasher: HasherType,
14
15 pub const Error = ReaderType.Error;
16 pub const Reader = std.io.Reader(*@This(), Error, read);
17
18 pub fn read(self: *@This(), buf: []u8) Error!usize {
19 const amt = try self.child_reader.read(buf);
20 self.hasher.update(buf);
21 return amt;
22 }
23
24 pub fn reader(self: *@This()) Reader {
25 return .{ .context = self };
26 }
27 };
28}
29
30pub fn hashedReader(
31 reader: anytype,
32 hasher: anytype,
33) HashedReader(@TypeOf(reader), @TypeOf(hasher)) {
34 return .{ .child_reader = reader, .hasher = hasher };
35}
36
737test {
838 _ = deflate;
939 _ = gzip;
lib/std/compress/gzip.zig+24-8
......@@ -44,8 +44,11 @@ pub fn GzipStream(comptime ReaderType: type) type {
4444 },
4545
4646 fn init(allocator: mem.Allocator, source: ReaderType) !Self {
47 var hasher = std.compress.hashedReader(source, std.hash.Crc32.init());
48 const hashed_reader = hasher.reader();
49
4750 // gzip header format is specified in RFC1952
48 const header = try source.readBytesNoEof(10);
51 const header = try hashed_reader.readBytesNoEof(10);
4952
5053 // Check the ID1/ID2 fields
5154 if (header[0] != 0x1f or header[1] != 0x8b)
......@@ -66,31 +69,31 @@ pub fn GzipStream(comptime ReaderType: type) type {
6669 _ = XFL;
6770
6871 const extra = if (FLG & FEXTRA != 0) blk: {
69 const len = try source.readIntLittle(u16);
72 const len = try hashed_reader.readIntLittle(u16);
7073 const tmp_buf = try allocator.alloc(u8, len);
7174 errdefer allocator.free(tmp_buf);
7275
73 try source.readNoEof(tmp_buf);
76 try hashed_reader.readNoEof(tmp_buf);
7477 break :blk tmp_buf;
7578 } else null;
7679 errdefer if (extra) |p| allocator.free(p);
7780
7881 const filename = if (FLG & FNAME != 0)
79 try source.readUntilDelimiterAlloc(allocator, 0, max_string_len)
82 try hashed_reader.readUntilDelimiterAlloc(allocator, 0, max_string_len)
8083 else
8184 null;
8285 errdefer if (filename) |p| allocator.free(p);
8386
8487 const comment = if (FLG & FCOMMENT != 0)
85 try source.readUntilDelimiterAlloc(allocator, 0, max_string_len)
88 try hashed_reader.readUntilDelimiterAlloc(allocator, 0, max_string_len)
8689 else
8790 null;
8891 errdefer if (comment) |p| allocator.free(p);
8992
9093 if (FLG & FHCRC != 0) {
91 // TODO: Evaluate and check the header checksum. The stdlib has
92 // no CRC16 yet :(
93 _ = try source.readIntLittle(u16);
94 const hash = try source.readIntLittle(u16);
95 if (hash != @truncate(u16, hasher.hasher.final()))
96 return error.WrongChecksum;
9497 }
9598
9699 return Self{
......@@ -230,3 +233,16 @@ test "sanity checks" {
230233 }, ""),
231234 );
232235}
236
237test "header checksum" {
238 try testReader(&[_]u8{
239 // GZIP header
240 0x1f, 0x8b, 0x08, 0x12, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00,
241
242 // header.FHCRC (should cover entire header)
243 0x99, 0xd6,
244
245 // GZIP data
246 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 }, "");
248}