diff --git a/libclamav/crypto.c b/libclamav/crypto.c index 3bb911dd44..d22c297b80 100644 --- a/libclamav/crypto.c +++ b/libclamav/crypto.c @@ -64,6 +64,8 @@ #define CLI_NSTR_EXT_SIG "E32B3AC1D501EE975296A45BA65DD699100DADD340FF3BBD1F1030C66D6BB16DBFBD53DF4D97BBD42EF8FC777E7C114A6074A87DD8095A5C08B3DD7B85817713047647EF396C58358C5C22B5C3ADF85CE8D0ABC429F89E936EC917B64DD00E02A712E6666FAE1A71591092BCEE59E3141758C4719B4B08589117B0FF7CDBDBB261F8486A193E2E720AE0B16D40DD5E56E97346CBD8010DC81B35332F41C9E93E61490802DDCDFC823D581BA6888588968C68A3C95B93949AF411682E73323F7469473F668B0958F6966849FF03BDE808866D127A2C058B16F17C741A9EE50812A5C7841224E55BF7ADDB5AEAE8EB5476F9BC8740178AB35926D5DC375583C641" #define CLI_ESTR_EXT_SIG "010001" +#define CLI_DI2_EXT_SIG "43b6cb0fc62b2d4e03432505300f7258cdd0be4b2de837cb8f0c394359b0b6d2810056525f178a623fbe5e6eb19eef42ae806583a7ae7bdfaa57d3aaf3d88311bf72538ae4b6843f6a10005bfada7c92b57fb6725d3ccbc9c7de0e91f89920a0eb1f8e039641730c8e7d8450d3a62d349624cec8dd5a4199a2ce6942c058fdc6ac8b5e566067c5f0a5667c86dd1e832327e4de6f8f169e6e20a0f5e08496897bd90797506d9669477eaa13b02862078d0c2cf9db1c70408f4f31eeabcd9603386450fa1781d4a3eaa9163fe5aae9d7c4791153a504334c8840ab8b1f85b3f5e62912c3e2c0a7bb74a7801edb21b30921cc422bd1aa15fa4a6d56fdf05703c156c1937930b86767ad4fd5cbbfc579650bba2e94971f885f020da8fc4a71241526c2a99615aff786184583a7d189c582dc3073dacf6e84eaf9c14f19315c08ad3812cc5acbfb8f42fe0e6b127b5b8df75078231620052c16076e6964fda5471c139d0573637065f8489ff1582c0bef13e5cc46421bc18eeca91fb88cd95e84548bf1b00257b626d9f99e2a3f0887e181ec7f41e1c01d1e55c440dd3f07fdc77005f5669a9475278cd0e9c04e953f538d0101dd14ea37c9d76a3d38bcda2659137f6540b4d5ca43dc3cbfe327b079d1dc449f99f68bdecb54548243165363443c9ec3c28b7e58ee1105d066d1f6fb35e86904592008102ef2045a33defefa8c7ad217c1786fa34dd8315d31032328e36ddcfe4542a655f8b5c099695890159ae00bb6bcfcc8c8c4a2a8b85ba41aa9ae1d067caaeac8b27dcb3c0bc490dff360d7d07b42d0372ed8fee4aac3ecf9bcbcb1c6d137b00fd13385af2dcd936debdd8760922c1bd5e6af5893d0f6a51529d1af42bc05266632c70dae56b3c800163b25ad12e53d20723f3517c3394790c49f8e48fb75310174e0448aa628b68a3f0d0a18756575d211b0551740d29ba7db06fb7d6b6ff0200531bc7a879d705e485470f45cdc78588883132211aae5506bfb523451e177896a84018fd996bc26cf845501d3f19fd948fd5ad7d9d13db578bee40f317092b96aa07acaada37ba2ba79d156bdd7f68f61e8cca0a357a054683df31961ce9913cec9a92de0135c63fa574111aab5baf093b121afb92511bd4b8e0b3fce609d27891b69466246b9cf6eced970142009b172d8c73d2ae25f8c86b3579b75fc7754932afc7aefc0c0255be40c1b67e2d94fe9ae103f807f37a05ab3ee1ff141d221c5942df34f61c9c7287fbc173b6caa1c19523cdf09d22c0b9b3fca5ef49144718c3d79ee9657ec69d5dc78fbde1cce4a5395c96af2f9172fe6c884bfc5335b89b192147c6ed6d9587db80c3c3d373447012eee4fc389b34807d068512815e1d45f7f26294fe6b4e3e52b282c4b57fa7ca39f9d27362925120bd9ec3e2f03e879405f1ba2dc9378244782ef384f244526eb8f81f91f3810a0bb55d228ae54f4a646f56e27cdd8bcb4eba316c7023e66b1cd37c11a7d793e88e93d274ed972a79e220b741ada013067e157211d403a09e7ed8136326ffec72a6638e6d12a64f45277375e620792e5ecfbb10251826251d0cbfca7fb48f0279a3699a0605b813f58f037d18e6aec14e460571fd01acacf65e27b384b2c3aac05fd82065fd287b61e3e4b8f84aa326dd7f6b86eab04352e02719e34655360786bc4236e5d962961e37c523cc03def7ad71e7bb3c2b5aca307559833571b87b486de9d8c3a4aac5ee9435643c62b1d80f41c10a1a08260dca5cbf309ce3eb5b680b7a0b867d6be0963923be21d068d5de36e42d3d86cf4dc8ee0ec48895a8b2f8597ada44e9f01da75864dc4feda2cca1e2ed5ac75" + #if OPENSSL_VERSION_NUMBER < 0x10100000L #define X509_CRL_get0_nextUpdate X509_CRL_get_nextUpdate #endif @@ -1354,7 +1356,7 @@ RSA *cli_build_ext_signing_key(void) } #elif OPENSSL_VERSION_MAJOR == 3 // Do this the OpenSSL 3 way, avoiding deprecation warnings -EVP_PKEY *cli_build_ext_signing_key(void) +EVP_PKEY *cli_build_ext_signing_key_RSA(void) { EVP_PKEY *pkey = EVP_PKEY_new(); BIGNUM *n = BN_new(); @@ -1445,6 +1447,112 @@ EVP_PKEY *cli_build_ext_signing_key(void) return pkey; } + +EVP_PKEY *cli_build_ext_signing_key_Dilithium2(void) +{ + EVP_PKEY *pkey = EVP_PKEY_new(); + int result = 0; + OSSL_PARAM params[2]; + + // Check bld and params + if (!pkey) { + EVP_PKEY_free(pkey); + return NULL; + } + + // Create a context for the public key + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "dilithium2", NULL); + if (!ctx) { + EVP_PKEY_free(pkey); + return NULL; + } + + // Initialize key generation from data + if (EVP_PKEY_fromdata_init(ctx) <= 0) { + EVP_PKEY_free(pkey); + return NULL; + } + + // Setup for binary conversion of the public key string + unsigned char *pubkey = NULL; + size_t key_hex_strlen = strlen(CLI_DI2_EXT_SIG); + size_t pubkey_len = key_hex_strlen / 2; + + pubkey = malloc(pubkey_len); + if (!pubkey) { + EVP_PKEY_free(pkey); + return NULL; + } + + // Convert the hex string to binary + result = OPENSSL_hexstr2buf_ex(pubkey, pubkey_len, NULL, CLI_DI2_EXT_SIG, 0); + if (!result) { + free(pubkey); + EVP_PKEY_free(pkey); + return NULL; + } + + // Build the parameters + params[0] = OSSL_PARAM_construct_octet_string("pub", pubkey, pubkey_len); + params[1] = OSSL_PARAM_construct_end(); + + if (!params[0].data) { + free(pubkey); + EVP_PKEY_free(pkey); + return NULL; + } + + // Construct the public key + int ret_code = EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params); + if (ret_code <= 0) { + // Actaully dump the errors here, as post quantum stuff is likely to change + // and knowing why it failed will be helpful to whomever has to debug this later. + cli_errmsg("cli_build_ext_signing_key_Dilithium2: failed to construct public key.\n"); + // Dump all the specific information OpenSSL has about this error. + const char *file; + int line; + const char *function; + const char *data; + int flags; + unsigned long error_code = ERR_peek_last_error_all(&file, &line, &function, &data, &flags); + cli_errmsg("cli_build_ext_signing_key_Dilithium2: OpenSSL error code: %lu (file: %s, line: %d, function: %s, data: %s, flags: %d)\n", error_code, file, line, function, data, flags); + + free(pubkey); + EVP_PKEY_free(pkey); + return NULL; + } + + // Cleanup + // DO NOT free params[0].data, it poitns to pubkey. + // OSSL_PARAM_free(params[0].data); + + // Free the public key + free(pubkey); + + // Free the context + if (ctx) + EVP_PKEY_CTX_free(ctx); + + return pkey; +} + +EVP_PKEY *cli_build_ext_signing_key(unsigned int keytype) +{ + switch (keytype) { + case 1: + cli_dbgmsg("cli_build_ext_signing_key: building RSA external signing key\n"); + return cli_build_ext_signing_key_RSA(); + break; + case 2: + cli_dbgmsg("cli_build_ext_signing_key: building Dilithium2 external signing key\n"); + return cli_build_ext_signing_key_Dilithium2(); + break; + default: + return NULL; + break; + } +} + #else #error "Unsupported OpenSSL version" #endif diff --git a/libclamav/crypto.h b/libclamav/crypto.h index 961cd93934..fb6ba8a26e 100644 --- a/libclamav/crypto.h +++ b/libclamav/crypto.h @@ -28,9 +28,9 @@ void cli_setup_fips_configuration(void); int cli_get_fips_mode(void); #if OPENSSL_VERSION_MAJOR == 1 -RSA *cli_build_ext_signing_key(void); +RSA *cli_build_ext_signing_key(unsigned int keytype); #elif OPENSSL_VERSION_MAJOR == 3 -EVP_PKEY *cli_build_ext_signing_key(void); +EVP_PKEY *cli_build_ext_signing_key(unsigned int keytype); #else #error "Unsupported OpenSSL version" #endif diff --git a/libclamav/dsig.c b/libclamav/dsig.c index b8b54f483f..1fb495da22 100644 --- a/libclamav/dsig.c +++ b/libclamav/dsig.c @@ -426,7 +426,7 @@ int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n return ret; } -int cli_hex2bin(const char *hex, unsigned char *bin, int len) +int cli_hex2bin(const char *hex, unsigned char *bin, unsigned int len) { // Use tricks to do this fast and without memory violations unsigned char *in = (unsigned char *)hex; @@ -461,108 +461,100 @@ int cli_hex2bin(const char *hex, unsigned char *bin, int len) cl_error_t cli_sigver_external(const char *file) { - cl_error_t result = CL_ERROR; - unsigned char sha256_bin[SHA256_DIGEST_LENGTH]; - char *sha256 = NULL; + cl_error_t result = CL_ERROR; + unsigned char *hash = NULL; + unsigned int hash_len = 0; unsigned char *sig_bin = NULL; - // Use the built-in method to hash the CVD file. - FILE *fs = fopen(file, "rb"); - if (fs == NULL) { - cli_errmsg("cli_cvd_ext_sig_verify: Can't open file %s\n", file); - return CL_EOPEN; - } - fseek(fs, 512, SEEK_SET); - sha256 = cli_hashstream(fs, NULL, 3); - fclose(fs); - if (sha256 == NULL) { - cli_errmsg("cli_cvd_ext_sig_verify: Can't generate SHA256 hash\n"); - result = CL_EMEM; - goto done; - } - cli_dbgmsg("SHA256(.tar.gz) = %s\n", sha256); - - // Build the RSA key from the exponent and modulus -#if OPENSSL_VERSION_MAJOR == 1 - RSA *rsa = cli_build_ext_signing_key(); - if (rsa == NULL) { - cli_errmsg("cli_cvd_ext_sig_verify: Can't create RSA key from public key\n"); - result = CL_EVERIFY; - goto done; - } -#elif OPENSSL_VERSION_MAJOR == 3 - EVP_PKEY *rsa = cli_build_ext_signing_key(); - if (rsa == NULL) { - cli_errmsg("cli_cvd_ext_sig_verify: Can't create RSA key from public key\n"); - result = CL_EVERIFY; - goto done; - } -#else -#error "Unsupported OpenSSL version" -#endif - - // Convert the sha256 hash to binary - if (cli_hex2bin(sha256, sha256_bin, SHA256_DIGEST_LENGTH) == -1) { - cli_errmsg("cli_cvd_ext_sig_verify: Can't convert sha256 hash to binary\n"); - result = CL_EVERIFY; - goto done; - } - // // External Signature processing // // Load the external signature file - char *sigfile = strdup(file); - if (sigfile == NULL) { + char *sig_filename = strdup(file); + if (sig_filename == NULL) { cli_errmsg("cli_cvd_ext_sig_verify: Can't allocate memory for signature file name\n"); result = CL_EMEM; goto done; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-truncation" - strncpy(sigfile + strlen(sigfile) - 4, ".sig", 4); + strncpy(sig_filename + strlen(sig_filename) - 4, ".sig", 4); #pragma GCC diagnostic pop - fs = fopen(sigfile, "rb"); + FILE *fs = fopen(sig_filename, "rb"); if (fs == NULL) { - cli_errmsg("cli_cvd_ext_sig_verify: Can't open signature file %s\n", sigfile); + cli_errmsg("cli_cvd_ext_sig_verify: Can't open signature file %s\n", sig_filename); result = CL_EOPEN; goto done; } // Read the signature file fseek(fs, 0, SEEK_END); - size_t siglen = (size_t)ftell(fs); + size_t sigfile_len = (size_t)ftell(fs); fseek(fs, 0, SEEK_SET); - char *sig = (char *)malloc(siglen + 1); - if (sig == NULL) { + char *sig_file = (char *)malloc(sigfile_len + 1); + if (sig_file == NULL) { cli_errmsg("cli_cvd_ext_sig_verify: Can't allocate memory for signature\n"); fclose(fs); result = CL_EMEM; goto done; } - if (fread(sig, 1, siglen, fs) != siglen) { + if (fread(sig_file, 1, sigfile_len, fs) != sigfile_len) { cli_errmsg("cli_cvd_ext_sig_verify: Can't read signature from file\n"); fclose(fs); result = CL_EVERIFY; goto done; } - sig[siglen] = 0; + sig_file[sigfile_len] = 0; fclose(fs); - // Extract the parts of the signature file - // it's kept in a format like: "SHA256 hash hex:RSA signature hex" - char *sig_seperator = strchr(sig, ':'); - if (sig_seperator == NULL) { + // + // Parse the signature file + // + + // Use strtok to extract the parts of the signature file + // it's kept in a format like: "hash algorithm:signature algorithm:SHA256 hash hex:RSA signature hex" + char *hash_alg = strtok(sig_file, ":"); + if (hash_alg == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't find hash algorithm in external database signature file\n"); + result = CL_EVERIFY; + goto done; + } + + // Parse the integer into our hash algorithm selector + int hash_algorithm = atoi(hash_alg); + + // Store the signature algorithm + char *sig_alg = strtok(NULL, ":"); + if (sig_alg == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't find signature algorithm in external database signature file\n"); + result = CL_EVERIFY; + goto done; + } + + // Parse the integer into our signature algorithm selector + int sig_algorithm = atoi(sig_alg); + + // Store the start of the hash + char *ext_hash = strtok(NULL, ":"); + if (ext_hash == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't find hash in external database signature file\n"); + result = CL_EVERIFY; + goto done; + } + + // Extract the signature hex string from the signature file + char *sig_hex = strtok(NULL, ":"); + if (sig_hex == NULL) { cli_errmsg("cli_cvd_ext_sig_verify: Can't find signature in external database signature file\n"); result = CL_EVERIFY; goto done; } - *sig_seperator = 0; - sig_seperator++; - siglen = strlen(sig_seperator) / 2; - sig_bin = (unsigned char *)malloc(siglen); + + // Now extract the binary of the signature + int siglen = strlen(sig_hex) / 2; + sig_bin = (unsigned char *)malloc(siglen); if (sig_bin == NULL) { cli_errmsg("cli_cvd_ext_sig_verify: Can't allocate memory for signature binary\n"); result = CL_EMEM; @@ -570,16 +562,88 @@ cl_error_t cli_sigver_external(const char *file) } // convert the signature to binary - if (cli_hex2bin(sig_seperator, sig_bin, siglen) == -1) { + if (cli_hex2bin(sig_hex, sig_bin, siglen) == -1) { cli_errmsg("cli_cvd_ext_sig_verify: Can't convert signature to binary\n"); result = CL_EVERIFY; } - // If we are using a verson of openssl less than 3.0.0, we need to use the RSA_verify function + // + // The signature file has been parsed. Now hash the database file using the selected algorithm + // + + // Use the built-in method to hash the CVD file. + FILE *fq = fopen(file, "rb"); + if (fq == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't open file %s\n", file); + return CL_EOPEN; + } + fseek(fq, 512, SEEK_SET); + char *db_hash = cli_hashstream_ex(fq, NULL, hash_algorithm, &hash_len); + fclose(fq); + if (db_hash == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't generate hash for algorithm: %d\n", hash_algorithm); + result = CL_EMEM; + goto done; + } + cli_dbgmsg("HASH OF(.tar.gz) using alg %d = %s\n", hash_algorithm, db_hash); + + // Allocate memory for the hash + hash = (unsigned char *)malloc(hash_len); + if (hash == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't allocate memory for hash\n"); + result = CL_EMEM; + goto done; + } + + // Convert the hash to binary + if (cli_hex2bin(db_hash, hash, hash_len) == -1) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't convert hash to binary\n"); + result = CL_EVERIFY; + goto done; + } + + // + // Sanity check the hash in the external signature file vs the internally generated hash + // + + // Compare the hashes (we trust the hash we generate from the db file) + if (strncmp(db_hash, sig_hex, strlen(db_hash)) == 0) { + cli_errmsg("cli_cvd_ext_sig_verify: Hash in external database signature file does not match hash of database file\n"); + result = CL_EVERIFY; + goto done; + } + + // + // Build the public key from raw values for the selected algorithm + // #if OPENSSL_VERSION_MAJOR == 1 + RSA *key = cli_build_ext_signing_key(sig_algorithm); + if (key == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't create public key from values\n"); + result = CL_EVERIFY; + goto done; + } +#elif OPENSSL_VERSION_MAJOR == 3 + EVP_PKEY *key = cli_build_ext_signing_key(sig_algorithm); + if (key == NULL) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't create public key from values\n"); + result = CL_EVERIFY; + goto done; + } +#else +#error "Unsupported OpenSSL version" +#endif + + cli_dbgmsg("cli_cvd_ext_sig_verify: Public key created from values\n"); + + // If we are using a version of openssl less than 3.0.0, we need to use the RSA_verify function +#if OPENSSL_VERSION_MAJOR == 1 + // OpenSSL library 1.1.x exclusively uses RSA_verify to verify RSA signatures + // The reasoning on this is that moving forward, FIPS systems will require + // versions of the OpenSSL library that are 3.0.x and above. + // verify the signature - // int sig_verify = RSA_verify(NID_sha256, sha256, strlen(sha256), sig_bin, siglen, rsa); - int sig_verify = RSA_verify(NID_sha256, sha256_bin, SHA256_DIGEST_LENGTH, sig_bin, siglen, rsa); + int sig_verify = RSA_verify(NID_sha256, hash, hash_Len, sig_bin, siglen, key); if (sig_verify != 1) { cli_errmsg("cli_cvd_ext_sig_verify: RSA signature verification failed for external database signature\n"); result = CL_EVERIFY; @@ -592,7 +656,7 @@ cl_error_t cli_sigver_external(const char *file) // verify the signature EVP_PKEY_CTX *pctx = NULL; - pctx = EVP_PKEY_CTX_new(rsa, NULL); + pctx = EVP_PKEY_CTX_new(key, NULL); if (pctx == NULL) { cli_errmsg("cli_cvd_ext_sig_verify: Can't create EVP_PKEY_CTX\n"); result = CL_EVERIFY; @@ -605,18 +669,52 @@ cl_error_t cli_sigver_external(const char *file) goto done; } - if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha256()) != 1) { + const EVP_MD *md = NULL; + switch (hash_algorithm) { + case 1: + // MD5 is not allowed in FIPS mode, verify we are not in FIPS mode + if (cli_get_fips_mode()) { + cli_errmsg("cli_cvd_ext_sig_verify: MD5 is not allowed in FIPS mode\n"); + result = CL_EVERIFY; + goto done; + } else { + md = EVP_md5(); + } + break; + case 2: + // SHA1 is not allowed in FIPS mode, verify we are not in FIPS mode + if (cli_get_fips_mode()) { + cli_errmsg("cli_cvd_ext_sig_verify: SHA1 is not allowed in FIPS mode\n"); + result = CL_EVERIFY; + goto done; + } else { + md = EVP_sha1(); + } + break; + case 3: + md = EVP_sha256(); + break; + case 4: + md = EVP_sha3_512(); + break; + default: + cli_errmsg("cli_cvd_ext_sig_verify: Unsupported hash algorithm\n"); + result = CL_EVERIFY; + goto done; + } + + if (EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) { cli_errmsg("cli_cvd_ext_sig_verify: Can't set signature MD\n"); result = CL_EVERIFY; goto done; } - if (EVP_PKEY_verify(pctx, sig_bin, siglen, sha256_bin, SHA256_DIGEST_LENGTH) != 1) { - cli_errmsg("cli_cvd_ext_sig_verify: RSA signature verification failed for external database signature\n"); + if (EVP_PKEY_verify(pctx, sig_bin, siglen, (const unsigned char *)hash, hash_len) != 1) { + cli_errmsg("cli_cvd_ext_sig_verify: Cryptographic signature verification failed for external database signature\n"); result = CL_EVERIFY; goto done; } else { - cli_dbgmsg("cli_cvd_ext_sig_verify: RSA signature verification successful for external database signature\n"); + cli_dbgmsg("cli_cvd_ext_sig_verify: Cryptographic signature verification successful for external database signature\n"); result = CL_SUCCESS; } @@ -627,15 +725,16 @@ cl_error_t cli_sigver_external(const char *file) done: // Clean up - if (sig) free(sig); - if (sigfile) free(sigfile); - if (sha256) free(sha256); + if (sig_filename) free(sig_filename); + if (sig_file) free(sig_file); + if (db_hash) free(db_hash); + if (hash) free(hash); if (sig_bin) free(sig_bin); #if OPENSSL_VERSION_NUMBER < 0x30000000L - if (rsa) RSA_free(rsa); + if (key) RSA_free(key); #else - if (rsa) EVP_PKEY_free(rsa); + if (key) EVP_PKEY_free(key); #endif return result; -} \ No newline at end of file +} diff --git a/libclamav/others.c b/libclamav/others.c index e4b19a0955..7e4088c45a 100644 --- a/libclamav/others.c +++ b/libclamav/others.c @@ -1233,18 +1233,19 @@ cl_error_t cli_checktimelimit(cli_ctx *ctx) return ret; } -/* - * Type: 1 = MD5, 2 = SHA1, 3 = SHA256 - */ -char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type) +// Not publicly exported, used for internal purposes to allow for hash length to be returned +char *cli_hashstream_ex(FILE *fs, unsigned char *digcpy, int type, unsigned int *hash_len) { - unsigned char digest[32]; + unsigned char *digest; char buff[FILEBUFF]; char *hashstr, *pt; const char *alg = NULL; - int i, bytes, size; + int i, bytes, size = 0; void *ctx; + if (!fs) + return NULL; + switch (type) { case 1: alg = "md5"; @@ -1254,23 +1255,38 @@ char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type) alg = "sha1"; size = 20; break; + case 3: + alg = "sha256"; + size = 32; + break; + case 4: + alg = "sha3-512"; + size = 64; + break; default: alg = "sha256"; size = 32; break; } + if (!(digest = (unsigned char *)calloc(size, sizeof(unsigned char)))) + return NULL; + ctx = cl_hash_init(alg); - if (!(ctx)) + if (!(ctx)) { + free(digest); return NULL; + } while ((bytes = fread(buff, 1, FILEBUFF, fs))) cl_update_hash(ctx, buff, bytes); cl_finish_hash(ctx, digest); - if (!(hashstr = (char *)calloc(size * 2 + 1, sizeof(char)))) + if (!(hashstr = (char *)calloc(size * 2 + 1, sizeof(unsigned char)))) { + free(digest); return NULL; + } pt = hashstr; for (i = 0; i < size; i++) { @@ -1281,9 +1297,22 @@ char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type) if (digcpy) memcpy(digcpy, digest, size); + if (hash_len != NULL) { + *hash_len = size; + } + + free(digest); return hashstr; } +/* + * Type: 1 = MD5, 2 = SHA1, 3 = SHA256, 4 = SHA3-512 + */ +char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type) +{ + return cli_hashstream_ex(fs, digcpy, type, 0); +} + char *cli_hashfile(const char *filename, int type) { FILE *fs; diff --git a/libclamav/others.h b/libclamav/others.h index 8cebf78d35..b3511556f8 100644 --- a/libclamav/others.h +++ b/libclamav/others.h @@ -1026,6 +1026,7 @@ char *cli_safer_strdup(const char *s); int cli_rmdirs(const char *dirname); char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type); +char *cli_hashstream_ex(FILE *fs, unsigned char *digcpy, int type, unsigned int *hash_len); char *cli_hashfile(const char *filename, int type); /** diff --git a/sigext/cvd_ext_sign.sh b/sigext/cvd_ext_sign.sh new file mode 100644 index 0000000000..666b066def --- /dev/null +++ b/sigext/cvd_ext_sign.sh @@ -0,0 +1,64 @@ +#!/bin/bash + + +if [ "$#" -ne 4 ]; then + echo "Usage: $0 " + echo "Example: $0 /path/to/file private_key.pem 1 1" + echo "Hash Algorithms: " + echo "1 - MD5" + echo "2 - SHA-256" + echo "3 - SHA3-512" + echo "Signature Algorithms: " + echo "1 - RSA" + echo "2 - Dilithium2" + exit 1 +fi + +file_path="$1" +private_key="$2" +hash_algorithm="$3" +signature_algorithm="$4" +offset=512 # 0x200 in decimal + +# Base on the $algorithm, select the right way to hash and sign +case $hash_algorithm in + '1') + hash_alg_name="md5" + # Generate the hexadecimal MD5 hash + hash_hex=$(tail -c +$((offset + 1)) "$file_path" | md5sum | awk '{print $1}') + ;; + '2') + hash_alg_name="sha1" + # Generate the hexadecimal SHA-1 hash + hash_hex=$(tail -c +$((offset + 1)) "$file_path" | shasum | awk '{print $1}') + ;; + '3') + hash_alg_name="sha256" + # Generate the hexadecimal SHA-256 hash + hash_hex=$(tail -c +$((offset + 1)) "$file_path" | sha256sum | awk '{print $1}') + ;; + '4') + hash_alg_name="sha3-512" + # Generate the hexidecimal SHA3-512 hash + hash_hex=$(tail -c +$((offset + 1)) "$file_path" | openssl dgst -sha3-512 | awk '{print $2}') + ;; +esac + +case $signature_algorithm in + '1') + # Create the RSA keypair with: + # openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048 + # openssl rsa -pubout -in private_key.pem -out public_key.pem + signature_hex=$(echo -n "$hash_hex" | xxd -r -p | openssl pkeyutl -sign -inkey "$private_key" -pkeyopt digest:$hash_alg_name | xxd -p -c 8192) + ;; + + '2') + # Create the dilithium2 key with: + # openssl genpkey -algorithm dilithium2 -out private_key_di2.pem + # openssl pkey -in private_key_di2.pem -pubout -out public_key_di2.pem + signature_hex=$(echo -n "$hash_hex" | xxd -r -p | openssl pkeyutl -sign -inkey "$private_key" -pkeyopt digest:$hash_alg_name | xxd -p -c 8192) + ;; +esac + +# Output the hexadecimal hash and the base64-encoded signature +echo "$hash_algorithm:$signature_algorithm:$hash_hex:$signature_hex" diff --git a/sigext/cvd_hash_sha256_sign.sh b/sigext/cvd_hash_sha256_sign.sh deleted file mode 100644 index 68aedc7360..0000000000 --- a/sigext/cvd_hash_sha256_sign.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Create the keypair with: -# openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048 -# openssl rsa -pubout -in private_key.pem -out public_key.pem - -# The public exponent and modulus can be extracted from the public key with: -# openssl rsa -pubin -in public_key.pem -text -noout - -# The public exponent and modulus can be used to create a public key in C with: -#define CLI_NSTR_EXT_SIG "E32B3AC1D501EE975296A45BA65DD699100DADD340FF3BBD1F1030C66D6BB16DBFBD53DF4D97BBD42EF8FC777E7C114A6074A87DD8095A5C08B3DD7B85817713047647EF396C58358C5C22B5C3ADF85CE8D0ABC429F89E936EC917B64DD00E02A712E6666FAE1A71591092BCEE59E3141758C4719B4B08589117B0FF7CDBDBB261F8486A193E2E720AE0B16D40DD5E56E97346CBD8010DC81B35332F41C9E93E61490802DDCDFC823D581BA6888588968C68A3C95B93949AF411682E73323F7469473F668B0958F6966849FF03BDE808866D127A2C058B16F17C741A9EE50812A5C7841224E55BF7ADDB5AEAE8EB5476F9BC8740178AB35926D5DC375583C641" -#define CLI_ESTR_EXT_SIG "010001" - - -if [ "$#" -ne 2 ]; then - echo "Usage: $0 " - exit 1 -fi - -file_path="$1" -private_key="$2" -offset=512 # 0x200 in decimal - -# Generate the hexadecimal SHA-256 hash -sha256_hash_hex=$(tail -c +$((offset + 1)) "$file_path" | sha256sum | awk '{print $1}') - -# Sign the binary hash (using the binary data corresponding to the hex hash) and output the signature in base64 -signature_hex=$(echo -n "$sha256_hash_hex" | xxd -r -p | openssl pkeyutl -sign -inkey "$private_key" -pkeyopt digest:sha256 | xxd -p -c 1024) - -# Output the hexadecimal hash and the base64-encoded signature -echo "$sha256_hash_hex:$signature_hex" - diff --git a/sigext/extract_di2_key.sh b/sigext/extract_di2_key.sh new file mode 100644 index 0000000000..fccd460dcd --- /dev/null +++ b/sigext/extract_di2_key.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Check if a key file is provided +if [ $# -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Input PEM file +PEM_FILE=$1 +DEFINE_NAME=$2 + +# The below command dumps out only the public key and formats it for teh define +HEX_STRING=$(openssl pkey -in "$PEM_FILE" -text -noout -pubout | grep -v "dilithium2 public key:" | grep -v "PQ key material:" | tr -d '\n\t :') + +# Print the compact hex string +echo "#define $DEFINE_NAME \"$HEX_STRING\"" +