rizin/librz/hash/hash.c
Giovanni c5e787d867
Fix openssl 3.0 support due changes on legacy algorithms. (#4215)
md4, md5 and other algorithms has been moved under the legacy provider
2024-02-12 17:44:04 +08:00

742 lines
21 KiB
C

// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
// SPDX-License-Identifier: LGPL-3.0-only
#include "config.h"
#include <rz_hash.h>
#include <rz_util.h>
#include <rz_lib.h>
#include <xxhash.h>
#include "algorithms/ssdeep/ssdeep.h"
RZ_LIB_VERSION(rz_hash);
#define hash_cfg_can_hmac(c) ((c)->status == RZ_MSG_DIGEST_STATUS_ALLOC)
#define hash_cfg_can_init(c) ((c)->status == RZ_MSG_DIGEST_STATUS_FINAL || (c)->status == RZ_MSG_DIGEST_STATUS_ALLOC)
#define hash_cfg_can_update(c) ((c)->status == RZ_MSG_DIGEST_STATUS_INIT || (c)->status == RZ_MSG_DIGEST_STATUS_UPDATE)
#define hash_cfg_can_final(c) ((c)->status == RZ_MSG_DIGEST_STATUS_ALLOC || (c)->status == RZ_MSG_DIGEST_STATUS_INIT || (c)->status == RZ_MSG_DIGEST_STATUS_UPDATE)
#define hash_cfg_has_finshed(c) ((c)->status == RZ_MSG_DIGEST_STATUS_FINAL)
typedef struct hash_cfg_config_t {
void *context;
ut8 *digest;
ut8 *hmac_key;
RzHashSize digest_size;
const RzHashPlugin *plugin;
} HashCfgConfig;
#if HAVE_LIB_SSL
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
* From openssl 3.0 some algos got moved to the legacy provider.
* this means that their availability requires to preload a provider
* before calling the initialization of each algorithms.
*/
#define REQUIRE_OPENSSL_PROVIDER 1
#include <openssl/provider.h>
typedef struct {
OSSL_PROVIDER *provider;
size_t counter;
} RzHashOpenSSL;
static RzHashOpenSSL *openssl_lib = NULL;
static void rz_hash_init_openssl_lib(void) {
if (!openssl_lib) {
openssl_lib = RZ_NEW0(RzHashOpenSSL);
if (!openssl_lib) {
RZ_LOG_ERROR("Cannot allocate RzHashOpenSSL\n");
return;
}
openssl_lib->provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1);
if (!OSSL_PROVIDER_available(NULL, "legacy")) {
RZ_LOG_WARN("Cannot load openssl legacy provider. Some algorithm might not be available.\n");
}
}
openssl_lib->counter++;
}
static void rz_hash_fini_openssl_lib(void) {
if (!openssl_lib || (--openssl_lib->counter) > 0) {
return;
}
OSSL_PROVIDER_unload(openssl_lib->provider);
free(openssl_lib);
openssl_lib = NULL;
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
#endif /* HAVE_LIB_SSL */
static RzHashPlugin *hash_static_plugins[] = { RZ_HASH_STATIC_PLUGINS };
/**
* \brief Calculates the ssdeep digest of the given input
*
* \param[in] input The input buffer
* \param[in] size The size of the input
*
* \return On success returns a valid pointer, otherwise NULL
*/
RZ_API RZ_OWN char *rz_hash_ssdeep(RZ_NONNULL const ut8 *input, size_t size) {
rz_return_val_if_fail(input, NULL);
char *digest = malloc(RZ_HASH_SSDEEP_DIGEST_SIZE);
if (!digest) {
RZ_LOG_ERROR("msg digest: cannot allocate ssdeep digest buffer\n");
return NULL;
}
RzSSDeep *ctx = rz_ssdeep_new();
if (!ctx) {
RZ_LOG_ERROR("msg digest: cannot allocate ssdeep context\n");
free(digest);
return NULL;
}
rz_ssdeep_update(ctx, input, size);
rz_ssdeep_fini(ctx, digest);
rz_ssdeep_free(ctx);
return digest;
}
/**
* \brief Calculates the distance of 2 ssdeep hashes and returns their similarity
*
* \param[in] hash_a Hash A to compare
* \param[in] hash_b Hash B to compare
*
* \return On success returns a number between 0.0 and 1.0 (included), otherwise negative
*/
RZ_API double rz_hash_ssdeep_compare(RZ_NONNULL const char *hash_a, RZ_NONNULL const char *hash_b) {
rz_return_val_if_fail(hash_a && hash_b, -1.0);
return rz_ssdeep_compare(hash_a, hash_b);
}
/**
* \brief Calculates the xxhash digest of the given input
*
* \param[in] input The input buffer
* \param[in] size The size of the input
*
* \return The resulting digest of the input
*/
RZ_API ut32 rz_hash_xxhash(RZ_NONNULL const ut8 *input, size_t size) {
rz_return_val_if_fail(input, 0);
return XXH32(input, size, 0);
}
/**
* \brief Calculates the entropy of the given input
*
* \param[in] data The input buffer
* \param[in] size The size of the input
*
* \return The resulting entropy of the input
*/
RZ_API double rz_hash_entropy(RZ_NONNULL const ut8 *data, ut64 len) {
rz_return_val_if_fail(data, 0.0);
const RzHashPlugin *plugin = &rz_hash_plugin_entropy;
ut8 *digest = NULL;
if (!plugin->small_block(data, len, &digest, NULL)) {
RZ_LOG_ERROR("msg digest: cannot calculate entropy\n");
return 0.0;
}
double e = rz_read_be_double(digest);
free(digest);
return e;
}
/**
* \brief Calculates the fractional entropy of the given input
*
* \param[in] data The input buffer
* \param[in] size The size of the input
*
* \return The resulting fractional entropy of the input
*/
RZ_API double rz_hash_entropy_fraction(RZ_NONNULL const ut8 *data, ut64 len) {
rz_return_val_if_fail(data, 0.0);
const RzHashPlugin *plugin = &rz_hash_plugin_entropy_fract;
ut8 *digest = NULL;
if (!plugin->small_block(data, len, &digest, NULL)) {
RZ_LOG_ERROR("msg digest: cannot calculate entropy fraction\n");
return 0.0;
}
double e = rz_read_be_double(digest);
free(digest);
return e;
}
static int hash_cfg_config_compare(const void *value, const void *data, void *user) {
const HashCfgConfig *mdc = (const HashCfgConfig *)data;
const char *name = (const char *)value;
return strcmp(name, mdc->plugin->name);
}
static void hash_cfg_config_free(HashCfgConfig *mdc) {
rz_return_if_fail(mdc && mdc->plugin);
mdc->plugin->context_free(mdc->context);
free(mdc->hmac_key);
free(mdc->digest);
free(mdc);
}
static HashCfgConfig *hash_cfg_config_new(const RzHashPlugin *plugin) {
rz_return_val_if_fail(plugin, NULL);
HashCfgConfig *mdc = RZ_NEW0(HashCfgConfig);
if (!mdc) {
RZ_LOG_ERROR("msg digest: cannot allocate memory for config.\n");
return NULL;
}
mdc->context = plugin->context_new();
if (!mdc->context) {
RZ_LOG_ERROR("msg digest: cannot allocate memory for context.\n");
free(mdc);
return NULL;
}
mdc->digest_size = plugin->digest_size(mdc->context);
rz_warn_if_fail(mdc->digest_size > 0);
mdc->digest = RZ_NEWS0(ut8, mdc->digest_size);
if (!mdc->context) {
RZ_LOG_ERROR("msg digest: cannot allocate memory for digest.\n");
plugin->context_free(mdc->context);
free(mdc);
return NULL;
}
mdc->plugin = plugin;
return mdc;
}
RZ_API RZ_BORROW const RzHashPlugin *rz_hash_plugin_by_index(RZ_NONNULL RzHash *rh, size_t index) {
rz_return_val_if_fail(rh, NULL);
RzListIter *it;
const RzHashPlugin *rhp;
size_t i = 0;
rz_list_foreach (rh->plugins, it, rhp) {
if (i == index) {
return rhp;
}
i++;
}
return NULL;
}
RZ_API RZ_BORROW const RzHashPlugin *rz_hash_plugin_by_name(RZ_NONNULL RzHash *rh, RZ_NONNULL const char *name) {
rz_return_val_if_fail(name && rh, NULL);
RzListIter *it;
const RzHashPlugin *rhp;
rz_list_foreach (rh->plugins, it, rhp) {
if (!strcmp(rhp->name, name)) {
return rhp;
}
}
return NULL;
}
RZ_API RZ_OWN RzHashCfg *rz_hash_cfg_new(RZ_NONNULL RzHash *rh) {
rz_return_val_if_fail(rh, NULL);
RzHashCfg *md = RZ_NEW0(RzHashCfg);
if (!md) {
RZ_LOG_ERROR("msg digest: cannot allocate memory.\n");
return NULL;
}
md->configurations = rz_list_newf((RzListFree)hash_cfg_config_free);
if (!md->configurations) {
RZ_LOG_ERROR("msg digest: cannot allocate memory for the configurations.\n");
free(md);
return NULL;
}
md->hash = rh;
return md;
}
/**
* \brief Returns a message digest context with the give algo already configured
*
* message digest allocates and configures already the structure
* with the given algorithm and runs also the algo init.
* when fails to allocate or configure or initialize, returns NULL.
* */
RZ_API RZ_OWN RzHashCfg *rz_hash_cfg_new_with_algo(RZ_NONNULL RzHash *rh, RZ_NONNULL const char *name, RZ_NULLABLE const ut8 *key, ut64 key_size) {
rz_return_val_if_fail(rh && name, NULL);
RzHashCfg *md = rz_hash_cfg_new(rh);
if (!md) {
return NULL;
}
if (!rz_hash_cfg_configure(md, name)) {
rz_hash_cfg_free(md);
return NULL;
}
if (key && !rz_hash_cfg_hmac(md, key, key_size)) {
rz_hash_cfg_free(md);
return NULL;
}
if (!rz_hash_cfg_init(md)) {
rz_hash_cfg_free(md);
return NULL;
}
return md;
}
RZ_API void rz_hash_cfg_free(RZ_NONNULL RzHashCfg *md) {
rz_return_if_fail(md);
rz_list_free(md->configurations);
free(md);
}
/**
* \brief Allocates and configures the plugin message digest context
*
* message digest internally allocates a HashCfgConfig which
* contains all the information needed for the plugin to work.
* */
RZ_API bool rz_hash_cfg_configure(RZ_NONNULL RzHashCfg *md, RZ_NONNULL const char *name) {
rz_return_val_if_fail(md && name, false);
if (rz_list_find(md->configurations, name, hash_cfg_config_compare, NULL)) {
RZ_LOG_WARN("msg digest: '%s' was already configured; skipping.\n", name);
return false;
}
bool is_all = !strcmp(name, "all");
if (is_all && rz_list_length(md->configurations) > 0) {
RZ_LOG_WARN("msg digest: '%s' was already configured; skipping.\n", name);
return false;
}
HashCfgConfig *mdc = NULL;
RzListIter *it;
const RzHashPlugin *plugin;
rz_list_foreach (md->hash->plugins, it, plugin) {
if (is_all || !strcmp(plugin->name, name)) {
mdc = hash_cfg_config_new(plugin);
if (!mdc) {
return false;
}
if (!rz_list_append(md->configurations, mdc)) {
RZ_LOG_ERROR("msg digest: cannot allocate memory for list entry.\n");
hash_cfg_config_free(mdc);
return false;
}
if (!is_all) {
return true;
}
}
}
if (is_all) {
return true;
}
RZ_LOG_ERROR("msg digest: '%s' does not exists.\n", name);
return false;
}
/**
* \brief Sets the key for the hmac algorithm
*
* message digest sets the hmac key
* */
RZ_API bool rz_hash_cfg_hmac(RZ_NONNULL RzHashCfg *md, RZ_NONNULL const ut8 *key, ut64 key_size) {
rz_return_val_if_fail(md && key && key_size && hash_cfg_can_hmac(md), false);
RzHashSize block_size = 0;
RzListIter *iter = NULL;
HashCfgConfig *mdc = NULL;
rz_list_foreach (md->configurations, iter, mdc) {
if (!mdc->plugin->support_hmac) {
// RZ_LOG_ERROR("msg digest: hmac is not supported by %s.\n", mdc->plugin->name);
continue;
}
block_size = mdc->plugin->block_size(mdc->context);
if (block_size < 1) {
RZ_LOG_ERROR("msg digest: hmac block size is < 1.\n");
return false;
}
mdc->hmac_key = malloc(block_size);
if (!mdc->hmac_key) {
RZ_LOG_ERROR("msg digest: cannot allocate memory for hmac key.\n");
return false;
}
memset(mdc->hmac_key, 0, block_size);
if (block_size < key_size) {
RzHashSize tmp_size;
ut8 *tmp = NULL;
if (!mdc->plugin->small_block(key, key_size, &tmp, &tmp_size)) {
RZ_LOG_ERROR("msg digest: failed to call init for hmac %s key.\n", mdc->plugin->name);
return false;
}
memcpy(mdc->hmac_key, tmp, tmp_size);
free(tmp);
} else {
memcpy(mdc->hmac_key, key, key_size);
}
}
return true;
}
/**
* \brief Resets/initialize the message digest contextes
*
* RzHashCfg contains a list of configurations; this method will call
* the init method of all the plugins stored in its list.
* */
RZ_API bool rz_hash_cfg_init(RZ_NONNULL RzHashCfg *md) {
rz_return_val_if_fail(md && hash_cfg_can_init(md), false);
RzListIter *iter = NULL;
HashCfgConfig *mdc = NULL;
rz_list_foreach (md->configurations, iter, mdc) {
if (!mdc->plugin->init(mdc->context)) {
RZ_LOG_ERROR("msg digest: failed to call init for %s.\n", mdc->plugin->name);
return false;
}
if (mdc->hmac_key) {
RzHashSize block_size = mdc->plugin->block_size(mdc->context);
ut8 *i_pad = malloc(block_size);
if (!i_pad) {
RZ_LOG_ERROR("msg digest: failed to allocate memory for ipad.\n");
return false;
}
for (ut32 i = 0; i < block_size; i++) {
i_pad[i] = 0x36 ^ mdc->hmac_key[i];
}
if (!mdc->plugin->update(mdc->context, i_pad, block_size)) {
RZ_LOG_ERROR("msg digest: failed to call update for hmac %s ipad.\n", mdc->plugin->name);
free(i_pad);
return false;
}
free(i_pad);
}
}
md->status = RZ_MSG_DIGEST_STATUS_INIT;
return true;
}
/**
* \brief Inserts data into each the message digest contextes
*
* RzHashCfg contains a list of configurations; this method will call
* the update method of all the plugins stored in its list.
* */
RZ_API bool rz_hash_cfg_update(RZ_NONNULL RzHashCfg *md, RZ_NONNULL const ut8 *data, ut64 size) {
rz_return_val_if_fail(md && hash_cfg_can_update(md), false);
RzListIter *iter = NULL;
HashCfgConfig *mdc = NULL;
rz_list_foreach (md->configurations, iter, mdc) {
if (!mdc->plugin->update(mdc->context, data, size)) {
RZ_LOG_ERROR("msg digest: failed to call update for %s.\n", mdc->plugin->name);
return false;
}
}
md->status = RZ_MSG_DIGEST_STATUS_UPDATE;
return true;
}
/**
* \brief Generates the final value of the message digest contextes
*
* RzHashCfg contains a list of configurations; this method will call
* the final method of all the plugins stored in its list.
* */
RZ_API bool rz_hash_cfg_final(RZ_NONNULL RzHashCfg *md) {
rz_return_val_if_fail(md && hash_cfg_can_final(md), false);
RzListIter *iter = NULL;
HashCfgConfig *mdc = NULL;
rz_list_foreach (md->configurations, iter, mdc) {
if (!mdc->plugin->final(mdc->context, mdc->digest)) {
RZ_LOG_ERROR("msg digest: failed to call final for %s.\n", mdc->plugin->name);
return false;
}
if (mdc->hmac_key) {
RzHashSize block_size = mdc->plugin->block_size(mdc->context);
ut8 *o_pad = malloc(block_size);
if (!o_pad) {
RZ_LOG_ERROR("msg digest: failed to allocate memory for opad.\n");
return false;
}
for (ut32 i = 0; i < block_size; i++) {
o_pad[i] = 0x5c ^ mdc->hmac_key[i];
}
if (!mdc->plugin->init(mdc->context)) {
RZ_LOG_ERROR("msg digest: failed to call init for hmac %s opad.\n", mdc->plugin->name);
free(o_pad);
return false;
}
if (!mdc->plugin->update(mdc->context, o_pad, block_size)) {
RZ_LOG_ERROR("msg digest: failed to call update for hmac %s opad.\n", mdc->plugin->name);
free(o_pad);
return false;
}
free(o_pad);
if (!mdc->plugin->update(mdc->context, mdc->digest, mdc->digest_size)) {
RZ_LOG_ERROR("msg digest: failed to call update for hmac %s opad.\n", mdc->plugin->name);
return false;
}
if (!mdc->plugin->final(mdc->context, mdc->digest)) {
RZ_LOG_ERROR("msg digest: failed to call final for hmac %s opad.\n", mdc->plugin->name);
return false;
}
}
}
md->status = RZ_MSG_DIGEST_STATUS_FINAL;
return true;
}
/**
* \brief Calculate the final hash by iterating its result N times.
*
* RzHashCfg contains a list of configurations; this method will iterate N times
* each configuration final result.
* */
RZ_API bool rz_hash_cfg_iterate(RZ_NONNULL RzHashCfg *md, size_t iterate) {
rz_return_val_if_fail(md && hash_cfg_has_finshed(md), false);
RzListIter *iter = NULL;
HashCfgConfig *mdc = NULL;
rz_list_foreach (md->configurations, iter, mdc) {
for (size_t i = 0; i < iterate; ++i) {
if (!mdc->plugin->init(mdc->context)) {
RZ_LOG_ERROR("msg digest: failed to call init %s for iteration.\n", mdc->plugin->name);
return false;
}
if (!mdc->plugin->update(mdc->context, mdc->digest, mdc->digest_size)) {
RZ_LOG_ERROR("msg digest: failed to call update %s for iteration.\n", mdc->plugin->name);
return false;
}
if (!mdc->plugin->final(mdc->context, mdc->digest)) {
RZ_LOG_ERROR("msg digest: failed to call final %s for iteration.\n", mdc->plugin->name);
return false;
}
}
}
return true;
}
/**
* \brief Returns the digest value of the requested algorithm name
*
* RzHashCfg contains a list of configurations; this method will search
* for the configuration with the given name and if found return the digest value.
* */
RZ_API RZ_BORROW const ut8 *rz_hash_cfg_get_result(RZ_NONNULL RzHashCfg *md, RZ_NONNULL const char *name, RZ_NONNULL ut32 *size) {
rz_return_val_if_fail(md && name && hash_cfg_has_finshed(md), false);
RzListIter *it = rz_list_find(md->configurations, name, hash_cfg_config_compare, NULL);
if (!it) {
RZ_LOG_ERROR("msg digest: cannot find configuration for '%s' algorithm.\n", name);
return NULL;
}
HashCfgConfig *mdc = (HashCfgConfig *)rz_list_iter_get_data(it);
rz_return_val_if_fail(mdc, NULL);
if (size) {
*size = mdc->digest_size;
}
return mdc->digest;
}
/**
* \brief Returns the digest value of the requested algorithm name
*
* RzHashCfg contains a list of configurations; this method will search
* for the configuration with the given name and if found return the digest value.
* */
RZ_API RZ_OWN char *rz_hash_cfg_get_result_string(RZ_NONNULL RzHashCfg *md, RZ_NONNULL const char *name, RZ_NULLABLE ut32 *size, bool invert) {
rz_return_val_if_fail(md && name && hash_cfg_has_finshed(md), false);
ut32 pos = 0;
RzListIter *it = rz_list_find(md->configurations, name, hash_cfg_config_compare, NULL);
if (!it) {
RZ_LOG_ERROR("msg digest: cannot find configuration for '%s' algorithm.\n", name);
return NULL;
}
HashCfgConfig *mdc = (HashCfgConfig *)rz_list_iter_get_data(it);
rz_return_val_if_fail(mdc, NULL);
if (!strncmp(name, "entropy", strlen("entropy"))) {
double entropy = rz_read_be_double(mdc->digest);
return rz_str_newf("%.8f", entropy);
} else if (!strcmp(name, "ssdeep")) {
return strdup((char *)mdc->digest);
}
char *string = malloc((mdc->digest_size * 2) + 1);
if (!string) {
RZ_LOG_ERROR("msg digest: cannot find allocate memory for string result.\n");
return NULL;
}
for (ut32 i = 0; i < mdc->digest_size; i++) {
pos = invert ? (mdc->digest_size - 1 - i) : i;
sprintf(&string[i * 2], "%02x", mdc->digest[pos]);
}
string[mdc->digest_size * 2] = 0;
if (size) {
*size = (mdc->digest_size * 2) + 1;
}
return string;
}
/**
* \brief Returns the digest size of the requested algorithm name
*
* Returns the digest size of the initialized configuration.
* */
RZ_API RzHashSize rz_hash_cfg_size(RZ_NONNULL RzHashCfg *md, RZ_NONNULL const char *name) {
rz_return_val_if_fail(md && name, 0);
RzListIter *it = rz_list_find(md->configurations, name, hash_cfg_config_compare, NULL);
if (!it) {
RZ_LOG_ERROR("msg digest: cannot find configuration for '%s' algorithm.\n", name);
return 0;
}
HashCfgConfig *mdc = (HashCfgConfig *)rz_list_iter_get_data(it);
rz_return_val_if_fail(mdc, 0);
return mdc->plugin->digest_size(mdc->context);
}
/**
* \brief Returns the digest size of the requested algorithm name
*
* Returns the digest size of the initialized configuration.
* */
RZ_API RZ_OWN ut8 *rz_hash_cfg_calculate_small_block(RZ_NONNULL RzHash *rh, RZ_NONNULL const char *name, RZ_NONNULL const ut8 *buffer, ut64 bsize, RZ_NONNULL RzHashSize *osize) {
rz_return_val_if_fail(rh && name && buffer, NULL);
ut8 *result = NULL;
const RzHashPlugin *plugin = rz_hash_plugin_by_name(rh, name);
if (!plugin) {
return NULL;
}
if (!plugin->small_block(buffer, bsize, &result, osize)) {
RZ_LOG_ERROR("msg digest: cannot calculate small block with %s.\n", plugin->name);
return NULL;
}
return result;
}
RZ_API RZ_OWN char *rz_hash_cfg_calculate_small_block_string(RZ_NONNULL RzHash *rh, RZ_NONNULL const char *name, RZ_NONNULL const ut8 *buffer, ut64 bsize, RZ_NULLABLE ut32 *size, bool invert) {
rz_return_val_if_fail(rh && name && buffer, NULL);
ut32 pos = 0;
RzHashSize digest_size = 0;
ut8 *digest = NULL;
if (!(digest = rz_hash_cfg_calculate_small_block(rh, name, buffer, bsize, &digest_size))) {
return NULL;
}
if (!strncmp(name, "entropy", strlen("entropy"))) {
double entropy = rz_read_be_double(digest);
free(digest);
return rz_str_newf("%.8f", entropy);
} else if (!strcmp(name, "ssdeep")) {
return (char *)digest;
}
char *string = malloc((digest_size * 2) + 1);
if (!string) {
RZ_LOG_ERROR("msg digest: cannot find allocate memory for string result.\n");
free(digest);
return NULL;
}
for (ut32 i = 0; i < digest_size; i++) {
pos = invert ? (digest_size - 1 - i) : i;
sprintf(&string[i * 2], "%02x", digest[pos]);
}
string[digest_size * 2] = 0;
if (size) {
*size = (digest_size * 2) + 1;
}
free(digest);
return string;
}
/**
* Create a new RzHash object where plugins can be registered and specific
* configurations can be created from.
*/
RZ_API RzHash *rz_hash_new(void) {
RzHash *rh = RZ_NEW0(RzHash);
if (!rh) {
return NULL;
}
#if REQUIRE_OPENSSL_PROVIDER
rz_hash_init_openssl_lib();
#endif /* REQUIRE_OPENSSL_PROVIDER */
rh->plugins = rz_list_new();
for (int i = 0; i < RZ_ARRAY_SIZE(hash_static_plugins); i++) {
rz_hash_plugin_add(rh, hash_static_plugins[i]);
}
return rh;
}
RZ_API void rz_hash_free(RZ_NULLABLE RzHash *rh) {
if (!rh) {
return;
}
rz_list_free(rh->plugins);
free(rh);
#if REQUIRE_OPENSSL_PROVIDER
rz_hash_fini_openssl_lib();
#endif /* REQUIRE_OPENSSL_PROVIDER */
}
/**
* \brief Add a new plugin to \p rh so that \p RzHashCfg can be created using
* specific algorithms.
*/
RZ_API bool rz_hash_plugin_add(RZ_NONNULL RzHash *rh, RZ_NONNULL RZ_OWN RzHashPlugin *plugin) {
rz_return_val_if_fail(rh && plugin && plugin->name, false);
RZ_PLUGIN_CHECK_AND_ADD(rh->plugins, plugin, RzHashPlugin);
return true;
}
RZ_API bool rz_hash_plugin_del(RZ_NONNULL RzHash *rh, RZ_NONNULL RzHashPlugin *plugin) {
rz_return_val_if_fail(rh && plugin, false);
return rz_list_delete_data(rh->plugins, plugin);
}