From 7f1b2160a5308399afbb4559bf64832e2e964d77 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 15 Aug 2023 17:38:48 +0200 Subject: [PATCH] Implement SHAKE_128/256_Cipher in terms of SHAKE_XOF --- src/lib/stream/shake_cipher/info.txt | 2 +- src/lib/stream/shake_cipher/shake_cipher.cpp | 122 ------------------ src/lib/stream/shake_cipher/shake_cipher.h | 129 +++++++++++++------ 3 files changed, 91 insertions(+), 162 deletions(-) delete mode 100644 src/lib/stream/shake_cipher/shake_cipher.cpp diff --git a/src/lib/stream/shake_cipher/info.txt b/src/lib/stream/shake_cipher/info.txt index 99f9516aa5d..ddbecad3dea 100644 --- a/src/lib/stream/shake_cipher/info.txt +++ b/src/lib/stream/shake_cipher/info.txt @@ -8,5 +8,5 @@ brief -> "SHAKE-128 and SHAKE-256 XOF presented as stream ciphers" -sha3 +shake_xof diff --git a/src/lib/stream/shake_cipher/shake_cipher.cpp b/src/lib/stream/shake_cipher/shake_cipher.cpp deleted file mode 100644 index 403c41c87c2..00000000000 --- a/src/lib/stream/shake_cipher/shake_cipher.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * SHAKE-128 and SHAKE-256 - * (C) 2016 Jack Lloyd - * 2022 René Meusel, Michael Boric - Rohde & Schwarz Cybersecurity - * - * Botan is released under the Simplified BSD License (see license.txt) - */ - -#include - -#include -#include -#include - -namespace Botan { - -SHAKE_Cipher::SHAKE_Cipher(size_t shake_rate) : m_shake_rate(shake_rate), m_buf_pos(0) { - BOTAN_ASSERT_NOMSG(shake_rate >= 72 && shake_rate <= 168); -} - -void SHAKE_Cipher::clear() { - zap(m_state); - zap(m_buffer); - m_buf_pos = 0; -} - -void SHAKE_Cipher::set_iv_bytes(const uint8_t /*iv*/[], size_t length) { - /* - * This could be supported in some way (say, by treating iv as - * a prefix or suffix of the key). - */ - if(length != 0) { - throw Invalid_IV_Length(name(), length); - } -} - -size_t SHAKE_Cipher::buffer_size() const { - return m_shake_rate; -} - -void SHAKE_Cipher::seek(uint64_t /*offset*/) { - throw Not_Implemented("SHAKE_Cipher::seek"); -} - -void SHAKE_Cipher::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) { - assert_key_material_set(); - - while(length >= m_shake_rate - m_buf_pos) { - xor_buf(out, in, &m_buffer[m_buf_pos], m_shake_rate - m_buf_pos); - length -= (m_shake_rate - m_buf_pos); - in += (m_shake_rate - m_buf_pos); - out += (m_shake_rate - m_buf_pos); - - SHA_3::permute(m_state.data()); - copy_out_le(m_buffer.data(), m_shake_rate, m_state.data()); - - m_buf_pos = 0; - } - xor_buf(out, in, &m_buffer[m_buf_pos], length); - m_buf_pos += length; -} - -void SHAKE_Cipher::generate_keystream(uint8_t out[], size_t length) { - assert_key_material_set(); - - if(m_buf_pos > 0) { - const size_t take = std::min(length, m_shake_rate - m_buf_pos); - copy_mem(out, &m_buffer[m_buf_pos], take); - out += take; - length -= take; - m_buf_pos += take; - - if(m_buf_pos == m_shake_rate) { - SHA_3::permute(m_state.data()); - m_buf_pos = 0; - } - } - - if(length == 0) { - return; - } - - BOTAN_ASSERT_NOMSG(m_buf_pos == 0); - - while(length >= m_shake_rate) { - copy_out_le(out, m_shake_rate, m_state.data()); - SHA_3::permute(m_state.data()); - length -= m_shake_rate; - out += m_shake_rate; - } - - copy_out_le(m_buffer.data(), m_shake_rate, m_state.data()); - - copy_mem(out, &m_buffer[0], length); - m_buf_pos += length; -} - -bool SHAKE_Cipher::has_keying_material() const { - return !m_state.empty(); -} - -void SHAKE_Cipher::key_schedule(const uint8_t key[], size_t length) { - const size_t SHAKE_BITRATE = m_shake_rate * 8; - m_state.resize(25); - m_buffer.resize(m_shake_rate); - zeroise(m_state); - - const size_t S_pos = SHA_3::absorb(SHAKE_BITRATE, m_state, 0, key, length); - SHA_3::finish(SHAKE_BITRATE, m_state, S_pos, 0x1F, 0x80); - copy_out_le(m_buffer.data(), m_buffer.size(), m_state.data()); - m_buf_pos = 0; -} - -Key_Length_Specification SHAKE_Cipher::key_spec() const { - return Key_Length_Specification(1, 160); -} - -SHAKE_128_Cipher::SHAKE_128_Cipher() : SHAKE_Cipher((1600 - 256) / 8) {} - -SHAKE_256_Cipher::SHAKE_256_Cipher() : SHAKE_Cipher((1600 - 512) / 8) {} - -} // namespace Botan diff --git a/src/lib/stream/shake_cipher/shake_cipher.h b/src/lib/stream/shake_cipher/shake_cipher.h index 86ba2986d06..cbf5e18d8fb 100644 --- a/src/lib/stream/shake_cipher/shake_cipher.h +++ b/src/lib/stream/shake_cipher/shake_cipher.h @@ -1,7 +1,7 @@ /* * SHAKE-128 and SHAKE-256 as a stream ciphers * (C) 2016 Jack Lloyd - * 2022 René Meusel, Michael Boric - Rohde & Schwarz Cybersecurity + * 2022, 2023 René Meusel, Michael Boric - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,69 +9,120 @@ #ifndef BOTAN_SHAKE_CIPHER_H_ #define BOTAN_SHAKE_CIPHER_H_ -#include +#include #include +#include namespace Botan { +namespace detail { + /** * Base class for SHAKE-based XOFs presented as a stream cipher +* +* TODO: This class could potentially act as a generic adapter to +* use arbitrary XOFs as a basis for stream ciphers. */ -class SHAKE_Cipher : public StreamCipher { - protected: - explicit SHAKE_Cipher(size_t shake_rate); +template +class XOF_Stream_Cipher_Adapter : public StreamCipher { public: - /** - * Seeking is not supported, this function will throw - */ - void seek(uint64_t offset) final; + XOF_Stream_Cipher_Adapter() : m_keystream_buffer(buffer_size()), m_bytes_generated(0) {} + + void seek(uint64_t) final { throw Not_Implemented("SHAKE_Cipher::seek"); } + + void clear() final { + m_xof.clear(); + zeroise(m_keystream_buffer); + m_bytes_generated = 0; + } - void clear() final; + Key_Length_Specification key_spec() const final { return Key_Length_Specification(1, 160); } - Key_Length_Specification key_spec() const final; + bool has_keying_material() const final { return !m_xof.accepts_input(); } - bool has_keying_material() const final; + size_t buffer_size() const final { return m_xof.block_size(); } - size_t buffer_size() const final; + std::string name() const override { return m_xof.name(); } + + std::unique_ptr new_object() const override { return std::make_unique(); } private: - void key_schedule(const uint8_t key[], size_t key_len) final; - /** - * Produce more XOF output - */ - void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) final; - void generate_keystream(uint8_t out[], size_t length) override; + void key_schedule(const uint8_t key[], size_t key_len) final { + m_xof.clear(); + m_xof.update({key, key_len}); + m_xof.output({}); // empty output request to transition the XOF to output state + } /** - * IV not supported, this function will throw unless iv_len == 0 + * Produce more XOF output */ - void set_iv_bytes(const uint8_t iv[], size_t iv_len) final; - - protected: - size_t m_shake_rate; + void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) final { + assert_key_material_set(); + + const auto block_size = m_keystream_buffer.size(); + + auto cipher_some = [&](size_t bytes) { + if(bytes > 0) { + BOTAN_ASSERT_NOMSG(bytes <= block_size); + BOTAN_ASSERT_NOMSG(bytes <= length); + generate_keystream_internal(std::span(m_keystream_buffer).first(bytes)); + xor_buf(out, m_keystream_buffer.data(), in, bytes); + out += bytes; + in += bytes; + length -= bytes; + } + }; + + // Bring us back into alignment with the XOF's underlying blocks + if(length > block_size) { + const auto bytes_to_alignment = block_size - m_bytes_generated % block_size; + cipher_some(bytes_to_alignment); + } + + // Consume the XOF's output stream block-wise as long as we can + while(length >= block_size) { + cipher_some(block_size); + } + + // Process remaining data, potentially causing misalignment + cipher_some(length); + } + + void generate_keystream(uint8_t out[], size_t length) override { + assert_key_material_set(); + generate_keystream_internal({out, length}); + } + + void generate_keystream_internal(std::span out) { + m_xof.output(out); + m_bytes_generated += out.size(); + } + + void set_iv_bytes(const uint8_t[], size_t iv_len) final { + // This could be supported in some way (say, by treating iv as a prefix + // or suffix of the key). + if(iv_len != 0) { + throw Invalid_IV_Length(name(), iv_len); + } + } - secure_vector m_state; // internal state - secure_vector m_buffer; // ciphertext buffer - size_t m_buf_pos; // position in m_buffer + private: + XofT m_xof; + secure_vector m_keystream_buffer; + size_t m_bytes_generated; }; -class SHAKE_128_Cipher final : public SHAKE_Cipher { - public: - SHAKE_128_Cipher(); - - std::string name() const override { return "SHAKE-128"; } +} // namespace detail - std::unique_ptr new_object() const override { return std::make_unique(); } +class SHAKE_128_Cipher final : public detail::XOF_Stream_Cipher_Adapter { + public: + using detail::XOF_Stream_Cipher_Adapter::XOF_Stream_Cipher_Adapter; }; -class SHAKE_256_Cipher final : public SHAKE_Cipher { +class SHAKE_256_Cipher final : public detail::XOF_Stream_Cipher_Adapter { public: - SHAKE_256_Cipher(); - - std::string name() const override { return "SHAKE-256"; } - - std::unique_ptr new_object() const override { return std::make_unique(); } + using detail::XOF_Stream_Cipher_Adapter::XOF_Stream_Cipher_Adapter; }; } // namespace Botan