]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
sha512_256: add support for GnuTLS and OpenSSL
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>
Wed, 6 Mar 2024 23:42:04 +0000 (00:42 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 7 Mar 2024 09:25:11 +0000 (10:25 +0100)
This is a follow-up for PR #12897.

Add support for SHA-512/256 digest calculation by TLS backends.
Currently only OpenSSL and GnuTLS (actually, nettle) support
SHA-512/256.

Closes #13070

lib/curl_sha512_256.c

index cabea4349a96de10a00710cf634e2c4442ad2410..1dd07dbc6a395ff7a40c1052b3845f06d7f8d6b6 100644 (file)
 #include "curl_sha512_256.h"
 #include "warnless.h"
 
+/* The recommended order of the TLS backends:
+ * * OpenSSL
+ * * GnuTLS
+ * * wolfSSL
+ * * Schannel SSPI
+ * * SecureTransport (Darwin)
+ * * mbedTLS
+ * * BearSSL
+ * * rustls
+ * Skip the backend if it does not support the required algorithm */
+
+#if defined(USE_OPENSSL)
+#  include <openssl/opensslv.h>
+#  if (!defined(LIBRESSL_VERSION_NUMBER) && \
+        defined(OPENSSL_VERSION_NUMBER) && \
+        (OPENSSL_VERSION_NUMBER >= 0x10100010L)) || \
+      (defined(LIBRESSL_VERSION_NUMBER) && \
+        (LIBRESSL_VERSION_NUMBER >= 0x3080000fL))
+#    include <openssl/opensslconf.h>
+#    if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512)
+#      include <openssl/evp.h>
+#      define USE_OPENSSL_SHA512_256          1
+#      define HAS_SHA512_256_IMPLEMENTATION   1
+#    endif
+#  endif
+#endif /* USE_OPENSSL */
+
+
+#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS)
+#  include <nettle/sha.h>
+#  if defined(SHA512_256_DIGEST_SIZE)
+#    define USE_GNUTLS_SHA512_256           1
+#    define HAS_SHA512_256_IMPLEMENTATION   1
+#  endif
+#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */
+
+#if defined(USE_OPENSSL_SHA512_256)
+
+/* OpenSSL does not provide macros for SHA-512/256 sizes */
+
+/**
+ * Size of the SHA-512/256 single processing block in bytes.
+ */
+#define SHA512_256_BLOCK_SIZE 128
+
+/**
+ * Size of the SHA-512/256 resulting digest in bytes.
+ * This is the final digest size, not intermediate hash.
+ */
+#define SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_LENGTH
+
+/**
+ * Context type used for SHA-512/256 calculations
+ */
+typedef EVP_MD_CTX *Curl_sha512_256_ctx;
+
+/**
+ * Initialise structure for SHA-512/256 calculation.
+ *
+ * @param context the calculation context
+ * @return CURLE_OK if succeed,
+ *         error code otherwise
+ */
+static CURLcode
+Curl_sha512_256_init(void *context)
+{
+  Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
+
+  *ctx = EVP_MD_CTX_create();
+  if(!*ctx)
+    return CURLE_OUT_OF_MEMORY;
+
+  if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) {
+    /* Check whether the header and this file use the same numbers */
+    DEBUGASSERT(EVP_MD_CTX_size(*ctx) == SHA512_256_DIGEST_SIZE);
+    /* Check whether the block size is correct */
+    DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == SHA512_256_BLOCK_SIZE);
+
+    return CURLE_OK; /* Success */
+  }
+
+  /* Cleanup */
+  EVP_MD_CTX_destroy(*ctx);
+  return CURLE_FAILED_INIT;
+}
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param context the calculation context
+ * @param data bytes to add to hash
+ * @return CURLE_OK if succeed,
+ *         error code otherwise
+ */
+static CURLcode
+Curl_sha512_256_update(void *context,
+                       const unsigned char *data,
+                       size_t length)
+{
+  Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
+
+  if(!EVP_DigestUpdate(*ctx, data, length))
+    return CURLE_SSL_CIPHER;
+
+  return CURLE_OK;
+}
+
+
+/**
+ * Finalise SHA-512/256 calculation, return digest.
+ *
+ * @param context the calculation context
+ * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
+ * @return CURLE_OK if succeed,
+ *         error code otherwise
+ */
+static CURLcode
+Curl_sha512_256_finish(unsigned char *digest,
+                       void *context)
+{
+  CURLcode ret;
+  Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
+
+  ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
+
+  EVP_MD_CTX_destroy(*ctx);
+  *ctx = NULL;
+
+  return ret;
+}
+
+#elif defined(USE_GNUTLS_SHA512_256)
+
+/**
+ * Context type used for SHA-512/256 calculations
+ */
+typedef struct sha512_256_ctx Curl_sha512_256_ctx;
+
+/**
+ * Initialise structure for SHA-512/256 calculation.
+ *
+ * @param context the calculation context
+ * @return always CURLE_OK
+ */
+static CURLcode
+Curl_sha512_256_init(void *context)
+{
+  Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
+
+  /* Check whether the header and this file use the same numbers */
+  DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE);
+
+  sha512_256_init(ctx);
+
+  return CURLE_OK;
+}
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param context the calculation context
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ * @return always CURLE_OK
+ */
+static CURLcode
+Curl_sha512_256_update(void *context,
+                       const unsigned char *data,
+                       size_t length)
+{
+  Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
+
+  DEBUGASSERT((data != NULL) || (length == 0));
+
+  sha512_256_update(ctx, length, (const uint8_t *)data);
+
+  return CURLE_OK;
+}
+
+
+/**
+ * Finalise SHA-512/256 calculation, return digest.
+ *
+ * @param context the calculation context
+ * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
+ * @return always CURLE_OK
+ */
+static CURLcode
+Curl_sha512_256_finish(unsigned char *digest,
+                       void *context)
+{
+  Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
+
+  sha512_256_digest(ctx, (size_t)SHA512_256_DIGEST_SIZE, (uint8_t *)digest);
+
+  return CURLE_OK;
+}
+
+#else /* No system or TLS backend SHA-512/256 implementation available */
+
+/* Use local implementation */
+#define HAS_SHA512_256_IMPLEMENTATION   1
+
 /* ** This implementation of SHA-512/256 hash calculation was originally ** *
  * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd.          ** *
  * ** The author ported the code to libcurl. The ported code is provided ** *
@@ -130,7 +335,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits)
 #define SHA512_256_HASH_SIZE_WORDS 8
 
 /**
- * Size of the SHA-512/256 resulting digest in bytes.
+ * Size of the SHA-512/256 resulting digest in words.
  * This is the final digest size, not intermediate hash.
  */
 #define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS  / 2)
