From: Evgeny Grin (Karlson2k) Date: Wed, 6 Mar 2024 23:42:04 +0000 (+0100) Subject: sha512_256: add support for GnuTLS and OpenSSL X-Git-Tag: curl-8_7_0~61 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=05268cf801a193b68411cfa298413c3e5ca79d4f;p=thirdparty%2Fcurl.git 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 --- diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index cabea4349a..1dd07dbc6a 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -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 +# if (!defined(LIBRESSL_VERSION_NUMBER) && \ + defined(OPENSSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER >= 0x10100010L)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER >= 0x3080000fL)) +# include +# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512) +# include +# 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 +# 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 */