Skip to content

Commit

Permalink
Clean up the Keccak_Permutation
Browse files Browse the repository at this point in the history
* replace single-shot ::expand() with more versatile ::squeeze()
* use BufferStuffer/BufferSlicer in ::squeeze() and ::absorb()
* remove low-level access to Keccak state
* remove static Keccak state mutation methods
* ::permute() is now private
  • Loading branch information
reneme committed Aug 18, 2023
1 parent faeca4a commit 2a2495b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 181 deletions.
7 changes: 3 additions & 4 deletions src/lib/hash/keccak/keccak.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ std::unique_ptr<HashFunction> Keccak_1600::copy_state() const {
return std::make_unique<Keccak_1600>(*this);
}

Keccak_1600::Keccak_1600(size_t output_bits) : m_keccak(2 * output_bits, 0, 0), m_output_length(output_bits/8) {
Keccak_1600::Keccak_1600(size_t output_bits) : m_keccak(2 * output_bits, 0, 0), m_output_length(output_bits / 8) {
// We only support the parameters for the SHA-3 proposal


if(output_bits != 224 && output_bits != 256 && output_bits != 384 && output_bits != 512) {
throw Invalid_Argument(fmt("Keccak_1600: Invalid output length {}", output_bits));
}
Expand All @@ -40,12 +39,12 @@ void Keccak_1600::clear() {
}

void Keccak_1600::add_data(const uint8_t input[], size_t length) {
m_keccak.absorb(std::span(input, length));
m_keccak.absorb({input, length});
}

void Keccak_1600::final_result(uint8_t output[]) {
m_keccak.finish();
m_keccak.expand(std::span(output, m_output_length));
m_keccak.squeeze({output, m_output_length});
m_keccak.clear();
}

Expand Down
6 changes: 3 additions & 3 deletions src/lib/hash/sha3/sha3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace Botan {

SHA_3::SHA_3(size_t output_bits) : m_keccak(2 * output_bits, 2, 2), m_output_length(output_bits/8) {
SHA_3::SHA_3(size_t output_bits) : m_keccak(2 * output_bits, 2, 2), m_output_length(output_bits / 8) {
// We only support the parameters for SHA-3 in this constructor

if(output_bits != 224 && output_bits != 256 && output_bits != 384 && output_bits != 512) {
Expand Down Expand Up @@ -44,12 +44,12 @@ void SHA_3::clear() {
}

void SHA_3::add_data(const uint8_t input[], size_t length) {
m_keccak.absorb(std::span(input, length));
m_keccak.absorb({input, length});
}

void SHA_3::final_result(uint8_t output[]) {
m_keccak.finish();
m_keccak.expand(std::span(output, m_output_length));
m_keccak.squeeze({output, m_output_length});
m_keccak.clear();
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/hash/sha3/sha3.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class SHA_3 : public HashFunction {
*/
explicit SHA_3(size_t output_bits);

size_t hash_block_size() const override { return m_keccak.hash_block_size(); }
size_t hash_block_size() const override { return m_keccak.byte_rate(); }

size_t output_length() const override { return m_output_length; }

Expand Down
202 changes: 87 additions & 115 deletions src/lib/permutations/keccak_perm/keccak_perm.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/*
* Keccak-FIPS
* Keccak Permutation
* (C) 2010,2016 Jack Lloyd
* (C) 2023 Falko Strenzke
* (C) 2023 René Meusel - Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
Expand All @@ -13,161 +14,132 @@
#include <botan/internal/fmt.h>
#include <botan/internal/keccak_perm_round.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/stl_util.h>

namespace Botan {

// static
void Keccak_Permutation::permute(uint64_t A[25]) {
#if defined(BOTAN_HAS_KECCKAK_FIPS_BMI2)
Keccak_Permutation::Keccak_Permutation(size_t capacity, uint64_t custom_padding, uint8_t custom_padding_bit_len) :
m_capacity(capacity),
m_byterate((1600 - capacity) / 8),
m_custom_padding(custom_padding),
m_custom_padding_bit_len(custom_padding_bit_len),
m_S(25), // 1600 bit
m_S_inpos(0),
m_S_outpos(0) {
BOTAN_ARG_CHECK(capacity % 64 == 0, "capacity must be a multiple of 64");
}

std::string Keccak_Permutation::provider() const {
#if defined(BOTAN_HAS_KECCAK_PERM_BMI2)
if(CPUID::has_bmi2()) {
return permute_bmi2(A);
return "bmi2";
}
#endif

static const uint64_t RC[24] = {0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008};

uint64_t T[25];

for(size_t i = 0; i != 24; i += 2) {
Keccak_Permutation_round(T, A, RC[i + 0]);
Keccak_Permutation_round(A, T, RC[i + 1]);
}
return "base";
}

void Keccak_Permutation::permute() {
Keccak_Permutation::permute(m_S.data());
void Keccak_Permutation::clear() {
zeroise(m_S);
m_S_inpos = 0;
m_S_outpos = 0;
}

//static
void Keccak_Permutation::absorb(std::span<const uint8_t> input_span) {
BufferSlicer input_slicer(input_span);

uint32_t Keccak_Permutation::absorb(size_t bitrate,
secure_vector<uint64_t>& S,
size_t S_pos,
std::span<const uint8_t> input_span) {
const uint8_t* input = input_span.data();
size_t length = input_span.size();
// Block-wise incorporation of the input data into the sponge state until
// all input bytes were processed
while(!input_slicer.empty()) {
const size_t to_take_this_round = std::min(input_slicer.remaining(), m_byterate - m_S_inpos);
BufferSlicer input_this_round(input_slicer.take(to_take_this_round));

while(length > 0) {
size_t to_take = std::min(length, bitrate / 8 - S_pos);

length -= to_take;

while(to_take && S_pos % 8) {
S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8));

++S_pos;
++input;
--to_take;
// If necessary, try to get aligned with the sponge state's 64-bit integer array
for(; !input_this_round.empty() && m_S_inpos % 8; ++m_S_inpos) {
m_S[m_S_inpos / 8] ^= static_cast<uint64_t>(input_this_round.take_byte()) << (8 * (m_S_inpos % 8));
}

while(to_take && to_take % 8 == 0) {
S[S_pos / 8] ^= load_le<uint64_t>(input, 0);
S_pos += 8;
input += 8;
to_take -= 8;
// Process as many aligned 64-bit integer values as possible
for(; input_this_round.remaining() >= 8; m_S_inpos += 8) {
m_S[m_S_inpos / 8] ^= load_le<uint64_t>(input_this_round.take(8).data(), 0);
}

while(to_take) {
S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8));

++S_pos;
++input;
--to_take;
// Read remaining output data, causing misalignment, if necessary
for(; !input_this_round.empty(); ++m_S_inpos) {
m_S[m_S_inpos / 8] ^= static_cast<uint64_t>(input_this_round.take_byte()) << (8 * (m_S_inpos % 8));
}

if(S_pos == bitrate / 8) {
Keccak_Permutation::permute(S.data());
S_pos = 0;
// We reached the end of a sponge state block... permute() and start over
if(m_S_inpos == m_byterate) {
permute();
m_S_inpos = 0;
}
}

return static_cast<uint32_t>(S_pos);
}

//static

void Keccak_Permutation::finish(
size_t bitrate, secure_vector<uint64_t>& S, size_t S_pos, uint64_t custom_padd, uint8_t custom_padd_bit_len) {
BOTAN_ARG_CHECK(bitrate % 64 == 0, "Keccak-FIPS bitrate must be multiple of 64");
// append the first bit of the final padding after the custom padding
uint8_t init_pad = static_cast<uint8_t>(custom_padd | uint64_t(1) << custom_padd_bit_len);
S[S_pos / 8] ^= static_cast<uint64_t>(init_pad) << (8 * (S_pos % 8));
// final bit of the padding of the last block
S[(bitrate / 64) - 1] ^= static_cast<uint64_t>(0x80) << 56;
Keccak_Permutation::permute(S.data());
}

//static

void Keccak_Permutation::expand(size_t bitrate, secure_vector<uint64_t>& S, std::span<uint8_t> output_span) {
uint8_t* output = output_span.data();
size_t output_length = output_span.size();
BOTAN_ARG_CHECK(bitrate % 64 == 0, "Keccak-FIPS bitrate must be multiple of 64");
void Keccak_Permutation::squeeze(std::span<uint8_t> output) {
BufferStuffer output_stuffer(output);

const size_t byterate = bitrate / 8;
// Block-wise readout of the sponge state until enough bytes
// were filled into the output buffer
while(!output_stuffer.full()) {
const size_t bytes_in_this_round = std::min(output_stuffer.remaining_capacity(), m_byterate - m_S_outpos);
BufferStuffer output_this_round(output_stuffer.next(bytes_in_this_round));

while(output_length > 0) {
const size_t copying = std::min(byterate, output_length);
// If necessary, try to get aligned with the sponge state's 64-bit integer array
for(; !output_this_round.full() && m_S_outpos % 8 != 0; ++m_S_outpos) {
output_this_round.next_byte() = static_cast<uint8_t>(m_S[m_S_outpos / 8] >> (8 * (m_S_outpos % 8)));
}

copy_out_vec_le(output, copying, S);
// Read out as many aligned 64-bit integer values as possible
for(; output_this_round.remaining_capacity() >= 8; m_S_outpos += 8) {
store_le(m_S[m_S_outpos / 8], output_this_round.next(8).data());
}

output += copying;
output_length -= copying;
// Read remaining output data, causing misalignment, if necessary
for(; !output_this_round.full(); ++m_S_outpos) {
output_this_round.next_byte() = static_cast<uint8_t>(m_S[m_S_outpos / 8] >> (8 * (m_S_outpos % 8)));
}

if(output_length > 0) {
Keccak_Permutation::permute(S.data());
// We reached the end of a sponge state block... permute() and start over
if(m_S_outpos == m_byterate) {
permute();
m_S_outpos = 0;
}
}
}

void Keccak_Permutation::expand(std::span<uint8_t> output_span) {
expand(m_bitrate, m_S, output_span);
}
void Keccak_Permutation::finish() {
// append the first bit of the final padding after the custom padding
uint8_t init_pad = static_cast<uint8_t>(m_custom_padding | uint64_t(1) << m_custom_padding_bit_len);
m_S[m_S_inpos / 8] ^= static_cast<uint64_t>(init_pad) << (8 * (m_S_inpos % 8));

Keccak_Permutation::Keccak_Permutation(size_t capacity, uint64_t custom_padd, uint8_t custom_padd_bit_len) :
m_capacity(static_cast<uint32_t>(capacity)),
m_bitrate(static_cast<uint32_t>(1600 - capacity)),
m_custom_padd(custom_padd),
m_custom_padd_bit_len(custom_padd_bit_len),
m_S(25), // 1600 bit
m_S_pos(0) {
// final bit of the padding of the last block
m_S[(m_byterate / 8) - 1] ^= static_cast<uint64_t>(0x80) << 56;

permute();
}

std::string Keccak_Permutation::provider() const {
#if defined(BOTAN_HAS_KECCKAK_FIPS_BMI2)
void Keccak_Permutation::permute() {
#if defined(BOTAN_HAS_KECCAK_PERM_BMI2)
if(CPUID::has_bmi2()) {
return "bmi2";
return permute_bmi2();
}
#endif

return "base";
}

void Keccak_Permutation::clear() {
zeroise(m_S);
m_S_pos = 0;
}

void Keccak_Permutation::absorb(std::span<const uint8_t> input) {
m_S_pos = Keccak_Permutation::absorb(m_bitrate, m_S, m_S_pos, input);
}

void Keccak_Permutation::finish() {
Keccak_Permutation::finish(m_bitrate, m_S, m_S_pos, m_custom_padd, m_custom_padd_bit_len);
static const uint64_t RC[24] = {0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008};

//BOTAN_ASSERT_NOMSG(output.size() >= m_output_bits / 8);
uint64_t T[25];

/*
* We never have to run the permutation again because we only support
* limited output lengths
*/
//copy_out_vec_le(output.data(), m_output_bits / 8, m_S);
//clear();
for(size_t i = 0; i != 24; i += 2) {
Keccak_Permutation_round(T, m_S.data(), RC[i + 0]);
Keccak_Permutation_round(m_S.data(), T, RC[i + 1]);
}
}

} // namespace Botan
Loading

0 comments on commit 2a2495b

Please sign in to comment.