diff --git a/xxhsum.c b/xxhsum.c index 8232409..3be0562 100644 --- a/xxhsum.c +++ b/xxhsum.c @@ -54,6 +54,18 @@ #include /* assert */ #include /* errno */ +typedef enum { + XSUM_SEED = 0, + XSUM_SECRET = 1 +} XSUM_seed_type_t; + +typedef struct { + XSUM_seed_type_t type; + XSUM_U64 seed; + void *secret; + size_t secret_length; +} XSUM_seed_t; + #define XXH_STATIC_LINKING_ONLY /* *_state_t */ #include "xxhash.h" @@ -93,7 +105,7 @@ static size_t XSUM_DEFAULT_SAMPLE_SIZE = 100 KB; #define MAX_MEM (2 GB - 64 MB) static const char stdinName[] = "-"; -typedef enum { algo_xxh32=0, algo_xxh64=1, algo_xxh128=2 } AlgoSelected; +typedef enum { algo_xxh32=0, algo_xxh64=1, algo_xxh128=2, algo_xxh3=3 } AlgoSelected; static AlgoSelected g_defaultAlgo = algo_xxh64; /* required within main() & XSUM_usage() */ /* <16 hex char> <'\0'> @@ -103,7 +115,6 @@ static AlgoSelected g_defaultAlgo = algo_xxh64; /* required within main() & X /* Maximum acceptable line length. */ #define MAX_LINE_LENGTH (32 KB) - /* ************************************ * Display macros **************************************/ @@ -511,6 +522,7 @@ typedef union { XXH32_hash_t xxh32; XXH64_hash_t xxh64; XXH128_hash_t xxh128; + XXH64_hash_t xxh3; } Multihash; /* @@ -521,16 +533,45 @@ typedef union { static Multihash XSUM_hashStream(FILE* inFile, AlgoSelected hashType, + XSUM_seed_t seed, void* buffer, size_t blockSize) { XXH32_state_t state32; XXH64_state_t state64; XXH3_state_t state128; + XXH3_state_t state3; + XXH_errorcode error_code; /* Init */ - (void)XXH32_reset(&state32, XXHSUM32_DEFAULT_SEED); - (void)XXH64_reset(&state64, XXHSUM64_DEFAULT_SEED); - (void)XXH3_128bits_reset(&state128); + switch(hashType) + { + case algo_xxh32: + error_code = XXH32_reset(&state32, (XSUM_U32) (seed.seed & 0xFFFFFFFF)); + break; + case algo_xxh64: + error_code = XXH64_reset(&state64, seed.seed); + break; + case algo_xxh128: + if (seed.type == XSUM_SEED) + error_code = XXH3_128bits_reset_withSeed(&state128, seed.seed); + else + error_code = XXH3_128bits_reset_withSecret(&state128, seed.secret, seed.secret_length); + break; + case algo_xxh3: + if (seed.type == XSUM_SEED) + error_code = XXH3_64bits_reset_withSeed(&state3, seed.seed); + else + error_code = XXH3_64bits_reset_withSecret(&state3, seed.secret, seed.secret_length); + break; + default: + assert(0); + } + + if (error_code != XXH_OK) { + XSUM_log("Error: An error occurred while initializing a state.\n"); + XSUM_log("Algo: %d\n", hashType); + exit(1); + } /* Load file & update hash */ { size_t readSize; @@ -538,17 +579,24 @@ XSUM_hashStream(FILE* inFile, switch(hashType) { case algo_xxh32: - (void)XXH32_update(&state32, buffer, readSize); + error_code = XXH32_update(&state32, buffer, readSize); break; case algo_xxh64: - (void)XXH64_update(&state64, buffer, readSize); + error_code = XXH64_update(&state64, buffer, readSize); break; case algo_xxh128: - (void)XXH3_128bits_update(&state128, buffer, readSize); + error_code = XXH3_128bits_update(&state128, buffer, readSize); + break; + case algo_xxh3: + error_code = XXH3_64bits_update(&state3, buffer, readSize); break; default: assert(0); } + if (error_code != XXH_OK) { + XSUM_log("Error: An error occurred while updating digest data.\n"); + exit(1); + } } if (ferror(inFile)) { XSUM_log("Error: a failure occurred reading the input file.\n"); @@ -567,6 +615,9 @@ XSUM_hashStream(FILE* inFile, case algo_xxh128: finalHash.xxh128 = XXH3_128bits_digest(&state128); break; + case algo_xxh3: + finalHash.xxh3 = XXH3_64bits_digest(&state3); + break; default: assert(0); } @@ -575,9 +626,9 @@ XSUM_hashStream(FILE* inFile, } /* algo_xxh32, algo_xxh64, algo_xxh128 */ -static const char* XSUM_algoName[] = { "XXH32", "XXH64", "XXH128" }; -static const char* XSUM_algoLE_name[] = { "XXH32_LE", "XXH64_LE", "XXH128_LE" }; -static const size_t XSUM_algoLength[] = { 4, 8, 16 }; +static const char* XSUM_algoName[] = { "XXH32", "XXH64", "XXH128", "XXH3" }; +static const char* XSUM_algoLE_name[] = { "XXH32_LE", "XXH64_LE", "XXH128_LE", "XXH3_LE" }; +static const size_t XSUM_algoLength[] = { 4, 8, 16, 8 }; #define XSUM_TABLE_ELT_SIZE(table) (sizeof(table) / sizeof(*table)) @@ -641,6 +692,7 @@ static XSUM_displayLine_f XSUM_kDisplayLine_fTable[2][2] = { static int XSUM_hashFile(const char* fileName, const AlgoSelected hashType, + const XSUM_seed_t seed, const Display_endianess displayEndianess, const Display_convention convention) { @@ -676,7 +728,7 @@ static int XSUM_hashFile(const char* fileName, } /* Stream file & update hash */ - hashValue = XSUM_hashStream(inFile, hashType, buffer, blockSize); + hashValue = XSUM_hashStream(inFile, hashType, seed, buffer, blockSize); fclose(inFile); free(buffer); @@ -703,6 +755,12 @@ static int XSUM_hashFile(const char* fileName, f_displayLine(fileName, &hcbe128, hashType); break; } + case algo_xxh3: + { XXH64_canonical_t hcbe3; + (void)XXH64_canonicalFromHash(&hcbe3, hashValue.xxh3); + f_displayLine(fileName, &hcbe3, hashType); + break; + } default: assert(0); /* not possible */ } @@ -717,6 +775,7 @@ static int XSUM_hashFile(const char* fileName, */ static int XSUM_hashFiles(char*const * fnList, int fnTotal, AlgoSelected hashType, + XSUM_seed_t seed, Display_endianess displayEndianess, Display_convention convention) { @@ -724,10 +783,10 @@ static int XSUM_hashFiles(char*const * fnList, int fnTotal, int result = 0; if (fnTotal==0) - return XSUM_hashFile(stdinName, hashType, displayEndianess, convention); + return XSUM_hashFile(stdinName, hashType, seed, displayEndianess, convention); for (fnNb=0; fnNbblockBuf, XSUM_parseFileArg->blockSize); + { Multihash const xxh = XSUM_hashStream(fp, algo_xxh32, (XSUM_seed_t){0}, XSUM_parseFileArg->blockBuf, XSUM_parseFileArg->blockSize); if (xxh.xxh32 == XXH32_hashFromCanonical(&parsedLine.canonical.xxh32)) { lineStatus = LineStatus_hashOk; } } break; case 64: - { Multihash const xxh = XSUM_hashStream(fp, algo_xxh64, XSUM_parseFileArg->blockBuf, XSUM_parseFileArg->blockSize); + { Multihash const xxh = XSUM_hashStream(fp, algo_xxh64, (XSUM_seed_t){0}, XSUM_parseFileArg->blockBuf, XSUM_parseFileArg->blockSize); if (xxh.xxh64 == XXH64_hashFromCanonical(&parsedLine.canonical.xxh64)) { lineStatus = LineStatus_hashOk; } } break; case 128: - { Multihash const xxh = XSUM_hashStream(fp, algo_xxh128, XSUM_parseFileArg->blockBuf, XSUM_parseFileArg->blockSize); + { Multihash const xxh = XSUM_hashStream(fp, algo_xxh128, (XSUM_seed_t){0}, XSUM_parseFileArg->blockBuf, XSUM_parseFileArg->blockSize); if (XXH128_isEqual(xxh.xxh128, XXH128_hashFromCanonical(&parsedLine.canonical.xxh128))) { lineStatus = LineStatus_hashOk; } } break; + case 3: + { Multihash const xxh = XSUM_hashStream(fp, algo_xxh3, (XSUM_seed_t){0}, XSUM_parseFileArg->blockBuf, XSUM_parseFileArg->blockSize); + if (xxh.xxh3 == XXH64_hashFromCanonical(&parsedLine.canonical.xxh3)) { + lineStatus = LineStatus_hashOk; + } } + break; + default: break; } @@ -1256,7 +1323,9 @@ static int XSUM_usage(const char* exename) XSUM_log( "Usage: %s [options] [files] \n\n", exename); XSUM_log( "When no filename provided or when '-' is provided, uses stdin as input. \n"); XSUM_log( "Options: \n"); - XSUM_log( " -H# algorithm selection: 0,1,2 or 32,64,128 (default: %i) \n", (int)g_defaultAlgo); + XSUM_log( " -H# algorithm selection: 0,1,2,3 or 32,64,128,364 (default: %i) \n", (int)g_defaultAlgo); + XSUM_log( " -s seed in hex\n"); + XSUM_log( " -S secret in hex (minimum %d decoded bytes)\n", XXH3_SECRET_SIZE_MIN); XSUM_log( " -c, --check read xxHash checksum from [files] and check them \n"); XSUM_log( " -h, --help display a long help page about advanced options \n"); return 0; @@ -1353,9 +1422,72 @@ static XSUM_U32 XSUM_readU32FromChar(const char** stringPtr) { return result; } +static int hex_decode_str_implied(const unsigned char *src, size_t len, unsigned char *dest) +{ + unsigned char low, high; + + if (len % 2) { + low = *src++; + + if (low >= '0' && low <= '9') { + low -= '0'; + } else if (low >= 'A' && low <= 'F') { + low -= 'A' - 10; + } else if (low >= 'a' && low <= 'f') { + low -= 'a' - 10; + } else { + return 0; + } + + *dest++ = low; + --len; + } + + for (; len > 0; len -= 2) { + high = *src++; + + if (high >= '0' && high <= '9') { + high -= '0'; + } else if (high >= 'A' && high <= 'F') { + high -= 'A' - 10; + } else if (high >= 'a' && high <= 'f') { + high -= 'a' - 10; + } else { + return 0; + } + + low = *src++; + + if (low >= '0' && low <= '9') { + low -= '0'; + } else if (low >= 'A' && low <= 'F') { + low -= 'A' - 10; + } else if (low >= 'a' && low <= 'f') { + low -= 'a' - 10; + } else { + return 0; + } + + *dest++ = high << 4 | low; + } + + return -1; +} + +static size_t calc_hex_decoded_str_length(size_t hex_encoded_length) +{ + if (hex_encoded_length == 0) + return 0; + + if (hex_encoded_length % 2) + ++hex_encoded_length; + + return hex_encoded_length / 2; +} + XSUM_API int XSUM_main(int argc, char* argv[]) { - int i, filenamesStart = 0; + int i, j, filenamesStart = 0; const char* const exename = XSUM_lastNameFromPath(argv[0]); XSUM_U32 benchmarkMode = 0; XSUM_U32 fileCheckMode = 0; @@ -1369,6 +1501,9 @@ XSUM_API int XSUM_main(int argc, char* argv[]) AlgoSelected algo = g_defaultAlgo; Display_endianess displayEndianess = big_endian; Display_convention convention = display_gnu; + XSUM_seed_t seed = {0}; + char seed_hex[32]; + char *secret_hex, *error; /* special case: xxhNNsum default to NN bits checksum */ if (strstr(exename, "xxh32sum") != NULL) algo = g_defaultAlgo = algo_xxh32; @@ -1424,11 +1559,54 @@ XSUM_API int XSUM_main(int argc, char* argv[]) case 64: algo = algo_xxh64; break; case 2 : case 128: algo = algo_xxh128; break; + case 3 : + case 364: algo = algo_xxh3; break; default: return XSUM_badusage(exename); } break; + case 's': argument++; + errno = 0; + j = 0; + while (*argument >= '0' && *argument <= '9' || *argument >= 'a' && *argument <= 'f' + || *argument >= 'A' && *argument <= 'F') { + if (j < 32) + seed_hex[j++] = *argument; + argument++; + } + seed_hex[j] = 0; + if (seed.type == XSUM_SECRET && seed.secret) + free(seed.secret); + seed.type = XSUM_SEED; + seed.seed = strtoull(seed_hex, 0, 16); + if (errno) { + error = strerror(errno); + XSUM_log("Error: Failed to parse seed argument: %s\n", error); + return XSUM_badusage(exename); + } + break; + + case 'S': argument++; + size_t hex_length = strlen(argument); + size_t hex_decoded_len = calc_hex_decoded_str_length(hex_length); + if (hex_decoded_len < XXH3_SECRET_SIZE_MIN) { + XSUM_log("Error: Secret needs to be at least %d bytes in length.\n", + XXH3_SECRET_SIZE_MIN); + return XSUM_badusage(exename); + } + if (seed.type == XSUM_SECRET && seed.secret) + free(seed.secret); + seed.type = XSUM_SECRET; + seed.secret = malloc(hex_decoded_len); + seed.secret_length = hex_decoded_len; + if (! hex_decode_str_implied(argument, hex_length, seed.secret)) { + XSUM_log("Error: Invalid hex argument: %s\n", argument); + return XSUM_badusage(exename); + } + argument += hex_length; + break; + /* File check mode */ case 'c': fileCheckMode=1; @@ -1479,6 +1657,14 @@ XSUM_API int XSUM_main(int argc, char* argv[]) } } /* for(i=1; i UINT32_MAX) { + XSUM_log("Error: Seed value too large for 32-bit algorithm.\n"); + return XSUM_badusage(exename); + } + /* Check benchmark mode */ if (benchmarkMode) { XSUM_logVerbose(2, FULL_WELCOME_MESSAGE(exename) ); @@ -1498,6 +1684,6 @@ XSUM_API int XSUM_main(int argc, char* argv[]) return XSUM_checkFiles(argv+filenamesStart, argc-filenamesStart, displayEndianess, strictMode, statusOnly, warn, (XSUM_logLevel < 2) /*quiet*/); } else { - return XSUM_hashFiles(argv+filenamesStart, argc-filenamesStart, algo, displayEndianess, convention); + return XSUM_hashFiles(argv+filenamesStart, argc-filenamesStart, algo, seed, displayEndianess, convention); } }