From b7fdb103a8f06f872323ae071aee62e60002f9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 29 Nov 2024 09:47:38 +0100 Subject: [PATCH 1/4] Modernize Key Derivation Functions Pass the parameters from the abstract (user-facing) interface as std::span_s and use the latest convenience tools in the implementations of the KDFs. --- src/lib/kdf/hkdf/hkdf.cpp | 120 +++++------- src/lib/kdf/hkdf/hkdf.h | 47 ++--- src/lib/kdf/kdf.h | 69 ++++--- src/lib/kdf/kdf1/kdf1.cpp | 32 ++-- src/lib/kdf/kdf1/kdf1.h | 15 +- src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp | 41 ++-- src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h | 15 +- src/lib/kdf/kdf2/kdf2.cpp | 44 ++--- src/lib/kdf/kdf2/kdf2.h | 15 +- src/lib/kdf/prf_tls/prf_tls.cpp | 50 ++--- src/lib/kdf/prf_tls/prf_tls.h | 15 +- src/lib/kdf/prf_x942/prf_x942.cpp | 57 +++--- src/lib/kdf/prf_x942/prf_x942.h | 15 +- src/lib/kdf/sp800_108/sp800_108.cpp | 190 ++++++++----------- src/lib/kdf/sp800_108/sp800_108.h | 69 +++---- src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp | 110 +++++------ src/lib/kdf/sp800_56a/sp800_56c_one_step.h | 72 +++---- src/lib/kdf/sp800_56c/sp800_56c_two_step.cpp | 23 +-- src/lib/kdf/sp800_56c/sp800_56c_two_step.h | 31 ++- src/tests/test_kdf.cpp | 4 +- 20 files changed, 413 insertions(+), 621 deletions(-) diff --git a/src/lib/kdf/hkdf/hkdf.cpp b/src/lib/kdf/hkdf/hkdf.cpp index b5b7a8b4334..287d5b57aab 100644 --- a/src/lib/kdf/hkdf/hkdf.cpp +++ b/src/lib/kdf/hkdf/hkdf.cpp @@ -2,6 +2,7 @@ * HKDF * (C) 2013,2015,2017 Jack Lloyd * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -11,6 +12,7 @@ #include #include #include +#include namespace Botan { @@ -22,20 +24,16 @@ std::string HKDF::name() const { return fmt("HKDF({})", m_prf->name()); } -void HKDF::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 { +void HKDF::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { HKDF_Extract extract(m_prf->new_object()); HKDF_Expand expand(m_prf->new_object()); secure_vector prk(m_prf->output_length()); - extract.kdf(prk.data(), prk.size(), secret, secret_len, salt, salt_len, nullptr, 0); - expand.kdf(key, key_len, prk.data(), prk.size(), nullptr, 0, label, label_len); + extract.derive_key(prk, secret, salt, {}); + expand.derive_key(key, prk, {}, label); } std::unique_ptr HKDF_Extract::new_object() const { @@ -46,42 +44,31 @@ std::string HKDF_Extract::name() const { return fmt("HKDF-Extract({})", m_prf->name()); } -void HKDF_Extract::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 { - if(key_len == 0) { +void HKDF_Extract::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { return; } const size_t prf_output_len = m_prf->output_length(); + BOTAN_ARG_CHECK(key.size() <= prf_output_len, "HKDF-Extract maximum output length exceeeded"); + BOTAN_ARG_CHECK(label.empty(), "HKDF-Extract does not support a label input"); - if(key_len > prf_output_len) { - throw Invalid_Argument("HKDF-Extract maximum output length exceeeded"); - } - - if(label_len > 0) { - throw Invalid_Argument("HKDF-Extract does not support a label input"); - } - - if(salt_len == 0) { + if(salt.empty()) { m_prf->set_key(std::vector(prf_output_len)); } else { - m_prf->set_key(salt, salt_len); + m_prf->set_key(salt); } - m_prf->update(secret, secret_len); + m_prf->update(secret); - if(key_len == prf_output_len) { + if(key.size() == prf_output_len) { m_prf->final(key); } else { - secure_vector prk; - m_prf->final(prk); - copy_mem(&key[0], prk.data(), key_len); + const auto prk = m_prf->final(); + copy_mem(key, std::span{prk}.first(key.size())); } } @@ -93,75 +80,54 @@ std::string HKDF_Expand::name() const { return fmt("HKDF-Expand({})", m_prf->name()); } -void HKDF_Expand::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 { - if(key_len == 0) { +void HKDF_Expand::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { return; } - if(key_len > m_prf->output_length() * 255) { - throw Invalid_Argument("HKDF-Expand maximum output length exceeeded"); - } - - m_prf->set_key(secret, secret_len); + BOTAN_ARG_CHECK(key.size() <= m_prf->output_length() * 255, "HKDF-Expand maximum output length exceeeded"); - uint8_t counter = 1; secure_vector h; - size_t offset = 0; - while(offset != key_len) { + BufferStuffer k(key); + m_prf->set_key(secret); + for(uint8_t counter = 1; !k.full(); ++counter) { m_prf->update(h); - m_prf->update(label, label_len); - m_prf->update(salt, salt_len); - m_prf->update(counter++); + m_prf->update(label); + m_prf->update(salt); + m_prf->update(counter); m_prf->final(h); - const size_t written = std::min(h.size(), key_len - offset); - copy_mem(&key[offset], h.data(), written); - offset += written; + const auto bytes_to_write = std::min(h.size(), k.remaining_capacity()); + k.append(std::span{h}.first(bytes_to_write)); } } secure_vector hkdf_expand_label(std::string_view hash_fn, - const uint8_t secret[], - size_t secret_len, + std::span secret, std::string_view label, - const uint8_t hash_val[], - size_t hash_val_len, + std::span hash_val, size_t length) { BOTAN_ARG_CHECK(length <= 0xFFFF, "HKDF-Expand-Label requested output too large"); BOTAN_ARG_CHECK(label.size() <= 0xFF, "HKDF-Expand-Label label too long"); - BOTAN_ARG_CHECK(hash_val_len <= 0xFF, "HKDF-Expand-Label hash too long"); - - const uint16_t length16 = static_cast(length); + BOTAN_ARG_CHECK(hash_val.size() <= 0xFF, "HKDF-Expand-Label hash too long"); HKDF_Expand hkdf(MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", hash_fn))); - secure_vector output(length16); - std::vector prefix(3 + label.size() + 1); - - prefix[0] = get_byte<0>(length16); - prefix[1] = get_byte<1>(length16); - prefix[2] = static_cast(label.size()); - - copy_mem(prefix.data() + 3, cast_char_ptr_to_uint8(label.data()), label.size()); - - prefix[3 + label.size()] = static_cast(hash_val_len); + const auto prefix = concat>(store_be(static_cast(length)), + store_be(static_cast(label.size())), + std::span{cast_char_ptr_to_uint8(label.data()), label.size()}, + store_be(static_cast(hash_val.size()))); /* * We do something a little dirty here to avoid copying the hash_val, * making use of the fact that Botan's KDF interface supports label+salt, * and knowing that our HKDF hashes first param label then param salt. */ - hkdf.kdf(output.data(), output.size(), secret, secret_len, hash_val, hash_val_len, prefix.data(), prefix.size()); - - return output; + return hkdf.derive_key(length, secret, hash_val, prefix); } } // namespace Botan diff --git a/src/lib/kdf/hkdf/hkdf.h b/src/lib/kdf/hkdf/hkdf.h index 1e64a5ba8e7..e5eca15c1eb 100644 --- a/src/lib/kdf/hkdf/hkdf.h +++ b/src/lib/kdf/hkdf/hkdf.h @@ -28,14 +28,11 @@ class HKDF final : public KDF { std::string name() const override; - void 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 override; + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; @@ -55,14 +52,11 @@ class HKDF_Extract final : public KDF { std::string name() const override; - void 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 override; + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; @@ -82,14 +76,11 @@ class HKDF_Expand final : public KDF { std::string name() const override; - void 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 override; + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; @@ -99,19 +90,15 @@ class HKDF_Expand final : public KDF { * HKDF-Expand-Label from TLS 1.3/QUIC * @param hash_fn the hash to use * @param secret the secret bits -* @param secret_len the length of secret * @param label the full label (no "TLS 1.3, " or "tls13 " prefix * is applied) * @param hash_val the previous hash value (used for chaining, may be empty) -* @param hash_val_len the length of hash_val * @param length the desired output length */ secure_vector BOTAN_TEST_API hkdf_expand_label(std::string_view hash_fn, - const uint8_t secret[], - size_t secret_len, + std::span secret, std::string_view label, - const uint8_t hash_val[], - size_t hash_val_len, + std::span hash_val, size_t length); } // namespace Botan diff --git a/src/lib/kdf/kdf.h b/src/lib/kdf/kdf.h index d6e0e8bf1df..89cb841a74a 100644 --- a/src/lib/kdf/kdf.h +++ b/src/lib/kdf/kdf.h @@ -1,6 +1,7 @@ /* * Key Derivation Function interfaces * (C) 1999-2007 Jack Lloyd +* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -62,14 +63,16 @@ class BOTAN_PUBLIC_API(2, 0) KDF { * @param label purpose for the derived keying material * @param label_len size of label in bytes */ - virtual void 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 = 0; + void 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 { + perform_kdf({key, key_len}, {secret, secret_len}, {salt, salt_len}, {label, label_len}); + } /** * Derive a key @@ -90,9 +93,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { size_t salt_len, const uint8_t label[] = nullptr, size_t label_len = 0) const { - T key(key_len); - kdf(key.data(), key.size(), secret, secret_len, salt, salt_len, label, label_len); - return key; + return derive_key(key_len, {secret, secret_len}, {salt, salt_len}, {label, label_len}); } /** @@ -109,12 +110,9 @@ class BOTAN_PUBLIC_API(2, 0) KDF { std::string_view salt = "", std::string_view label = "") const { return derive_key(key_len, - secret.data(), - secret.size(), - cast_char_ptr_to_uint8(salt.data()), - salt.length(), - cast_char_ptr_to_uint8(label.data()), - label.length()); + secret, + {cast_char_ptr_to_uint8(salt.data()), salt.length()}, + {cast_char_ptr_to_uint8(label.data()), label.length()}); } /** @@ -128,8 +126,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { std::span secret, std::span salt, std::span label) const { - return kdf( - key.data(), key.size(), secret.data(), secret.size(), salt.data(), salt.size(), label.data(), label.size()); + return perform_kdf(key, secret, salt, label); } /** @@ -145,8 +142,9 @@ class BOTAN_PUBLIC_API(2, 0) KDF { std::span secret, std::span salt, std::span label) const { - return derive_key( - key_len, secret.data(), secret.size(), salt.data(), salt.size(), label.data(), label.size()); + T key(key_len); + perform_kdf(key, secret, salt, label); + return key; } /** @@ -164,8 +162,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { const uint8_t salt[], size_t salt_len, std::string_view label = "") const { - return derive_key( - key_len, secret.data(), secret.size(), salt, salt_len, cast_char_ptr_to_uint8(label.data()), label.size()); + return derive_key(key_len, secret, {salt, salt_len}, {cast_char_ptr_to_uint8(label.data()), label.size()}); } /** @@ -184,12 +181,9 @@ class BOTAN_PUBLIC_API(2, 0) KDF { std::string_view salt = "", std::string_view label = "") const { return derive_key(key_len, - secret, - secret_len, - cast_char_ptr_to_uint8(salt.data()), - salt.length(), - cast_char_ptr_to_uint8(label.data()), - label.length()); + {secret, secret_len}, + {cast_char_ptr_to_uint8(salt.data()), salt.length()}, + {cast_char_ptr_to_uint8(label.data()), label.length()}); } /** @@ -201,6 +195,23 @@ class BOTAN_PUBLIC_API(2, 0) KDF { * @return new object representing the same algorithm as *this */ KDF* clone() const { return this->new_object().release(); } + + protected: + /** + * Internal customization point for subclasses + * + * The byte size of the @p key span is the number of bytes to be produced + * by the concrete key derivation function. + * + * @param key the output buffer for the to-be-derived key + * @param secret the secret input + * @param salt a diversifier + * @param label purpose for the derived keying material + */ + virtual void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const = 0; }; /** diff --git a/src/lib/kdf/kdf1/kdf1.cpp b/src/lib/kdf/kdf1/kdf1.cpp index 5713044fc7c..7ed31442b95 100644 --- a/src/lib/kdf/kdf1/kdf1.cpp +++ b/src/lib/kdf/kdf1/kdf1.cpp @@ -1,6 +1,7 @@ /* * KDF1 * (C) 1999-2007 Jack Lloyd +* (C) 2024i René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -20,33 +21,28 @@ std::unique_ptr KDF1::new_object() const { return std::make_unique(m_hash->new_object()); } -void KDF1::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 { - if(key_len == 0) { +void KDF1::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { return; } - if(key_len > m_hash->output_length()) { - throw Invalid_Argument("KDF1 maximum output length exceeeded"); - } + const size_t hash_output_len = m_hash->output_length(); + BOTAN_ARG_CHECK(key.size() <= hash_output_len, "KDF1 maximum output length exceeeded"); - m_hash->update(secret, secret_len); - m_hash->update(label, label_len); - m_hash->update(salt, salt_len); + m_hash->update(secret); + m_hash->update(label); + m_hash->update(salt); - if(key_len == m_hash->output_length()) { + if(key.size() == hash_output_len) { // In this case we can hash directly into the output buffer m_hash->final(key); } else { // Otherwise a copy is required - secure_vector v = m_hash->final(); - copy_mem(key, v.data(), key_len); + const auto v = m_hash->final(); + copy_mem(key, std::span{v}.first(key.size())); } } diff --git a/src/lib/kdf/kdf1/kdf1.h b/src/lib/kdf/kdf1/kdf1.h index 8e84073a0d2..ab531eaa115 100644 --- a/src/lib/kdf/kdf1/kdf1.h +++ b/src/lib/kdf/kdf1/kdf1.h @@ -22,20 +22,17 @@ class KDF1 final : public KDF { std::unique_ptr new_object() const override; - void 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 override; - /** * @param hash function to use */ explicit KDF1(std::unique_ptr hash) : m_hash(std::move(hash)) {} + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; + private: std::unique_ptr m_hash; }; diff --git a/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp b/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp index 07068fc035e..bcc18b26f5d 100644 --- a/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp +++ b/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp @@ -1,6 +1,7 @@ /* * KDF1 from ISO 18033-2 * (C) 2016 Philipp Weber +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,41 +10,33 @@ #include #include +#include namespace Botan { -void KDF1_18033::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 { - if(key_len == 0) { +void KDF1_18033::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { return; } - const size_t blocks_required = key_len / m_hash->output_length(); + const size_t blocks_required = key.size() / m_hash->output_length(); + BOTAN_ARG_CHECK(blocks_required < 0xFFFFFFFF, "KDF1-18033 maximum output length exceeeded"); - if(blocks_required >= 0xFFFFFFFE) { - throw Invalid_Argument("KDF1-18033 maximum output length exceeeded"); - } - - uint32_t counter = 0; secure_vector h; - size_t offset = 0; - while(offset != key_len) { - m_hash->update(secret, secret_len); - m_hash->update_be(counter++); - m_hash->update(label, label_len); - m_hash->update(salt, salt_len); + BufferStuffer k(key); + for(uint32_t counter = 0; !k.full(); ++counter) { + m_hash->update(secret); + m_hash->update_be(counter); + m_hash->update(label); + m_hash->update(salt); m_hash->final(h); - const size_t added = std::min(h.size(), key_len - offset); - copy_mem(&key[offset], h.data(), added); - offset += added; + const auto bytes_to_write = std::min(h.size(), k.remaining_capacity()); + k.append(std::span{h}.first(bytes_to_write)); } } diff --git a/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h b/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h index 8e317c8aba1..7f8725513d4 100644 --- a/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h +++ b/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h @@ -22,20 +22,17 @@ class KDF1_18033 final : public KDF { std::unique_ptr new_object() const override; - void 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 override; - /** * @param hash function to use */ explicit KDF1_18033(std::unique_ptr hash) : m_hash(std::move(hash)) {} + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; + private: std::unique_ptr m_hash; }; diff --git a/src/lib/kdf/kdf2/kdf2.cpp b/src/lib/kdf/kdf2/kdf2.cpp index b562910fd68..cde627b5694 100644 --- a/src/lib/kdf/kdf2/kdf2.cpp +++ b/src/lib/kdf/kdf2/kdf2.cpp @@ -1,6 +1,7 @@ /* * KDF2 * (C) 1999-2007 Jack Lloyd +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #include #include +#include namespace Botan { @@ -20,41 +22,31 @@ std::unique_ptr KDF2::new_object() const { return std::make_unique(m_hash->new_object()); } -void KDF2::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 { - if(key_len == 0) { +void KDF2::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { return; } - const size_t blocks_required = key_len / m_hash->output_length(); + const size_t blocks_required = key.size() / m_hash->output_length(); + BOTAN_ARG_CHECK(blocks_required < 0xFFFFFFFF, "KDF2 maximum output length exceeeded"); - if(blocks_required >= 0xFFFFFFFE) { - throw Invalid_Argument("KDF2 maximum output length exceeeded"); - } - - uint32_t counter = 1; secure_vector h; - size_t offset = 0; - while(offset != key_len) { - m_hash->update(secret, secret_len); + BufferStuffer k(key); + for(uint32_t counter = 1; !k.full(); ++counter) { + BOTAN_ASSERT_NOMSG(counter != 0); // no overflow + + m_hash->update(secret); m_hash->update_be(counter); - m_hash->update(label, label_len); - m_hash->update(salt, salt_len); + m_hash->update(label); + m_hash->update(salt); m_hash->final(h); - const size_t added = std::min(h.size(), key_len - offset); - copy_mem(&key[offset], h.data(), added); - offset += added; - - counter += 1; - BOTAN_ASSERT_NOMSG(counter != 0); // no overflow + const auto bytes_to_write = std::min(h.size(), k.remaining_capacity()); + k.append(std::span{h}.first(bytes_to_write)); } } diff --git a/src/lib/kdf/kdf2/kdf2.h b/src/lib/kdf/kdf2/kdf2.h index ff7b41e11d1..a0f87184c51 100644 --- a/src/lib/kdf/kdf2/kdf2.h +++ b/src/lib/kdf/kdf2/kdf2.h @@ -22,20 +22,17 @@ class KDF2 final : public KDF { std::unique_ptr new_object() const override; - void 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 override; - /** * @param hash the hash function to use */ explicit KDF2(std::unique_ptr hash) : m_hash(std::move(hash)) {} + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; + private: std::unique_ptr m_hash; }; diff --git a/src/lib/kdf/prf_tls/prf_tls.cpp b/src/lib/kdf/prf_tls/prf_tls.cpp index bf8b6c1f996..cb205bc67cb 100644 --- a/src/lib/kdf/prf_tls/prf_tls.cpp +++ b/src/lib/kdf/prf_tls/prf_tls.cpp @@ -1,6 +1,7 @@ /* * TLSv1.2 PRF * (C) 2004-2010 Jack Lloyd +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #include #include +#include namespace Botan { @@ -17,34 +19,29 @@ namespace { /* * TLS PRF P_hash function */ -void P_hash(uint8_t out[], - size_t out_len, +void P_hash(std::span out, MessageAuthenticationCode& mac, - const uint8_t secret[], - size_t secret_len, - const uint8_t salt[], - size_t salt_len) { + std::span secret, + std::span salt) { try { - mac.set_key(secret, secret_len); + mac.set_key(secret); } catch(Invalid_Key_Length&) { - throw Internal_Error(fmt("The premaster secret of {} bytes is too long for TLS-PRF", secret_len)); + throw Internal_Error(fmt("The premaster secret of {} bytes is too long for TLS-PRF", secret.size())); } - secure_vector A(salt, salt + salt_len); + secure_vector A(salt.begin(), salt.end()); secure_vector h; - size_t offset = 0; - - while(offset != out_len) { + BufferStuffer o(out); + while(!o.full()) { A = mac.process(A); mac.update(A); - mac.update(salt, salt_len); + mac.update(salt); mac.final(h); - const size_t writing = std::min(h.size(), out_len - offset); - xor_buf(&out[offset], h.data(), writing); - offset += writing; + const size_t writing = std::min(h.size(), o.remaining_capacity()); + xor_buf(o.next(writing), std::span{h}.first(writing)); } } @@ -58,21 +55,12 @@ std::unique_ptr TLS_12_PRF::new_object() const { return std::make_unique(m_mac->new_object()); } -void TLS_12_PRF::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 { - secure_vector msg; - - msg.reserve(label_len + salt_len); - msg += std::make_pair(label, label_len); - msg += std::make_pair(salt, salt_len); - - P_hash(key, key_len, *m_mac, secret, secret_len, msg.data(), msg.size()); +void TLS_12_PRF::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + const auto msg = concat>(label, salt); + P_hash(key, *m_mac, secret, msg); } } // namespace Botan diff --git a/src/lib/kdf/prf_tls/prf_tls.h b/src/lib/kdf/prf_tls/prf_tls.h index 23d62c0a5fc..5544cad65b7 100644 --- a/src/lib/kdf/prf_tls/prf_tls.h +++ b/src/lib/kdf/prf_tls/prf_tls.h @@ -22,20 +22,17 @@ class TLS_12_PRF final : public KDF { std::unique_ptr new_object() const override; - void 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 override; - /** * @param mac MAC algorithm to use */ explicit TLS_12_PRF(std::unique_ptr mac) : m_mac(std::move(mac)) {} + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; + private: std::unique_ptr m_mac; }; diff --git a/src/lib/kdf/prf_x942/prf_x942.cpp b/src/lib/kdf/prf_x942/prf_x942.cpp index a8a51dc49c9..59c588d781b 100644 --- a/src/lib/kdf/prf_x942/prf_x942.cpp +++ b/src/lib/kdf/prf_x942/prf_x942.cpp @@ -1,6 +1,7 @@ /* * X9.42 PRF * (C) 1999-2007 Jack Lloyd +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,6 +11,7 @@ #include #include #include +#include #include namespace Botan { @@ -20,48 +22,35 @@ namespace { * Encode an integer as an OCTET STRING */ std::vector encode_x942_int(uint32_t n) { - uint8_t n_buf[4] = {0}; - store_be(n, n_buf); + const auto n_buf = store_be(n); std::vector output; - DER_Encoder(output).encode(n_buf, 4, ASN1_Type::OctetString); + DER_Encoder(output).encode(n_buf.data(), n_buf.size(), ASN1_Type::OctetString); return output; } } // namespace -void X942_PRF::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 { - if(key_len == 0) { +void X942_PRF::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { return; } - const size_t blocks_required = key_len / 20; // Fixed to use SHA-1 - - if(blocks_required >= 0xFFFFFFFE) { - throw Invalid_Argument("X942_PRF maximum output length exceeeded"); - } + constexpr size_t sha1_output_bytes = 20; // Fixed to use SHA-1 + const size_t blocks_required = key.size() / sha1_output_bytes; + BOTAN_ARG_CHECK(blocks_required < 0xFFFFFFFF, "X942_PRF maximum output length exceeeded"); auto hash = HashFunction::create("SHA-1"); + const auto in = concat>(label, salt); - secure_vector h; - secure_vector in; - size_t offset = 0; - uint32_t counter = 1; - - in.reserve(salt_len + label_len); - in += std::make_pair(label, label_len); - in += std::make_pair(salt, salt_len); - - while(offset != key_len && counter) { - hash->update(secret, secret_len); + BufferStuffer k(key); + for(uint32_t counter = 1; !k.full(); ++counter) { + BOTAN_ASSERT_NOMSG(counter != 0); // overflow check + hash->update(secret); hash->update( DER_Encoder() .start_sequence() @@ -71,22 +60,20 @@ void X942_PRF::kdf(uint8_t key[], .raw_bytes(encode_x942_int(counter)) .end_cons() - .encode_if(salt_len != 0, DER_Encoder().start_explicit(0).encode(in, ASN1_Type::OctetString).end_explicit()) + .encode_if(!salt.empty(), DER_Encoder().start_explicit(0).encode(in, ASN1_Type::OctetString).end_explicit()) .start_explicit(2) - .raw_bytes(encode_x942_int(static_cast(8 * key_len))) + .raw_bytes(encode_x942_int(static_cast(8 * key.size()))) .end_explicit() .end_cons() .get_contents()); + std::array h; hash->final(h); - const size_t copied = std::min(h.size(), key_len - offset); - copy_mem(&key[offset], h.data(), copied); - offset += copied; - ++counter; - BOTAN_ASSERT_NOMSG(counter != 0); + const size_t writing = std::min(h.size(), k.remaining_capacity()); + k.append(std::span{h}.first(writing)); } } diff --git a/src/lib/kdf/prf_x942/prf_x942.h b/src/lib/kdf/prf_x942/prf_x942.h index a98bdb0ba17..67e60982c9b 100644 --- a/src/lib/kdf/prf_x942/prf_x942.h +++ b/src/lib/kdf/prf_x942/prf_x942.h @@ -22,19 +22,16 @@ class X942_PRF final : public KDF { std::unique_ptr new_object() const override { return std::make_unique(m_key_wrap_oid); } - void 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 override; - explicit X942_PRF(std::string_view oid) : m_key_wrap_oid(OID::from_string(oid)) {} explicit X942_PRF(const OID& oid) : m_key_wrap_oid(oid) {} + private: + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; + private: OID m_key_wrap_oid; }; diff --git a/src/lib/kdf/sp800_108/sp800_108.cpp b/src/lib/kdf/sp800_108/sp800_108.cpp index 66dc902dc4d..3bdf4a659b2 100644 --- a/src/lib/kdf/sp800_108/sp800_108.cpp +++ b/src/lib/kdf/sp800_108/sp800_108.cpp @@ -1,6 +1,7 @@ /* * KDFs defined in NIST SP 800-108 * (C) 2016 Kai Michaelis +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,7 +11,7 @@ #include #include #include -#include +#include namespace Botan { @@ -22,51 +23,37 @@ std::unique_ptr SP800_108_Counter::new_object() const { return std::make_unique(m_prf->new_object()); } -void SP800_108_Counter::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 { - const std::size_t prf_len = m_prf->output_length(); - - const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len; - - if(blocks_required > 0xFFFFFFFF) { - throw Invalid_Argument("SP800_108_Counter output size too large"); +void SP800_108_Counter::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { + return; } - const uint8_t delim = 0; - const uint32_t length = static_cast(key_len * 8); + const auto length = static_cast(key.size() * 8); + const auto prf_len = m_prf->output_length(); - uint8_t* p = key; - uint32_t counter = 1; - uint8_t be_len[4] = {0}; - secure_vector tmp; + const uint64_t blocks_required = (key.size() + prf_len - 1) / prf_len; + BOTAN_ARG_CHECK(blocks_required < 0xFFFFFFFF, "SP800_108_Counter output size too large"); - store_be(length, be_len); - m_prf->set_key(secret, secret_len); + constexpr uint8_t delim = 0; - while(p < key + key_len) { - const std::size_t to_copy = std::min(key + key_len - p, prf_len); - uint8_t be_cnt[4] = {0}; - - store_be(counter, be_cnt); + BufferStuffer k(key); + m_prf->set_key(secret); + secure_vector tmp(prf_len); + for(uint32_t counter = 1; !k.full(); ++counter) { + BOTAN_ASSERT(counter != 0, "No counter overflow"); - m_prf->update(be_cnt, 4); - m_prf->update(label, label_len); + m_prf->update(store_be(counter)); + m_prf->update(label); m_prf->update(delim); - m_prf->update(salt, salt_len); - m_prf->update(be_len, 4); + m_prf->update(salt); + m_prf->update(store_be(length)); m_prf->final(tmp); - copy_mem(p, tmp.data(), to_copy); - p += to_copy; - - ++counter; - BOTAN_ASSERT(counter != 0, "No counter overflow"); + const auto bytes_to_write = std::min(tmp.size(), k.remaining_capacity()); + k.append(std::span{tmp}.first(bytes_to_write)); } } @@ -78,54 +65,42 @@ std::unique_ptr SP800_108_Feedback::new_object() const { return std::make_unique(m_prf->new_object()); } -void SP800_108_Feedback::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 { - const uint32_t length = static_cast(key_len * 8); - const std::size_t prf_len = m_prf->output_length(); - const std::size_t iv_len = (salt_len >= prf_len ? prf_len : 0); - const uint8_t delim = 0; - - const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len; - - if(blocks_required > 0xFFFFFFFF) { - throw Invalid_Argument("SP800_108_Feedback output size too large"); +void SP800_108_Feedback::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { + return; } - uint8_t* p = key; - uint32_t counter = 1; - uint8_t be_len[4] = {0}; - secure_vector prev(salt, salt + iv_len); - secure_vector ctx(salt + iv_len, salt + salt_len); + const auto length = static_cast(key.size() * 8); + const auto prf_len = m_prf->output_length(); + const auto iv_len = (salt.size() >= prf_len ? prf_len : 0); + constexpr uint8_t delim = 0; - store_be(length, be_len); - m_prf->set_key(secret, secret_len); + const uint64_t blocks_required = (key.size() + prf_len - 1) / prf_len; + BOTAN_ARG_CHECK(blocks_required < 0xFFFFFFFF, "SP800_108_Feedback output size too large"); - while(p < key + key_len) { - const std::size_t to_copy = std::min(key + key_len - p, prf_len); - uint8_t be_cnt[4] = {0}; + BufferSlicer s(salt); + auto prev = s.copy_as_secure_vector(iv_len); + const auto ctx = s.take(s.remaining()); + BOTAN_ASSERT_NOMSG(s.empty()); - store_be(counter, be_cnt); + BufferStuffer k(key); + m_prf->set_key(secret); + for(uint32_t counter = 1; !k.full(); ++counter) { + BOTAN_ASSERT(counter != 0, "No counter overflow"); m_prf->update(prev); - m_prf->update(be_cnt, 4); - m_prf->update(label, label_len); + m_prf->update(store_be(counter)); + m_prf->update(label); m_prf->update(delim); m_prf->update(ctx); - m_prf->update(be_len, 4); + m_prf->update(store_be(length)); m_prf->final(prev); - copy_mem(p, prev.data(), to_copy); - p += to_copy; - - ++counter; - - BOTAN_ASSERT(counter != 0, "No overflow"); + const auto bytes_to_write = std::min(prev.size(), k.remaining_capacity()); + k.append(std::span{prev}.first(bytes_to_write)); } } @@ -137,63 +112,46 @@ std::unique_ptr SP800_108_Pipeline::new_object() const { return std::make_unique(m_prf->new_object()); } -void SP800_108_Pipeline::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 { - const uint32_t length = static_cast(key_len * 8); - const std::size_t prf_len = m_prf->output_length(); - const uint8_t delim = 0; - - const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len; - - if(blocks_required > 0xFFFFFFFF) { - throw Invalid_Argument("SP800_108_Feedback output size too large"); +void SP800_108_Pipeline::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + if(key.empty()) { + return; } - uint8_t* p = key; - uint32_t counter = 1; - uint8_t be_len[4] = {0}; - secure_vector ai, ki; + const auto length = static_cast(key.size() * 8); + const auto prf_len = m_prf->output_length(); + constexpr uint8_t delim = 0; - store_be(length, be_len); - m_prf->set_key(secret, secret_len); + const uint64_t blocks_required = (key.size() + prf_len - 1) / prf_len; + BOTAN_ARG_CHECK(blocks_required < 0xFFFFFFFF, "SP800_108_Feedback output size too large"); // A(0) - std::copy(label, label + label_len, std::back_inserter(ai)); - ai.emplace_back(delim); - std::copy(salt, salt + salt_len, std::back_inserter(ai)); - std::copy(be_len, be_len + 4, std::back_inserter(ai)); + auto ai = concat>(label, store_be(delim), salt, store_be(length)); + secure_vector ki; + + BufferStuffer k(key); + m_prf->set_key(secret); + for(uint32_t counter = 1; !k.full(); ++counter) { + BOTAN_ASSERT(counter != 0, "No counter overflow"); - while(p < key + key_len) { // A(i) m_prf->update(ai); m_prf->final(ai); // K(i) - const std::size_t to_copy = std::min(key + key_len - p, prf_len); - uint8_t be_cnt[4] = {0}; - - store_be(counter, be_cnt); - m_prf->update(ai); - m_prf->update(be_cnt, 4); - m_prf->update(label, label_len); + m_prf->update(store_be(counter)); + m_prf->update(label); m_prf->update(delim); - m_prf->update(salt, salt_len); - m_prf->update(be_len, 4); + m_prf->update(salt); + m_prf->update(store_be(length)); m_prf->final(ki); - copy_mem(p, ki.data(), to_copy); - p += to_copy; - - ++counter; - - BOTAN_ASSERT(counter != 0, "No overflow"); + const auto bytes_to_write = std::min(ki.size(), k.remaining_capacity()); + k.append(std::span{ki}.first(bytes_to_write)); } } + } // namespace Botan diff --git a/src/lib/kdf/sp800_108/sp800_108.h b/src/lib/kdf/sp800_108/sp800_108.h index f428f983020..9476d8fe2c7 100644 --- a/src/lib/kdf/sp800_108/sp800_108.h +++ b/src/lib/kdf/sp800_108/sp800_108.h @@ -22,6 +22,12 @@ class SP800_108_Counter final : public KDF { std::unique_ptr new_object() const override; + /** + * @param mac MAC algorithm to use + */ + explicit SP800_108_Counter(std::unique_ptr mac) : m_prf(std::move(mac)) {} + + private: /** * Derive a key using the SP800-108 KDF in Counter mode. * @@ -29,29 +35,16 @@ class SP800_108_Counter final : public KDF { * and [i]_2 (the value r) to 32 bits. * * @param key resulting keying material - * @param key_len the desired output length in bytes * @param secret K_I - * @param secret_len size of K_I in bytes * @param salt Context - * @param salt_len size of Context in bytes * @param label Label - * @param label_len size of Label in bytes * * @throws Invalid_Argument key_len > 2^32 */ - void 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 override; - - /** - * @param mac MAC algorithm to use - */ - explicit SP800_108_Counter(std::unique_ptr mac) : m_prf(std::move(mac)) {} + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; @@ -66,6 +59,9 @@ class SP800_108_Feedback final : public KDF { std::unique_ptr new_object() const override; + explicit SP800_108_Feedback(std::unique_ptr mac) : m_prf(std::move(mac)) {} + + private: /** * Derive a key using the SP800-108 KDF in Feedback mode. * @@ -73,26 +69,16 @@ class SP800_108_Feedback final : public KDF { * codes the length of [L]_2 and [i]_2 (the value r) to 32 bits. * * @param key resulting keying material - * @param key_len the desired output length in bytes * @param secret K_I - * @param secret_len size of K_I in bytes * @param salt IV || Context - * @param salt_len size of Context plus IV in bytes * @param label Label - * @param label_len size of Label in bytes * * @throws Invalid_Argument key_len > 2^32 */ - void 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 override; - - explicit SP800_108_Feedback(std::unique_ptr mac) : m_prf(std::move(mac)) {} + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; @@ -107,6 +93,9 @@ class SP800_108_Pipeline final : public KDF { std::unique_ptr new_object() const override; + explicit SP800_108_Pipeline(std::unique_ptr mac) : m_prf(std::move(mac)) {} + + private: /** * Derive a key using the SP800-108 KDF in Double Pipeline mode. * @@ -114,26 +103,16 @@ class SP800_108_Pipeline final : public KDF { * codes the length of [L]_2 and [i]_2 (the value r) to 32 bits. * * @param key resulting keying material - * @param key_len the desired output length in bytes * @param secret K_I - * @param secret_len size of K_I in bytes * @param salt Context - * @param salt_len size of Context in bytes * @param label Label - * @param label_len size of Label in bytes * * @throws Invalid_Argument key_len > 2^32 */ - void 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 override; - - explicit SP800_108_Pipeline(std::unique_ptr mac) : m_prf(std::move(mac)) {} + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; diff --git a/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp b/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp index 087f9baaa45..fbea406679c 100644 --- a/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp +++ b/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp @@ -4,6 +4,7 @@ * * (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski. * (C) 2024 Fabian Albert - Rohde & Schwarz Cybersecurity +* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -82,19 +83,12 @@ void kdm_internal(std::span output_buffer, } // 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, [](HashFunction&) { /* NOP */ }); +void SP800_56C_One_Step_Hash::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + BOTAN_ARG_CHECK(salt.empty(), "SP800_56A_Hash does not support a non-empty salt"); + kdm_internal(key, secret, label, *m_hash, [](HashFunction&) { /* NOP */ }); } std::string SP800_56C_One_Step_Hash::name() const { @@ -113,26 +107,21 @@ SP800_56C_One_Step_HMAC::SP800_56C_One_Step_HMAC(std::unique_ptr( - {key, key_len}, {secret, secret_len}, {label, label_len}, *m_mac, [&](MessageAuthenticationCode& kdf_mac) { - // 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# key .. - - // SP 800-56Cr2 specifies if the salt is empty then a block of zeros - // equal to the hash's underlying block size are used. However for HMAC - // 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}); - }); +void SP800_56C_One_Step_HMAC::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + kdm_internal(key, secret, label, *m_mac, [&](MessageAuthenticationCode& kdf_mac) { + // 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# key .. + + // SP 800-56Cr2 specifies if the salt is empty then a block of zeros + // equal to the hash's underlying block size are used. However for HMAC + // this is equivalent to setting a zero-length key, so the same call + // works for either case. + kdf_mac.set_key(salt); + }); } std::string SP800_56C_One_Step_HMAC::name() const { @@ -144,36 +133,31 @@ std::unique_ptr SP800_56C_One_Step_HMAC::new_object() const { } // 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, [&](MessageAuthenticationCode& kdf_mac) { - // 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 KMAC# key ... - if(salt_len == 0) { - // 4.1 Implementation-Dependent Parameters 3 - // If H(x) = KMAC128[or 256](salt, x, H_outputBits, "KDF"), - // then – in the absence of an agreed-upon alternative – - // the default_salt shall be an all - zero string of - // 164 bytes [or 132 bytes] - kdf_mac.set_key(std::vector(default_salt_length(), 0)); - } else { - 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{'K', 'D', 'F'}); - }); +void SP800_56A_One_Step_KMAC_Abstract::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { + auto mac = create_kmac_instance(key.size()); + kdm_internal(key, secret, label, *mac, [&](MessageAuthenticationCode& kdf_mac) { + // 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 KMAC# key ... + if(salt.empty()) { + // 4.1 Implementation-Dependent Parameters 3 + // If H(x) = KMAC128[or 256](salt, x, H_outputBits, "KDF"), + // then – in the absence of an agreed-upon alternative – + // the default_salt shall be an all - zero string of + // 164 bytes [or 132 bytes] + kdf_mac.set_key(std::vector(default_salt_length(), 0)); + } else { + kdf_mac.set_key(salt); + } + + // 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{'K', 'D', 'F'}); + }); } std::unique_ptr SP800_56C_One_Step_KMAC128::create_kmac_instance( diff --git a/src/lib/kdf/sp800_56a/sp800_56c_one_step.h b/src/lib/kdf/sp800_56a/sp800_56c_one_step.h index 5924966c975..7626f271dc5 100644 --- a/src/lib/kdf/sp800_56a/sp800_56c_one_step.h +++ b/src/lib/kdf/sp800_56a/sp800_56c_one_step.h @@ -27,34 +27,27 @@ class SP800_56C_One_Step_Hash final : public KDF { std::unique_ptr new_object() const override; + /** + * @param hash the hash function to use as the auxiliary function + */ + explicit SP800_56C_One_Step_Hash(std::unique_ptr hash) : m_hash(std::move(hash)) {} + + private: /** * Derive a key using the SP800-56Cr2 One-Step KDF. * * @param key DerivedKeyingMaterial output buffer - * @param key_len the desired output length in bytes * @param secret shared secret Z - * @param secret_len size of Z in bytes * @param salt the salt. Ignored. - * @param salt_len size of salt in bytes. Must be 0. * @param label FixedInfo - * @param label_len size of label in bytes * * @throws Invalid_Argument if key_len > (2^32 - 1) * Hash output bits. * Or thrown if salt is non-empty */ - void 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 override; - - /** - * @param hash the hash function to use as the auxiliary function - */ - explicit SP800_56C_One_Step_Hash(std::unique_ptr hash) : m_hash(std::move(hash)) {} + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_hash; @@ -69,33 +62,26 @@ class SP800_56C_One_Step_HMAC final : public KDF { std::unique_ptr new_object() const override; + /** + * @param mac the HMAC to use as the auxiliary function + */ + explicit SP800_56C_One_Step_HMAC(std::unique_ptr mac); + + private: /** * Derive a key using the SP800-56Cr2 One-Step KDF. * * @param key DerivedKeyingMaterial output buffer - * @param key_len the desired output length in bytes * @param secret shared secret Z - * @param secret_len size of Z in bytes * @param salt the salt. If empty the default_salt is used. - * @param salt_len size of salt in bytes * @param label FixedInfo - * @param label_len size of label in bytes * * @throws Invalid_Argument if key_len > (2^32 - 1) * HMAC output bits */ - void 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 override; - - /** - * @param mac the HMAC to use as the auxiliary function - */ - explicit SP800_56C_One_Step_HMAC(std::unique_ptr mac); + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_mac; @@ -105,29 +91,21 @@ class SP800_56C_One_Step_HMAC final : public KDF { * NIST SP800-56Cr2 One-Step KDF using KMAC (Abstract class) */ class SP800_56A_One_Step_KMAC_Abstract : public KDF { - public: + private: /** * Derive a key using the SP800-56Cr2 One-Step KDF. * * @param key DerivedKeyingMaterial output buffer - * @param key_len the desired output length in bytes * @param secret shared secret Z - * @param secret_len size of Z in bytes * @param salt the salt. If empty the default_salt is used. - * @param salt_len size of salt in bytes * @param label FixedInfo - * @param label_len size of label in bytes * * @throws Invalid_Argument if key_len > (2^32 - 1) * KMAC output bits */ - void 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 override; + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const final; protected: virtual std::unique_ptr create_kmac_instance(size_t output_byte_len) const = 0; diff --git a/src/lib/kdf/sp800_56c/sp800_56c_two_step.cpp b/src/lib/kdf/sp800_56c/sp800_56c_two_step.cpp index 1a1bf4008dc..7b6a2a0c55a 100644 --- a/src/lib/kdf/sp800_56c/sp800_56c_two_step.cpp +++ b/src/lib/kdf/sp800_56c/sp800_56c_two_step.cpp @@ -1,6 +1,7 @@ /* * Two-Step KDF defined in NIST SP 800-56Cr2 (Section 5) * (C) 2016 Kai Michaelis +* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,23 +20,17 @@ std::unique_ptr SP800_56C_Two_Step::new_object() const { return std::make_unique(m_prf->new_object(), m_exp->new_object()); } -void SP800_56C_Two_Step::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 { +void SP800_56C_Two_Step::perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const { // Randomness Extraction - secure_vector k_dk; - - m_prf->set_key(salt, salt_len); - m_prf->update(secret, secret_len); - m_prf->final(k_dk); + m_prf->set_key(salt); + m_prf->update(secret); + const auto k_dk = m_prf->final(); // Key Expansion - m_exp->kdf(key, key_len, k_dk.data(), k_dk.size(), nullptr, 0, label, label_len); + m_exp->derive_key(key, k_dk, {} /* no salt */, label); } } // namespace Botan diff --git a/src/lib/kdf/sp800_56c/sp800_56c_two_step.h b/src/lib/kdf/sp800_56c/sp800_56c_two_step.h index 99da9238def..8c146571b8f 100644 --- a/src/lib/kdf/sp800_56c/sp800_56c_two_step.h +++ b/src/lib/kdf/sp800_56c/sp800_56c_two_step.h @@ -22,6 +22,14 @@ class SP800_56C_Two_Step final : public KDF { std::unique_ptr new_object() const override; + /** + * @param mac MAC algorithm used for randomness extraction + * @param exp KDF used for key expansion + */ + SP800_56C_Two_Step(std::unique_ptr mac, std::unique_ptr exp) : + m_prf(std::move(mac)), m_exp(std::move(exp)) {} + + private: /** * Derive a key using the SP800-56C Two-Step KDF. * @@ -29,29 +37,14 @@ class SP800_56C_Two_Step final : public KDF { * expansion step to the empty string. * * @param key derived keying material K_M - * @param key_len the desired output length in bytes * @param secret shared secret Z - * @param secret_len size of Z in bytes * @param salt salt s of the extraction step - * @param salt_len size of s in bytes * @param label label for the expansion step - * @param label_len size of label in bytes */ - void 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 override; - - /** - * @param mac MAC algorithm used for randomness extraction - * @param exp KDF used for key expansion - */ - SP800_56C_Two_Step(std::unique_ptr mac, std::unique_ptr exp) : - m_prf(std::move(mac)), m_exp(std::move(exp)) {} + void perform_kdf(std::span key, + std::span secret, + std::span salt, + std::span label) const override; private: std::unique_ptr m_prf; diff --git a/src/tests/test_kdf.cpp b/src/tests/test_kdf.cpp index e97d6c849a7..35f5add8f84 100644 --- a/src/tests/test_kdf.cpp +++ b/src/tests/test_kdf.cpp @@ -75,8 +75,8 @@ class HKDF_Expand_Label_Tests final : public Text_Based_Test { return result; } - Botan::secure_vector output = Botan::hkdf_expand_label( - hash_name, secret.data(), secret.size(), label, hashval.data(), hashval.size(), expected.size()); + Botan::secure_vector output = + Botan::hkdf_expand_label(hash_name, secret, label, hashval, expected.size()); result.test_eq("Output matches", output, expected); From 29fc82f7b01ec4a6e01b4243e919b762d609b98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Tue, 3 Dec 2024 08:55:45 +0100 Subject: [PATCH 2/4] Add KDF::derive_key() producing a std::array<> --- src/lib/kdf/kdf.h | 49 ++++++++++++++++++++++++++++++++++++++++++ src/tests/test_kdf.cpp | 5 +++++ 2 files changed, 54 insertions(+) diff --git a/src/lib/kdf/kdf.h b/src/lib/kdf/kdf.h index 89cb841a74a..15a97e914bc 100644 --- a/src/lib/kdf/kdf.h +++ b/src/lib/kdf/kdf.h @@ -186,6 +186,55 @@ class BOTAN_PUBLIC_API(2, 0) KDF { {cast_char_ptr_to_uint8(label.data()), label.length()}); } + /** + * Derive a key + * @tparam key_len the desired output length in bytes + * @param secret the secret input + * @param salt a diversifier + * @param label purpose for the derived keying material + * @return the derived key + */ + template + std::array derive_key(std::span secret, + std::span salt = {}, + std::span label = {}) { + std::array key; + perform_kdf(key, secret, salt, label); + return key; + } + + /** + * Derive a key + * @tparam key_len the desired output length in bytes + * @param secret the secret input + * @param salt a diversifier + * @param label purpose for the derived keying material + * @return the derived key + */ + template + std::array derive_key(std::span secret, + std::span salt = {}, + std::string_view label = "") { + return derive_key(secret, salt, {cast_char_ptr_to_uint8(label.data()), label.size()}); + } + + /** + * Derive a key + * @tparam key_len the desired output length in bytes + * @param secret the secret input + * @param salt a diversifier + * @param label purpose for the derived keying material + * @return the derived key + */ + template + std::array derive_key(std::span secret, + std::string_view salt = "", + std::string_view label = "") { + return derive_key(secret, + {cast_char_ptr_to_uint8(salt.data()), salt.size()}, + {cast_char_ptr_to_uint8(label.data()), label.size()}); + } + /** * @return new object representing the same algorithm as *this */ diff --git a/src/tests/test_kdf.cpp b/src/tests/test_kdf.cpp index 35f5add8f84..b70c55cdf2b 100644 --- a/src/tests/test_kdf.cpp +++ b/src/tests/test_kdf.cpp @@ -42,6 +42,11 @@ class KDF_KAT_Tests final : public Text_Based_Test { result.test_eq("name", kdf->name(), kdf_name); result.test_eq("derived key", kdf->derive_key(expected.size(), secret, salt, label), expected); + if(expected.size() == 32) { + const auto key = kdf->derive_key<32>(secret, salt, label); + result.test_eq("derived key as array", Botan::secure_vector{key.begin(), key.end()}, expected); + } + // Test that clone works auto clone = kdf->new_object(); result.confirm("Clone has different pointer", kdf.get() != clone.get()); From 321404242bef697dde5d52dd277b27e6a1c9656b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Tue, 3 Dec 2024 09:00:06 +0100 Subject: [PATCH 3/4] Deprecate uint8_t* based KDF APIs --- src/lib/kdf/kdf.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/kdf/kdf.h b/src/lib/kdf/kdf.h index 15a97e914bc..3fce79b43c9 100644 --- a/src/lib/kdf/kdf.h +++ b/src/lib/kdf/kdf.h @@ -63,6 +63,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { * @param label purpose for the derived keying material * @param label_len size of label in bytes */ + BOTAN_DEPRECATED("Use KDF::derive_key") void kdf(uint8_t key[], size_t key_len, const uint8_t secret[], @@ -86,6 +87,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { * @return the derived key */ template > + BOTAN_DEPRECATED("Use std::span or std::string_view overloads") T derive_key(size_t key_len, const uint8_t secret[], size_t secret_len, @@ -157,6 +159,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { * @return the derived key */ template > + BOTAN_DEPRECATED("Use std::span or std::string_view overloads") T derive_key(size_t key_len, std::span secret, const uint8_t salt[], @@ -175,6 +178,7 @@ class BOTAN_PUBLIC_API(2, 0) KDF { * @return the derived key */ template > + BOTAN_DEPRECATED("Use std::span or std::string_view overloads") T derive_key(size_t key_len, const uint8_t secret[], size_t secret_len, From f57796ea65ce843621062572daa86f1f25753fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Tue, 3 Dec 2024 09:17:38 +0100 Subject: [PATCH 4/4] Update documentation of KDF::derive_key overloads --- doc/api_ref/kdf.rst | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/doc/api_ref/kdf.rst b/doc/api_ref/kdf.rst index fd5df1c5d73..495e8a08d6a 100644 --- a/doc/api_ref/kdf.rst +++ b/doc/api_ref/kdf.rst @@ -34,36 +34,32 @@ two contexts. Create a new KDF object. Throws an exception if the named key derivation function was not available + .. cpp:function:: void KDF::derive_key(std::span key, \ + std::span secret, \ + std::span salt, \ + std::span label) const + + Performs a key derivation using ``secret`` as secret input, and ``salt``, + and ``label`` as deversifiers. The passed ``key`` buffer is fully filled + with key material derived from the inputs. + .. cpp:function:: template> \ - T derive_key(size_t key_len, \ - std::span secret, \ - std::span salt, \ - std::span label) const + T KDF::derive_key(size_t key_len, \ + std::span secret, \ + std::span salt, \ + std::span label) const This version is parameterized to the output buffer type, so it can be used to return a ``std::vector``, a ``secure_vector``, or anything else satisfying the ``resizable_byte_buffer`` concept. - .. cpp:function:: secure_vector derive_key( \ - 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 - - .. cpp:function:: secure_vector derive_key( \ - size_t key_len, const std::vector& secret, \ - const std::vector& salt, \ - const std::vector& label) const - - .. cpp:function:: secure_vector derive_key( \ - size_t key_len, const std::vector& secret, \ - const uint8_t* salt, size_t salt_len) const - - .. cpp:function:: secure_vector derive_key( \ - size_t key_len, const uint8_t* secret, size_t secret_len, \ - const std::string& salt) const + .. cpp:function:: template \ + std::array KDF::derive_key(std::span secret, \ + std::span salt, \ + std::span label) const + + This version returns the key material as a std::array<> of ``key_len`` + bytes. All variations on the same theme. Deterministically creates a uniform random value from *secret*, *salt*, and *label*, whose