Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIST SP 800-108r1-upd1: KDF Counter Implementation #1644

Merged
merged 5 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
100 changes: 100 additions & 0 deletions crypto/fipsmodule/kdf/kbkdf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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_hmac(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 == 0 || h_output_bytes > EVP_MAX_MD_SIZE) {
goto err;
}

if (out_len > SIZE_MAX - h_output_bytes) {
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 = ((uint64_t)out_len + (uint64_t)h_output_bytes - 1) /
(uint64_t)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:
skmcgrail marked this conversation as resolved.
Show resolved Hide resolved
if (ret <= 0 && out_key && out_len > 0) {
OPENSSL_cleanse(out_key, out_len);
}
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_hmac(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_hmac(NULL, out.size(), EVP_sha256(), &secret[0],
sizeof(secret), NULL, 0));

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

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

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

// zero-length secret
ASSERT_FALSE(KBKDF_ctr_hmac(out.data(), out.size(), EVP_sha256(), &secret[0],
0, NULL, 0));
}
17 changes: 11 additions & 6 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,10 +224,11 @@ 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;
uint64_t n = ((uint64_t)out_len + (uint64_t)h_output_bytes - 1) /
(uint64_t)h_output_bytes;

// NIST.SP.800-56Cr2 Step 2:
// Verify that the number of output chunks does not exceed an unsigned 32-bit
Expand Down Expand Up @@ -259,8 +260,9 @@ static int SSKDF(const sskdf_variant *variant, sskdf_variant_ctx *ctx,
}

// NIST.SP.800-56Cr2: Step 6.3. Step 7, Step 8
// Combine the output from |out_key_i| with the output written to |out_key| so far.
// Ensure that we only copy |out_len| bytes in total from all chunks.
// Combine the output from |out_key_i| with the output written to |out_key|
// so far. Ensure that we only copy |out_len| bytes in total from all
// chunks.
todo = h_output_bytes;
if (todo > out_len - done) {
todo = out_len - done;
Expand All @@ -278,6 +280,9 @@ static int SSKDF(const sskdf_variant *variant, sskdf_variant_ctx *ctx,
ret = 1;

err:
if (ret <= 0 && out_key && out_len > 0) {
OPENSSL_cleanse(out_key, out_len);
}
return ret;
}

Expand Down
Loading
Loading