diff --git a/doc/checksums.txt b/doc/checksums.txt index e445050..cc64077 100644 --- a/doc/checksums.txt +++ b/doc/checksums.txt @@ -70,12 +70,13 @@ The next block after the array begins with the checksum tag and is padded by 0-bytes. The tag is a single line of printable text and has the following format: - libisofs_checksum_tag_v1 pos=... range_start=... range_size=... md5=... \n + libisofs_checksum_tag_v1 pos=# range_start=# range_size=# md5=# self=#\n Example: - libisofs_checksum_tag_v1 pos=81552 range_start=32 range_size=81520 md5=f172b994e8eb565a011d220b2a8b7a19 +libisofs_checksum_tag_v1 pos=81552 range_start=32 range_size=81520 md5=f172b994e8eb565a011d220b2a8b7a19 self=020975b2aa1189d455db2c09560b8732 -There are four parameters: +There are five parameters. The first three are decimal numbers, the others +are strings of 32 hex digits. pos= gives the block address where the tag supposes itself to be stored. @@ -85,16 +86,23 @@ There are four parameters: range_start= The block address where the session is supposed to start. If this does not - match the session start on media then the image volume descriptors of the - image have been been relocated. (The latter can happen with overwriteable - media.) + match the session start on media then the volume descriptors of the + image have been relocated. (This can happen with overwriteable media. If + checksumming started at LBA 0 and finds range_start=32, then one has to + restart checksumming at LBA 32. See libburn/doc/cookbook.txt + "ISO 9660 multi-session emulation on overwriteable media" for background + information.) range_size= The number of blocks beginning at range_start which are covered by the checksum of the tag. md5= - The checksum of the tag. Encoded as 32 hex digits. + The checksum payload of the tag as lower case hex digits. + + self= + The MD5 checksum of the tag itself up to and including the last hex digit of + parameter "md5=". The newline character at the end is mandatory. For now all bytes of the block after that newline shall be zero. There may arise future extensions. diff --git a/libisofs/fs_image.c b/libisofs/fs_image.c index 4e3a11c..e22e681 100644 --- a/libisofs/fs_image.c +++ b/libisofs/fs_image.c @@ -3201,7 +3201,7 @@ int iso_image_import(IsoImage *image, IsoDataSource *src, ) break; if (i < 16) { - iso_msg_submit(image->id, ISO_MD5_ARRAY_CORRUPTED, 0, + iso_msg_submit(image->id, ISO_MD5_AREA_CORRUPTED, 0, "MD5 checksum array appears damaged and not trustworthy for verifications."); free(image->checksum_array); image->checksum_array = NULL; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 723bfb2..c5229c9 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -4992,7 +4992,8 @@ int iso_gzip_get_refcounts(off_t *gzip_count, off_t *gunzip_count, int flag); /* ---------------------------- MD5 Checksums --------------------------- */ /* Production and loading of MD5 checksums is controlled by calls - iso_write_opts_set_record_md5() and iso_read_opts_set_no_md5() + iso_write_opts_set_record_md5() and iso_read_opts_set_no_md5(). + For data representation details see doc/checksums.txt . */ /** @@ -5042,6 +5043,41 @@ int iso_image_get_session_md5(IsoImage *image, uint32_t *start_lba, */ int iso_file_get_md5(IsoImage *image, IsoFile *file, char md5[16], int flag); +/** + * Check a data block whether it is a libisofs session checksum tag and + * eventually obtain its recorded parameters. These tags get written after + * the checksum arrays and can be detected without loading the image tree. + * One may start reading and computing MD5 at the suspected image session + * start and look out for a session tag on the fly. + * @param data + * A complete and aligned data block read from an ISO image session. + * @param pos + * Returns the LBA where the tag supposes itself to be stored. + * If this does not match the data block LBA then the tag might be + * image data payload and should be ignored for image checksumming. + * @param range_start + * Returns the block address where the session is supposed to start. + * If this does not match the session start on media then the image + * volume descriptors have been been relocated. + * A proper checksum will only emerge if computing started at range_start. + * @param range_size + * Returns the number of blocks beginning at range_start which are + * covered by parameter md5. + * @param md5 + * Returns 16 byte of MD5 checksum. + * @return + * 0= not a checksum tag, return parameters are invalid + * 1= checksum tag found + * <0= error + * return parameters are valid with error ISO_MD5_AREA_CORRUPTED + * but not trustworthy because the tag seems corrupted. + * + * @since 0.6.22 + */ +int iso_util_decode_md5_tag(char data[2048], uint32_t *pos, + uint32_t *range_start, uint32_t *range_size, + char md5[16], int flag); + /* The following functions allow to do own MD5 computations. E.g for comparing the result with a recorded checksum. @@ -5366,11 +5402,11 @@ int iso_md5_end(void **md5_context, char result[16]); #define ISO_ZLIB_EARLY_EOF 0xE830FEA1 /** - * Checksum array appears damaged and not trustworthy for verifications. + * Checksum area appears damaged and not trustworthy for verifications. * (WARNING,HIGH, -352) * @since 0.6.22 */ -#define ISO_MD5_ARRAY_CORRUPTED 0xD030FEA0 +#define ISO_MD5_AREA_CORRUPTED 0xD030FEA0 /* ! PLACE NEW ERROR CODES HERE ! */ diff --git a/libisofs/md5.c b/libisofs/md5.c index f0f3f67..3c0a18f 100644 --- a/libisofs/md5.c +++ b/libisofs/md5.c @@ -593,6 +593,17 @@ int checksum_writer_write_data(IsoImageWriter *writer) for (i = 0; i < 16; i++) sprintf(tag_block + l + 2 * i, "%2.2x", ((unsigned char *) md5)[i]); + + res = iso_md5_start(&ctx); + if (res > 0) { + iso_md5_compute(ctx, tag_block, l + 32); + iso_md5_end(&ctx, md5); + strcpy(tag_block + l + 32, " self="); + l += 32 + 6; + for (i = 0; i < 16; i++) + sprintf(tag_block + l + 2 * i, "%2.2x", + ((unsigned char *) md5)[i]); + } tag_block[l + 32] = '\n'; } wres = iso_write(t, tag_block, 2048); diff --git a/libisofs/messages.c b/libisofs/messages.c index 03bce5b..229a8dc 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -260,8 +260,8 @@ const char *iso_error_to_msg(int errcode) return "Cannot set global zisofs parameters while filters exist"; case ISO_ZLIB_EARLY_EOF: return "Premature EOF of zlib input stream"; - case ISO_MD5_ARRAY_CORRUPTED: - return "Checksum array appears damaged and not trustworthy for verifications"; + case ISO_MD5_AREA_CORRUPTED: + return "Checksum area appears damaged and not trustworthy for verifications"; default: return "Unknown error"; } diff --git a/libisofs/util.c b/libisofs/util.c index 0223a04..3dccb40 100644 --- a/libisofs/util.c +++ b/libisofs/util.c @@ -1528,3 +1528,93 @@ int iso_util_decode_len_bytes(uint32_t *data, char *buffer, int *data_len, } +int iso_util_dec_to_uint32(char *dec, uint32_t *value, int flag) +{ + double num; + + sscanf(dec, "%lf", &num); + if (num < 0 || num > 4294967295.0) + return 0; + *value = num; + return 1; +} + + +int iso_util_hex_to_bin(char *hex, char *bin, int bin_size, int *bin_count, + int flag) +{ + static char *allowed = {"0123456789ABCDEFabcdef"}; + char b[3]; + int i; + unsigned int u; + + b[2] = 0; + *bin_count = 0; + for (i = 0; i < bin_size; i++) { + b[0] = hex[2 * i]; + b[1] = hex[2 * i + 1]; + if (strchr(allowed, b[0]) == NULL || strchr(allowed, b[1]) == NULL) + break; + sscanf(b, "%x", &u); + ((unsigned char *) bin)[i] = u; + (*bin_count)++; + } + return (*bin_count > 0); +} + + +int iso_util_decode_md5_tag(char data[2048], uint32_t *pos, + uint32_t *range_start, uint32_t *range_size, + char md5[16], int flag) +{ + static char *tag_magic= "libisofs_checksum_tag_v1 pos="; + static int magic_len= 29; + int ret, bin_count, i; + char *cpt, self_md5[16], tag_md5[16]; + void *ctx = NULL; + + if (strncmp(data, tag_magic, magic_len) != 0) + return(0); + cpt = data + magic_len; + ret = iso_util_dec_to_uint32(cpt, pos, 0); + if (ret <= 0) + return 0; + cpt = strstr(cpt, "range_start="); + if (cpt == NULL) + return(0); + ret = iso_util_dec_to_uint32(cpt + 12, range_start, 0); + if (ret <= 0) + return 0; + cpt = strstr(cpt, "range_size="); + if (cpt == NULL) + return(0); + ret = iso_util_dec_to_uint32(cpt + 11, range_size, 0); + if (ret <= 0) + return 0; + cpt = strstr(cpt, "md5="); + if (cpt == NULL) + return(0); + ret = iso_util_hex_to_bin(cpt + 4, md5, 16, &bin_count, 0); + if (ret <= 0 || bin_count != 16) + return 0; + + cpt += 4 + 32; + ret = iso_md5_start(&ctx); + if (ret < 0) + return ret; + iso_md5_compute(ctx, data , cpt - data); + iso_md5_end(&ctx, tag_md5); + cpt = strstr(cpt, "self="); + if (cpt == NULL) + return(0); + ret = iso_util_hex_to_bin(cpt + 5, self_md5, 16, &bin_count, 0); + if (ret <= 0 || bin_count != 16) + return 0; + for(i= 0; i < 16; i++) + if(self_md5[i] != tag_md5[i]) + return ISO_MD5_AREA_CORRUPTED; + if (*(cpt + 5 + 32) != '\n') + return 0; + return(1); +} +