@@ -161,7 +366,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits)
 /**
  * SHA-512/256 calculation context
  */
-struct Sha512_256Ctx
+struct mhdx_sha512_256ctx
 {
   /**
    * Intermediate hash value. The variable is properly aligned. Smart
@@ -186,6 +391,11 @@ struct Sha512_256Ctx
   curl_uint64_t count_bits_hi;
 };
 
+/**
+ * Context type used for SHA-512/256 calculations
+ */
+typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx;
+
 
 /**
  * Initialise structure for SHA-512/256 calculation.
@@ -196,7 +406,7 @@ struct Sha512_256Ctx
 static CURLcode
 MHDx_sha512_256_init(void *context)
 {
-  struct Sha512_256Ctx *const ctx = (struct Sha512_256Ctx *) context;
+  struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context;
 
   /* Check whether the header and this file use the same numbers */
   DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE);
@@ -393,21 +603,22 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
  * @param context the calculation context
  * @param data bytes to add to hash
  * @param length number of bytes in @a data
+ * @return always CURLE_OK
  */
-static void
+static CURLcode
 MHDx_sha512_256_update(void *context,
                        const unsigned char *data,
-                       unsigned int length)
+                       size_t length)
 {
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
-  struct Sha512_256Ctx *const ctx = (struct Sha512_256Ctx *) context;
+  struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context;
   /* the void pointer here is required to mute Intel compiler warning */
   void *const ctx_buf = ctx->buffer;
 
   DEBUGASSERT((data != NULL) || (length == 0));
 
   if(0 == length)
-    return; /* Shortcut, do nothing */
+    return CURLE_OK; /* Shortcut, do nothing */
 
   /* Note: (count & (SHA512_256_BLOCK_SIZE-1))
      equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
@@ -446,6 +657,8 @@ MHDx_sha512_256_update(void *context,
        to the buffer. */
     memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length);
   }
