diff --git a/libclamav/crypto.c b/libclamav/crypto.c index e4824fdb79..d22c297b80 100644 --- a/libclamav/crypto.c +++ b/libclamav/crypto.c @@ -52,6 +52,19 @@ #include #include +#include +#include +#include +#include +#include + +// These are the public key components for the external signature +// Ideally these should be packaged in a way that allows individual entites +// to use their own keys if they should so desire. +#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 @@ -125,6 +138,74 @@ time_t timegm(struct tm *t) } #endif +/** + * This variable determines if we are operating in FIPS mode, default to no. + */ +int cli_fips_mode = 0; + +/** + * @brief This function determines if we are running in FIPS mode and sets the cl_fips_mode variable + * + * This function is called by cl_init() and does not need to be called by the user + * + * @return int Returns 1 if we are running in FIPS mode, 0 otherwise + * + */ + +void cli_setup_fips_configuration(void) +{ +#if OPENSSL_VERSION_MAJOR == 1 +// OpenSSL 1.x (1.0 or 1.1) +#ifdef OPENSSL_FIPS + if (FIPS_mode()) { + cli_infomsg_simple("cl_setup_fips_configuration: FIPS mode provider found.\n"); + cl_fips_mode = 1; + } else { + cli_infomsg_simple("cl_setup_fips_configuration: FIPS mode provider was not found.\n"); + cl_fips_mode = 0; + } +#else + cl_fips_mode = 0; +#endif + +#elif OPENSSL_VERSION_MAJOR == 3 + // OpenSSL 3.0.x + OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new(); + if (libctx == NULL) { + cli_warnmsg("cl_setup_fips_configuration: Failed to create libctx.\n"); + cli_fips_mode = 0; + return; + } + + OSSL_PROVIDER *fips = OSSL_PROVIDER_load(libctx, "fips"); + if (fips != NULL) { + cli_infomsg_simple("cl_setup_fips_configuration: FIPS mode provider found.\n"); + cli_fips_mode = 1; + OSSL_PROVIDER_unload(fips); + OSSL_LIB_CTX_free(libctx); + } else { + cli_infomsg_simple("cl_setup_fips_configuration: FIPS mode provider was not found.\n"); + cli_fips_mode = 0; + OSSL_LIB_CTX_free(libctx); + } +#else +#error "Unsupported OpenSSL version" +#endif +} + +/** + * @brief Return the status of our FIPS condition. + * + * This function allows users of the library to determine if the library is running in FIPS mode. + * + * @return int Returns 1 if we are running in FIPS mode, 0 otherwise + */ + +int cli_get_fips_mode(void) +{ + return cli_fips_mode; +} + /** * @brief This function initializes the openssl crypto system * @@ -145,6 +226,8 @@ int cl_initialize_crypto(void) ERR_load_crypto_strings(); #endif + cli_setup_fips_configuration(); + return 0; } @@ -170,7 +253,12 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign size_t cur; int winres = 0; - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) return NULL; @@ -260,7 +348,12 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) const EVP_MD *md; unsigned char *res; - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) return NULL; @@ -392,7 +485,12 @@ int cl_verify_signature_hash(EVP_PKEY *pkey, const char *alg, unsigned char *sig const EVP_MD *md; size_t mdsz; - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) return -1; @@ -437,7 +535,12 @@ int cl_verify_signature_fd(EVP_PKEY *pkey, const char *alg, unsigned char *sig, if (!(digest)) return -1; - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) { free(digest); return -1; @@ -506,7 +609,12 @@ int cl_verify_signature(EVP_PKEY *pkey, const char *alg, unsigned char *sig, uns return -1; } - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) { free(digest); if (decode) @@ -725,7 +833,12 @@ unsigned char *cl_sign_data(EVP_PKEY *pkey, const char *alg, unsigned char *hash unsigned int siglen; unsigned char *sig; - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) return NULL; @@ -1148,7 +1261,12 @@ void *cl_hash_init(const char *alg) EVP_MD_CTX *ctx; const EVP_MD *md; - md = EVP_get_digestbyname(alg); + // If OpenSSL is running in FIPS mode, we need to use the FIPS-compliant md5 lookup of the algorithm + if (cli_fips_mode && !strncasecmp(alg, "md5", 3)) + md = EVP_MD_fetch(NULL, alg, "-fips"); + else + md = EVP_get_digestbyname(alg); + if (!(md)) return NULL; @@ -1210,3 +1328,231 @@ void cl_hash_destroy(void *ctx) EVP_MD_CTX_destroy((EVP_MD_CTX *)ctx); } + +#if OPENSSL_VERSION_MAJOR == 1 +RSA *cli_build_ext_signing_key(void) +{ + RSA *rsa = RSA_new(); + BIGNUM *n = BN_new(); + BIGNUM *e = BN_new(); + + if (!rsa || !n || !e) { + RSA_free(rsa); + BN_free(n); + BN_free(e); + return NULL; + } + + if (!BN_hex2bn(&n, CLI_NSTR_EXT_SIG) || !BN_hex2bn(&e, CLI_ESTR_EXT_SIG)) { + RSA_free(rsa); + BN_free(n); + BN_free(e); + return NULL; + } + rsa->n = n; + rsa->e = e; + + return rsa; +} +#elif OPENSSL_VERSION_MAJOR == 3 +// Do this the OpenSSL 3 way, avoiding deprecation warnings +EVP_PKEY *cli_build_ext_signing_key_RSA(void) +{ + EVP_PKEY *pkey = EVP_PKEY_new(); + BIGNUM *n = BN_new(); + BIGNUM *e = BN_new(); + OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + OSSL_PARAM *params = NULL; + int result = 0; + + // Check bld and params + if (!pkey || !n || !e || !bld) { + EVP_PKEY_free(pkey); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + return NULL; + } + + // Set the public key components + if (!BN_hex2bn(&n, CLI_NSTR_EXT_SIG) || !BN_hex2bn(&e, CLI_ESTR_EXT_SIG)) { + EVP_PKEY_free(pkey); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + return NULL; + } + + result = OSSL_PARAM_BLD_push_BN(bld, "n", n); + if (!result) { + EVP_PKEY_free(pkey); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + return NULL; + } + + result = OSSL_PARAM_BLD_push_BN(bld, "e", e); + if (!result) { + EVP_PKEY_free(pkey); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + return NULL; + } + + result = OSSL_PARAM_BLD_push_BN(bld, "d", NULL); + if (!result) { + EVP_PKEY_free(pkey); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + return NULL; + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (!params) { + EVP_PKEY_free(pkey); + BN_free(n); + BN_free(e); + OSSL_PARAM_BLD_free(bld); + return NULL; + } + + OSSL_PARAM_BLD_free(bld); + BN_free(n); + BN_free(e); + + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (!ctx) { + EVP_PKEY_free(pkey); + return NULL; + } + + if (EVP_PKEY_fromdata_init(ctx) <= 0) { + EVP_PKEY_free(pkey); + return NULL; + } + + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) { + EVP_PKEY_free(pkey); + return NULL; + } + + if (params) + OSSL_PARAM_free(params); + + if (ctx) + EVP_PKEY_CTX_free(ctx); + + 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 new file mode 100644 index 0000000000..fb6ba8a26e --- /dev/null +++ b/libclamav/crypto.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2011-2013 Sourcefire, Inc. + * + * Authors: aCaB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __CRYPTO_H +#define __CRYPTO_H +#include +#include + +void cli_setup_fips_configuration(void); +int cli_get_fips_mode(void); + +#if OPENSSL_VERSION_MAJOR == 1 +RSA *cli_build_ext_signing_key(unsigned int keytype); +#elif OPENSSL_VERSION_MAJOR == 3 +EVP_PKEY *cli_build_ext_signing_key(unsigned int keytype); +#else +#error "Unsupported OpenSSL version" +#endif + +#endif diff --git a/libclamav/cvd.c b/libclamav/cvd.c index 6d54dc7998..790e38f5a3 100644 --- a/libclamav/cvd.c +++ b/libclamav/cvd.c @@ -46,10 +46,13 @@ #include "others.h" #include "dsig.h" #include "str.h" +#include "crypto.h" #include "cvd.h" #include "readdb.h" #include "default.h" +#include + #define TAR_BLOCKSIZE 512 static void cli_untgz_cleanup(char *path, gzFile infile, FILE *outfile, int fdd) @@ -627,6 +630,14 @@ cl_error_t cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, cli_dbgmsg("in cli_cvdload()\n"); + // FIPS verification MUST preclude other verification if in FIPS mode. + if (cli_get_fips_mode()) { + ret = cli_sigver_external(filename); + if (ret != CL_SUCCESS) { + return ret; + } + } + /* verify */ if ((ret = cli_cvdverify(fs, &cvd, dbtype))) return ret; diff --git a/libclamav/dsig.c b/libclamav/dsig.c index b0cf212f07..1fb495da22 100644 --- a/libclamav/dsig.c +++ b/libclamav/dsig.c @@ -34,6 +34,7 @@ #include "clamav.h" #include "others.h" +#include "crypto.h" #include "dsig.h" #include "str.h" @@ -424,3 +425,316 @@ int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n BN_free(e); return ret; } + +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; + unsigned char *out = bin; + int retlen = len / 2; + + while (len--) { + *out = 0; + if (*in >= '0' && *in <= '9') + *out = *in - '0'; + else if (*in >= 'A' && *in <= 'F') + *out = *in - 'A' + 10; + else if (*in >= 'a' && *in <= 'f') + *out = *in - 'a' + 10; + else + return -1; + in++; + *out <<= 4; + if (*in >= '0' && *in <= '9') + *out |= *in - '0'; + else if (*in >= 'A' && *in <= 'F') + *out |= *in - 'A' + 10; + else if (*in >= 'a' && *in <= 'f') + *out |= *in - 'a' + 10; + else + return -1; + in++; + out++; + } + return retlen; +} + +cl_error_t cli_sigver_external(const char *file) +{ + cl_error_t result = CL_ERROR; + unsigned char *hash = NULL; + unsigned int hash_len = 0; + unsigned char *sig_bin = NULL; + + // + // External Signature processing + // + + // Load the external signature file + 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(sig_filename + strlen(sig_filename) - 4, ".sig", 4); +#pragma GCC diagnostic pop + FILE *fs = fopen(sig_filename, "rb"); + if (fs == NULL) { + 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 sigfile_len = (size_t)ftell(fs); + fseek(fs, 0, SEEK_SET); + 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_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_file[sigfile_len] = 0; + fclose(fs); + + // + // 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; + } + + // 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; + goto done; + } + + // convert the signature to binary + 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; + } + + // + // 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, 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; + goto done; + } else { + cli_dbgmsg("cli_cvd_ext_sig_verify: RSA signature verification successful for external database signature\n"); + result = CL_SUCCESS; + } +#elif OPENSSL_VERSION_MAJOR == 3 + // verify the signature + EVP_PKEY_CTX *pctx = 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; + goto done; + } + + if (EVP_PKEY_verify_init(pctx) != 1) { + cli_errmsg("cli_cvd_ext_sig_verify: Can't initialize EVP_PKEY_verify_init\n"); + result = CL_EVERIFY; + goto done; + } + + 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, (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: Cryptographic signature verification successful for external database signature\n"); + result = CL_SUCCESS; + } + + if (pctx) EVP_PKEY_CTX_free(pctx); +#else +#error "Unsupported OpenSSL version" +#endif + +done: + // Clean up + 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 (key) RSA_free(key); +#else + if (key) EVP_PKEY_free(key); +#endif + + return result; +} diff --git a/libclamav/dsig.h b/libclamav/dsig.h index 61666010c4..aa569b7623 100644 --- a/libclamav/dsig.h +++ b/libclamav/dsig.h @@ -31,6 +31,7 @@ cl_error_t cli_versig(const char *md5, const char *dsig); int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n_str, const char *e_str); +cl_error_t cli_sigver_external(const char *file); /** * @brief Connect to a signing server, send the data to be signed, and return the digital signature. diff --git a/libclamav/libclamav.map b/libclamav/libclamav.map index a34a05dbb6..ccf5ae716f 100644 --- a/libclamav/libclamav.map +++ b/libclamav/libclamav.map @@ -302,6 +302,9 @@ CLAMAV_PRIVATE { cli_magic_scan_buff; cli_checklimits; cli_matchmeta; + cli_setup_fips_configuration; + cli_fips_mode; + cli_get_fips_mode; __cli_strcasestr; __cli_strndup; 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 7f0d267255..862f20a477 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/libfreshclam/libfreshclam_internal.c b/libfreshclam/libfreshclam_internal.c index 5afde46710..8906ff92ae 100644 --- a/libfreshclam/libfreshclam_internal.c +++ b/libfreshclam/libfreshclam_internal.c @@ -74,6 +74,7 @@ #include "clamav.h" #include "others.h" #include "str.h" +#include "crypto.h" #include "cvd.h" #include "regex_list.h" @@ -88,6 +89,7 @@ #include "libfreshclam.h" #include "libfreshclam_internal.h" #include "dns.h" +#include "clamav.h" #define DB_FILENAME_MAX 60 #define CVD_HEADER_SIZE 512 @@ -1478,6 +1480,66 @@ static fc_error_t getcvd( goto done; } + // + // If we are in FIPS mode, download the external signature file + // + char *sigfile = NULL; + char *extSigTmpFile = NULL; + char *extSigUrl = NULL; + if (cli_get_fips_mode()) { + + // The external signature file is the same as the CVD file, but with a different extension. + sigfile = strdup(cvdfile); + if (!sigfile) { + logg(LOGG_ERROR, "Can't allocate memory for external signature file name!\n"); + status = FC_EMEM; + goto done; + } + +// Change the extension to .sig +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" + strncpy(sigfile + strlen(sigfile) - 4, ".sig", 4); +#pragma GCC diagnostic pop + + urlLen = strlen(server) + strlen("/") + strlen(sigfile); + extSigUrl = malloc(urlLen + 1); + if (!extSigUrl) { + logg(LOGG_ERROR, "Can't allocate memory for external signature file URL!\n"); + status = FC_EMEM; + goto done; + } + + // Create a temporary file for the external signature (same name as tmpFile but with an .sig extension instead of .tmp) + extSigTmpFile = strdup(tmpfile); + if (!extSigTmpFile) { + logg(LOGG_ERROR, "Can't allocate memory for external signature temp file name!\n"); + status = FC_EMEM; + goto done; + } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" + strncpy(extSigTmpFile + strlen(extSigTmpFile) - 4, ".sig", 4); +#pragma GCC diagnostic pop + + // Construct the URL + snprintf(extSigUrl, urlLen + 1, "%s/%s", server, sigfile); + + printf("Downloading external signature file %s from %s\n", sigfile, extSigUrl); + + // Download the external signature file + ret = downloadFile(extSigUrl, extSigTmpFile, 1, logerr, ifModifiedSince); + if (ret == FC_UPTODATE) { + logg(LOGG_INFO, "%s is up-to-date.\n", sigfile); + status = ret; + goto done; + } else if (ret > FC_UPTODATE) { + logg(logerr ? LOGG_ERROR : LOGG_WARNING, "Can't download %s from %s\n", sigfile, extSigUrl); + status = ret; + goto done; + } + } + /* Temporarily rename file to correct extension for verification. */ tmpfile_with_extension = strdup(tmpfile); if (!tmpfile_with_extension) { @@ -1492,6 +1554,27 @@ static fc_error_t getcvd( goto done; } + // If we're in FIPS mode, temporarily rename the external signature file to the expected path: + // The database file will be something like: /tmp/clam.d/tmp.b2b103a70a/clamav-3bbac78e36cbf974e1060de6dbbbfea3.tmp-daily.cld + // The expected external signature will be: /tmp/clam.d/clam.d/tmp.b2b103a70a/clamav-3bbac78e36cbf974e1060de6dbbbfea3.tmp-daily.sig + char *extSigTmpFileWithExtension = NULL; + if (cli_get_fips_mode()) { + // Temprorarily rename the the sig file to the expected path + extSigTmpFileWithExtension = strdup(tmpfile_with_extension); + if (!extSigTmpFileWithExtension) { + logg(LOGG_ERROR, "Can't allocate memory for external signature temp file with extension!\n"); + status = FC_EMEM; + goto done; + } + strncpy(extSigTmpFileWithExtension + strlen(extSigTmpFileWithExtension) - 4, sigfile + strlen(sigfile) - 4, 4); + printf("RENAMING %s to %s\n", extSigTmpFile, extSigTmpFileWithExtension); + if (rename(extSigTmpFile, extSigTmpFileWithExtension) == -1) { + logg(LOGG_ERROR, "Can't rename %s to %s: %s\n", extSigTmpFile, extSigTmpFileWithExtension, strerror(errno)); + status = FC_EDBDIRACCESS; + goto done; + } + } + if (CL_SUCCESS != (cl_ret = cl_cvdverify(tmpfile_with_extension))) { logg(LOGG_ERROR, "Verification: %s\n", cl_strerror(cl_ret)); status = FC_EBADCVD; @@ -1511,6 +1594,15 @@ static fc_error_t getcvd( goto done; } + // Rename the .sig file if we are in FIPS mode + if (cli_get_fips_mode()) { + if (rename(extSigTmpFileWithExtension, extSigTmpFile) == -1) { + logg(LOGG_ERROR, "Can't rename %s to %s: %s\n", extSigTmpFileWithExtension, extSigTmpFile, strerror(errno)); + status = FC_EDBDIRACCESS; + goto done; + } + } + if (cvd->version < remoteVersion) { logg(LOGG_DEBUG, "The %s database downloaded from %s is older than the version advertised in the DNS TXT record.\n", cvdfile, @@ -1522,6 +1614,21 @@ static fc_error_t getcvd( status = FC_SUCCESS; done: + if (cli_get_fips_mode()) { + if (NULL != extSigTmpFileWithExtension) { + free(extSigTmpFileWithExtension); + } + if (NULL != extSigTmpFile) { + free(extSigTmpFile); + } + if (NULL != sigfile) { + free(sigfile); + } + if (NULL != extSigUrl) { + free(extSigUrl); + } + } + if (NULL != cvd) { cl_cvdfree(cvd); } @@ -2475,6 +2582,29 @@ fc_error_t updatedb( status = FC_EDBDIRACCESS; goto done; } + + // // If we are running in FIPS mode, we need to move the .sig file in, as well. + if (cli_get_fips_mode()) { + // just copy tmpfile into tmpsigfile and replace the .tmp with .sig + char *tmpsigfile = strdup(tmpfile); + strcpy(tmpsigfile + strlen(tmpsigfile) - 4, ".sig"); + + // do the same for tmpfile_with_extension + char *tmpsigfile_with_extension = strdup(tmpfile_with_extension); + strcpy(tmpsigfile_with_extension + strlen(tmpsigfile_with_extension) - 4, ".sig"); + + // do the rename + if (rename(tmpsigfile, tmpsigfile_with_extension) == -1) { + logg(LOGG_ERROR, "(line %d) updatedb: Can't rename %s to %s: %s\n", __LINE__, tmpsigfile, tmpsigfile_with_extension, strerror(errno)); + status = FC_EDBDIRACCESS; + free(tmpsigfile); + free(tmpsigfile_with_extension); + goto done; + } + free(tmpsigfile); + free(tmpsigfile_with_extension); + } + free(tmpfile); tmpfile = tmpfile_with_extension; tmpfile_with_extension = NULL; @@ -2504,6 +2634,28 @@ fc_error_t updatedb( goto done; } + // If we are running in FIPS mode, we need to move the .sig file in, as well. + if (cli_get_fips_mode()) { + // just duplicate the newLocalFileName buffer and replace the .cld with .sig + char *newLocalSigFilename = strdup(newLocalFilename); + strcpy(newLocalSigFilename + strlen(newLocalSigFilename) - 4, ".sig"); + + // do the same for tmpfile + char *tmpsigfile = strdup(tmpfile); + strcpy(tmpsigfile + strlen(tmpsigfile) - 4, ".sig"); + + // do the rename + if (rename(tmpsigfile, newLocalSigFilename) == -1) { + logg(LOGG_ERROR, "(line %d) updatedb: Can't rename %s to %s: %s\n", __LINE__, tmpsigfile, newLocalSigFilename, strerror(errno)); + status = FC_EDBDIRACCESS; + free(tmpsigfile); + free(newLocalSigFilename); + goto done; + } + free(tmpsigfile); + free(newLocalSigFilename); + } + /* If we just updated from a CVD to a CLD, delete the old CVD */ if ((NULL != localFilename) && !access(localFilename, R_OK) && strcmp(newLocalFilename, localFilename)) if (unlink(localFilename)) 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/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\"" + diff --git a/sigext/extract_mod_and_exp.sh b/sigext/extract_mod_and_exp.sh new file mode 100644 index 0000000000..f75552178a --- /dev/null +++ b/sigext/extract_mod_and_exp.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This produces the defines needed to use a generated signing key with the ClamAV Database External signature +# system. You must replace the define values for CLI_NSTR_EXT_SIG and CLI_ESTR_EXT_SIG with the values produced +# by this script in order to change the public signing key that CalmAV uses to validate the external signature +# files when in FIPS mode. + +# Useage: +# $ ./extract_mod_and_exp.sh public_key.pem CLI_NSTR_EXT_SIG CLI_ESTR_EXT_SIG +# #define CLI_NSTR_EXT_SIG "E32B3AC1D501EE975296A45BA65DD699100DADD340FF3BBD1F1030C66D6BB16DBFBD53DF4D97BBD42EF8FC777E7C114A6074A87DD8095A5C08B3DD7B85817713047647EF396C58358C5C22B5C3ADF85CE8D0ABC429F89E936EC917B64DD00E02A712E6666FAE1A71591092BCEE59E3141758C4719B4B08589117B0FF7CDBDBB261F8486A193E2E720AE0B16D40DD5E56E97346CBD8010DC81B35332F41C9E93E61490802DDCDFC823D581BA6888588968C68A3C95B93949AF411682E73323F7469473F668B0958F6966849FF03BDE808866D127A2C058B16F17C741A9EE50812A5C7841224E55BF7ADDB5AEAE8EB5476F9BC8740178AB35926D5DC375583C641" +# #define CLI_ESTR_EXT_SIG 65537 + +# Check for correct number of arguments +if [ "$#" -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Assign command line arguments to variables +public_key_file=$1 +modulus_name=$2 +exponent_name=$3 + +# Extract the modulus and exponent +modulus=$(openssl rsa -in "$public_key_file" -pubin -noout -modulus | sed 's/Modulus=//') +exponent=$(openssl rsa -in "$public_key_file" -pubin -noout -text | grep 'Exponent' | awk '{print $2}') + +# Format the modulus as a single line string +formatted_modulus=$(echo $modulus | tr -d '\n') + +# Create the #define strings +echo "#define $modulus_name \"$formatted_modulus\"" +echo "#define $exponent_name $exponent" +