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

One-Step Key Derivation Method with KMAC #4121

Merged
merged 3 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 3 additions & 2 deletions doc/api_ref/kdf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,20 @@ e.g. ``X9.42-PRF(KeyWrap.TripleDES)``, ``X9.42-PRF(1.2.840.113549.1.9.16.3.7)``
SP800-56A
~~~~~~~~~~

KDF from NIST SP 800-56A.
KDF from NIST SP 800-56Ar2 or One-Step KDF of SP 800-56Cr2.

Available if ``BOTAN_HAS_SP800_56A`` is defined.

Algorithm specification names:

- ``SP800-56A(<HashFunction>)``, e.g. ``SP800-56A(SHA-256)``
- ``SP800-56A(HMAC(<HashFunction>))``, e.g. ``SP800-56A(HMAC(SHA-256))``
- ``SP800-56A(KMAC-128)`` or ``SP800-56A(KMAC-256)``

SP800-56C
~~~~~~~~~~

KDF from NIST SP 800-56C.
Two-Step KDF from NIST SP 800-56Cr2.

Available if ``BOTAN_HAS_SP800_56C`` is defined.

Expand Down
18 changes: 12 additions & 6 deletions src/lib/kdf/kdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@
#endif

#if defined(BOTAN_HAS_SP800_56A)
#include <botan/internal/sp800_56a.h>
#include <botan/internal/sp800_56c_one_step.h>
#endif

#if defined(BOTAN_HAS_SP800_56C)
#include <botan/internal/sp800_56c.h>
#include <botan/internal/sp800_56c_two_step.h>
#endif