+
+  return CURLE_OK;
 }
 
 
@@ -466,12 +679,13 @@ MHDx_sha512_256_update(void *context,
  *
  * @param context the calculation context
  * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
+ * @return always CURLE_OK
  */
-static void
+static CURLcode
 MHDx_sha512_256_finish(unsigned char *digest,
                        void *context)
 {
-  struct Sha512_256Ctx *const ctx = (struct Sha512_256Ctx *) context;
+  struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context;
   curl_uint64_t num_bits;   /**< Number of processed bits */
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
   /* the void pointer here is required to mute Intel compiler warning */
@@ -538,9 +752,18 @@ MHDx_sha512_256_finish(unsigned char *digest,
   MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]);
 
   /* Erase potentially sensitive data. */
-  memset(ctx, 0, sizeof(struct Sha512_256Ctx));
+  memset(ctx, 0, sizeof(struct mhdx_sha512_256ctx));
+
+  return CURLE_OK;
 }
 
+/* Map to the local implementation */
+#define Curl_sha512_256_init    MHDx_sha512_256_init
+#define Curl_sha512_256_update  MHDx_sha512_256_update
+#define Curl_sha512_256_finish  MHDx_sha512_256_finish
+
+#endif /* Local SHA-512/256 code */
+
 
 /**
  * Compute SHA-512/256 hash for the given data in one function call
@@ -553,35 +776,54 @@ CURLcode
 Curl_sha512_256it(unsigned char *output, const unsigned char *input,
                   size_t input_size)
 {
-  struct Sha512_256Ctx ctx;
-  static const unsigned int max_step_size = (unsigned int)(-1);
+  Curl_sha512_256_ctx ctx;
+  CURLcode res;
+
+  res = Curl_sha512_256_init(&ctx);
+  if(res != CURLE_OK)
+    return res;
 
-  (void) MHDx_sha512_256_init(&ctx); /* Always succeed */
+  res = Curl_sha512_256_update(&ctx, (const void *) input, input_size);
 
-  while(input_size >= max_step_size) {
-    MHDx_sha512_256_update(&ctx, (const void *) input, max_step_size);
-    input += max_step_size;
-    input_size -= max_step_size;
+  if(res != CURLE_OK) {
+    (void) Curl_sha512_256_finish(output, &ctx);
+    return res;
   }
-  MHDx_sha512_256_update(&ctx, (const void *) input,
-                          curlx_uztoui(input_size));
 
-  MHDx_sha512_256_finish(output, &ctx);
+  return Curl_sha512_256_finish(output, &ctx);
+}
 
-  return CURLE_OK;
+/* Wrapper function, takes 'unsigned int' as length type, returns void */
+static void
+Curl_sha512_256_update_i(void *context,
+                         const unsigned char *data,
+                         unsigned int length)
+{
+  /* Hypothetically the function may fail, but assume it does not */
+  (void) Curl_sha512_256_update(context, data, length);
 }
 
+/* Wrapper function, returns void */
+static void
+Curl_sha512_256_finish_v(unsigned char *result,
+                         void *context)
+{
+  /* Hypothetically the function may fail, but assume it does not */
+  (void) Curl_sha512_256_finish(result, context);
+}
+
+/* Wrapper function, takes 'unsigned int' as length type, returns void */
 
 const struct HMAC_params Curl_HMAC_SHA512_256[] = {
   {
     /* Initialize context procedure. */
-    MHDx_sha512_256_init,
+    Curl_sha512_256_init,
     /* Update context with data. */
-    MHDx_sha512_256_update,
+    Curl_sha512_256_update_i,
     /* Get final result procedure. */
-    MHDx_sha512_256_finish,
+    Curl_sha512_256_finish_v,
     /* Context structure size. */
-    sizeof(struct Sha512_256Ctx),
+    sizeof(Curl_sha512_256_ctx),
     /* Maximum key length (bytes). */
     SHA512_256_BLOCK_SIZE,
     /* Result length (bytes). */
@@ -589,5 +831,4 @@ const struct HMAC_params Curl_HMAC_SHA512_256[] = {
   }
 };
 
-
 #endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */