sha512_256: add support for GnuTLS and OpenSSL

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
This commit is contained in:
Evgeny Grin (Karlson2k) 2024-03-07 00:42:04 +01:00 committed by Daniel Stenberg
parent 1e517e9f5c
commit 05268cf801
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -29,6 +29,211 @@
#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;
(void) MHDx_sha512_256_init(&ctx); /* Always succeed */
res = Curl_sha512_256_init(&ctx);
if(res != CURLE_OK)
return res;
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;
res = Curl_sha512_256_update(&ctx, (const void *) input, input_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 CURLE_OK;
return Curl_sha512_256_finish(output, &ctx);
}
/* 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 */