Skip to content

Commit

Permalink
NIST SP 800-108r1-upd1: KDF Counter Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
skmcgrail committed Jun 19, 2024
1 parent e3d34d7 commit df6b053
Show file tree
Hide file tree
Showing 9 changed files with 1,732 additions and 737 deletions.
1 change: 1 addition & 0 deletions crypto/fipsmodule/bcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
#include "evp/p_rsa.c"
#include "hkdf/hkdf.c"
#include "hmac/hmac.c"
#include "kdf/kbkdf.c"
#include "kdf/sskdf.c"
#include "md4/md4.c"
#include "md5/md5.c"
Expand Down
2 changes: 2 additions & 0 deletions crypto/fipsmodule/kdf/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#define SSKDF_MAX_INPUT_LEN (1 << 30)
#define SSKDF_COUNTER_SIZE 4

#define KBKDF_COUNTER_SIZE 4

typedef struct {
void *data;
} sskdf_variant_ctx;
Expand Down
92 changes: 92 additions & 0 deletions crypto/fipsmodule/kdf/kbkdf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include <openssl/kdf.h>
#include "internal.h"

int KBKDF_ctr(uint8_t *out_key, size_t out_len, const EVP_MD *digest,
const uint8_t *secret, size_t secret_len, const uint8_t *info,
size_t info_len) {
int ret = 0;

HMAC_CTX *hmac_ctx = NULL;

if (!out_key || out_len == 0 || !secret || secret_len == 0) {
goto err;
}

hmac_ctx = HMAC_CTX_new();
if (!hmac_ctx) {
goto err;
}

if (!HMAC_Init_ex(hmac_ctx, secret, secret_len, digest, NULL)) {
goto err;
}

// Determine the length of the output in bytes of a single invocation of the
// HMAC function.
size_t h_output_bytes = HMAC_size(hmac_ctx);
if (!h_output_bytes || h_output_bytes <= 0) {
goto err;
}

// NIST.SP.800-108r1-upd1: Step 1:
// Determine how many output chunks are required to produce the requested
// output length |out_len|. This determines how many times the variant compute
// function will be called to output key material.
uint64_t n = (out_len + h_output_bytes - 1) / h_output_bytes;

// NIST.SP.800-108r1-upd1: Step 2:
// Verify that the number of output chunks does not exceed an unsigned 32-bit
// integer.
if (n > UINT32_MAX) {
goto err;
}

size_t done = 0;

for (uint32_t i = 0; i < n; i++) {
uint8_t out_key_i[EVP_MAX_MD_SIZE];
uint8_t counter[KBKDF_COUNTER_SIZE];
size_t todo;

// Increment the counter
CRYPTO_store_u32_be(&counter[0], i + 1);

uint32_t written;

// NIST.SP.800-108r1-upd1: Step 4a:
// K(i) := PRF(K_IN, [i] || FixedInfo)
// Note |hmac_ctx| has already been configured with the secret key
if (!HMAC_Init_ex(hmac_ctx, NULL, 0, NULL, NULL) ||
!HMAC_Update(hmac_ctx, &counter[0], KBKDF_COUNTER_SIZE) ||
!HMAC_Update(hmac_ctx, info, info_len) ||
!HMAC_Final(hmac_ctx, out_key_i, &written) ||
written != h_output_bytes) {
OPENSSL_cleanse(&out_key_i[0], EVP_MAX_MD_SIZE);
goto err;
}

// NIST.SP.800-108r1-upd1: Step 4b, Step 5
// result := result || K(i)
todo = h_output_bytes;
if (todo > out_len - done) {
todo = out_len - done;
}
OPENSSL_memcpy(out_key + done, out_key_i, todo);
done += todo;

// When we are finished clear the temporary buffer to cleanse key material
// from stack.
if (done == out_len) {
OPENSSL_cleanse(&out_key_i[0], EVP_MAX_MD_SIZE);
}
}

ret = 1;

err:
HMAC_CTX_free(hmac_ctx);
return ret;
}
64 changes: 64 additions & 0 deletions crypto/fipsmodule/kdf/kdf_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,67 @@ TEST(SSKDFTest, HMACNegativeTests) {
ASSERT_FALSE(SSKDF_hmac(out.data(), out.size(), EVP_sha256(), &secret[0], 0,
NULL, 0, NULL, 0));
}

