curl/lib/curl_sha512_256.c
Evgeny Grin (Karlson2k) 05268cf801
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
2024-03-07 10:25:11 +01:00

835 lines
29 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
#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 ** *
* ** under curl license. ** *
* ** This is a minimal version with minimal optimisations. Performance ** *
* ** can be significantly improved. Big-endian store and load macros ** *
* ** are obvious targets for optimisation. ** */
#ifdef __GNUC__
# if defined(__has_attribute) && defined(__STDC_VERSION__)
# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901
# define MHDX_INLINE inline __attribute__((always_inline))
# endif
# endif
#endif
#if !defined(MHDX_INLINE) && \
defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
# if _MSC_VER >= 1400
# define MHDX_INLINE __forceinline
# else
# define MHDX_INLINE /* empty */
# endif
#endif
#if !defined(MHDX_INLINE)
# if defined(inline)
/* Assume that 'inline' macro was already defined correctly by
* the build system. */
# define MHDX_INLINE inline
# elif defined(__cplusplus)
/* The code is compiled with C++ compiler.
* C++ always supports 'inline'. */
# define MHDX_INLINE inline
# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
/* C99 (and later) supports 'inline' keyword */
# define MHDX_INLINE inline
# elif defined(__GNUC__) && __GNUC__ >= 3
/* GCC supports '__inline__' as an extension */
# define MHDX_INLINE __inline__
# else
# define MHDX_INLINE /* empty */
# endif
#endif
/* Bits manipulation macros and functions.
Can be moved to other headers to reuse. */
#define MHDX_GET_64BIT_BE(ptr) \
( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[3]) << 32) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[4]) << 24) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[5]) << 16) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \
(curl_uint64_t)(((const unsigned char*)(ptr))[7]) )
#define MHDX_PUT_64BIT_BE(ptr,val) do { \
((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \
((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \
((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \
((unsigned char*)(ptr))[4]=(unsigned char)(((curl_uint64_t)(val)) >> 24); \
((unsigned char*)(ptr))[3]=(unsigned char)(((curl_uint64_t)(val)) >> 32); \
((unsigned char*)(ptr))[2]=(unsigned char)(((curl_uint64_t)(val)) >> 40); \
((unsigned char*)(ptr))[1]=(unsigned char)(((curl_uint64_t)(val)) >> 48); \
((unsigned char*)(ptr))[0]=(unsigned char)(((curl_uint64_t)(val)) >> 56); \
} while(0)
/* Defined as a function. The macro version may duplicate the binary code
* size as each argument is used twice, so if any calculation is used
* as an argument, the calculation could be done twice. */
static MHDX_INLINE curl_uint64_t
MHDx_rotr64(curl_uint64_t value, unsigned int bits)
{
bits %= 64;
if(0 == bits)
return value;
/* Defined in a form which modern compiler could optimise. */
return (value >> bits) | (value << (64 - bits));
}
/* SHA-512/256 specific data */
/**
* Number of bits in a single SHA-512/256 word.
*/
#define SHA512_256_WORD_SIZE_BITS 64
/**
* Number of bytes in a single SHA-512/256 word.
*/
#define SHA512_256_BYTES_IN_WORD (SHA512_256_WORD_SIZE_BITS / 8)
/**
* Hash is kept internally as 8 64-bit words.
* This is the intermediate hash size, used during computing the final digest.
*/
#define SHA512_256_HASH_SIZE_WORDS 8
/**
* 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)
/**
* 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_SIZE_WORDS * SHA512_256_BYTES_IN_WORD)
/**
* Size of the SHA-512/256 single processing block in bits.
*/
#define SHA512_256_BLOCK_SIZE_BITS 1024
/**
* Size of the SHA-512/256 single processing block in bytes.
*/
#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
/**
* Size of the SHA-512/256 single processing block in words.
*/
#define SHA512_256_BLOCK_SIZE_WORDS \
(SHA512_256_BLOCK_SIZE_BITS / SHA512_256_WORD_SIZE_BITS)
/**
* SHA-512/256 calculation context
*/
struct mhdx_sha512_256ctx
{
/**
* Intermediate hash value. The variable is properly aligned. Smart
* compilers may automatically use fast load/store instruction for big
* endian data on little endian machine.
*/
curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS];
/**
* SHA-512/256 input data buffer. The buffer is properly aligned. Smart
* compilers may automatically use fast load/store instruction for big
* endian data on little endian machine.
*/
curl_uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS];
/**
* The number of bytes, lower part
*/
curl_uint64_t count;
/**
* The number of bits, high part. Unlike lower part, this counts the number
* of bits, not bytes.
*/
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.
*
* @param context the calculation context
* @return always CURLE_OK
*/
static CURLcode
MHDx_sha512_256_init(void *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);
DEBUGASSERT(sizeof(curl_uint64_t) == 8);
/* Initial hash values, see FIPS PUB 180-4 section 5.3.6.2 */
/* Values generated by "IV Generation Function" as described in
* section 5.3.6 */
ctx->H[0] = CURL_UINT64_C(0x22312194FC2BF72C);
ctx->H[1] = CURL_UINT64_C(0x9F555FA3C84C64C2);
ctx->H[2] = CURL_UINT64_C(0x2393B86B6F53B151);
ctx->H[3] = CURL_UINT64_C(0x963877195940EABD);
ctx->H[4] = CURL_UINT64_C(0x96283EE2A88EFFE3);
ctx->H[5] = CURL_UINT64_C(0xBE5E1E2553863992);
ctx->H[6] = CURL_UINT64_C(0x2B0199FC2C85B8AA);
ctx->H[7] = CURL_UINT64_C(0x0EB72DDC81C52CA2);
/* Initialise number of bytes and high part of number of bits. */
ctx->count = CURL_UINT64_C(0);
ctx->count_bits_hi = CURL_UINT64_C(0);
return CURLE_OK;
}
/**
* Base of the SHA-512/256 transformation.
* Gets a full 128 bytes block of data and updates hash values;
* @param H hash values
* @param data the data buffer with #SHA512_256_BLOCK_SIZE bytes block
*/
static void
MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
const void *data)
{
/* Working variables,
see FIPS PUB 180-4 section 6.7, 6.4. */
curl_uint64_t a = H[0];
curl_uint64_t b = H[1];
curl_uint64_t c = H[2];
curl_uint64_t d = H[3];
curl_uint64_t e = H[4];
curl_uint64_t f = H[5];
curl_uint64_t g = H[6];
curl_uint64_t h = H[7];
/* Data buffer, used as a cyclic buffer.
See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */
curl_uint64_t W[16];
/* 'Ch' and 'Maj' macro functions are defined with widely-used optimisation.
See FIPS PUB 180-4 formulae 4.8, 4.9. */
#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
/* Four 'Sigma' macro functions.
See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
#define SIG0(x) \
( MHDx_rotr64((x), 28) ^ MHDx_rotr64((x), 34) ^ MHDx_rotr64((x), 39) )
#define SIG1(x) \
( MHDx_rotr64((x), 14) ^ MHDx_rotr64((x), 18) ^ MHDx_rotr64((x), 41) )
#define sig0(x) \
( MHDx_rotr64((x), 1) ^ MHDx_rotr64((x), 8) ^ ((x) >> 7) )
#define sig1(x) \
( MHDx_rotr64((x), 19) ^ MHDx_rotr64((x), 61) ^ ((x) >> 6) )
if(1) {
unsigned int t;
/* K constants array.
See FIPS PUB 180-4 section 4.2.3 for K values. */
static const curl_uint64_t K[80] = {
CURL_UINT64_C(0x428a2f98d728ae22), CURL_UINT64_C(0x7137449123ef65cd),
CURL_UINT64_C(0xb5c0fbcfec4d3b2f), CURL_UINT64_C(0xe9b5dba58189dbbc),
CURL_UINT64_C(0x3956c25bf348b538), CURL_UINT64_C(0x59f111f1b605d019),
CURL_UINT64_C(0x923f82a4af194f9b), CURL_UINT64_C(0xab1c5ed5da6d8118),
CURL_UINT64_C(0xd807aa98a3030242), CURL_UINT64_C(0x12835b0145706fbe),
CURL_UINT64_C(0x243185be4ee4b28c), CURL_UINT64_C(0x550c7dc3d5ffb4e2),
CURL_UINT64_C(0x72be5d74f27b896f), CURL_UINT64_C(0x80deb1fe3b1696b1),
CURL_UINT64_C(0x9bdc06a725c71235), CURL_UINT64_C(0xc19bf174cf692694),
CURL_UINT64_C(0xe49b69c19ef14ad2), CURL_UINT64_C(0xefbe4786384f25e3),
CURL_UINT64_C(0x0fc19dc68b8cd5b5), CURL_UINT64_C(0x240ca1cc77ac9c65),
CURL_UINT64_C(0x2de92c6f592b0275), CURL_UINT64_C(0x4a7484aa6ea6e483),
CURL_UINT64_C(0x5cb0a9dcbd41fbd4), CURL_UINT64_C(0x76f988da831153b5),
CURL_UINT64_C(0x983e5152ee66dfab), CURL_UINT64_C(0xa831c66d2db43210),
CURL_UINT64_C(0xb00327c898fb213f), CURL_UINT64_C(0xbf597fc7beef0ee4),
CURL_UINT64_C(0xc6e00bf33da88fc2), CURL_UINT64_C(0xd5a79147930aa725),
CURL_UINT64_C(0x06ca6351e003826f), CURL_UINT64_C(0x142929670a0e6e70),
CURL_UINT64_C(0x27b70a8546d22ffc), CURL_UINT64_C(0x2e1b21385c26c926),
CURL_UINT64_C(0x4d2c6dfc5ac42aed), CURL_UINT64_C(0x53380d139d95b3df),
CURL_UINT64_C(0x650a73548baf63de), CURL_UINT64_C(0x766a0abb3c77b2a8),
CURL_UINT64_C(0x81c2c92e47edaee6), CURL_UINT64_C(0x92722c851482353b),
CURL_UINT64_C(0xa2bfe8a14cf10364), CURL_UINT64_C(0xa81a664bbc423001),
CURL_UINT64_C(0xc24b8b70d0f89791), CURL_UINT64_C(0xc76c51a30654be30),
CURL_UINT64_C(0xd192e819d6ef5218), CURL_UINT64_C(0xd69906245565a910),
CURL_UINT64_C(0xf40e35855771202a), CURL_UINT64_C(0x106aa07032bbd1b8),
CURL_UINT64_C(0x19a4c116b8d2d0c8), CURL_UINT64_C(0x1e376c085141ab53),
CURL_UINT64_C(0x2748774cdf8eeb99), CURL_UINT64_C(0x34b0bcb5e19b48a8),
CURL_UINT64_C(0x391c0cb3c5c95a63), CURL_UINT64_C(0x4ed8aa4ae3418acb),
CURL_UINT64_C(0x5b9cca4f7763e373), CURL_UINT64_C(0x682e6ff3d6b2b8a3),
CURL_UINT64_C(0x748f82ee5defb2fc), CURL_UINT64_C(0x78a5636f43172f60),
CURL_UINT64_C(0x84c87814a1f0ab72), CURL_UINT64_C(0x8cc702081a6439ec),
CURL_UINT64_C(0x90befffa23631e28), CURL_UINT64_C(0xa4506cebde82bde9),
CURL_UINT64_C(0xbef9a3f7b2c67915), CURL_UINT64_C(0xc67178f2e372532b),
CURL_UINT64_C(0xca273eceea26619c), CURL_UINT64_C(0xd186b8c721c0c207),
CURL_UINT64_C(0xeada7dd6cde0eb1e), CURL_UINT64_C(0xf57d4f7fee6ed178),
CURL_UINT64_C(0x06f067aa72176fba), CURL_UINT64_C(0x0a637dc5a2c898a6),
CURL_UINT64_C(0x113f9804bef90dae), CURL_UINT64_C(0x1b710b35131c471b),
CURL_UINT64_C(0x28db77f523047d84), CURL_UINT64_C(0x32caab7b40c72493),
CURL_UINT64_C(0x3c9ebe0a15c9bebc), CURL_UINT64_C(0x431d67c49c100d4c),
CURL_UINT64_C(0x4cc5d4becb3e42b6), CURL_UINT64_C(0x597f299cfc657e2a),
CURL_UINT64_C(0x5fcb6fab3ad6faec), CURL_UINT64_C(0x6c44198c4a475817)
};
/* One step of SHA-512/256 computation,
see FIPS PUB 180-4 section 6.4.2 step 3.
* Note: this macro updates working variables in-place, without rotation.
* Note: the first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in
FIPS PUB 180-4 section 6.4.2 step 3.
the second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in
FIPS PUB 180-4 section 6.4.2 step 3.
* Note: 'wt' must be used exactly one time in this macro as macro for
'wt' calculation may change other data as well every time when
used. */
#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
(vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \
(vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
/* One step of SHA-512/256 computation with working variables rotation,
see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns
all working variables on each step. */
#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
curl_uint64_t tmp_h_ = (vH); \
SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \
(vH) = (vG); \
(vG) = (vF); \
(vF) = (vE); \
(vE) = (vD); \
(vD) = (vC); \
(vC) = (vB); \
(vB) = (vA); \
(vA) = tmp_h_; } while(0)
/* Get value of W(t) from input data buffer for 0 <= t <= 15,
See FIPS PUB 180-4 section 6.2.
Input data must be read in big-endian bytes order,
see FIPS PUB 180-4 section 3.1.2. */
#define SHA512_GET_W_FROM_DATA(buf,t) \
MHDX_GET_64BIT_BE( \
((const unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD)
/* During first 16 steps, before making any calculation on each step, the
W element is read from the input data buffer as a big-endian value and
stored in the array of W elements. */
for(t = 0; t < 16; ++t) {
SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \
W[t] = SHA512_GET_W_FROM_DATA(data, t));
}
/* 'W' generation and assignment for 16 <= t <= 79.
See FIPS PUB 180-4 section 6.4.2.
As only the last 16 'W' are used in calculations, it is possible to
use 16 elements array of W as a cyclic buffer.
Note: ((t-16) & 15) have same value as (t & 15) */
#define Wgen(w,t) \
(curl_uint64_t)( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15]) \
+ (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) )
/* During the last 64 steps, before making any calculation on each step,
current W element is generated from other W elements of the cyclic
buffer and the generated value is stored back in the cyclic buffer. */
for(t = 16; t < 80; ++t) {
SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \
W[t & 15] = Wgen(W, t));
}
}
/* Compute and store the intermediate hash.
See FIPS PUB 180-4 section 6.4.2 step 4. */
H[0] += a;
H[1] += b;
H[2] += c;
H[3] += d;
H[4] += e;
H[5] += f;
H[6] += g;
H[7] += h;
}
/**
* 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
MHDx_sha512_256_update(void *context,
const unsigned char *data,
size_t length)
{
unsigned int bytes_have; /**< Number of bytes in the context buffer */
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 CURLE_OK; /* Shortcut, do nothing */
/* Note: (count & (SHA512_256_BLOCK_SIZE-1))
equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
ctx->count += length;
if(length > ctx->count)
ctx->count_bits_hi += 1U << 3; /* Value wrap */
ctx->count_bits_hi += ctx->count >> 61;
ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF);
if(0 != bytes_have) {
unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have;
if(length >= bytes_left) {
/* Combine new data with data in the buffer and process the full
block. */
memcpy(((unsigned char *) ctx_buf) + bytes_have,
data,
bytes_left);
data += bytes_left;
length -= bytes_left;
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
bytes_have = 0;
}
}
while(SHA512_256_BLOCK_SIZE <= length) {
/* Process any full blocks of new data directly,
without copying to the buffer. */
MHDx_sha512_256_transform(ctx->H, data);
data += SHA512_256_BLOCK_SIZE;
length -= SHA512_256_BLOCK_SIZE;
}
if(0 != length) {
/* Copy incomplete block of new data (if any)
to the buffer. */
memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length);
}
return CURLE_OK;
}
/**
* Size of "length" insertion in bits.
* See FIPS PUB 180-4 section 5.1.2.
*/
#define SHA512_256_SIZE_OF_LEN_ADD_BITS 128
/**
* Size of "length" insertion in bytes.
*/
#define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8)
/**
* 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
MHDx_sha512_256_finish(unsigned char *digest,
void *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 */
void *const ctx_buf = ctx->buffer;
/* Memorise the number of processed bits.
The padding and other data added here during the postprocessing must
not change the amount of hashed data. */
num_bits = ctx->count << 3;
/* Note: (count & (SHA512_256_BLOCK_SIZE-1))
equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
/* Input data must be padded with a single bit "1", then with zeros and
the finally the length of data in bits must be added as the final bytes
of the last block.
See FIPS PUB 180-4 section 5.1.2. */
/* Data is always processed in form of bytes (not by individual bits),
therefore position of the first padding bit in byte is always
predefined (0x80). */
/* Buffer always have space at least for one byte (as full buffers are
processed when formed). */
((unsigned char *) ctx_buf)[bytes_have++] = 0x80U;
if(SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) {
/* No space in the current block to put the total length of message.
Pad the current block with zeros and process it. */
if(bytes_have < SHA512_256_BLOCK_SIZE)
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
SHA512_256_BLOCK_SIZE - bytes_have);
/* Process the full block. */
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
/* Start the new block. */
bytes_have = 0;
}
/* Pad the rest of the buffer with zeros. */
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
/* Put high part of number of bits in processed message and then lower
part of number of bits as big-endian values.
See FIPS PUB 180-4 section 5.1.2. */
/* Note: the target location is predefined and buffer is always aligned */
MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD, \
ctx->count_bits_hi);
MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD \
+ SHA512_256_BYTES_IN_WORD, \
num_bits);
/* Process the full final block. */
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
/* Put in BE mode the leftmost part of the hash as the final digest.
See FIPS PUB 180-4 section 6.7. */
MHDX_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]);
MHDX_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]);
MHDX_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]);
MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]);
/* Erase potentially sensitive data. */
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
* @param[out] output the pointer to put the hash
* @param[in] input the pointer to the data to process
* @param input_size the size of the data pointed by @a input
* @return always #CURLE_OK
*/
CURLcode
Curl_sha512_256it(unsigned char *output, const unsigned char *input,
size_t input_size)
{
Curl_sha512_256_ctx ctx;
CURLcode res;
res = Curl_sha512_256_init(&ctx);
if(res != CURLE_OK)
return res;
res = Curl_sha512_256_update(&ctx, (const void *) input, input_size);
if(res != CURLE_OK) {
(void) Curl_sha512_256_finish(output, &ctx);
return res;
}
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. */
Curl_sha512_256_init,
/* Update context with data. */
Curl_sha512_256_update_i,
/* Get final result procedure. */
Curl_sha512_256_finish_v,
/* Context structure size. */
sizeof(Curl_sha512_256_ctx),
/* Maximum key length (bytes). */
SHA512_256_BLOCK_SIZE,
/* Result length (bytes). */
SHA512_256_DIGEST_SIZE
}
};
#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */