Skip to content

Commit

Permalink
Implement SHAKE_128/256_Cipher in terms of SHAKE_XOF
Browse files Browse the repository at this point in the history
  • Loading branch information
reneme committed Aug 16, 2023
1 parent b1d555a commit 7f1b216
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 162 deletions.
2 changes: 1 addition & 1 deletion src/lib/stream/shake_cipher/info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ brief -> "SHAKE-128 and SHAKE-256 XOF presented as stream ciphers"
</module_info>

<requires>
sha3
shake_xof
</requires>
122 changes: 0 additions & 122 deletions src/lib/stream/shake_cipher/shake_cipher.cpp

This file was deleted.

129 changes: 90 additions & 39 deletions src/lib/stream/shake_cipher/shake_cipher.h
Original file line number Diff line number Diff line change
@@ -1,77 +1,128 @@
/*
* 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)
*/

#ifndef BOTAN_SHAKE_CIPHER_H_
#define BOTAN_SHAKE_CIPHER_H_

#include <botan/secmem.h>
#include <botan/exceptn.h>
#include <botan/stream_cipher.h>
#include <botan/internal/shake_xof.h>

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 <typename XofT, typename DerivedT>
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<StreamCipher> new_object() const override { return std::make_unique<DerivedT>(); }

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<uint8_t> 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<uint64_t> m_state; // internal state
secure_vector<uint8_t> m_buffer; // ciphertext buffer
size_t m_buf_pos; // position in m_buffer
private:
XofT m_xof;
secure_vector<uint8_t> 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<StreamCipher> new_object() const override { return std::make_unique<SHAKE_128_Cipher>(); }
class SHAKE_128_Cipher final : public detail::XOF_Stream_Cipher_Adapter<SHAKE_128_XOF, SHAKE_128_Cipher> {
public:
using detail::XOF_Stream_Cipher_Adapter<SHAKE_128_XOF, SHAKE_128_Cipher>::XOF_Stream_Cipher_Adapter;
};

class SHAKE_256_Cipher final : public SHAKE_Cipher {
class SHAKE_256_Cipher final : public detail::XOF_Stream_Cipher_Adapter<SHAKE_256_XOF, SHAKE_256_Cipher> {
public:
SHAKE_256_Cipher();

std::string name() const override { return "SHAKE-256"; }

std::unique_ptr<StreamCipher> new_object() const override { return std::make_unique<SHAKE_256_Cipher>(); }
using detail::XOF_Stream_Cipher_Adapter<SHAKE_256_XOF, SHAKE_256_Cipher>::XOF_Stream_Cipher_Adapter;
};

} // namespace Botan
Expand Down

0 comments on commit 7f1b216

Please sign in to comment.