cgzipcrc

How do I generate gzip files with the optional CRC16 field?


I've written my own gzip decompressor which is fully compliant with RFC1951 (DEFLATE) and RFC1952 (gzip format), but I am struggling to test the optional CRC16 field present in the gzip header.

If the flag FHCRC is present, a CRC16 applicable to the gzip header will be present right before the deflated data. As part of my final testing campaign I'd like to check if I've been able to generate a program fully inter-operable with other tools, which according to my understanding of RFC1952, means that my program shall reject a gzip which has an invalid value of CRC16 (if it is present).

However, I have not found any way to generate a gzip file containing a CRC16.

Do you know how I can generate a valid gzip file signed with a CRC16, that I could use to test my program? Or is it an unused feature of the spec?


Solution

  • Using zlib. With deflateSetHeader() you can provide any header contents you like, including setting hcrc true to include a gzip header CRC. You'd have to mess with the result manually to create a test file with an invalid header crc.

    Here is some C code to load up a gzip header with all the goodies:

    #include <stdio.h>
    #include <string.h>
    #include <time.h>
    #include "zlib.h"
    
    #define TEXT "This is a test of the emergency broadcast system.\n" \
                 "Remember, this is only a test.\n"
    
    int main(void) {
        z_stream strm = {0};
        int ret = deflateInit2(&strm, -1, 8, 15 + 16, 8, 0);
        if (ret != Z_OK)
            return 1;
    
        gz_header head;
        head.text = 1;
        head.time = time(NULL);
        head.os = 3;
        head.extra = (unsigned char *)"x1\004\0abcd";
        head.extra_len = 8;
        head.name = (unsigned char *)"foo.bar";
        head.comment = (unsigned char *)"no comment";
        head.hcrc = 1;
        deflateSetHeader(&strm, &head);
    
        strm.avail_in = strlen(TEXT);
        strm.next_in = (unsigned char *)TEXT;
        unsigned char out[256];
        strm.avail_out = sizeof(out);
        strm.next_out = out;
        ret = deflate(&strm, Z_FINISH);
        if (ret != Z_STREAM_END)
            return 1;
    
        deflateEnd(&strm);
    
        fwrite(out, 1, sizeof(out) - strm.avail_out, stdout);
        return 0;
    }