+
+ sha512(in, inlen, digest);
+ SHA512(in, inlen, ref_digest);
+ ASSERT(memcmp(digest, ref_digest, SHA512_DIGEST_SIZE) == 0);
+ }
+}
+#endif /* ENABLE_ALG_TESTS */
+
+/*----------------------------------------------------------------------------*
+ * HKDF implementation *
+ *----------------------------------------------------------------------------*/
+
+static void hmac_sha512(const u8 *key, size_t keylen, const u8 *msg,
+ size_t msglen, u8 mac[SHA512_DIGEST_SIZE])
+{
+ u8 *ibuf = xmalloc(SHA512_BLOCK_SIZE + msglen);
+ u8 obuf[SHA512_BLOCK_SIZE + SHA512_DIGEST_SIZE];
+
+ ASSERT(keylen <= SHA512_BLOCK_SIZE); /* keylen > bs not implemented */
+
+ memset(ibuf, 0x36, SHA512_BLOCK_SIZE);
+ xor(ibuf, ibuf, key, keylen);
+ memcpy(&ibuf[SHA512_BLOCK_SIZE], msg, msglen);
+
+ memset(obuf, 0x5c, SHA512_BLOCK_SIZE);
+ xor(obuf, obuf, key, keylen);
+ sha512(ibuf, SHA512_BLOCK_SIZE + msglen, &obuf[SHA512_BLOCK_SIZE]);
+ sha512(obuf, sizeof(obuf), mac);
+
+ free(ibuf);
+}
+
+static void hkdf_sha512(const u8 *ikm, size_t ikmlen,
+ const u8 *salt, size_t saltlen,
+ const u8 *info, size_t infolen,
+ u8 *output, size_t outlen)
+{
+ static const u8 default_salt[SHA512_DIGEST_SIZE];
+ u8 prk[SHA512_DIGEST_SIZE]; /* pseudorandom key */
+ u8 *buf = xmalloc(1 + infolen + SHA512_DIGEST_SIZE);
+ u8 counter = 1;
+ size_t i;
+
+ if (saltlen == 0) {
+ salt = default_salt;
+ saltlen = sizeof(default_salt);
+ }
+
+ /* HKDF-Extract */
+ ASSERT(ikmlen > 0);
+ hmac_sha512(salt, saltlen, ikm, ikmlen, prk);
+
+ /* HKDF-Expand */
+ for (i = 0; i < outlen; i += SHA512_DIGEST_SIZE) {
+ u8 *p = buf;
+ u8 tmp[SHA512_DIGEST_SIZE];
+
+ ASSERT(counter != 0);
+ if (i > 0) {
+ memcpy(p, &output[i - SHA512_DIGEST_SIZE],
+ SHA512_DIGEST_SIZE);
+ p += SHA512_DIGEST_SIZE;
+ }
+ memcpy(p, info, infolen);
+ p += infolen;
+ *p++ = counter++;
+ hmac_sha512(prk, sizeof(prk), buf, p - buf, tmp);
+ memcpy(&output[i], tmp, MIN(sizeof(tmp), outlen - i));
+ }
+ free(buf);
+}
+
+#ifdef ENABLE_ALG_TESTS
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+static void openssl_hkdf_sha512(const u8 *ikm, size_t ikmlen,
+ const u8 *salt, size_t saltlen,
+ const u8 *info, size_t infolen,
+ u8 *output, size_t outlen)
+{
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ size_t actual_outlen = outlen;
+
+ ASSERT(pctx != NULL);
+ ASSERT(EVP_PKEY_derive_init(pctx) > 0);
+ ASSERT(EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) > 0);
+ ASSERT(EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikmlen) > 0);
+ ASSERT(EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltlen) > 0);
+ ASSERT(EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infolen) > 0);
+ ASSERT(EVP_PKEY_derive(pctx, output, &actual_outlen) > 0);
+ ASSERT(actual_outlen == outlen);
+ EVP_PKEY_CTX_free(pctx);
+}
+
+static void test_hkdf_sha512(void)
+{
+ unsigned long num_tests = NUM_ALG_TEST_ITERATIONS;
+
+ while (num_tests--) {
+ u8 ikm[SHA512_DIGEST_SIZE];
+ u8 salt[SHA512_DIGEST_SIZE];
+ u8 info[128];
+ u8 actual_output[512];
+ u8 expected_output[sizeof(actual_output)];
+ size_t ikmlen = 1 + (rand() % sizeof(ikm));
+ size_t saltlen = rand() % (1 + sizeof(salt));
+ size_t infolen = rand() % (1 + sizeof(info));
+ size_t outlen = rand() % (1 + sizeof(actual_output));
+
+ rand_bytes(ikm, ikmlen);
+ rand_bytes(salt, saltlen);
+ rand_bytes(info, infolen);
+
+ hkdf_sha512(ikm, ikmlen, salt, saltlen, info, infolen,
+ actual_output, outlen);
+ openssl_hkdf_sha512(ikm, ikmlen, salt, saltlen, info, infolen,
+ expected_output, outlen);
+ ASSERT(memcmp(actual_output, expected_output, outlen) == 0);