namespace Botan {
Expand Down Expand Up @@ -160,10 +160,16 @@ std::unique_ptr<KDF> KDF::create(std::string_view algo_spec, std::string_view pr
#if defined(BOTAN_HAS_SP800_56A)
if(req.algo_name() == "SP800-56A" && req.arg_count() == 1) {
if(auto hash = HashFunction::create(req.arg(0))) {
return std::make_unique<SP800_56A_Hash>(std::move(hash));
return std::make_unique<SP800_56C_One_Step_Hash>(std::move(hash));
}
if(req.arg(0) == "KMAC-128") {
return std::make_unique<SP800_56C_One_Step_KMAC128>();
}
if(req.arg(0) == "KMAC-256") {
return std::make_unique<SP800_56C_One_Step_KMAC256>();
}
if(auto mac = MessageAuthenticationCode::create(req.arg(0))) {
return std::make_unique<SP800_56A_HMAC>(std::move(mac));
return std::make_unique<SP800_56C_One_Step_HMAC>(std::move(mac));
}
}
#endif
Expand All @@ -173,11 +179,11 @@ std::unique_ptr<KDF> KDF::create(std::string_view algo_spec, std::string_view pr
std::unique_ptr<KDF> exp(kdf_create_mac_or_hash<SP800_108_Feedback>(req.arg(0)));
if(exp) {
if(auto mac = MessageAuthenticationCode::create(req.arg(0))) {
return std::make_unique<SP800_56C>(std::move(mac), std::move(exp));
return std::make_unique<SP800_56C_Two_Step>(std::move(mac), std::move(exp));
}

if(auto mac = MessageAuthenticationCode::create(fmt("HMAC({})", req.arg(0)))) {
return std::make_unique<SP800_56C>(std::move(mac), std::move(exp));
return std::make_unique<SP800_56C_Two_Step>(std::move(mac), std::move(exp));
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/kdf/sp800_56a/info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ name -> "NIST SP800-56A"

<requires>
hmac
kmac
</requires>
112 changes: 0 additions & 112 deletions src/lib/kdf/sp800_56a/sp800_56a.cpp

This file was deleted.

192 changes: 192 additions & 0 deletions src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* KDF defined in NIST SP 800-56a revision 2 (Single-step key-derivation function)
* or in NIST SP 800-56C revision 2 (Section 4 - One-Step KDM)
*
* (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski.
* (C) 2024 Fabian Albert - Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/sp800_56c_one_step.h>

#include <botan/exceptn.h>
#include <botan/internal/bit_ops.h>
#include <botan/internal/fmt.h>
#include <botan/internal/kmac.h>

#include <functional>

namespace Botan {

namespace {
/**
* @brief One-Step Key Derivation as defined in SP800-56Cr2 Section 4
*/
void kdm_internal(std::span<uint8_t> output_buffer,
std::span<const uint8_t> z,
std::span<const uint8_t> fixed_info,
Buffered_Computation& h,
const std::function<void(Buffered_Computation*)>& reset_h_callback) {
FAlbertDev marked this conversation as resolved.
Show resolved Hide resolved
size_t l = output_buffer.size() * 8;
// 1. If L > 0, then set reps = ceil(L / H_outputBits); otherwise,
// output an error indicator and exit this process without
// performing the remaining actions (i.e., omit steps 2 through 8).
BOTAN_ARG_CHECK(l > 0, "Zero KDM output length");
size_t reps = ceil_division(l, h.output_length() * 8);

// 2. If reps > (2^32 − 1), then output an error indicator and exit this
// process without performing the remaining actions
// (i.e., omit steps 3 through 8).
BOTAN_ARG_CHECK(reps <= 0xFFFFFFFF, "Too large KDM output length");

// 3. Initialize a big-endian 4-byte unsigned integer counter as
// 0x00000000, corresponding to a 32-bit binary representation of
// the number zero.
uint32_t counter = 0;

// 4. If counter || Z || FixedInfo is more than max_H_inputBits bits
// long, then output an error indicator and exit this process
// without performing any of the remaining actions (i.e., omit
// steps 5 through 8). => SHA3 and KMAC are unlimited

// 5. Initialize Result(0) as an empty bit string
// (i.e., the null string).
secure_vector<uint8_t> result;

// 6. For i = 1 to reps, do the following:
for(size_t i = 1; i <= reps; i++) {
// 6.1. Increment counter by 1.
counter++;
// Reset the hash/MAC object. For MAC, also set the key (salt) and IV.
reset_h_callback(&h);

// 6.2 Compute K(i) = H(counter || Z || FixedInfo).
h.update_be(counter);
h.update(z);
h.update(fixed_info);
auto k_i = h.final();

// 6.3. Set Result(i) = Result(i−1) || K(i).
result.insert(result.end(), k_i.begin(), k_i.end());
}

// 7. Set DerivedKeyingMaterial equal to the leftmost L bits of Result(reps).
copy_mem(output_buffer, std::span(result).subspan(0, output_buffer.size()));
}

} // namespace

void SP800_56C_One_Step_Hash::kdf(uint8_t key[],
size_t key_len,
const uint8_t secret[],
size_t secret_len,
const uint8_t salt[],
size_t salt_len,
const uint8_t label[],
size_t label_len) const {
BOTAN_UNUSED(salt);
BOTAN_ARG_CHECK(salt_len == 0, "SP800_56A_Hash does not support a non-empty salt");

kdm_internal({key, key_len}, {secret, secret_len}, {label, label_len}, *m_hash, [](Buffered_Computation* kdf) {
HashFunction* hash = dynamic_cast<HashFunction*>(kdf);
BOTAN_ASSERT_NONNULL(hash);
hash->clear();
});
}

std::string SP800_56C_One_Step_Hash::name() const {
return fmt("SP800-56A({})", m_hash->name());
}

std::unique_ptr<KDF> SP800_56C_One_Step_Hash::new_object() const {
return std::make_unique<SP800_56C_One_Step_Hash>(m_hash->new_object());
}

SP800_56C_One_Step_HMAC::SP800_56C_One_Step_HMAC(std::unique_ptr<MessageAuthenticationCode> mac) :
m_mac(std::move(mac)) {
// TODO: we need a MessageAuthenticationCode::is_hmac
if(!m_mac->name().starts_with("HMAC(")) {
throw Algorithm_Not_Found("Only HMAC can be used with SP800_56A_HMAC");
}
}

void SP800_56C_One_Step_HMAC::kdf(uint8_t key[],
size_t key_len,
const uint8_t secret[],
size_t secret_len,
const uint8_t salt[],
size_t salt_len,
const uint8_t label[],
size_t label_len) const {
kdm_internal({key, key_len}, {secret, secret_len}, {label, label_len}, *m_mac, [&](Buffered_Computation* kdf) {
MessageAuthenticationCode* kdf_mac = dynamic_cast<MessageAuthenticationCode*>(kdf);
BOTAN_ASSERT_NONNULL(kdf_mac);
kdf_mac->clear();
// 4.1 Option 2 and 3 - An implementation dependent byte string, salt,
// whose (non-null) value may be optionally provided in
// OtherInput, serves as the HMAC#/KMAC# key ..

// SP 800-56A specifies if the salt is empty then a block of zeros
// equal to the hash's underlying block size are used. However this
FAlbertDev marked this conversation as resolved.
Show resolved Hide resolved
// is equivalent to setting a zero-length key, so the same call
// works for either case.
kdf_mac->set_key(std::span{salt, salt_len});
});
}

std::string SP800_56C_One_Step_HMAC::name() const {
return fmt("SP800-56A({})", m_mac->name());
}

std::unique_ptr<KDF> SP800_56C_One_Step_HMAC::new_object() const {
return std::make_unique<SP800_56C_One_Step_HMAC>(m_mac->new_object());
}

// Option 3 - KMAC
void SP800_56A_One_Step_KMAC_Abstract::kdf(uint8_t key[],
size_t key_len,
const uint8_t secret[],
size_t secret_len,
const uint8_t salt[],
size_t salt_len,
const uint8_t label[],
size_t label_len) const {
auto mac = create_kmac_instance(key_len);
kdm_internal({key, key_len}, {secret, secret_len}, {label, label_len}, *mac, [&](Buffered_Computation* kdf) {
MessageAuthenticationCode* kdf_mac = dynamic_cast<MessageAuthenticationCode*>(kdf);
BOTAN_ASSERT_NONNULL(kdf_mac);
kdf_mac->clear();
// 4.1 Option 2 and 3 - An implementation dependent byte string, salt,
// whose (non-null) value may be optionally provided in
// OtherInput, serves as the HMAC#/KMAC# key ..

// SP 800-56A specifies if the salt is empty then a block of zeros
// equal to the hash's underlying block size are used. However this
// is equivalent to setting a zero-length key, so the same call
// works for either case.
kdf_mac->set_key(std::span{salt, salt_len});
FAlbertDev marked this conversation as resolved.
Show resolved Hide resolved

// 4.1 Option 2 and 3 - An implementation dependent byte string, salt,
// whose (non-null) value may be optionally provided in
// OtherInput, serves as the HMAC#/KMAC# key ...
kdf_mac->set_key(std::span{salt, salt_len});

// 4.1 Option 3 - The "customization string" S shall be the byte string
// 01001011 || 01000100 || 01000110, which represents the sequence
// of characters 'K', 'D', and 'F' in 8-bit ASCII.
kdf_mac->start(std::array<uint8_t, 3>{'K', 'D', 'F'});
});
}

std::unique_ptr<MessageAuthenticationCode> SP800_56C_One_Step_KMAC128::create_kmac_instance(
size_t output_byte_len) const {
return std::make_unique<KMAC128>(output_byte_len * 8);
}

std::unique_ptr<MessageAuthenticationCode> SP800_56C_One_Step_KMAC256::create_kmac_instance(
size_t output_byte_len) const {
return std::make_unique<KMAC256>(output_byte_len * 8);
}

} // namespace Botan
Loading
Loading