Browse Source

SHA-512/256: implement hash algorithm

Closes #12897
Evgeny Grin 2 months ago
parent
commit
cbe41d151d
4 changed files with 650 additions and 1 deletions
  1. 2 0
      lib/Makefile.inc
  2. 601 0
      lib/curl_sha512_256.c
  3. 44 0
      lib/curl_sha512_256.h
  4. 3 1
      tests/test1165.pl

+ 2 - 0
lib/Makefile.inc

@@ -134,6 +134,7 @@ LIB_CFILES =         \
   curl_range.c       \
   curl_rtmp.c        \
   curl_sasl.c        \
+  curl_sha512_256.c  \
   curl_sspi.c        \
   curl_threads.c     \
   curl_trc.c         \
@@ -277,6 +278,7 @@ LIB_HFILES =         \
   curl_setup.h       \
   curl_setup_once.h  \
   curl_sha256.h      \
+  curl_sha512_256.h  \
   curl_sspi.h        \
   curl_threads.h     \
   curl_trc.h         \

+ 601 - 0
lib/curl_sha512_256.c

@@ -0,0 +1,601 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+/* ** 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 single SHA-512/256 word.
+ */
+#define SHA512_256_WORD_SIZE_BITS 64
+
+/**
+ * Number of bytes in 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 intermediate hash size, used during computing the final digest.
+ */
+#define SHA512_256_HASH_SIZE_WORDS 8
+
+/**
+ * Size of SHA-512/256 resulting digest in bytes.
+ * This is the final digest size, not intermediate hash.
+ */
+#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS  / 2)
+
+/**
+ * Size of 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 SHA-512/256 single processing block in bits.
+ */
+#define SHA512_256_BLOCK_SIZE_BITS 1024
+
+/**
+ * Size of SHA-512/256 single processing block in bytes.
+ */
+#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of 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 Sha512_256Ctx
+{
+  /**
+   * Intermediate hash value
+   * The variable is properly aligned. Smart compiler
+   * 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 compiler
+   * 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;
+};
+
+
+/**
+ * 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 Sha512_256Ctx *const ctx = (struct 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 SHA-512/256 transformation.
+ * Gets 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))) )
+  /* Unoptimized (original) versions: */
+/* #define Ch(x,y,z)  ( ( (x) & (y) ) ^ ( ~(x) & (z) ) )          */
+/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
+
+  /* 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.
+     * Note: this version of macro reassign all working variable 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 calculations on each step,
+       the W element is read from the input data buffer as 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 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_CAST( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15])   \
+                      + (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) )
+
+    /* During last 64 steps, before making any calculations 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
+ */
+static void
+MHDx_sha512_256_update(void *context,
+                        const unsigned char *data,
+                        unsigned int length)
+{
+  unsigned int bytes_have; /**< Number of bytes in the context buffer */
+  struct Sha512_256Ctx *const ctx = (struct Sha512_256Ctx *) context;
+  /* Required to mute Intel compiler warning */
+  void *const ctx_buf = ctx->buffer;
+
+  DEBUGASSERT((data != NULL) || (length == 0));
+
+  if(0 == length)
+    return; /* 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(CURL_UINT64_CAST(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);
+  }
+}
+
+
+
+/**
+ * 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
+ */
+static void
+MHDx_sha512_256_finish(unsigned char *digest,
+                        void *context)
+{
+  struct Sha512_256Ctx *const ctx = (struct Sha512_256Ctx *) context;
+  curl_uint64_t num_bits;   /**< Number of processed bits */
+  unsigned int bytes_have; /**< Number of bytes in the context buffer */
+  /* 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 Sha512_256Ctx));
+}
+
+
+/**
+ * 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)
+{
+  struct Sha512_256Ctx ctx;
+  static const unsigned int max_step_size = (unsigned int)(-1);
+
+  (void) MHDx_sha512_256_init(&ctx); /* Always succeed */
+
+  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;
+  }
+  MHDx_sha512_256_update(&ctx, (const void *) input,
+                          curlx_uztoui(input_size));
+
+  MHDx_sha512_256_finish(output, &ctx);
+
+  return CURLE_OK;
+}
+
+
+const struct HMAC_params Curl_HMAC_SHA512_256[] = {
+  {
+    /* Initialize context procedure. */
+    MHDx_sha512_256_init,
+    /* Update context with data. */
+    MHDx_sha512_256_update,
+    /* Get final result procedure. */
+    MHDx_sha512_256_finish,
+    /* Context structure size. */
+    sizeof(struct Sha512_256Ctx),
+    /* Maximum key length (bytes). */
+    SHA512_256_BLOCK_SIZE,
+    /* Result length (bytes). */
+    SHA512_256_DIGEST_SIZE
+  }
+};
+
+
+#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */

+ 44 - 0
lib/curl_sha512_256.h

@@ -0,0 +1,44 @@
+#ifndef HEADER_CURL_SHA512_256_H
+#define HEADER_CURL_SHA512_256_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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
+ *
+ ***************************************************************************/
+
+#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
+
+#include <curl/curl.h>
+#include "curl_hmac.h"
+
+#define CURL_HAVE_SHA512_256
+
+extern const struct HMAC_params Curl_HMAC_SHA512_256[1];
+
+#define SHA512_256_DIGEST_LENGTH 32
+
+CURLcode
+Curl_sha512_256it(unsigned char *output, const unsigned char *input,
+                  size_t input_size);
+
+#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */
+
+#endif /* HEADER_CURL_SHA256_H */

+ 3 - 1
tests/test1165.pl

@@ -87,7 +87,9 @@ sub scan_file {
     while(<F>) {
         while(s/(CURL_DISABLE_[A-Z0-9_]+)//) {
             my ($sym)=($1);
-            $file{$sym} = $source;
+            if(not $sym =~ /^(CURL_DISABLE_SHA512_256)/) { # Skip this symbol, to be implemented
+                $file{$sym} = $source;
+            }
         }
     }
     close F;