From a7f47e7142127f54821c018b8037841cc727c00e Mon Sep 17 00:00:00 2001 From: defanor Date: Thu, 28 Sep 2023 19:19:18 +0300 Subject: Support Nettle and OpenSSL for hashing, in addition to Libgcrypt --- README | 2 +- configure.ac | 21 +++++++-- src/Makefile.am | 5 ++- src/rexmpp.c | 13 ++++-- src/rexmpp_digest.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rexmpp_digest.h | 44 ++++++++++++++++++ src/rexmpp_jingle.c | 37 +++++++-------- src/rexmpp_tls.c | 19 +++----- 8 files changed, 228 insertions(+), 39 deletions(-) create mode 100644 src/rexmpp_digest.c create mode 100644 src/rexmpp_digest.h diff --git a/README b/README index 3f9f1b7..eb28c13 100644 --- a/README +++ b/README @@ -15,7 +15,7 @@ it easy to implement a decent client application using it. Mandatory dependencies: - libxml2, libexpat, or rxml (Rust) -- libgcrypt +- gcrypt, nettle, or openssl Optional dependencies: diff --git a/configure.ac b/configure.ac index 8515da1..bb25c33 100644 --- a/configure.ac +++ b/configure.ac @@ -38,10 +38,25 @@ AM_CONDITIONAL([USE_RUST], [test "x$with_rust" == "xyes"]) LT_INIT -# Libgcrypt +# Cryptographic libraries, for hashing + +AC_ARG_WITH([gcrypt], + AS_HELP_STRING([--without-gcrypt], [do not use gcrypt])) +AC_ARG_WITH([nettle], + AS_HELP_STRING([--without-nettle], [do not use nettle])) + +AS_IF([test "x$with_gcrypt" != "xno"], + [AM_PATH_LIBGCRYPT([], + [AC_DEFINE([HAVE_GCRYPT], [1], [Libgcrypt is available])])]) + +AS_IF([test "x$with_gcrypt" == "xno" -a "x$with_nettle" != "xno"], + [PKG_CHECK_MODULES([NETTLE], [nettle], + [AC_DEFINE([HAVE_NETTLE], [1], [Libnettle is available])])]) + +AS_IF([test "x$with_gcrypt" == "xno" -a "x$with_nettle" == "xno"], + [PKG_CHECK_MODULES([OPENSSL], [openssl], + [AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL is available])])]) -AM_PATH_LIBGCRYPT([], - [AC_DEFINE([HAVE_GCRYPT], [1], [Libgcrypt is available])]) # libnice (+ glib) and libsrtp for media calls, optional diff --git a/src/Makefile.am b/src/Makefile.am index dcf4069..d027b1b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,13 +16,14 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \ rexmpp_sasl.h rexmpp_sasl.c \ rexmpp_xml.h rexmpp_xml.c \ rexmpp_utf8.h \ - rexmpp_random.h rexmpp_random.c + rexmpp_random.h rexmpp_random.c \ + rexmpp_digest.h rexmpp_digest.c include_HEADERS = config.h rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \ rexmpp_dns.h rexmpp_tls.h rexmpp_jid.h rexmpp_openpgp.h rexmpp_console.h \ rexmpp_pubsub.h rexmpp_http_upload.h rexmpp_jingle.h rexmpp_base64.h \ rexmpp_sasl.h rexmpp_xml.h rexmpp_utf8.h rexmpp_xml_parser.h \ - rexmpp_random.h + rexmpp_random.h rexmpp_digest.h librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML2_CFLAGS) $(EXPAT_CFLAGS) \ $(GNUTLS_CFLAGS) $(LIBDANE_CFLAGS) $(OPENSSL_CFLAGS) \ $(GSASL_CFLAGS) $(UNBOUND_CFLAGS) $(CARES_CFLAGS) $(GPGME_CFLAGS) \ diff --git a/src/rexmpp.c b/src/rexmpp.c index 60d138f..e19386d 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -44,6 +44,7 @@ #include "rexmpp_base64.h" #include "rexmpp_sasl.h" #include "rexmpp_random.h" +#include "rexmpp_digest.h" struct rexmpp_iq_cacher { rexmpp_iq_callback_t cb; @@ -202,11 +203,15 @@ char *rexmpp_capabilities_hash (rexmpp_t *s, size_t out_len = 0; char *str = rexmpp_capabilities_string(s, info); if (str != NULL) { - unsigned int sha1_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1); + size_t sha1_len = rexmpp_digest_len(REXMPP_DIGEST_SHA1); char *sha1 = malloc(sha1_len); if (sha1 != NULL) { - gcry_md_hash_buffer(GCRY_MD_SHA1, sha1, str, strlen(str)); - rexmpp_base64_to(sha1, sha1_len, &out, &out_len); + if (rexmpp_digest_buffer(REXMPP_DIGEST_SHA1, + str, strlen(str), + sha1, sha1_len) == 0) + { + rexmpp_base64_to(sha1, sha1_len, &out, &out_len); + } free(sha1); } free(str); @@ -606,6 +611,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, return REXMPP_E_JID; } +#ifdef HAVE_GCRYPT if (! gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { rexmpp_log(s, LOG_DEBUG, "Initializing libgcrypt"); if (gcry_check_version(NULL) == NULL) { @@ -614,6 +620,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, } gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } +#endif s->xml_parser = rexmpp_xml_parser_new(&sax, s); diff --git a/src/rexmpp_digest.c b/src/rexmpp_digest.c new file mode 100644 index 0000000..e123636 --- /dev/null +++ b/src/rexmpp_digest.c @@ -0,0 +1,126 @@ +/** + @file rexmpp_digest.c + @brief Cryptographic functions + @author defanor + @date 2023 + @copyright MIT license. +*/ + +#include "config.h" +#include "rexmpp_digest.h" + +#if defined(HAVE_GCRYPT) +#include +#elif defined(HAVE_NETTLE) +#include +#include +#elif defined(HAVE_OPENSSL) +#include +#endif + +size_t rexmpp_digest_len (rexmpp_digest_algorithm algo) { + switch (algo) { + case REXMPP_DIGEST_SHA1: return 20; + case REXMPP_DIGEST_SHA256: return 32; + case REXMPP_DIGEST_SHA3_256: return 32; + default: return 0; + } +} + +int rexmpp_digest_buffer (rexmpp_digest_algorithm algo, + const void *in, + size_t in_len, + void *out, + size_t out_len) +{ + rexmpp_digest_t ctx; + int err = rexmpp_digest_init(&ctx, algo); + if (err) { + return err; + } + err = rexmpp_digest_update(&ctx, in, in_len); + if (err) { + return err; + } + return rexmpp_digest_finish(&ctx, out, out_len); +} + +int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo) { +#if defined(HAVE_GCRYPT) + int gcry_algo = GCRY_MD_NONE; + switch (algo) { + case REXMPP_DIGEST_SHA1: gcry_algo = GCRY_MD_SHA1; break; + case REXMPP_DIGEST_SHA256: gcry_algo = GCRY_MD_SHA256; break; + case REXMPP_DIGEST_SHA3_256: gcry_algo = GCRY_MD_SHA3_256; break; + default: return -1; + } + gcry_error_t err = gcry_md_open(ctx, gcry_algo, 0); + if (err != GPG_ERR_NO_ERROR) { + return -1; + } +#elif defined(HAVE_NETTLE) + ctx->nh = NULL; + switch (algo) { + case REXMPP_DIGEST_SHA1: ctx->nh = &nettle_sha1; break; + case REXMPP_DIGEST_SHA256: ctx->nh = &nettle_sha256; break; + case REXMPP_DIGEST_SHA3_256: ctx->nh = &nettle_sha3_256; break; + default: return -1; + } + ctx->nh_ctx = malloc(ctx->nh->context_size); + ctx->nh->init(ctx->nh_ctx); +#elif defined(HAVE_OPENSSL) + const EVP_MD *md = NULL; + switch (algo) { + case REXMPP_DIGEST_SHA1: md = EVP_sha1(); break; + case REXMPP_DIGEST_SHA256: md = EVP_sha256(); break; + case REXMPP_DIGEST_SHA3_256: md = EVP_sha3_256(); break; + default: return -1; + } + *ctx = EVP_MD_CTX_new(); + if (! EVP_DigestInit(*ctx, md)) { + EVP_MD_CTX_free(*ctx); + return -1; + } +#endif + return 0; +} + +int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len) { +#if defined(HAVE_GCRYPT) + printf("wirte: %s %lu\n", (const char*)in, len); + gcry_md_write(*ctx, in, len); +#elif defined(HAVE_NETTLE) + ctx->nh->update(ctx->nh_ctx, len, in); +#elif defined(HAVE_OPENSSL) + if (! EVP_DigestUpdate(*ctx, in, len)) { + return -1; + } +#endif + return 0; +} + +int rexmpp_digest_finish (rexmpp_digest_t *ctx, void *out, size_t len) { + int ret = 0; +#if defined(HAVE_GCRYPT) + if (out != NULL) { + unsigned char *result = gcry_md_read(*ctx, 0); + if (result != NULL) { + memcpy(out, result, len); + } else { + ret = -1; + } + } + gcry_md_close(*ctx); +#elif defined(HAVE_NETTLE) + ctx->nh->digest(ctx->nh_ctx, len, out); + free(ctx->nh_ctx); +#elif defined(HAVE_OPENSSL) + (void)len; + if (! EVP_DigestFinal_ex(*ctx, out, NULL)) { + ret = -1; + } + EVP_MD_CTX_free(*ctx); + *ctx = NULL; +#endif + return ret; +} diff --git a/src/rexmpp_digest.h b/src/rexmpp_digest.h new file mode 100644 index 0000000..77f198a --- /dev/null +++ b/src/rexmpp_digest.h @@ -0,0 +1,44 @@ +/** + @file rexmpp_digest.h + @brief Cryptographic functions + @author defanor + @date 2023 + @copyright MIT license. +*/ + +#ifndef REXMPP_DIGEST_H +#define REXMPP_DIGEST_H + +typedef enum { + REXMPP_DIGEST_SHA1, + REXMPP_DIGEST_SHA256, + REXMPP_DIGEST_SHA3_256 +} rexmpp_digest_algorithm; + + +#if defined(HAVE_GCRYPT) +#include +typedef gcry_md_hd_t rexmpp_digest_t; +#elif defined(HAVE_NETTLE) +#include +struct rexmpp_digest { + const struct nettle_hash *nh; + void *nh_ctx; +}; +typedef struct rexmpp_digest rexmpp_digest_t; +#elif defined(HAVE_OPENSSL) +#include +typedef EVP_MD_CTX* rexmpp_digest_t; +#endif + +size_t rexmpp_digest_len (rexmpp_digest_algorithm algo); +int rexmpp_digest_buffer (rexmpp_digest_algorithm algo, + const void *in, + size_t in_len, + void *out, + size_t out_len); +int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo); +int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len); +int rexmpp_digest_finish (rexmpp_digest_t *ctx, void *out, size_t len); + +#endif diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c index 42d0099..a147a5f 100644 --- a/src/rexmpp_jingle.c +++ b/src/rexmpp_jingle.c @@ -27,7 +27,6 @@ A/V calls over ICE-UDP + DTLS-SRTP: #include #include #include -#include #include "config.h" @@ -51,6 +50,7 @@ A/V calls over ICE-UDP + DTLS-SRTP: #include "rexmpp_base64.h" #include "rexmpp_random.h" #include "rexmpp_tls.h" +#include "rexmpp_digest.h" /* https://en.wikipedia.org/wiki/G.711 */ @@ -665,27 +665,24 @@ rexmpp_jingle_send_file (rexmpp_t *s, } char buf[4096]; - gcry_md_hd_t hd; - gcry_error_t err = gcry_md_open(&hd, GCRY_MD_SHA256, 0); - if (err != GPG_ERR_NO_ERROR) { - rexmpp_log(s, LOG_ERR, "Failed to create a MD object: %s", - gcry_strerror(err)); + rexmpp_digest_t sha256, sha3_256; + if (rexmpp_digest_init(&sha256, REXMPP_DIGEST_SHA256)) { + rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-256 digest object"); fclose(fh); return REXMPP_E_OTHER; } - err = gcry_md_enable(hd, GCRY_MD_SHA3_256); - if (err != GPG_ERR_NO_ERROR) { - rexmpp_log(s, LOG_ERR, "Failed to add sha3-256 to the MD object: %s", - gcry_strerror(err)); + if (rexmpp_digest_init(&sha3_256, REXMPP_DIGEST_SHA3_256)) { + rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-3-256 digest object"); + rexmpp_digest_finish(&sha256, NULL, 0); fclose(fh); return REXMPP_E_OTHER; } size_t len = fread(buf, 1, 4096, fh); while (len > 0) { - gcry_md_write(hd, buf, len); + rexmpp_digest_update(&sha256, buf, len); + rexmpp_digest_update(&sha3_256, buf, len); len = fread(buf, 1, 4096, fh); } - gcry_md_final(hd); char *sid = rexmpp_gen_id(s); char *ibb_sid = rexmpp_gen_id(s); @@ -718,10 +715,14 @@ rexmpp_jingle_send_file (rexmpp_t *s, rexmpp_xml_add_text(file_name, basename(path)); rexmpp_xml_add_child(file, file_name); + char hash[32]; char *hash_base64 = NULL; size_t hash_base64_len = 0; - rexmpp_base64_to((char*)gcry_md_read(hd, GCRY_MD_SHA256), - gcry_md_get_algo_dlen(GCRY_MD_SHA256), + + rexmpp_digest_finish(&sha256, hash, + rexmpp_digest_len(REXMPP_DIGEST_SHA256)); + rexmpp_base64_to(hash, + rexmpp_digest_len(REXMPP_DIGEST_SHA256), &hash_base64, &hash_base64_len); rexmpp_xml_t *file_hash = @@ -733,8 +734,10 @@ rexmpp_jingle_send_file (rexmpp_t *s, hash_base64 = NULL; hash_base64_len = 0; - rexmpp_base64_to((char*)gcry_md_read(hd, GCRY_MD_SHA3_256), - gcry_md_get_algo_dlen(GCRY_MD_SHA3_256), + rexmpp_digest_finish(&sha3_256, hash, + rexmpp_digest_len(REXMPP_DIGEST_SHA3_256)); + rexmpp_base64_to(hash, + rexmpp_digest_len(REXMPP_DIGEST_SHA3_256), &hash_base64, &hash_base64_len); file_hash = rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2"); @@ -743,8 +746,6 @@ rexmpp_jingle_send_file (rexmpp_t *s, free(hash_base64); rexmpp_xml_add_child(file, file_hash); - gcry_md_close(hd); - long fsize = ftell(fh); fseek(fh, 0, SEEK_SET); snprintf(buf, 11, "%ld", fsize); diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c index 3d77927..c8decc0 100644 --- a/src/rexmpp_tls.c +++ b/src/rexmpp_tls.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "config.h" @@ -26,6 +25,7 @@ #endif #include "rexmpp.h" +#include "rexmpp_digest.h" #include "rexmpp_tls.h" rexmpp_tls_t *rexmpp_jingle_component_dtls(void *p); @@ -814,11 +814,9 @@ int rexmpp_tls_my_fp (rexmpp_t *s, char *fp_str, size_t *fp_size) { - gcry_md_hd_t hd; - gcry_error_t err = gcry_md_open(&hd, GCRY_MD_SHA256, 0); - if (err != GPG_ERR_NO_ERROR) { - rexmpp_log(s, LOG_ERR, "Failed to create an MD object: %s", - gcry_strerror(err)); + rexmpp_digest_t digest_ctx; + if (rexmpp_digest_init(&digest_ctx, REXMPP_DIGEST_SHA256)) { + rexmpp_log(s, LOG_ERR, "Failed to initialize a digest object"); return -1; } @@ -834,22 +832,19 @@ int rexmpp_tls_my_fp (rexmpp_t *s, unsigned char *buf[4096]; size_t len = fread(buf, 1, 4096, fh); while (len > 0) { - gcry_md_write(hd, buf, len); + rexmpp_digest_update(&digest_ctx, buf, len); len = fread(buf, 1, 4096, fh); } - gcry_md_final(hd); fclose(fh); - *fp_size = gcry_md_get_algo_dlen(GCRY_MD_SHA256); - - memcpy(raw_fp, gcry_md_read(hd, GCRY_MD_SHA256), *fp_size); + *fp_size = rexmpp_digest_len(REXMPP_DIGEST_SHA256); + rexmpp_digest_finish(&digest_ctx, raw_fp, *fp_size); size_t i; for (i = 0; i < (*fp_size); i++) { snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF); } fp_str[(*fp_size) * 3 - 1] = 0; - gcry_md_close(hd); return 0; } -- cgit v1.2.3