Skip to content

Commit

Permalink
WIP: adapt SHAKE cipher
Browse files Browse the repository at this point in the history
  • Loading branch information
reneme committed Aug 18, 2023
1 parent 8cb93a8 commit 9d16394
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 78 deletions.
112 changes: 44 additions & 68 deletions src/lib/stream/shake_cipher/shake_cipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@
#include <botan/internal/shake_cipher.h>

#include <botan/exceptn.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/sha3.h>

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;
}
SHAKE_Cipher::SHAKE_Cipher(size_t keccak_capacity) :
m_keccak(keccak_capacity, 0xF, 4),
m_has_keying_material(false),
m_keystream_buffer(buffer_size()),
m_bytes_generated(0) {}

void SHAKE_Cipher::set_iv_bytes(const uint8_t /*iv*/[], size_t length) {
/*
Expand All @@ -34,89 +28,71 @@ void SHAKE_Cipher::set_iv_bytes(const uint8_t /*iv*/[], size_t 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);

Keccak_Permutation::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::clear() {
m_keccak.clear();
m_has_keying_material = false;
zeroise(m_keystream_buffer);
m_bytes_generated = 0;
}

void SHAKE_Cipher::generate_keystream(uint8_t out[], size_t length) {
void SHAKE_Cipher::cipher_bytes(const uint8_t in[], 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) {
Keccak_Permutation::permute(m_state.data());
m_buf_pos = 0;
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;
}
}
};

if(length == 0) {
return;
// 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);
}

BOTAN_ASSERT_NOMSG(m_buf_pos == 0);

while(length >= m_shake_rate) {
copy_out_le(out, m_shake_rate, m_state.data());
Keccak_Permutation::permute(m_state.data());
length -= m_shake_rate;
out += m_shake_rate;
// Consume the XOF's output stream block-wise as long as we can
while(length >= block_size) {
cipher_some(block_size);
}

copy_out_le(m_buffer.data(), m_shake_rate, m_state.data());
// Process remaining data, potentially causing misalignment
cipher_some(length);
}

copy_mem(out, &m_buffer[0], length);
m_buf_pos += length;
void SHAKE_Cipher::generate_keystream(uint8_t out[], size_t length) {
assert_key_material_set();
generate_keystream_internal({out, length});
}

bool SHAKE_Cipher::has_keying_material() const {
return !m_state.empty();
void SHAKE_Cipher::generate_keystream_internal(std::span<uint8_t> out) {
m_keccak.squeeze(out);
m_bytes_generated += out.size();
}

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 = Keccak_Permutation::absorb(SHAKE_BITRATE, m_state, 0, std::span(key, length));
Keccak_Permutation::finish(SHAKE_BITRATE, m_state, S_pos, 0xF, 4);
copy_out_le(m_buffer.data(), m_buffer.size(), m_state.data());
m_buf_pos = 0;
m_keccak.absorb({key, length});
m_keccak.finish();
m_has_keying_material = true;
}

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_128_Cipher::SHAKE_128_Cipher() : SHAKE_Cipher(256) {}

SHAKE_256_Cipher::SHAKE_256_Cipher() : SHAKE_Cipher((1600 - 512) / 8) {}
SHAKE_256_Cipher::SHAKE_256_Cipher() : SHAKE_Cipher(512) {}

} // namespace Botan
21 changes: 11 additions & 10 deletions src/lib/stream/shake_cipher/shake_cipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
#ifndef BOTAN_SHAKE_CIPHER_H_
#define BOTAN_SHAKE_CIPHER_H_

#include <botan/secmem.h>
#include <botan/stream_cipher.h>
#include <botan/internal/keccak_perm.h>

namespace Botan {

Expand All @@ -19,7 +19,7 @@ namespace Botan {
*/
class SHAKE_Cipher : public StreamCipher {
protected:
explicit SHAKE_Cipher(size_t shake_rate);
explicit SHAKE_Cipher(size_t keccak_capacity);

public:
/**
Expand All @@ -31,9 +31,9 @@ class SHAKE_Cipher : public StreamCipher {

Key_Length_Specification key_spec() const final;

bool has_keying_material() const final;
bool has_keying_material() const final { return m_has_keying_material; }

size_t buffer_size() const final;
size_t buffer_size() const final { return m_keccak.byte_rate(); }

private:
void key_schedule(const uint8_t key[], size_t key_len) final;
Expand All @@ -43,17 +43,18 @@ class SHAKE_Cipher : public StreamCipher {
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 generate_keystream_internal(std::span<uint8_t> out);

/**
* IV not supported, this function will throw unless iv_len == 0
*/
void set_iv_bytes(const uint8_t iv[], size_t iv_len) final;

protected:
size_t m_shake_rate;

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:
Keccak_Permutation m_keccak;
bool m_has_keying_material;
secure_vector<uint8_t> m_keystream_buffer;
size_t m_bytes_generated;
};

class SHAKE_128_Cipher final : public SHAKE_Cipher {
Expand Down

0 comments on commit 9d16394

Please sign in to comment.