From bf6de56bc2d4332d4b8e8d53bbf60362c03874f3 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 12 Oct 2022 17:30:12 +0100 Subject: [PATCH] Implement image verification. --- CMakeLists.txt | 2 +- include/aaruformat/decls.h | 2 + include/aaruformat/errors.h | 1 + src/verify.c | 241 ++++++++++++++++++++++++++++++++++++ tool/CMakeLists.txt | 2 +- tool/aaruformattool.h | 1 + tool/main.c | 30 +++++ tool/verify.c | 46 +++++++ 8 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 src/verify.c create mode 100644 tool/verify.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 776ad66..40ab76f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,7 @@ add_library(aaruformat SHARED include/aaruformat/consts.h include/aaruformat/enu src/close.c include/aaruformat/errors.h src/read.c include/aaruformat/crc64.h src/cst.c src/ecc_cd.c src/helpers.c src/simd.c include/aaruformat/simd.h src/crc64/crc64.c src/crc64/crc64_clmul.c src/crc64/crc64_vmull.c src/crc64/arm_vmull.c src/crc64/arm_vmull.h src/spamsum.c include/aaruformat/spamsum.h include/aaruformat/flac.h - src/flac.c src/lzma.c src/lru.c include/aaruformat/lru.h include/aaruformat/endian.h) + src/flac.c src/lzma.c src/lru.c include/aaruformat/lru.h include/aaruformat/endian.h src/verify.c) include_directories(include include/aaruformat) diff --git a/include/aaruformat/decls.h b/include/aaruformat/decls.h index a005a10..f73f32f 100644 --- a/include/aaruformat/decls.h +++ b/include/aaruformat/decls.h @@ -79,6 +79,8 @@ AARU_EXPORT int32_t AARU_CALL aaruf_read_sector_long(void* context, uint8_t* data, uint32_t* length); +AARU_EXPORT int32_t AARU_CALL aaruf_verify_image(void* context); + AARU_EXPORT int32_t AARU_CALL aaruf_cst_transform(const uint8_t* interleaved, uint8_t* sequential, size_t length); AARU_EXPORT int32_t AARU_CALL aaruf_cst_untransform(const uint8_t* sequential, uint8_t* interleaved, size_t length); diff --git a/include/aaruformat/errors.h b/include/aaruformat/errors.h index 3a5b550..48144c5 100644 --- a/include/aaruformat/errors.h +++ b/include/aaruformat/errors.h @@ -36,6 +36,7 @@ #define AARUF_ERROR_INVALID_TRACK_FORMAT -15 #define AARUF_ERROR_SECTOR_TAG_NOT_PRESENT -16 #define AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK -17 +#define AARUF_ERROR_INVALID_BLOCK_CRC -18 #define AARUF_STATUS_OK 0 #define AARUF_STATUS_SECTOR_NOT_DUMPED 1 diff --git a/src/verify.c b/src/verify.c new file mode 100644 index 0000000..bdf0ac5 --- /dev/null +++ b/src/verify.c @@ -0,0 +1,241 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2022 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include + +#include + +#define VERIFY_SIZE 1048576 + +int32_t aaruf_verify_image(void* context) +{ + aaruformatContext* ctx; + uint64_t crc64; + int i; + IndexHeader index_header; + IndexEntry* index_entries; + size_t read_bytes; + void* buffer; + crc64_ctx* crc64_context; + BlockHeader block_header; + uint64_t verified_bytes; + DdtHeader ddt_header; + TracksHeader tracks_header; + + if(context == NULL) return AARUF_ERROR_NOT_AARUFORMAT; + + ctx = context; + + // Not a libaaruformat context + if(ctx->magic != AARU_MAGIC) return AARUF_ERROR_NOT_AARUFORMAT; + + // This will traverse all blocks and check their CRC64 without uncompressing them + fprintf(stderr, "Checking index integrity at %lu.\n", ctx->header.indexOffset); + fseek(ctx->imageStream, ctx->header.indexOffset, SEEK_SET); + + read_bytes = fread(&index_header, 1, sizeof(IndexHeader), ctx->imageStream); + + if(read_bytes != sizeof(IndexHeader)) + { + fprintf(stderr, "Could not read index header.\n"); + return AARUF_ERROR_CANNOT_READ_HEADER; + } + + if(index_header.identifier != IndexBlock) + { + fprintf(stderr, "Incorrect index identifier.\n"); + return AARUF_ERROR_CANNOT_READ_INDEX; + } + + fprintf(stderr, "Index at %lu contains %d entries.\n", ctx->header.indexOffset, index_header.entries); + + index_entries = malloc(sizeof(IndexEntry) * index_header.entries); + + if(index_entries == NULL) + { + fprintf(stderr, "Cannot allocate memory for index entries.\n"); + return AARUF_ERROR_NOT_ENOUGH_MEMORY; + } + + read_bytes = fread(index_entries, 1, sizeof(IndexEntry) * index_header.entries, ctx->imageStream); + + if(read_bytes != sizeof(IndexEntry) * index_header.entries) + { + fprintf(stderr, "Could not read index entries.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_INDEX; + } + + crc64 = aaruf_crc64_data((const uint8_t*)index_entries, sizeof(IndexEntry) * index_header.entries); + + // Due to how C# wrote it, it is effectively reversed + if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64); + + if(crc64 != index_header.crc64) + { + fprintf(stderr, "Expected index CRC 0x%16lX but got 0x%16lX.\n", index_header.crc64, crc64); + free(index_entries); + return AARUF_ERROR_INVALID_BLOCK_CRC; + } + + buffer = malloc(VERIFY_SIZE); + + if(buffer == NULL) + { + fprintf(stderr, "Cannot allocate memory for buffer.\n"); + free(index_entries); + return AARUF_ERROR_NOT_ENOUGH_MEMORY; + } + + for(i = 0; i < index_header.entries; i++) + { + fprintf(stderr, + "Checking block with type %4.4s at position %" PRIu64 "\n", + (char*)&index_entries[i].blockType, + index_entries[i].offset); + + fseek(ctx->imageStream, index_entries[i].offset, SEEK_SET); + + switch(index_entries[i].blockType) + { + case DataBlock: + read_bytes = fread(&block_header, 1, sizeof(BlockHeader), ctx->imageStream); + if(read_bytes != sizeof(BlockHeader)) + { + fprintf(stderr, "Could not read block header.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + crc64_context = aaruf_crc64_init(); + + if(crc64_context == NULL) + { + fprintf(stderr, "Could not initialize CRC64.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + verified_bytes = 0; + + while(verified_bytes + VERIFY_SIZE < block_header.cmpLength) + { + read_bytes = fread(buffer, 1, VERIFY_SIZE, ctx->imageStream); + aaruf_crc64_update(crc64_context, buffer, read_bytes); + verified_bytes += read_bytes; + } + + read_bytes = fread(buffer, 1, block_header.cmpLength - verified_bytes, ctx->imageStream); + aaruf_crc64_update(crc64_context, buffer, read_bytes); + + aaruf_crc64_final(crc64_context, &crc64); + + // Due to how C# wrote it, it is effectively reversed + if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64); + + if(crc64 != block_header.cmpCrc64) + { + fprintf(stderr, "Expected block CRC 0x%16lX but got 0x%16lX.\n", block_header.cmpCrc64, crc64); + free(index_entries); + return AARUF_ERROR_INVALID_BLOCK_CRC; + } + + break; + case DeDuplicationTable: + read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader), ctx->imageStream); + if(read_bytes != sizeof(DdtHeader)) + { + fprintf(stderr, "Could not read DDT header.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + crc64_context = aaruf_crc64_init(); + + if(crc64_context == NULL) + { + fprintf(stderr, "Could not initialize CRC64.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + verified_bytes = 0; + + while(verified_bytes + VERIFY_SIZE < ddt_header.cmpLength) + { + read_bytes = fread(buffer, 1, VERIFY_SIZE, ctx->imageStream); + aaruf_crc64_update(crc64_context, buffer, read_bytes); + verified_bytes += read_bytes; + } + + read_bytes = fread(buffer, 1, ddt_header.cmpLength - verified_bytes, ctx->imageStream); + aaruf_crc64_update(crc64_context, buffer, read_bytes); + + aaruf_crc64_final(crc64_context, &crc64); + + // Due to how C# wrote it, it is effectively reversed + if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64); + + if(crc64 != ddt_header.cmpCrc64) + { + fprintf(stderr, "Expected DDT CRC 0x%16lX but got 0x%16lX.\n", ddt_header.cmpCrc64, crc64); + free(index_entries); + return AARUF_ERROR_INVALID_BLOCK_CRC; + } + + break; + case TracksBlock: + read_bytes = fread(&tracks_header, 1, sizeof(TracksHeader), ctx->imageStream); + if(read_bytes != sizeof(TracksHeader)) + { + fprintf(stderr, "Could not read tracks header.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + crc64_context = aaruf_crc64_init(); + + if(crc64_context == NULL) + { + fprintf(stderr, "Could not initialize CRC64.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + read_bytes = fread(buffer, 1, tracks_header.entries * sizeof(TrackEntry), ctx->imageStream); + aaruf_crc64_update(crc64_context, buffer, read_bytes); + + aaruf_crc64_final(crc64_context, &crc64); + + // Due to how C# wrote it, it is effectively reversed + if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64); + + if(crc64 != tracks_header.crc64) + { + fprintf(stderr, "Expected DDT CRC 0x%16lX but got 0x%16lX.\n", tracks_header.crc64, crc64); + free(index_entries); + return AARUF_ERROR_INVALID_BLOCK_CRC; + } + + break; + default: fprintf(stderr, "Ignoring block type %4.4s.\n", (char*)&index_entries[i].blockType); break; + } + } + + return AARUF_STATUS_OK; +} \ No newline at end of file diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt index b0c7218..4c8dbce 100644 --- a/tool/CMakeLists.txt +++ b/tool/CMakeLists.txt @@ -4,5 +4,5 @@ find_package(ICU COMPONENTS uc REQUIRED) include_directories(${ICU_INCLUDE_DIRS}) -add_executable(aaruformattool main.c main.h aaruformattool.h identify.c info.c helpers.c read.c printhex.c) +add_executable(aaruformattool main.c main.h aaruformattool.h identify.c info.c helpers.c read.c printhex.c verify.c) target_link_libraries(aaruformattool "aaruformat" ICU::uc) diff --git a/tool/aaruformattool.h b/tool/aaruformattool.h index 52fed52..d28a6c6 100644 --- a/tool/aaruformattool.h +++ b/tool/aaruformattool.h @@ -27,5 +27,6 @@ char* byte_array_to_hex_string(const unsigned char* array, int array_size); int read(unsigned long long sector_no, char* path); int printhex(unsigned char* array, unsigned int length, int width, bool color); int read_long(unsigned long long sector_no, char* path); +int verify(char* path); #endif // LIBAARUFORMAT_TOOL_AARUFORMATTOOL_H_ diff --git a/tool/main.c b/tool/main.c index f83e16d..b200ee4 100644 --- a/tool/main.c +++ b/tool/main.c @@ -37,6 +37,7 @@ void usage() printf("\tinfo\tPrints information about a given AaruFormat image.\n"); printf("\tread\tReads a sector and prints it out on screen.\n"); printf("\tread_long\tReads a sector with all its prefixes and suffixes and prints it out on screen.\n"); + printf("\tverify\tVerifies the integrity of all blocks in a AaruFormat image.\n"); printf("\n"); printf("For help on the verb invoke the tool with the verb and no arguments.\n"); } @@ -87,6 +88,17 @@ void usage_read_long() printf("\t\tPath to AaruFormat image to print information from.\n"); } +void usage_verify() +{ + printf("\n"); + printf("Usage:\n"); + printf("aaruformattool verify \n"); + printf("Verifies the integrity of all blocks in a AaruFormat image.\n"); + printf("\n"); + printf("Arguments:\n"); + printf("\t\tPath to AaruFormat image to verify.\n"); +} + int main(int argc, char* argv[]) { uint64_t sector_no = 0; @@ -195,5 +207,23 @@ int main(int argc, char* argv[]) return read(sector_no, argv[3]); } + if(strncmp(argv[1], "verify", strlen("verify")) == 0) + { + if(argc == 2) + { + usage_verify(); + return -1; + } + + if(argc > 3) + { + fprintf(stderr, "Invalid number of arguments\n"); + usage_verify(); + return -1; + } + + return verify(argv[2]); + } + return 0; } diff --git a/tool/verify.c b/tool/verify.c new file mode 100644 index 0000000..52120e2 --- /dev/null +++ b/tool/verify.c @@ -0,0 +1,46 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2022 Natalia Portillo. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include + +int verify(char* path) +{ + aaruformatContext* ctx; + uint32_t res; + + ctx = aaruf_open(path); + + if(ctx == NULL) + { + printf("Error %d when opening AaruFormat image.\n", errno); + return errno; + } + + res = aaruf_verify_image(ctx); + + if(res == AARUF_STATUS_OK) printf("Image blocks contain no errors.\n"); + else if(res == AARUF_ERROR_INVALID_BLOCK_CRC) + printf("A block contains an invalid CRC value.\n"); + else + printf("Error %d verifying image.\n", res); + + return res; +} \ No newline at end of file