winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

pure.h (60213B)


      1 // pure.h - C99 interface:
      2 
      3 #ifndef PURE_H
      4 #define PURE_H
      5 
      6 #include <assert.h>
      7 #include <stdint.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <stdio.h>
     11 
     12 #include "pure_constants.h"
     13 #include "pure_errors.h"
     14 #include "pure_signatures.h"
     15 #include "pure_routines.h"
     16 
     17 #include "zlib.h"
     18 
     19 typedef struct pure_ctx {
     20   uint64_t flags;
     21   uint64_t depth;
     22   uint64_t files;
     23   uint64_t archives;
     24   uint64_t size;
     25   uint64_t compressed_size;
     26   uint64_t uncompressed_size;
     27 } pure_ctx;
     28 
     29 typedef struct pure_zip_lfh {
     30   uint64_t offset;
     31   uint64_t length;
     32   uint64_t version_minimum;
     33   uint64_t general_purpose_bit_flag;
     34   uint64_t compression_method;
     35   uint64_t last_mod_file_time;
     36   uint64_t last_mod_file_date;
     37   uint64_t crc32;
     38   uint64_t compressed_size;
     39   uint64_t uncompressed_size;
     40   uint8_t* file_name;
     41   uint64_t file_name_length;
     42   uint8_t* extra_field;
     43   uint64_t extra_field_length;
     44   uint8_t zip64;
     45 } pure_zip_lfh;
     46 
     47 typedef struct pure_zip_ddr {
     48   uint64_t offset;
     49   uint64_t length;
     50   uint64_t crc32;
     51   uint64_t compressed_size;
     52   uint64_t uncompressed_size;
     53   uint8_t zip64;
     54 } pure_zip_ddr;
     55 
     56 typedef struct pure_zip_cdh {
     57   uint64_t offset;
     58   uint64_t length;
     59   uint64_t version_made;
     60   uint64_t version_minimum;
     61   uint64_t general_purpose_bit_flag;
     62   uint64_t compression_method;
     63   uint64_t last_mod_file_time;
     64   uint64_t last_mod_file_date;
     65   uint64_t crc32;
     66   uint64_t compressed_size;
     67   uint64_t uncompressed_size;
     68   uint8_t* file_name;
     69   uint64_t file_name_length;
     70   uint8_t* extra_field;
     71   uint64_t extra_field_length;
     72   uint8_t* file_comment;
     73   uint64_t file_comment_length;
     74   uint64_t disk;
     75   uint64_t internal_file_attributes;
     76   uint64_t external_file_attributes;
     77   uint64_t relative_offset;
     78   uint64_t unix_mode;
     79   uint8_t directory;
     80   uint8_t zip64;
     81 } pure_zip_cdh;
     82 
     83 typedef struct pure_zip_eocdr_64 {
     84   uint64_t offset;
     85   uint64_t length;
     86   uint64_t version_made;
     87   uint64_t version_minimum;
     88   uint64_t disk;
     89   uint64_t cd_disk;
     90   uint64_t cd_disk_records;
     91   uint64_t cd_records;
     92   uint64_t cd_size;
     93   uint64_t cd_offset;
     94   uint8_t* extensible_data_sector;
     95   uint64_t extensible_data_sector_length;
     96 } pure_zip_eocdr_64;
     97 
     98 typedef struct pure_zip_eocdl_64 {
     99   uint64_t offset;
    100   uint64_t length;
    101   uint64_t disk;
    102   uint64_t eocdr_64_offset;
    103   uint64_t disks;
    104 } pure_zip_eocdl_64;
    105 
    106 typedef struct pure_zip_eocdr {
    107   uint64_t offset;
    108   uint64_t length;
    109   uint64_t disk;
    110   uint64_t cd_disk;
    111   uint64_t cd_disk_records;
    112   uint64_t cd_records;
    113   uint64_t cd_size;
    114   uint64_t cd_offset;
    115   uint8_t* comment;
    116   uint64_t comment_length;
    117   uint8_t zip64;
    118 } pure_zip_eocdr;
    119 
    120 int pure_zip_crc32(
    121   const uint8_t* buffer,
    122   const uint64_t size,
    123   uint64_t* result
    124 ) {
    125   assert(SIZE_MAX >= UINT32_MAX);
    126   assert(size <= SIZE_MAX);
    127   assert(*result == 0);
    128   unsigned long checksum = crc32_z(0UL, Z_NULL, 0);
    129   checksum = crc32_z(checksum, buffer, (size_t) size);
    130   *result = (uint64_t) checksum;
    131   return 0;
    132 }
    133 
    134 void pure_zip_debug_lfh(const pure_zip_lfh* header) {
    135   printf("LOCAL FILE HEADER:\n");
    136   printf(
    137     "offset=%llu length=%llu\n",
    138     header->offset,
    139     header->length
    140   );
    141   printf(
    142     "general_purpose_bit_flag=%llu version_minimum=%llu\n",
    143     header->general_purpose_bit_flag,
    144     header->version_minimum
    145   );
    146   printf(
    147     "compression_method=%llu last_mod_file_time=%llu last_mod_file_date=%llu\n",
    148     header->compression_method,
    149     header->last_mod_file_time,
    150     header->last_mod_file_date
    151   );
    152   printf(
    153     "crc32=%llu compressed_size=%llu uncompressed_size=%llu\n",
    154     header->crc32,
    155     header->compressed_size,
    156     header->uncompressed_size
    157   );
    158   printf("file_name=\"");
    159   fwrite(header->file_name, 1, header->file_name_length, stdout);
    160   printf("\"\n");
    161   printf(
    162     "file_name_length=%llu extra_field_length=%llu\n",
    163     header->file_name_length,
    164     header->extra_field_length
    165   );
    166   printf("\n");
    167 }
    168 
    169 void pure_zip_debug_ddr(const pure_zip_ddr* header) {
    170   printf("DATA DESCRIPTOR RECORD:\n");
    171   printf(
    172     "offset=%llu length=%llu\n",
    173     header->offset,
    174     header->length
    175   );
    176   printf(
    177     "crc32=%llu compressed_size=%llu uncompressed_size=%llu\n",
    178     header->crc32,
    179     header->compressed_size,
    180     header->uncompressed_size
    181   );
    182   printf("\n");
    183 }
    184 
    185 void pure_zip_debug_cdh(const pure_zip_cdh* header) {
    186   printf("CENTRAL DIRECTORY HEADER:\n");
    187   printf(
    188     "offset=%llu length=%llu\n",
    189     header->offset,
    190     header->length
    191   );
    192   printf(
    193     "general_purpose_bit_flag=%llu version_minimum=%llu version_made=%llu\n",
    194     header->general_purpose_bit_flag,
    195     header->version_minimum,
    196     header->version_made
    197   );
    198   printf(
    199     "compression_method=%llu last_mod_file_time=%llu last_mod_file_date=%llu\n",
    200     header->compression_method,
    201     header->last_mod_file_time,
    202     header->last_mod_file_date
    203   );
    204   printf(
    205     "crc32=%llu compressed_size=%llu uncompressed_size=%llu\n",
    206     header->crc32,
    207     header->compressed_size,
    208     header->uncompressed_size
    209   );
    210   printf("file_name=\"");
    211   fwrite(header->file_name, 1, header->file_name_length, stdout);
    212   printf("\"\n");
    213   printf(
    214     "file_name_length=%llu extra_field_length=%llu file_comment_length=%llu\n",
    215     header->file_name_length,
    216     header->extra_field_length,
    217     header->file_comment_length
    218   );
    219   printf("file_comment=\"");
    220   fwrite(header->file_comment, 1, header->file_comment_length, stdout);
    221   printf("\"\n");
    222   printf(
    223     "internal_file_attributes=%llu external_file_attributes=%llu\n",
    224     header->internal_file_attributes,
    225     header->external_file_attributes
    226   );
    227   printf(
    228     "disk=%llu relative_offset=%llu\n",
    229     header->disk,
    230     header->relative_offset
    231   );
    232   printf(
    233     "unix_mode=%llu directory=%u zip64=%u\n",
    234     header->unix_mode,
    235     header->directory,
    236     header->zip64
    237   );
    238   printf("\n");
    239 }
    240 
    241 void pure_zip_debug_eocdl_64(const pure_zip_eocdl_64* header) {
    242   printf("ZIP64 END OF CENTRAL DIRECTORY LOCATOR:\n");
    243   printf("offset=%llu\n", header->offset);
    244   printf("length=%llu\n", header->length);
    245   printf("disk=%llu\n", header->disk);
    246   printf("eocdr_64_offset=%llu\n", header->eocdr_64_offset);
    247   printf("disks=%llu\n", header->disks);
    248   printf("\n");
    249 }
    250 
    251 void pure_zip_debug_eocdr_64(const pure_zip_eocdr_64* header) {
    252   printf("ZIP64 END OF CENTRAL DIRECTORY RECORD:\n");
    253   printf("offset=%llu\n", header->offset);
    254   printf("length=%llu\n", header->length);
    255   printf("version_made=%llu\n", header->version_made);
    256   printf("version_minimum=%llu\n", header->version_minimum);
    257   printf("disk=%llu\n", header->disk);
    258   printf("cd_disk=%llu\n", header->cd_disk);
    259   printf("cd_disk_records=%llu\n", header->cd_disk_records);
    260   printf("cd_records=%llu\n", header->cd_records);
    261   printf("cd_size=%llu\n", header->cd_size);
    262   printf("cd_offset=%llu\n", header->cd_offset);
    263   printf(
    264     "extensible_data_sector_length=%llu\n",
    265     header->extensible_data_sector_length
    266   );
    267   printf("\n");
    268 }
    269 
    270 void pure_zip_debug_eocdr(const pure_zip_eocdr* header) {
    271   printf("END OF CENTRAL DIRECTORY RECORD:\n");
    272   printf("offset=%llu\n", header->offset);
    273   printf("length=%llu\n", header->length);
    274   printf("disk=%llu\n", header->disk);
    275   printf("cd_disk=%llu\n", header->cd_disk);
    276   printf("cd_disk_records=%llu\n", header->cd_disk_records);
    277   printf("cd_records=%llu\n", header->cd_records);
    278   printf("cd_size=%llu\n", header->cd_size);
    279   printf("cd_offset=%llu\n", header->cd_offset);
    280   printf("comment=\"");
    281   fwrite(header->comment, 1, header->comment_length, stdout);
    282   printf("\"\n");
    283   printf("comment_length=%llu\n", header->comment_length);
    284   printf("\n");
    285 }
    286 
    287 int pure_zip_verify_compression_ratio(
    288   const uint64_t compressed_size,
    289   const uint64_t uncompressed_size
    290 ) {
    291   // Here, compressed size and uncompressed size could relate to the respective
    292   // sizes of an individual file or the sum of the sizes across an archive.
    293   //
    294   // We do not use only a ratio limit to detect dangerous compression ratios,
    295   // since an extreme ratio can be mitigated by a negligible uncompressed size.
    296   //
    297   // Instead, we calculate a score that combines the ratio and uncompressed size
    298   // and then compare this score against a limit. The higher the ratio and the
    299   // larger the uncompressed size, the more dangerous the file or archive.
    300   //
    301   // Conversely, a file or archive with high ratio but negligible uncompressed
    302   // size is less likely to be dangerous, since while it may have high power,
    303   // this power is multiplied by less weight.
    304   if (compressed_size == 0) return 0; // Prevent divide-by-zero.
    305   if (uncompressed_size < PURE_ZIP_COMPRESSION_RATIO_SIZE_MIN) return 0;
    306   const uint64_t ratio = uncompressed_size / compressed_size;
    307   const uint64_t score = ratio * uncompressed_size;
    308   if (score > PURE_ZIP_COMPRESSION_RATIO_SCORE_MAX) {
    309     return PURE_E_ZIP_BOMB_RATIO;
    310   }
    311   return 0;
    312 }
    313 
    314 int pure_zip_verify_compression_method(const uint64_t value) {
    315   if (value == PURE_ZIP_COMPRESSION_METHOD_NONE) return 0;
    316   if (value == PURE_ZIP_COMPRESSION_METHOD_DEFLATE) return 0;
    317   // CVE-2016-9844:
    318   // Defend vulnerable implementations against overflow when the two-byte
    319   // compression method in the central directory file header exceeds 999.
    320   // https://bugs.launchpad.net/ubuntu/+source/unzip/+bug/1643750
    321   if (value > 999) return PURE_E_ZIP_COMPRESSION_METHOD_DANGEROUS;
    322   if (value == 99) return PURE_E_ZIP_COMPRESSION_METHOD_ENCRYPTED;
    323   return PURE_E_ZIP_COMPRESSION_METHOD_UNSUPPORTED;
    324 }
    325 
    326 int pure_zip_verify_compression_method_sizes(
    327   const uint64_t compression_method,
    328   const uint64_t compressed_size,
    329   const uint64_t uncompressed_size
    330 ) {
    331   {
    332     int error = pure_zip_verify_compression_method(compression_method);
    333     if (error) return error;
    334   }
    335   if (compression_method == 0 && compressed_size != uncompressed_size) {
    336     return PURE_E_ZIP_STORED_COMPRESSION_SIZE_MISMATCH;
    337   }
    338   // CVE-2018-18384:
    339   // Defend vulnerable implementations against buffer overflow.
    340   // https://bugzilla.suse.com/show_bug.cgi?id=1110194
    341   if (
    342     uncompressed_size > 0 &&
    343     compressed_size > uncompressed_size &&
    344     compressed_size / uncompressed_size >= 2
    345   ) {
    346     return PURE_E_ZIP_DANGEROUS_NEGATIVE_COMPRESSION_RATIO;
    347   }
    348   return 0;
    349 }
    350 
    351 int pure_zip_verify_date(const uint64_t value) {
    352   /*
    353   An MS-DOS date is a packed 16-bit (2 bytes) value in which bits in the value
    354   represent the day, month, and year:
    355   
    356      Bits:  0-4   5-8    9-15
    357      Unit:  Day   Month  Year
    358     Range:  1-31  1-12   Relative to 1980
    359   */
    360   if (value > PURE_16_BIT_MAX) return PURE_E_ZIP_DATE_OVERFLOW;
    361   uint64_t y = (value >> 9) + 1980;
    362   uint64_t m = (value >> 5) & 15;
    363   uint64_t d = value & 31;
    364   // 7 bits supports up to 127 years, or 2107 (1980 + 127).
    365   // However, years after 2099 are not correctly handled by some software:
    366   if (y > 2099) return PURE_E_ZIP_DATE_YEAR_OVERFLOW;
    367   if (m > 12) return PURE_E_ZIP_DATE_MONTH_OVERFLOW;
    368   if (d > 31) return PURE_E_ZIP_DATE_DAY_OVERFLOW;
    369   return 0;
    370 }
    371 
    372 int pure_zip_verify_directory(const pure_zip_cdh* header) {
    373   assert(header->directory == 0 || header->directory == 1);
    374   if (header->directory) {
    375     if (header->compressed_size > 0) return PURE_E_ZIP_DIRECTORY_COMPRESSED;
    376     if (header->uncompressed_size > 0) return PURE_E_ZIP_DIRECTORY_UNCOMPRESSED;
    377   }
    378   return 0;
    379 }
    380 
    381 int pure_zip_verify_disk(const uint64_t value) {
    382   if (value != 0) return PURE_E_ZIP_MULTIPLE_DISKS;
    383   return 0;
    384 }
    385 
    386 int pure_zip_verify_extra_field(
    387   const uint8_t* extra_field,
    388   const uint64_t extra_field_length,
    389   const uint8_t* file_name,
    390   const uint64_t file_name_length
    391 ) {
    392   if (extra_field_length > PURE_ZIP_EXTRA_FIELD_MAX) {
    393     return PURE_E_ZIP_EXTRA_FIELD_MAX;
    394   }
    395   // The extra field contains a variety of optional data such as OS-specific
    396   // attributes. It is divided into chunks, each with a 16-bit ID code and a
    397   // 16-bit length.
    398   if (extra_field_length != 0 && extra_field_length < 4) {
    399     return PURE_E_ZIP_EXTRA_FIELD_MIN;
    400   }
    401   // Check extra field attribute sizes, and specifically the unicode path:
    402   uint64_t offset = 0;
    403   while (offset + 2 + 2 <= extra_field_length) {
    404     const uint16_t id = pure_u16(extra_field + offset + 0);
    405     const uint16_t size = pure_u16(extra_field + offset + 2);
    406     if (offset + 2 + 2 + size > extra_field_length) {
    407       return PURE_E_ZIP_EXTRA_FIELD_ATTRIBUTE_OVERFLOW;
    408     }
    409     if (id == 0x7075) {
    410       // We expect at least a 1-byte version followed by a 4-byte crc32:
    411       if (size < 1 + 4) return PURE_E_ZIP_EXTRA_FIELD_UNICODE_PATH_OVERFLOW;
    412       assert(offset + 2 + 2 + 1 <= extra_field_length);
    413       uint8_t version = extra_field[offset + 2 + 2];
    414       if (version != 1) return PURE_E_ZIP_EXTRA_FIELD_UNICODE_PATH_VERSION;
    415       // We require the unicode path to match the central directory file even if
    416       // the crc32 of the non-unicode path is different. Otherwise, an attacker
    417       // could present an alternative extension to bypass content inspection.
    418       const uint8_t* unicode_path = extra_field + offset + 2 + 2 + 1 + 4;
    419       const uint64_t unicode_path_length = size - 1 - 4;
    420       if (
    421         unicode_path_length != file_name_length ||
    422         memcmp(unicode_path, file_name, (size_t) file_name_length)
    423       ) {
    424         return PURE_E_ZIP_EXTRA_FIELD_UNICODE_PATH_DIFF;
    425       }
    426     }
    427     offset += 4;
    428     offset += size;
    429   }
    430   if (offset > extra_field_length) return PURE_E_ZIP_EXTRA_FIELD_OVERFLOW;
    431   if (offset < extra_field_length) {
    432     if (pure_zeroes(extra_field, offset, extra_field_length)) {
    433       return PURE_E_ZIP_EXTRA_FIELD_UNDERFLOW_ZEROED;
    434     } else {
    435       return PURE_E_ZIP_EXTRA_FIELD_UNDERFLOW_BUFFER_BLEED;
    436     }
    437   }
    438   return 0;
    439 }
    440 
    441 int pure_zip_verify_file_name(
    442   const uint8_t* file_name,
    443   const uint64_t file_name_length
    444 ) {
    445   // A "file name" in this context is a path name with multiple components.
    446   // The file name length may be 0 if the archiver input came from stdin.
    447 
    448   // CVE-2018-1000035:
    449   // Heap-based buffer overflow in password protected ZIP archives.
    450   // https://sec-consult.com/en/blog/advisories/
    451   //   multiple-vulnerabilities-in-infozip-unzip/index.html
    452   // We want to defend vulnerable implementations against PATH_MAX allocation
    453   // bugs, e.g. malloc(N * PATH_MAX) where the assumption is that user data
    454   // cannot exceed PATH_MAX.
    455   if (file_name_length > PURE_PATH_MAX) return PURE_E_ZIP_FILE_NAME_LENGTH;
    456 
    457   // CVE-2003-0282 (aka "JELMER"):
    458   // Some zip implementations filter control characters amongst others.
    459   // This behavior can be exploited to mask ".." in a directory traversal.
    460   // https://www.securityfocus.com/archive/1/321090
    461   if (pure_path_control_characters(file_name, file_name_length)) {
    462     return PURE_E_ZIP_FILE_NAME_CONTROL_CHARACTERS;
    463   }
    464   if (pure_path_drive(file_name, file_name_length)) {
    465     return PURE_E_ZIP_FILE_NAME_TRAVERSAL_DRIVE_PATH;
    466   }
    467   if (pure_path_relative(file_name, file_name_length)) {
    468     return PURE_E_ZIP_FILE_NAME_TRAVERSAL_RELATIVE_PATH;
    469   }
    470   if (pure_path_double_dots(file_name, file_name_length)) {
    471     return PURE_E_ZIP_FILE_NAME_TRAVERSAL_DOUBLE_DOTS;
    472   }
    473   if (pure_path_component_overflow(file_name, file_name_length)) {
    474     return PURE_E_ZIP_FILE_NAME_COMPONENT_OVERFLOW;
    475   }
    476   // All slashes must be forward according to the APPNOTE.TXT specification:
    477   for (uint64_t index = 0; index < file_name_length; index++) {
    478     if (file_name[index] == PURE_BACKSLASH) {
    479       return PURE_E_ZIP_FILE_NAME_BACKSLASH;
    480     }
    481   }
    482   return 0;
    483 }
    484 
    485 int pure_zip_verify_flags(const uint64_t value) {
    486   if (value > PURE_16_BIT_MAX) return PURE_E_ZIP_FLAG_OVERFLOW;
    487   if (value & (1 << 0)) return PURE_E_ZIP_FLAG_TRADITIONAL_ENCRYPTION;
    488   if (value & (1 << 4)) return PURE_E_ZIP_FLAG_ENHANCED_DEFLATE;
    489   if (value & (1 << 5)) return PURE_E_ZIP_FLAG_COMPRESSED_PATCHED_DATA;
    490   if (value & (1 << 6)) return PURE_E_ZIP_FLAG_STRONG_ENCRYPTION;
    491   if (value & (1 << 7)) return PURE_E_ZIP_FLAG_UNUSED_BIT_7;
    492   if (value & (1 << 8)) return PURE_E_ZIP_FLAG_UNUSED_BIT_8;
    493   if (value & (1 << 9)) return PURE_E_ZIP_FLAG_UNUSED_BIT_9;
    494   if (value & (1 << 10)) return PURE_E_ZIP_FLAG_UNUSED_BIT_10;
    495   if (value & (1 << 12)) return PURE_E_ZIP_FLAG_ENHANCED_COMPRESSION;
    496   if (value & (1 << 13)) return PURE_E_ZIP_FLAG_MASKED_LOCAL_HEADERS;
    497   if (value & (1 << 14)) return PURE_E_ZIP_FLAG_RESERVED_BIT_14;
    498   if (value & (1 << 15)) return PURE_E_ZIP_FLAG_RESERVED_BIT_15;
    499   return 0;
    500 }
    501 
    502 int pure_zip_verify_string(
    503   const uint8_t* string,
    504   const uint64_t string_length,
    505   const uint64_t utf8
    506 ) {
    507   if (string_length > PURE_ZIP_STRING_MAX) return PURE_E_ZIP_STRING_MAX;
    508   for (uint64_t index = 0; index < string_length; index++) {
    509     if (string[index] == 0) return PURE_E_ZIP_STRING_NULL_BYTE;
    510   }
    511   if (utf8) {
    512     // TO DO: Verify that UTF-8 encoding is valid:
    513     // Some systems such as macOS never bother to set bit 11 to indicate UTF-8.
    514     // We therefore always attempt UTF-8 and fallback to CP437 only on error.
    515     // If the string must be UTF-8 then reject the string as invalid.
    516   }
    517   return 0;
    518 }
    519 
    520 int pure_zip_verify_symlink(
    521   const pure_zip_cdh* cdh,
    522   const pure_zip_lfh* lfh,
    523   const uint8_t* buffer
    524 ) {
    525   if ((cdh->unix_mode & PURE_UNIX_MODE_MASK) != PURE_UNIX_MODE_LNK) return 0;
    526   if (cdh->compression_method != 0) return PURE_E_ZIP_SYMLINK_COMPRESSED;
    527   assert(cdh->relative_offset == lfh->offset);
    528   assert(!pure_overflow(cdh->relative_offset, lfh->length, cdh->offset));
    529   const uint8_t* symlink = buffer + cdh->relative_offset + lfh->length;
    530   const uint64_t symlink_length = cdh->compressed_size;
    531   // Check for PATH_MAX overflow:
    532   if (symlink_length > PURE_PATH_MAX) return PURE_E_ZIP_SYMLINK_LENGTH;
    533   // Check for control characters used to mask a directory traversal:
    534   if (pure_path_control_characters(symlink, symlink_length)) {
    535     return PURE_E_ZIP_SYMLINK_CONTROL_CHARACTERS;
    536   }
    537   // Check for a directory traversal:
    538   if (pure_path_drive(symlink, symlink_length)) {
    539     return PURE_E_ZIP_SYMLINK_TRAVERSAL_DRIVE_PATH;
    540   }
    541   if (pure_path_relative(symlink, symlink_length)) {
    542     return PURE_E_ZIP_SYMLINK_TRAVERSAL_RELATIVE_PATH;
    543   }
    544   if (pure_path_double_dots(symlink, symlink_length)) {
    545     return PURE_E_ZIP_SYMLINK_TRAVERSAL_DOUBLE_DOTS;
    546   }
    547   // Check for path component overflow:
    548   if (pure_path_component_overflow(symlink, symlink_length)) {
    549     return PURE_E_ZIP_SYMLINK_COMPONENT_OVERFLOW;
    550   }
    551   return 0;
    552 }
    553 
    554 int pure_zip_verify_unix_mode(const uint64_t value) {
    555   if (value > PURE_16_BIT_MAX) return PURE_E_ZIP_UNIX_MODE_OVERFLOW;
    556   // Detect dangerous file types:
    557   if ((value & PURE_UNIX_MODE_MASK) == PURE_UNIX_MODE_BLK) {
    558     return PURE_E_ZIP_UNIX_MODE_BLOCK_DEVICE;
    559   }
    560   if ((value & PURE_UNIX_MODE_MASK) == PURE_UNIX_MODE_CHR) {
    561     return PURE_E_ZIP_UNIX_MODE_CHARACTER_DEVICE;
    562   }
    563   if ((value & PURE_UNIX_MODE_MASK) == PURE_UNIX_MODE_FIFO) {
    564     return PURE_E_ZIP_UNIX_MODE_FIFO;
    565   }
    566   if ((value & PURE_UNIX_MODE_MASK) == PURE_UNIX_MODE_SOCK) {
    567     return PURE_E_ZIP_UNIX_MODE_SOCKET;
    568   }
    569   // Detect dangerous permissions:
    570   // CVE-2005-0602
    571   // https://marc.info/?l=bugtraq&m=110960796331943&w=2
    572   // 01000:
    573   if (value & 512) return PURE_E_ZIP_UNIX_MODE_PERMISSIONS_STICKY;
    574   // 02000:
    575   if (value & 1024) return PURE_E_ZIP_UNIX_MODE_PERMISSIONS_SETGID;
    576   // 04000:
    577   if (value & 2048) return PURE_E_ZIP_UNIX_MODE_PERMISSIONS_SETUID;
    578   return 0;
    579 }
    580 
    581 int pure_zip_verify_time(const uint64_t value) {
    582   /*
    583   An MS-DOS time is a packed 16-bit (2 bytes) value in which bits in the value
    584   represent the hour, minute, and second:
    585   
    586      Bits:  0-4     5-10    11-15
    587      Unit:  Second  Minute  Hour
    588     Range:  0-59    0-59    0-23
    589   */
    590   if (value > PURE_16_BIT_MAX) return PURE_E_ZIP_TIME_OVERFLOW;
    591   uint64_t h = (value >> 11);
    592   uint64_t m = (value >> 5) & 63;
    593   uint64_t s = (value & 31) * 2;
    594   if (h > 23) return PURE_E_ZIP_TIME_HOUR_OVERFLOW;
    595   if (m > 59) return PURE_E_ZIP_TIME_MINUTE_OVERFLOW;
    596   if (s > 59) return PURE_E_ZIP_TIME_SECOND_OVERFLOW;
    597   return 0;
    598 }
    599 
    600 int pure_zip_verify_lfh(const pure_zip_lfh* header) {
    601   assert(
    602     header->length == (
    603       PURE_ZIP_LFH_MIN +
    604       header->file_name_length +
    605       header->extra_field_length
    606     )
    607   );
    608   {
    609     int error = pure_zip_verify_flags(header->general_purpose_bit_flag);
    610     if (error) return error;
    611   }
    612   {
    613     int error = pure_zip_verify_compression_method_sizes(
    614       header->compression_method,
    615       header->compressed_size,
    616       header->uncompressed_size
    617     );
    618     if (error) return error;
    619   }
    620   {
    621     int error = pure_zip_verify_date(header->last_mod_file_date);
    622     if (error) return error;
    623   }
    624   {
    625     int error = pure_zip_verify_time(header->last_mod_file_time);
    626     if (error) return error;
    627   }
    628   {
    629     int error = pure_zip_verify_file_name(
    630       header->file_name,
    631       header->file_name_length
    632     );
    633     if (error) return error;
    634   }
    635   {
    636     int error = pure_zip_verify_string(
    637       header->file_name,
    638       header->file_name_length,
    639       header->general_purpose_bit_flag & PURE_ZIP_FLAG_UTF8
    640     );
    641     if (error) return error;
    642   }
    643   {
    644     int error = pure_zip_verify_extra_field(
    645       header->extra_field,
    646       header->extra_field_length,
    647       header->file_name,
    648       header->file_name_length
    649     );
    650     if (error) return error;
    651   }
    652   assert(header->zip64 == 0 || header->zip64 == 1);
    653   return 0;
    654 }
    655 
    656 int pure_zip_verify_cdh(const pure_zip_cdh* header) {
    657   assert(
    658     header->length == (
    659       PURE_ZIP_CDH_MIN +
    660       header->file_name_length +
    661       header->extra_field_length +
    662       header->file_comment_length
    663     )
    664   );
    665   {
    666     int error = pure_zip_verify_flags(header->general_purpose_bit_flag);
    667     if (error) return error;
    668   }
    669   {
    670     int error = pure_zip_verify_compression_method_sizes(
    671       header->compression_method,
    672       header->compressed_size,
    673       header->uncompressed_size
    674     );
    675     if (error) return error;
    676   }
    677   // An LFH may have a zero compressed size with a non-zero uncompressed size
    678   // because the actual sizes may be in the DDR, but a CDH cannot be ex nihilo.
    679   // We cross-check the DDR against the CDH, so this check applies to the DDR:
    680   if (header->compressed_size == 0 && header->uncompressed_size != 0) {
    681     return PURE_E_ZIP_EX_NIHILO;
    682   }
    683   {
    684     int error = pure_zip_verify_date(header->last_mod_file_date);
    685     if (error) return error;
    686   }
    687   {
    688     int error = pure_zip_verify_time(header->last_mod_file_time);
    689     if (error) return error;
    690   }
    691   {
    692     int error = pure_zip_verify_file_name(
    693       header->file_name,
    694       header->file_name_length
    695     );
    696     if (error) return error;
    697   }
    698   {
    699     int error = pure_zip_verify_string(
    700       header->file_name,
    701       header->file_name_length,
    702       header->general_purpose_bit_flag & PURE_ZIP_FLAG_UTF8
    703     );
    704     if (error) return error;
    705   }
    706   {
    707     int error = pure_zip_verify_extra_field(
    708       header->extra_field,
    709       header->extra_field_length,
    710       header->file_name,
    711       header->file_name_length
    712     );
    713     if (error) return error;
    714   }
    715   {
    716     int error = pure_zip_verify_string(
    717       header->file_comment,
    718       header->file_comment_length,
    719       header->general_purpose_bit_flag & PURE_ZIP_FLAG_UTF8
    720     );
    721     if (error) return error;
    722   }
    723   {
    724     int error = pure_zip_verify_disk(header->disk);
    725     if (error) return error;
    726   }
    727   {
    728     int error = pure_zip_verify_unix_mode(header->unix_mode);
    729     if (error) return error;
    730   }
    731   {
    732     int error = pure_zip_verify_directory(header);
    733     if (error) return error;
    734   }
    735   assert(header->zip64 == 0 || header->zip64 == 1);
    736   return 0;
    737 }
    738 
    739 int pure_zip_decode_eief_64(
    740   const uint8_t* extra_field,
    741   const uint64_t length,
    742   uint64_t* compressed_size,
    743   uint64_t* uncompressed_size,
    744   uint64_t* relative_offset,
    745   uint64_t* disk,
    746   uint8_t* zip64,
    747   uint8_t lfh
    748 ) {
    749   assert(
    750     *compressed_size   == PURE_32_BIT_MAX ||
    751     *uncompressed_size == PURE_32_BIT_MAX ||
    752     *relative_offset   == PURE_32_BIT_MAX ||
    753     *disk              == PURE_16_BIT_MAX
    754   );
    755   assert(*zip64 == 0);
    756   assert(lfh == 0 || lfh == 1);
    757   if (length > PURE_ZIP_EXTRA_FIELD_MAX) return PURE_E_ZIP_EXTRA_FIELD_MAX;
    758   if (length != 0 && length < 4) return PURE_E_ZIP_EXTRA_FIELD_MIN;
    759   uint64_t offset = 0;
    760   while (offset + 4 <= length) {
    761     const uint16_t id = pure_u16(extra_field + offset + 0);
    762     const uint16_t size = pure_u16(extra_field + offset + 2);
    763     offset += 4;
    764     if (offset + size > length) {
    765       return PURE_E_ZIP_EXTRA_FIELD_ATTRIBUTE_OVERFLOW;
    766     }
    767     if (id == 0x0001) {
    768       *zip64 = 1;
    769       uint64_t index = 0;
    770       if (*uncompressed_size == PURE_32_BIT_MAX) {
    771         if ((index += 8) > size) return PURE_E_ZIP_EIEF_64_UNCOMPRESSED_SIZE;
    772         *uncompressed_size = pure_u64(extra_field + offset + index - 8);
    773       }
    774       if (*compressed_size == PURE_32_BIT_MAX) {
    775         if ((index += 8) > size) return PURE_E_ZIP_EIEF_64_COMPRESSED_SIZE;
    776         *compressed_size = pure_u64(extra_field + offset + index - 8);
    777       }
    778       if (*relative_offset == PURE_32_BIT_MAX) {
    779         if ((index += 8) > size) return PURE_E_ZIP_EIEF_64_RELATIVE_OFFSET;
    780         *relative_offset = pure_u64(extra_field + offset + index - 8);
    781       }
    782       if (*disk == PURE_16_BIT_MAX) {
    783         if ((index += 4) > size) return PURE_E_ZIP_EIEF_64_DISK;
    784         *disk = pure_u32(extra_field + offset + index - 4);
    785       }
    786       assert(offset + size <= length);
    787       if (index < size) {
    788         if (pure_zeroes(extra_field, offset + index, offset + size)) {
    789           return PURE_E_ZIP_EIEF_64_UNDERFLOW_ZEROED;
    790         } else {
    791           return PURE_E_ZIP_EIEF_64_UNDERFLOW_BUFFER_BLEED;
    792         }
    793       }
    794       // The EIEF in an LFH must include both uncompressed and compressed size:
    795       if (lfh && index != 16) return PURE_E_ZIP_EIEF_64_LFH;
    796       assert(index == size);
    797       return 0;
    798     }
    799     offset += size;
    800   }
    801   assert(offset <= length);
    802   return 0;
    803 }
    804 
    805 int pure_zip_decode_lfh(
    806   const uint8_t* buffer,
    807   const uint64_t size,
    808   const uint64_t offset,
    809   pure_zip_lfh* header
    810 ) {
    811   assert(offset < size);
    812   if (pure_overflow(offset, PURE_ZIP_LFH_MIN, size)) {
    813     return PURE_E_ZIP_LFH_OVERFLOW;
    814   }
    815   if (!pure_eq(buffer, size, offset, PURE_S_ZIP_LFH, PURE_L_ZIP_LFH)) {
    816     return PURE_E_ZIP_LFH_SIGNATURE;
    817   }
    818   header->offset = offset;
    819   assert(PURE_L_ZIP_LFH == 4);
    820   header->version_minimum = pure_u16(buffer + offset + 4);
    821   header->general_purpose_bit_flag = pure_u16(buffer + offset + 6);
    822   header->compression_method = pure_u16(buffer + offset + 8);
    823   header->last_mod_file_time = pure_u16(buffer + offset + 10);
    824   header->last_mod_file_date = pure_u16(buffer + offset + 12);
    825   header->crc32 = pure_u32(buffer + offset + 14);
    826   header->compressed_size = pure_u32(buffer + offset + 18);
    827   header->uncompressed_size = pure_u32(buffer + offset + 22);
    828   header->file_name_length = pure_u16(buffer + offset + 26);
    829   header->extra_field_length = pure_u16(buffer + offset + 28);
    830   assert(PURE_ZIP_LFH_MIN == 30);
    831   header->length = PURE_ZIP_LFH_MIN;
    832   header->file_name = (uint8_t*) (buffer + header->offset + header->length);
    833   header->length += header->file_name_length;
    834   if (pure_overflow(header->offset, header->length, size)) {
    835     return PURE_E_ZIP_LFH_FILE_NAME_OVERFLOW;
    836   }
    837   header->extra_field = (uint8_t*) (buffer + header->offset + header->length);
    838   header->length += header->extra_field_length;
    839   if (pure_overflow(header->offset, header->length, size)) {
    840     return PURE_E_ZIP_LFH_EXTRA_FIELD_OVERFLOW;
    841   }
    842   // ZIP64:
    843   header->zip64 = 0;
    844   if (
    845     header->compressed_size   == PURE_32_BIT_MAX ||
    846     header->uncompressed_size == PURE_32_BIT_MAX
    847   ) {
    848     uint64_t relative_offset = 0;
    849     uint64_t disk = 0;
    850     int error = pure_zip_decode_eief_64(
    851       header->extra_field,
    852       header->extra_field_length,
    853       &header->compressed_size,
    854       &header->uncompressed_size,
    855       &relative_offset,
    856       &disk,
    857       &header->zip64,
    858       1
    859     );
    860     if (error) return error;
    861   }
    862   {
    863     int error = pure_zip_verify_lfh(header);
    864     if (error) return error;
    865   }
    866   return 0;
    867 }
    868 
    869 int pure_zip_decode_ddr(
    870   const uint8_t* buffer,
    871   const uint64_t size,
    872   uint64_t offset,
    873   pure_zip_ddr* header
    874 ) {
    875   assert(offset < size);
    876   assert(header->zip64 == 0 || header->zip64 == 1);
    877   const uint64_t min = header->zip64 ? PURE_ZIP_DDR_64_MIN : PURE_ZIP_DDR_MIN;
    878   // The DDR signature is optional but we expect at least 4 bytes regardless:
    879   if (pure_overflow(offset, PURE_L_ZIP_DDR, size)) {
    880     return PURE_E_ZIP_DDR_OVERFLOW;
    881   }
    882   if (pure_eq(buffer, size, offset, PURE_S_ZIP_DDR, PURE_L_ZIP_DDR)) {
    883     header->offset = offset;
    884     header->length = PURE_L_ZIP_DDR + min;
    885     offset += PURE_L_ZIP_DDR;
    886   } else {
    887     header->offset = offset;
    888     header->length = min;
    889   }
    890   if (pure_overflow(offset, min, size)) return PURE_E_ZIP_DDR_OVERFLOW;
    891   if (header->zip64) {
    892     assert(min == 4 + 8 + 8);
    893     header->crc32 = pure_u32(buffer + offset + 0);
    894     header->compressed_size = pure_u64(buffer + offset + 4);
    895     header->uncompressed_size = pure_u64(buffer + offset + 12);
    896   } else {
    897     assert(min == 4 + 4 + 4);
    898     header->crc32 = pure_u32(buffer + offset + 0);
    899     header->compressed_size = pure_u32(buffer + offset + 4);
    900     header->uncompressed_size = pure_u32(buffer + offset + 8);
    901   }
    902   return 0;
    903 }
    904 
    905 int pure_zip_decode_cdh(
    906   const uint8_t* buffer,
    907   const uint64_t size,
    908   const uint64_t offset,
    909   pure_zip_cdh* header
    910 ) {
    911   assert(offset < size);
    912   if (pure_overflow(offset, PURE_ZIP_CDH_MIN, size)) {
    913     return PURE_E_ZIP_CDH_OVERFLOW;
    914   }
    915   if (!pure_eq(buffer, size, offset, PURE_S_ZIP_CDH, PURE_L_ZIP_CDH)) {
    916     return PURE_E_ZIP_CDH_SIGNATURE;
    917   }
    918   header->offset = offset;
    919   assert(PURE_L_ZIP_CDH == 4);
    920   header->version_made = pure_u16(buffer + offset + 4);
    921   header->version_minimum = pure_u16(buffer + offset + 6);
    922   header->general_purpose_bit_flag = pure_u16(buffer + offset + 8);
    923   header->compression_method = pure_u16(buffer + offset + 10);
    924   header->last_mod_file_time = pure_u16(buffer + offset + 12);
    925   header->last_mod_file_date = pure_u16(buffer + offset + 14);
    926   header->crc32 = pure_u32(buffer + offset + 16);
    927   header->compressed_size = pure_u32(buffer + offset + 20);
    928   header->uncompressed_size = pure_u32(buffer + offset + 24);
    929   header->file_name_length = pure_u16(buffer + offset + 28);
    930   header->extra_field_length = pure_u16(buffer + offset + 30);
    931   header->file_comment_length = pure_u16(buffer + offset + 32);
    932   header->disk = pure_u16(buffer + offset + 34);
    933   header->internal_file_attributes = pure_u16(buffer + offset + 36);
    934   header->external_file_attributes = pure_u32(buffer + offset + 38);
    935   header->relative_offset = pure_u32(buffer + offset + 42);
    936   assert(PURE_ZIP_CDH_MIN == 46);
    937   header->length = PURE_ZIP_CDH_MIN;
    938   header->file_name = (uint8_t*) (buffer + header->offset + header->length);
    939   header->length += header->file_name_length;
    940   if (pure_overflow(header->offset, header->length, size)) {
    941     return PURE_E_ZIP_CDH_FILE_NAME_OVERFLOW;
    942   }
    943   header->extra_field = (uint8_t*) (buffer + header->offset + header->length);
    944   header->length += header->extra_field_length;
    945   if (pure_overflow(header->offset, header->length, size)) {
    946     return PURE_E_ZIP_CDH_EXTRA_FIELD_OVERFLOW;
    947   }
    948   header->file_comment = (uint8_t*) (buffer + header->offset + header->length);
    949   header->length += header->file_comment_length;
    950   if (pure_overflow(header->offset, header->length, size)) {
    951     return PURE_E_ZIP_CDH_FILE_COMMENT_OVERFLOW;
    952   }
    953   header->unix_mode = 0;
    954   if ((header->version_made >> 8) == PURE_ZIP_VERSION_MADE_UNIX) {
    955     header->unix_mode = header->external_file_attributes >> 16;
    956   }
    957   header->directory = (
    958     (header->unix_mode & PURE_UNIX_MODE_MASK) == PURE_UNIX_MODE_DIR ||
    959     (header->external_file_attributes & 0x0010) ||
    960     (
    961       header->file_name_length > 0 &&
    962       header->file_name[header->file_name_length - 1] == PURE_FORWARD_SLASH
    963     )
    964   );
    965   // ZIP64:
    966   header->zip64 = 0;
    967   if (
    968     header->compressed_size   == PURE_32_BIT_MAX ||
    969     header->uncompressed_size == PURE_32_BIT_MAX ||
    970     header->relative_offset   == PURE_32_BIT_MAX ||
    971     header->disk              == PURE_16_BIT_MAX
    972   ) {
    973     int error = pure_zip_decode_eief_64(
    974       header->extra_field,
    975       header->extra_field_length,
    976       &header->compressed_size,
    977       &header->uncompressed_size,
    978       &header->relative_offset,
    979       &header->disk,
    980       &header->zip64,
    981       0
    982     );
    983     if (error) return error;
    984   }
    985   // These are critical checks and must not be moved to pure_zip_verify_cdh():
    986   if (header->relative_offset > size) {
    987     return PURE_E_ZIP_CDH_RELATIVE_OFFSET_OVERFLOW;
    988   }
    989   if (header->relative_offset > offset) {
    990     return PURE_E_ZIP_CDH_RELATIVE_OFFSET_OVERLAP;
    991   }
    992   {
    993     int error = pure_zip_verify_cdh(header);
    994     if (error) return error;
    995   }
    996   return 0;
    997 }
    998 
    999 int pure_zip_decode_eocdl_64(
   1000   const uint8_t* buffer,
   1001   const uint64_t size,
   1002   const uint64_t offset,
   1003   pure_zip_eocdl_64* header
   1004 ) {
   1005   assert(offset < size);
   1006   if (pure_overflow(offset, PURE_ZIP_EOCDL_64, size)) {
   1007     return PURE_E_ZIP_EOCDL_64_OVERFLOW;
   1008   }
   1009   if (
   1010     !pure_eq(buffer, size, offset, PURE_S_ZIP_EOCDL_64, PURE_L_ZIP_EOCDL_64)
   1011   ) {
   1012     return PURE_E_ZIP_EOCDL_64_SIGNATURE;
   1013   }
   1014   header->offset = offset;
   1015   assert(PURE_L_ZIP_EOCDL_64 == 4);
   1016   header->disk = pure_u32(buffer + offset + 4);
   1017   header->eocdr_64_offset = pure_u64(buffer + offset + 8);
   1018   header->disks = pure_u32(buffer + offset + 16);
   1019   assert(PURE_ZIP_EOCDL_64 == 20);
   1020   header->length = PURE_ZIP_EOCDL_64;
   1021   if (header->disk != 0) return PURE_E_ZIP_EOCDL_64_DISK;
   1022   if (
   1023     pure_overflow(
   1024       header->eocdr_64_offset,
   1025       PURE_ZIP_EOCDR_64_MIN,
   1026       header->offset
   1027     )
   1028   ) {
   1029     return PURE_E_ZIP_EOCDR_EOCDL_64_OVERFLOW;
   1030   }
   1031   if (header->disks != 0 && header->disks != 1) {
   1032     return PURE_E_ZIP_EOCDL_64_DISKS;
   1033   }
   1034   return 0;
   1035 }
   1036 
   1037 int pure_zip_decode_eocdr_64(
   1038   const uint8_t* buffer,
   1039   const uint64_t size,
   1040   const uint64_t offset,
   1041   pure_zip_eocdr_64* header
   1042 ) {
   1043   assert(offset < size);
   1044   if (pure_overflow(offset, PURE_ZIP_EOCDR_64_MIN, size)) {
   1045     return PURE_E_ZIP_EOCDR_64_OVERFLOW;
   1046   }
   1047   if (
   1048     !pure_eq(buffer, size, offset, PURE_S_ZIP_EOCDR_64, PURE_L_ZIP_EOCDR_64)
   1049   ) {
   1050     return PURE_E_ZIP_EOCDR_64_SIGNATURE;
   1051   }
   1052   header->offset = offset;
   1053   assert(PURE_L_ZIP_EOCDR_64 == 4);
   1054   // The value stored in "size of zip64 end of central directory record" is the
   1055   // size of the remaining record and excludes the leading 12 bytes:
   1056   // size_remaining = PURE_ZIP_EOCDR_64_MIN + extensible_data_sector_length - 12
   1057   // extensible_data_sector_length = size_remaining - PURE_ZIP_EOCDR_64_MIN + 12
   1058   header->extensible_data_sector_length = (
   1059     pure_u64(buffer + offset + 4) - (PURE_ZIP_EOCDR_64_MIN - 12)
   1060   );
   1061   header->version_made = pure_u16(buffer + offset + 12);
   1062   header->version_minimum = pure_u16(buffer + offset + 14);
   1063   header->disk = pure_u32(buffer + offset + 16);
   1064   header->cd_disk = pure_u32(buffer + offset + 20);
   1065   header->cd_disk_records = pure_u64(buffer + offset + 24);
   1066   header->cd_records = pure_u64(buffer + offset + 32);
   1067   header->cd_size = pure_u64(buffer + offset + 40);
   1068   header->cd_offset = pure_u64(buffer + offset + 48);
   1069   assert(PURE_ZIP_EOCDR_64_MIN == 56);
   1070   header->length = PURE_ZIP_EOCDR_64_MIN;
   1071   header->extensible_data_sector = (
   1072     (uint8_t*) (buffer + header->offset + header->length)
   1073   );
   1074   header->length += header->extensible_data_sector_length;
   1075   return 0;
   1076 }
   1077 
   1078 int pure_zip_decode_eocdr_64_inherit(
   1079   pure_zip_eocdr* a,
   1080   const pure_zip_eocdr_64* b
   1081 ) {
   1082   // Inherit only those values that are maxed out in the EOCDR:
   1083   if (a->disk == PURE_16_BIT_MAX) a->disk = b->disk;
   1084   if (a->cd_disk == PURE_16_BIT_MAX) a->cd_disk = b->cd_disk;
   1085   if (a->cd_disk_records == PURE_16_BIT_MAX) {
   1086     a->cd_disk_records = b->cd_disk_records;
   1087   }
   1088   if (a->cd_records == PURE_16_BIT_MAX) a->cd_records = b->cd_records;
   1089   if (a->cd_size == PURE_32_BIT_MAX) a->cd_size = b->cd_size;
   1090   if (a->cd_offset == PURE_32_BIT_MAX) a->cd_offset = b->cd_offset;
   1091   // Verify that all values are now in agreement between the EOCDR and EOCDR_64:
   1092   if (a->disk != b->disk) return PURE_E_ZIP_DIFF_EOCDR_DISK;
   1093   if (a->cd_disk != b->cd_disk) return PURE_E_ZIP_DIFF_EOCDR_CD_DISK;
   1094   if (a->cd_disk_records != b->cd_disk_records) {
   1095     return PURE_E_ZIP_DIFF_EOCDR_CD_DISK_RECORDS;
   1096   }
   1097   if (a->cd_records != b->cd_records) return PURE_E_ZIP_DIFF_EOCDR_CD_RECORDS;
   1098   if (a->cd_size != b->cd_size) return PURE_E_ZIP_DIFF_EOCDR_CD_SIZE;
   1099   if (a->cd_offset != b->cd_offset) return PURE_E_ZIP_DIFF_EOCDR_CD_OFFSET;
   1100   return 0;
   1101 }
   1102 
   1103 int pure_zip_decode_eocdr_64_upgrade(
   1104   const uint8_t* buffer,
   1105   const uint64_t size,
   1106   pure_zip_eocdr* header
   1107 ) {
   1108   header->zip64 = 0;
   1109   if (
   1110     header->disk            != PURE_16_BIT_MAX &&
   1111     header->cd_disk         != PURE_16_BIT_MAX &&
   1112     header->cd_disk_records != PURE_16_BIT_MAX &&
   1113     header->cd_records      != PURE_16_BIT_MAX &&
   1114     header->cd_size         != PURE_32_BIT_MAX &&
   1115     header->cd_offset       != PURE_32_BIT_MAX
   1116   ) {
   1117     return 0;
   1118   }
   1119   pure_zip_eocdl_64 eocdl_64;
   1120   pure_zip_eocdr_64 eocdr_64;
   1121   if (header->offset < PURE_ZIP_EOCDL_64) {
   1122     return PURE_E_ZIP_EOCDL_64_NEGATIVE_OFFSET;
   1123   }
   1124   assert(header->offset >= PURE_ZIP_EOCDL_64);
   1125   {
   1126     int error = pure_zip_decode_eocdl_64(
   1127       buffer,
   1128       size,
   1129       header->offset - PURE_ZIP_EOCDL_64,
   1130       &eocdl_64
   1131     );
   1132     // A header field may actually be FFFF or FFFFFFFF without any Zip64 format:
   1133     if (error == PURE_E_ZIP_EOCDL_64_SIGNATURE) return 0;
   1134     if (error) return error;
   1135   }
   1136   assert(!pure_overflow(eocdl_64.offset, eocdl_64.length, header->offset));
   1137   assert(eocdl_64.offset + eocdl_64.length == header->offset);
   1138   assert(
   1139     !pure_overflow(
   1140       eocdl_64.eocdr_64_offset,
   1141       PURE_ZIP_EOCDR_64_MIN,
   1142       eocdl_64.offset
   1143     )
   1144   );
   1145   {
   1146     int error = pure_zip_decode_eocdr_64(
   1147       buffer,
   1148       size,
   1149       eocdl_64.eocdr_64_offset,
   1150       &eocdr_64
   1151     );
   1152     if (error) return error;
   1153   }
   1154   const uint64_t eocdl_offset = eocdr_64.offset + eocdr_64.length;
   1155   assert(eocdl_offset > eocdr_64.offset);
   1156   if (eocdl_offset > eocdl_64.offset) return PURE_E_ZIP_EOCDR_EOCDL_64_OVERFLOW;
   1157   if (eocdl_offset < eocdl_64.offset) {
   1158     assert(eocdl_64.offset <= size);
   1159     if (pure_zeroes(buffer, eocdl_offset, eocdl_64.offset)) {
   1160       return PURE_E_ZIP_EOCDR_EOCDL_64_UNDERFLOW_ZEROED;
   1161     } else {
   1162       return PURE_E_ZIP_EOCDR_EOCDL_64_UNDERFLOW_BUFFER_BLEED;
   1163     }
   1164   }
   1165   assert(!pure_overflow(eocdr_64.offset, eocdr_64.length, eocdl_64.offset));
   1166   assert(eocdr_64.offset + eocdr_64.length == eocdl_64.offset);
   1167   {
   1168     int error = pure_zip_decode_eocdr_64_inherit(header, &eocdr_64);
   1169     if (error) return error;
   1170   }
   1171   header->zip64 = 1;
   1172   header->offset = eocdr_64.offset;
   1173   header->length = eocdr_64.length + eocdl_64.length + header->length;
   1174   return 0;
   1175 }
   1176 
   1177 int pure_zip_decode_eocdr(
   1178   const uint8_t* buffer,
   1179   const uint64_t size,
   1180   const uint64_t offset,
   1181   pure_zip_eocdr* header
   1182 ) {
   1183   assert(offset < size);
   1184   if (pure_overflow(offset, PURE_ZIP_EOCDR_MIN, size)) {
   1185     return PURE_E_ZIP_EOCDR_OVERFLOW;
   1186   }
   1187   if (!pure_eq(buffer, size, offset, PURE_S_ZIP_EOCDR, PURE_L_ZIP_EOCDR)) {
   1188     return PURE_E_ZIP_EOCDR_SIGNATURE;
   1189   }
   1190   // We consider header->offset to start at EOCDR_64 or EOCDR.
   1191   // We consider header->length to include EOCDR_64, EOCDL_64 and EOCDR.
   1192   header->offset = offset;
   1193   assert(PURE_L_ZIP_EOCDR == 4);
   1194   header->disk = pure_u16(buffer + offset + 4);
   1195   header->cd_disk = pure_u16(buffer + offset + 6);
   1196   header->cd_disk_records = pure_u16(buffer + offset + 8);
   1197   header->cd_records = pure_u16(buffer + offset + 10);
   1198   header->cd_size = pure_u32(buffer + offset + 12);
   1199   header->cd_offset = pure_u32(buffer + offset + 16);
   1200   header->comment_length = pure_u16(buffer + offset + 20);
   1201   assert(PURE_ZIP_EOCDR_MIN == 22);
   1202   header->length = PURE_ZIP_EOCDR_MIN;
   1203   header->comment = (uint8_t*) (buffer + header->offset + header->length);
   1204   header->length += header->comment_length;
   1205   if (pure_overflow(header->offset, header->length, size)) {
   1206     return PURE_E_ZIP_EOCDR_COMMENT_OVERFLOW;
   1207   }
   1208   {
   1209     // If we find an EOCDR_64 and EOCDL_64, we modify header->(offset, length):
   1210     const uint64_t length = PURE_ZIP_EOCDR_MIN + header->comment_length;
   1211     assert(header->length == length);
   1212     int error = pure_zip_decode_eocdr_64_upgrade(buffer, size, header);
   1213     if (error) return error;
   1214     assert(header->zip64 == 0 || header->zip64 == 1);
   1215     if (header->zip64) {
   1216       const uint64_t length_64 = PURE_ZIP_EOCDR_64_MIN + PURE_ZIP_EOCDL_64;
   1217       assert(header->offset <= offset - length_64);
   1218       assert(header->length >= length + length_64);
   1219     } else {
   1220       assert(header->offset == offset);
   1221       assert(header->length == length);
   1222     }
   1223   }
   1224   if (header->cd_size < header->cd_records * PURE_ZIP_CDH_MIN) {
   1225     return PURE_E_ZIP_EOCDR_SIZE_OVERFLOW;
   1226   }
   1227   if (header->cd_size > 0 && header->cd_records == 0) {
   1228     return PURE_E_ZIP_EOCDR_SIZE_UNDERFLOW;
   1229   }
   1230   if (pure_overflow(header->cd_offset, header->cd_size, header->offset)) {
   1231     return PURE_E_ZIP_CD_EOCDR_OVERFLOW;
   1232   }
   1233   if (header->disk != 0 || header->cd_disk != 0) {
   1234     return PURE_E_ZIP_MULTIPLE_DISKS;
   1235   }
   1236   if (header->cd_disk_records != header->cd_records) {
   1237     return PURE_E_ZIP_EOCDR_RECORDS;
   1238   }
   1239   {
   1240     int error = pure_zip_verify_string(
   1241       header->comment,
   1242       header->comment_length,
   1243       // The EOCDR has no General Purpose Bit Flag with which to indicate UTF-8.
   1244       // Therefore, the comment encoding must always be CP437:
   1245       0
   1246     );
   1247     if (error) return error;
   1248   }
   1249   uint64_t suffix_offset = header->offset + header->length;
   1250   if (suffix_offset < size) {
   1251     if (pure_zeroes(buffer, suffix_offset, size)) {
   1252       return PURE_E_ZIP_APPENDED_DATA_ZEROED;
   1253     } else {
   1254       return PURE_E_ZIP_APPENDED_DATA_BUFFER_BLEED;
   1255     }
   1256   }
   1257   return 0;
   1258 }
   1259 
   1260 // Compare LFH against CDH taking Data Descriptor Record into account:
   1261 int pure_zip_diff_cld(
   1262   const uint64_t cdh_value,
   1263   const uint64_t lfh_value,
   1264   const pure_zip_lfh* lfh
   1265 ) {
   1266   // LFH matches CDH:
   1267   if (lfh_value == cdh_value) return 0;
   1268   // LFH delegates value to Data Descriptor:
   1269   if ((lfh->general_purpose_bit_flag & (1 << 3)) && lfh_value == 0) return 0;
   1270   // LFH diverges from CDH:
   1271   return 1;
   1272 }
   1273 
   1274 // Compare DDR against CDH:
   1275 int pure_zip_diff_cdh_ddr(const pure_zip_cdh* cdh, const pure_zip_ddr* ddr) {
   1276   if (ddr->crc32 != cdh->crc32) {
   1277     return PURE_E_ZIP_DIFF_DDR_CRC32;
   1278   }
   1279   if (ddr->compressed_size != cdh->compressed_size) {
   1280     return PURE_E_ZIP_DIFF_DDR_COMPRESSED_SIZE;
   1281   }
   1282   if (ddr->uncompressed_size != cdh->uncompressed_size) {
   1283     return PURE_E_ZIP_DIFF_DDR_UNCOMPRESSED_SIZE;
   1284   }
   1285   return 0;
   1286 }
   1287 
   1288 // Compare LFH against CDH:
   1289 int pure_zip_diff_cdh_lfh(const pure_zip_cdh* cdh, const pure_zip_lfh* lfh) {
   1290   if (lfh->general_purpose_bit_flag != cdh->general_purpose_bit_flag) {
   1291     return PURE_E_ZIP_DIFF_LFH_GENERAL_PURPOSE_BIT_FLAG;
   1292   }
   1293   if (lfh->compression_method != cdh->compression_method) {
   1294     return PURE_E_ZIP_DIFF_LFH_COMPRESSION_METHOD;
   1295   }
   1296   if (lfh->last_mod_file_time != cdh->last_mod_file_time) {
   1297     return PURE_E_ZIP_DIFF_LFH_LAST_MOD_FILE_TIME;
   1298   }
   1299   if (lfh->last_mod_file_date != cdh->last_mod_file_date) {
   1300     return PURE_E_ZIP_DIFF_LFH_LAST_MOD_FILE_DATE;
   1301   }
   1302   if (pure_zip_diff_cld(cdh->crc32, lfh->crc32, lfh)) {
   1303     return PURE_E_ZIP_DIFF_LFH_CRC32;
   1304   }
   1305   if (pure_zip_diff_cld(cdh->compressed_size, lfh->compressed_size, lfh)) {
   1306     return PURE_E_ZIP_DIFF_LFH_COMPRESSED_SIZE;
   1307   }
   1308   if (pure_zip_diff_cld(cdh->uncompressed_size, lfh->uncompressed_size, lfh)) {
   1309     return PURE_E_ZIP_DIFF_LFH_UNCOMPRESSED_SIZE;
   1310   }
   1311   if (lfh->file_name_length != cdh->file_name_length) {
   1312     return PURE_E_ZIP_DIFF_LFH_FILE_NAME_LENGTH;
   1313   }
   1314   // We assume decode_lfh() and decode_cdh() have already checked for overflow:
   1315   if (memcmp(lfh->file_name, cdh->file_name, (size_t) cdh->file_name_length)) {
   1316     return PURE_E_ZIP_DIFF_LFH_FILE_NAME;
   1317   }
   1318   return 0;
   1319 }
   1320 
   1321 // Compare LFH against DDR:
   1322 int pure_zip_diff_ddr_lfh(const pure_zip_ddr* ddr, const pure_zip_lfh* lfh) {
   1323   if (lfh->crc32 != 0 && lfh->crc32 != ddr->crc32) {
   1324     return PURE_E_ZIP_DIFF_LFH_DDR_CRC32;
   1325   }
   1326   if (
   1327     lfh->compressed_size != 0 &&
   1328     lfh->compressed_size != ddr->compressed_size
   1329   ) {
   1330     return PURE_E_ZIP_DIFF_LFH_DDR_COMPRESSED_SIZE;
   1331   }
   1332   if (
   1333     lfh->uncompressed_size != 0 &&
   1334     lfh->uncompressed_size != ddr->uncompressed_size
   1335   ) {
   1336     return PURE_E_ZIP_DIFF_LFH_DDR_UNCOMPRESSED_SIZE;
   1337   }
   1338   return 0;
   1339 }
   1340 
   1341 int pure_zip_inflate_raw(
   1342   const uint8_t* compressed,
   1343   const uint64_t compressed_size,
   1344   const uint8_t* uncompressed,
   1345   const uint64_t uncompressed_size
   1346 ) {
   1347   if (uncompressed_size == 0) return 0;
   1348   z_stream z;
   1349   z.zalloc = Z_NULL;
   1350   z.zfree = Z_NULL;
   1351   z.opaque = Z_NULL;
   1352   z.avail_in = 0;
   1353   z.avail_out = 0;
   1354   z.next_in = Z_NULL;
   1355   z.next_out = Z_NULL;
   1356   // We indicate raw inflate to zlib by setting window_bits to negative:
   1357   // We further allow for the greatest possible compression window size (15).
   1358   int window_bits = -15;
   1359   {
   1360     int error = inflateInit2(&z, window_bits);
   1361     if (error != Z_OK) return PURE_E_ZIP_INFLATE;
   1362   }
   1363   // TO DO: Switch to streaming zlib inflate to support larger file sizes.
   1364   assert(sizeof(z.avail_in) >= 4);
   1365   assert(sizeof(z.avail_out) >= 4);
   1366   assert(compressed_size <= UINT32_MAX);
   1367   assert(uncompressed_size <= UINT32_MAX);
   1368   z.avail_in = (unsigned int) compressed_size;
   1369   z.avail_out = (unsigned int) uncompressed_size;
   1370   z.next_in = (uint8_t*) compressed;
   1371   z.next_out = (uint8_t*) uncompressed;
   1372   int error = inflate(&z, Z_FINISH);
   1373   (void) inflateEnd(&z);
   1374   if (error == Z_STREAM_END) {
   1375     if (z.avail_in > 0) return PURE_E_ZIP_INFLATE_COMPRESSED_UNDERFLOW;
   1376     if (z.avail_out > 0) return PURE_E_ZIP_INFLATE_UNCOMPRESSED_UNDERFLOW;
   1377     assert(z.total_in == compressed_size);
   1378     assert(z.total_out == uncompressed_size);
   1379     return 0;
   1380   } else {
   1381     if (error == Z_NEED_DICT) return PURE_E_ZIP_INFLATE_DICTIONARY;
   1382     if (error == Z_STREAM_ERROR) return PURE_E_ZIP_INFLATE_STREAM;
   1383     if (error == Z_DATA_ERROR) return PURE_E_ZIP_INFLATE_DATA;
   1384     if (error == Z_MEM_ERROR) return PURE_E_ZIP_INFLATE_MEMORY;
   1385     if (error == Z_BUF_ERROR) {
   1386       // If avail_out and avail_in are both zero then it is likely an output
   1387       // overflow since the input stream has more chance of terminating first.
   1388       // We therefore check avail_out first since we cannot further distinguish.
   1389       if (!z.avail_out) return PURE_E_ZIP_BOMB_INFLATE_UNCOMPRESSED_OVERFLOW;
   1390       if (!z.avail_in) return PURE_E_ZIP_BOMB_INFLATE_COMPRESSED_OVERFLOW;
   1391     }
   1392     return PURE_E_ZIP_INFLATE;
   1393   }
   1394 }
   1395 
   1396 int pure_zip_locate_eocdr(
   1397   const uint8_t* buffer,
   1398   const uint64_t size,
   1399   uint64_t* offset
   1400 ) {
   1401   assert(*offset == 0);
   1402   if (size < PURE_ZIP_EOCDR_MIN) return PURE_E_ZIP_TOO_SMALL;
   1403   int64_t index = size - PURE_ZIP_EOCDR_MIN;
   1404   // The EOCDR can be at most EOCDR_MIN plus a variable length comment.
   1405   // The variable length of the comment can be at most a 16-bit integer.
   1406   // Assuming no garbage after the EOCDR, our maximum search distance is:
   1407   int64_t floor = size - PURE_ZIP_EOCDR_MIN - PURE_ZIP_EOCDR_COMMENT_MAX;
   1408   if (index < 0) index = 0;
   1409   if (floor < 0) floor = 0;
   1410   assert(!pure_overflow((uint64_t) index, PURE_L_ZIP_EOCDR, size));
   1411   assert(PURE_L_ZIP_EOCDR == 4);
   1412   assert(sizeof(PURE_S_ZIP_EOCDR) == 4);
   1413   while (index >= floor) {
   1414     if (
   1415       buffer[index + 0] == PURE_S_ZIP_EOCDR[0] &&
   1416       buffer[index + 1] == PURE_S_ZIP_EOCDR[1] &&
   1417       buffer[index + 2] == PURE_S_ZIP_EOCDR[2] &&
   1418       buffer[index + 3] == PURE_S_ZIP_EOCDR[3]
   1419     ) {
   1420       assert(size >= PURE_ZIP_EOCDR_MIN);
   1421       assert(!pure_overflow((uint64_t) index, 4, size));
   1422       *offset = (uint64_t) index;
   1423       return 0;
   1424     }
   1425     index--;
   1426   }
   1427   return PURE_E_ZIP_EOCDR_NOT_FOUND;
   1428 }
   1429 
   1430 int pure_zip_locate_first_lfh(
   1431   const uint8_t* buffer,
   1432   const uint64_t size,
   1433   const pure_zip_eocdr* eocdr,
   1434   uint64_t* offset
   1435 ) {
   1436   assert(size >= 8);
   1437   assert(*offset == 0);
   1438   // We expect a Local File Header or End Of Central Directory Record signature:
   1439   // TO DO: Test empty file OK.
   1440   // TO DO: Test non-empty file OK.
   1441   const uint8_t* string = eocdr->cd_records ? PURE_S_ZIP_LFH : PURE_S_ZIP_EOCDR;
   1442   const uint64_t string_size = PURE_L_ZIP_LFH;
   1443   assert(PURE_L_ZIP_EOCDR == PURE_L_ZIP_LFH);
   1444   if (pure_eq(buffer, size, 0, string, string_size)) {
   1445     *offset = 0;
   1446     return 0;
   1447   }
   1448   // A spanned/split archive may be preceded by a special spanning signature:
   1449   // See APPNOTE 8.5.3 and 8.5.4.
   1450   // TO DO: Test spanned file OK (empty, non-empty).
   1451   // TO DO: Test split file OK (empty, non-empty).
   1452   if (
   1453     pure_eq(buffer, size, 0, PURE_S_ZIP_SPAN, PURE_L_ZIP_SPAN) ||
   1454     pure_eq(buffer, size, 0, PURE_S_ZIP_TEMP, PURE_L_ZIP_TEMP)
   1455   ) {
   1456     assert(PURE_L_ZIP_TEMP == PURE_L_ZIP_SPAN);
   1457     if (pure_eq(buffer, size, PURE_L_ZIP_SPAN, string, string_size)) {
   1458       *offset = PURE_L_ZIP_SPAN;
   1459       return 0;
   1460     }
   1461   }
   1462   uint64_t prepended_data_size = 0;
   1463   int error = pure_search(
   1464     buffer,                             // buffer
   1465     size,                               // buffer_size
   1466     0,                                  // search_offset
   1467     PURE_ZIP_PREPENDED_DATA_SEARCH_MAX, // search_size
   1468     string,                             // string
   1469     string_size,                        // string_size
   1470     &prepended_data_size                // offset
   1471   );
   1472   // We could not find any end to the prepended data:
   1473   if (error) return PURE_E_ZIP_PREPENDED_DATA;
   1474   assert(prepended_data_size != 0);
   1475   assert(prepended_data_size <= size);
   1476   if (pure_zeroes(buffer, 0, prepended_data_size)) {
   1477     return PURE_E_ZIP_PREPENDED_DATA_ZEROED;
   1478   } else {
   1479     return PURE_E_ZIP_PREPENDED_DATA_BUFFER_BLEED;
   1480   }
   1481 }
   1482 
   1483 // Forward declaration required by pure_zip_data():
   1484 int pure_zip_meta_data(
   1485   pure_ctx* ctx,
   1486   const uint8_t* buffer,
   1487   const uint64_t size
   1488 );
   1489 
   1490 int pure_zip_data(
   1491   pure_ctx* ctx,
   1492   const uint8_t* buffer,
   1493   const pure_zip_cdh* cdh,
   1494   const pure_zip_lfh* lfh,
   1495   uint8_t** data,
   1496   uint64_t* data_size
   1497 ) {
   1498   if (cdh->directory) {
   1499     assert(cdh->compressed_size == 0);
   1500     assert(cdh->uncompressed_size == 0);
   1501     return 0;
   1502   }
   1503   if (cdh->uncompressed_size == 0) {
   1504     if (cdh->compressed_size == 0) return 0;
   1505     if (
   1506       cdh->compressed_size == 2 &&
   1507       cdh->compression_method == PURE_ZIP_COMPRESSION_METHOD_DEFLATE &&
   1508       buffer[cdh->relative_offset + lfh->length + 0] == 3 &&
   1509       buffer[cdh->relative_offset + lfh->length + 1] == 0
   1510     ) {
   1511       return 0;
   1512     }
   1513     return PURE_E_ZIP_AD_NIHILO;
   1514   }
   1515   assert(cdh->compressed_size > 0);
   1516   assert(cdh->compressed_size <= SIZE_MAX);
   1517   assert(cdh->uncompressed_size > 0);
   1518   assert(cdh->uncompressed_size <= SIZE_MAX);
   1519   // We verify the compression ratio here, after first checking for LFH overlap:
   1520   // We do this to return PURE_E_ZIP_BOMB_FIFIELD before PURE_E_ZIP_BOMB_RATIO.
   1521   if (
   1522     pure_overflow(ctx->compressed_size, cdh->compressed_size, UINT64_MAX) ||
   1523     pure_overflow(ctx->uncompressed_size, cdh->uncompressed_size, UINT64_MAX)
   1524   ) {
   1525     return PURE_E_UINT64_OVERFLOW;
   1526   }
   1527   ctx->compressed_size += cdh->compressed_size;
   1528   ctx->uncompressed_size += cdh->uncompressed_size;
   1529   {
   1530     int error = pure_zip_verify_compression_ratio(
   1531       ctx->compressed_size,
   1532       ctx->uncompressed_size
   1533     );
   1534     if (error) return error;
   1535   }
   1536   uint8_t* raw = NULL;
   1537   if (cdh->compression_method == PURE_ZIP_COMPRESSION_METHOD_DEFLATE) {
   1538     {
   1539       int error = pure_realloc(data, data_size, cdh->uncompressed_size);
   1540       if (error) return error;
   1541     }
   1542     assert(*data != NULL);
   1543     assert(*data_size >= cdh->uncompressed_size);
   1544     {
   1545       int error = pure_zip_inflate_raw(
   1546         buffer + cdh->relative_offset + lfh->length, // compressed
   1547         cdh->compressed_size,                        // compressed_size
   1548         *data,                                       // uncompressed
   1549         cdh->uncompressed_size                       // uncompressed_size
   1550       );
   1551       if (error) return error;
   1552     }
   1553     raw = *data;
   1554   } else {
   1555     assert(cdh->compression_method == PURE_ZIP_COMPRESSION_METHOD_NONE);
   1556     raw = (uint8_t*) (buffer + cdh->relative_offset + lfh->length);
   1557   }
   1558   assert(raw != NULL);
   1559   {
   1560     uint64_t checksum = 0;
   1561     int error = pure_zip_crc32(raw, cdh->uncompressed_size, &checksum);
   1562     if (error) return error;
   1563     if (checksum != cdh->crc32) return PURE_E_ZIP_CRC32;
   1564   }
   1565   // TO DO: Check for common ZIP extensions in addition to PK signature.
   1566   if (pure_eq(raw, cdh->uncompressed_size, 0, PURE_S_ZIP_PK, PURE_L_ZIP_PK)) {
   1567     int error = pure_zip_meta_data(ctx, raw, cdh->uncompressed_size);
   1568     if (error) return error;
   1569   } else {
   1570     if ((++ctx->files) > PURE_FILES_MAX) return PURE_E_ZIP_BOMB_FILES;
   1571   }
   1572   return 0;
   1573 }
   1574 
   1575 int pure_zip_meta(
   1576   pure_ctx* ctx,
   1577   const uint8_t* buffer,
   1578   const uint64_t size,
   1579   uint8_t** data,
   1580   uint64_t* data_size
   1581 ) {
   1582   // Update and check context against limits:
   1583   if ((++ctx->depth) > PURE_DEPTH_MAX) return PURE_E_ZIP_BOMB_DEPTH;
   1584   if ((++ctx->files) > PURE_FILES_MAX) return PURE_E_ZIP_BOMB_FILES;
   1585   if ((++ctx->archives) > PURE_ARCHIVES_MAX) return PURE_E_ZIP_BOMB_ARCHIVES;
   1586   if (pure_overflow(ctx->size, size, UINT64_MAX)) return PURE_E_UINT64_OVERFLOW;
   1587   if ((ctx->size += size) > PURE_SIZE_MAX) return PURE_E_SIZE_MAX;
   1588 
   1589   // A zip file must contain at least an end of central directory record:
   1590   if (size < PURE_ZIP_EOCDR_MIN) return PURE_E_ZIP_TOO_SMALL;
   1591 
   1592   // ZIP64:
   1593   if (size > 4294967295) return PURE_E_ZIP_SIZE_4GB;
   1594 
   1595   // Malicious archive signatures (almost certainly when masquerading as a ZIP):
   1596   if (pure_eq(buffer, size, 0, PURE_S_RAR, PURE_L_RAR)) return PURE_E_ZIP_RAR;
   1597   if (pure_eq(buffer, size, 0, PURE_S_TAR, PURE_L_TAR)) return PURE_E_ZIP_TAR;
   1598   if (pure_eq(buffer, size, 0, PURE_S_XAR, PURE_L_XAR)) return PURE_E_ZIP_XAR;
   1599   
   1600   // Locate and decode end of central directory record:
   1601   uint64_t eocdr_offset = 0;
   1602   pure_zip_eocdr eocdr;
   1603   {
   1604     int error = pure_zip_locate_eocdr(buffer, size, &eocdr_offset);
   1605     if (error) return error;
   1606   }
   1607   {
   1608     int error = pure_zip_decode_eocdr(buffer, size, eocdr_offset, &eocdr);
   1609     if (error) return error;
   1610   }
   1611 
   1612   // Locate the offset of the first local file header:
   1613   uint64_t lfh_offset = 0;
   1614   {
   1615     int error = pure_zip_locate_first_lfh(buffer, size, &eocdr, &lfh_offset);
   1616     if (error) return error;
   1617     assert(lfh_offset == 0 || lfh_offset == PURE_L_ZIP_SPAN);
   1618   }
   1619 
   1620   // Compare central directory headers with local file headers:
   1621   pure_zip_cdh cdh;
   1622   pure_zip_lfh lfh;
   1623   pure_zip_ddr ddr;
   1624   pure_zip_cdh cdh_p;
   1625   pure_zip_lfh lfh_p;
   1626   uint64_t cdh_offset = eocdr.cd_offset;
   1627   uint64_t cdh_record = 0;
   1628   while (cdh_record < eocdr.cd_records) {
   1629     // Central Directory Header:
   1630     {
   1631       int error = pure_zip_decode_cdh(buffer, size, cdh_offset, &cdh);
   1632       if (error) return error;
   1633     }
   1634     if (lfh_offset > cdh.relative_offset) {
   1635       if (
   1636         cdh.directory && cdh.relative_offset == 0 &&
   1637         cdh.crc32 == 0 && cdh.compressed_size == 0 && cdh.uncompressed_size == 0
   1638       ) {
   1639         return PURE_E_ZIP_DIRECTORY_HAS_NO_LFH;
   1640       }
   1641       return PURE_E_ZIP_BOMB_FIFIELD;
   1642     }
   1643     if (lfh_offset < cdh.relative_offset) {
   1644       assert(cdh.relative_offset <= size);
   1645       if (pure_zeroes(buffer, lfh_offset, cdh.relative_offset)) {
   1646         return PURE_E_ZIP_LFH_UNDERFLOW_ZEROED;
   1647       } else {
   1648         return PURE_E_ZIP_LFH_UNDERFLOW_BUFFER_BLEED;
   1649       }
   1650     }
   1651     // Local File Header:
   1652     {
   1653       assert(cdh.relative_offset == lfh_offset);
   1654       int error = pure_zip_decode_lfh(buffer, size, cdh.relative_offset, &lfh);
   1655       if (error) return error;
   1656     }
   1657     {
   1658       int error = pure_zip_diff_cdh_lfh(&cdh, &lfh);
   1659       if (error) return error;
   1660     }
   1661     {
   1662       int error = pure_zip_verify_symlink(&cdh, &lfh, buffer);
   1663       if (error) return error;
   1664     }
   1665     assert(lfh.length >= PURE_ZIP_LFH_MIN);
   1666     lfh_offset += lfh.length;
   1667     // File Data (compressed or uncompressed):
   1668     if (pure_overflow(lfh_offset, cdh.compressed_size, UINT64_MAX)) {
   1669       return PURE_E_UINT64_OVERFLOW;
   1670     }
   1671     lfh_offset += cdh.compressed_size;
   1672     if (lfh_offset > size) return PURE_E_ZIP_LFH_DATA_OVERFLOW;
   1673     // Data Descriptor Record (optional):
   1674     if (lfh.general_purpose_bit_flag & (1 << 3)) {
   1675       {
   1676         ddr.zip64 = lfh.zip64;
   1677         int error = pure_zip_decode_ddr(buffer, size, lfh_offset, &ddr);
   1678         if (error) return error;
   1679       }
   1680       {
   1681         int error = pure_zip_diff_cdh_ddr(&cdh, &ddr);
   1682         if (error) return error;
   1683       }
   1684       {
   1685         int error = pure_zip_diff_ddr_lfh(&ddr, &lfh);
   1686         if (error) return error;
   1687       }
   1688       lfh_offset += ddr.length;
   1689     }
   1690     if (lfh_offset > eocdr.cd_offset) return PURE_E_ZIP_LF_OVERFLOW;
   1691     // We descend into the data only after checking for LFH overlap above:
   1692     // We can therefore descend only after decoding at least two entries.
   1693     if (cdh_record > 0) {
   1694       int error = pure_zip_data(ctx, buffer, &cdh_p, &lfh_p, data, data_size);
   1695       if (error) return error;
   1696     }
   1697     // Shallow copy the CDH and LFH to descend next time around the loop:
   1698     cdh_p = cdh;
   1699     lfh_p = lfh;
   1700     assert(cdh.length >= PURE_ZIP_CDH_MIN);
   1701     cdh_offset += cdh.length;
   1702     cdh_record++;
   1703   }
   1704   // Descend into the previous CDH and LFH:
   1705   if (cdh_record > 0) {
   1706     int error = pure_zip_data(ctx, buffer, &cdh_p, &lfh_p, data, data_size);
   1707     if (error) return error;
   1708   }
   1709   if (lfh_offset > eocdr.cd_offset) return PURE_E_ZIP_LF_OVERFLOW;
   1710   if (lfh_offset < eocdr.cd_offset) {
   1711     assert(eocdr.cd_offset <= size);
   1712     if (pure_zeroes(buffer, lfh_offset, eocdr.cd_offset)) {
   1713       return PURE_E_ZIP_LF_UNDERFLOW_ZEROED;
   1714     } else {
   1715       return PURE_E_ZIP_LF_UNDERFLOW_BUFFER_BLEED;
   1716     }
   1717   }
   1718   uint64_t cdh_offset_expected = eocdr.cd_offset + eocdr.cd_size;
   1719   if (cdh_offset > cdh_offset_expected) return PURE_E_ZIP_CD_OVERFLOW;
   1720   if (cdh_offset < cdh_offset_expected) {
   1721     assert(cdh_offset_expected <= size);
   1722     if (pure_zeroes(buffer, cdh_offset, cdh_offset_expected)) {
   1723       return PURE_E_ZIP_CD_UNDERFLOW_ZEROED;
   1724     } else {
   1725       return PURE_E_ZIP_CD_UNDERFLOW_BUFFER_BLEED;
   1726     }
   1727   }
   1728   if (cdh_offset < eocdr.offset) {
   1729     assert(eocdr.offset <= size);
   1730     if (pure_zeroes(buffer, cdh_offset, eocdr.offset)) {
   1731       return PURE_E_ZIP_CD_EOCDR_UNDERFLOW_ZEROED;
   1732     } else {
   1733       return PURE_E_ZIP_CD_EOCDR_UNDERFLOW_BUFFER_BLEED;
   1734     }
   1735   }
   1736   assert(cdh_offset == eocdr.offset);
   1737   assert(cdh_offset + eocdr.length == size);
   1738   assert(ctx->depth > 0);
   1739   ctx->depth--;
   1740   return PURE_E_OK;
   1741 }
   1742 
   1743 int pure_zip_meta_data(
   1744   pure_ctx* ctx,
   1745   const uint8_t* buffer,
   1746   const uint64_t size
   1747 ) {
   1748   uint8_t* data = NULL;
   1749   uint64_t data_size = 0;
   1750   int error = pure_zip_meta(ctx, buffer, size, &data, &data_size);
   1751   pure_free(&data, &data_size);
   1752   assert(data == NULL);
   1753   assert(data_size == 0);
   1754   return error;
   1755 }
   1756 
   1757 int pure_zip(
   1758   const uint8_t* buffer, // Zip file buffer
   1759   const uint64_t size,   // Size of zip file buffer in bytes
   1760   const uint64_t flags   // Bit flags (optional)
   1761 ) {
   1762   // SPEC: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
   1763 
   1764   // Further, as per ISO/IEC 21320-1:2015, we disable support for:
   1765   // * multiple disks
   1766   // * encryption and archive headers
   1767   // * encryption mechanisms
   1768   // * compression methods other than 0 or 8
   1769   // * ZIP64 version 2 (and we also disable ZIP64 version 1)
   1770   // * unused and reserved flags
   1771   
   1772   // Contrary to ISO/IEC 21320-1:2015, we do not require:
   1773   // * `version needed to extract` to be at most 45 (too many false positives)
   1774   // * bit 11 (UTF-8) when a string byte exceeds 0x7F (this could also be CP437)
   1775   pure_ctx ctx;
   1776   ctx.flags = flags;
   1777   ctx.depth = 0;
   1778   ctx.files = 0;
   1779   ctx.archives = 0;
   1780   ctx.size = 0;
   1781   ctx.compressed_size = 0;
   1782   ctx.uncompressed_size = 0;
   1783   return pure_zip_meta_data(&ctx, buffer, size);
   1784 }
   1785 
   1786 int pure_zip_bomb(const int error) {
   1787   if (error == PURE_E_ZIP_BOMB_ARCHIVES) return 1;
   1788   if (error == PURE_E_ZIP_BOMB_DEPTH) return 1;
   1789   if (error == PURE_E_ZIP_BOMB_FILES) return 1;
   1790   if (error == PURE_E_ZIP_BOMB_FIFIELD) return 1;
   1791   if (error == PURE_E_ZIP_BOMB_RATIO) return 1;
   1792   if (error == PURE_E_ZIP_BOMB_INFLATE_COMPRESSED_OVERFLOW) return 1;
   1793   if (error == PURE_E_ZIP_BOMB_INFLATE_UNCOMPRESSED_OVERFLOW) return 1;
   1794   return 0;
   1795 }
   1796 
   1797 const char* pure_error_code(const int error) {
   1798   assert(error >= 0);
   1799   assert(error < PURE_E_ENUM_LENGTH);
   1800   if (error < 0 || error >= PURE_E_ENUM_LENGTH) return "";
   1801   return PURE_E_CODES[error];
   1802 }
   1803 
   1804 const char* pure_error_string(const int error) {
   1805   assert(error >= 0);
   1806   assert(error < PURE_E_ENUM_LENGTH);
   1807   if (error < 0 || error >= PURE_E_ENUM_LENGTH) return "";
   1808   return PURE_E_STRINGS[error];
   1809 }
   1810 
   1811 #endif /* PURE_H */