TEST(KBKDFCounterTest, TestVectors) {
FileTestGTest(
"crypto/fipsmodule/kdf/test/kbkdf_counter.txt", [](FileTest *t) {
const EVP_MD *md;
std::string hash;
ASSERT_TRUE(t->GetAttribute(&hash, "HASH"));
if (hash == "SHA1") {
md = EVP_sha1();
} else if (hash == "SHA-224") {
md = EVP_sha224();
} else if (hash == "SHA-256") {
md = EVP_sha256();
} else if (hash == "SHA-384") {
md = EVP_sha384();
} else if (hash == "SHA-512") {
md = EVP_sha512();
} else {
FAIL() << "Unknown HASH=" + hash;
}

std::vector<uint8_t> secret, info, expect;

ASSERT_TRUE(t->GetBytes(&secret, "SECRET"));
if (t->HasAttribute("INFO")) {
ASSERT_TRUE(t->GetBytes(&info, "INFO"));
} else {
info = std::vector<uint8_t>(0);
}
ASSERT_TRUE(t->GetBytes(&expect, "EXPECT"));

std::vector<uint8_t> out(expect.size());

ASSERT_TRUE(KBKDF_ctr(out.data(), out.size(), md, secret.data(),
secret.size(), info.data(), info.size()));
ASSERT_EQ(Bytes(expect.data(), expect.size()),
Bytes(out.data(), out.size()));
});
}

TEST(KBKDFCounterTest, NegativeTests) {
const uint8_t secret[16] = {0};
std::vector<uint8_t> out(16);

// NULL output
ASSERT_FALSE(KBKDF_ctr(NULL, out.size(), EVP_sha256(), &secret[0],
sizeof(secret), NULL, 0));

// zero-length output
ASSERT_FALSE(KBKDF_ctr(out.data(), 0, EVP_sha256(), &secret[0],
sizeof(secret), NULL, 0));

// NULL Digest
ASSERT_FALSE(KBKDF_ctr(out.data(), out.size(), NULL, &secret[0],
sizeof(secret), NULL, 0));

// NULL secret
ASSERT_FALSE(KBKDF_ctr(out.data(), out.size(), EVP_sha256(), NULL,
sizeof(secret), NULL, 0));

// zero-length secret
ASSERT_FALSE(KBKDF_ctr(out.data(), out.size(), EVP_sha256(), &secret[0],
0, NULL, 0));
}
6 changes: 3 additions & 3 deletions crypto/fipsmodule/kdf/sskdf.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static int sskdf_variant_hmac_compute(sskdf_variant_ctx *ctx, uint8_t *out,

// NIST.SP.800-56Cr2: Step 6.2 HMAC-hash(salt, counter || secret || info)
// Note: |variant_ctx->hmac_ctx| is already initalized with the salt during
// it's initial construction.
// its initial construction.
if (!HMAC_Init_ex(variant_ctx->hmac_ctx, NULL, 0, NULL, NULL) ||
!HMAC_Update(variant_ctx->hmac_ctx, &counter[0], SSKDF_COUNTER_SIZE) ||
!HMAC_Update(variant_ctx->hmac_ctx, secret, secret_len) ||
Expand Down Expand Up @@ -224,8 +224,8 @@ static int SSKDF(const sskdf_variant *variant, sskdf_variant_ctx *ctx,
}

// NIST.SP.800-56Cr2 Step 1:
// Determine how many output chunks are required to produce the request output
// length |out_len|. This determines how many times the variant compute
// Determine how many output chunks are required to produce the requested
// output length |out_len|. This determines how many times the variant compute
// function will be called to output key material.
uint64_t n = (out_len + h_output_bytes - 1) / h_output_bytes;

Expand Down
Loading

0 comments on commit df6b053

Please sign in to comment.