Skip to content

Commit

Permalink
added KMAC256
Browse files Browse the repository at this point in the history
adapted kmac to new keccak permutation
  • Loading branch information
falko-strenzke committed Aug 21, 2023
1 parent 3ced414 commit e07e82d
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/lib/mac/kmac/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<defines>
KMAC -> 20230601
</defines>

<module_info>
name -> "KMAC"
</module_info>

<requires>
keccak_perm
</requires>

185 changes: 185 additions & 0 deletions src/lib/mac/kmac/kmac.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* KMAC
* (C) 2023 Falko Strenzke
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/assert.h>
#include <botan/exceptn.h>
#include <botan/secmem.h>
#include <botan/types.h>
#include <botan/internal/kmac.h>
#include <limits>
#include <string>
#include <vector>

namespace Botan {

/**
* KMAC
* https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
*
*
* newX = bytepad(encode_string(K), 136) ‖ input ‖ right_encode(L)
* T = bytebad(encode_string("KMAC" ‖ encode_string(S), 136)) // S = nonce
* return Keccak[512](T ‖ newX ‖ 00, L)
*
*/

// regarding the interface see https://github.com/randombit/botan/issues/3262

namespace {

template <bool IS_LEFT_ENCODE, typename T>
size_t left_or_right_encode(size_t s, T& output_container) {
int i;
size_t bytes_appended = 0;
// determine number of octets needed to encode s
for(i = sizeof(s); i > 0; i--) {
uint8_t t = (s >> ((i - 1) * 8) & static_cast<size_t>(0xFF));
if(t != 0) {
break;
}
}
if(i == 0) {
i = 1;
}
if(IS_LEFT_ENCODE) {
output_container.push_back(static_cast<uint8_t>(i));
bytes_appended++;
}
// big endian encoding of s
for(int j = i; j > 0; j--) {
output_container.push_back(s >> (j - 1) * 8 & (static_cast<size_t>(0xFF)));
bytes_appended++;
}
if(!IS_LEFT_ENCODE) {
output_container.push_back(static_cast<uint8_t>(i));
bytes_appended++;
}
return bytes_appended;
}

template <typename T>
size_t left_encode(size_t s, T& output_container) {
return left_or_right_encode<true>(s, output_container);
}

template <typename T>
size_t right_encode(size_t s, T& output_container) {
size_t result = left_or_right_encode<false>(s, output_container);
return result;
}

size_t byte_len_from_bit_len(size_t bit_length) {
if(bit_length % 8) {
throw Invalid_Argument("cannot convert byte length to bit length that is not a multiple of 8");
}
return bit_length / 8;
}

size_t bit_len_from_byte_len(size_t byte_length) {
size_t bit_length = 8 * byte_length;
if(bit_length < byte_length) {
throw Botan::Invalid_Argument("byte length is too large. Only byte lengths of up to " +
std::to_string(std::numeric_limits<size_t>::max() / 8) +
" are supported on this platform in this function.");
}
return bit_length;
}

template <typename T>
void encode_string(const uint8_t input[], size_t input_byte_length, T& output_container) {
left_encode(bit_len_from_byte_len(input_byte_length), output_container);
output_container.insert(output_container.end(), input, &input[input_byte_length]);
}

template <typename T>
void byte_pad(uint8_t input[], size_t input_byte_length, size_t w_in_bytes, T& output_container) {
size_t written_bytes = left_encode(w_in_bytes, output_container);
output_container.insert(output_container.end(), input, &input[input_byte_length]);
written_bytes += input_byte_length;
if(w_in_bytes > written_bytes) {
size_t nb_trail_zeroes = w_in_bytes - written_bytes;
std::vector<uint8_t> trailing_zeroes(nb_trail_zeroes, 0);
output_container.insert(output_container.end(), &trailing_zeroes[0], &trailing_zeroes[trailing_zeroes.size()]);
}
}

} // namespace

void KMAC256::clear() {
zap(m_key);
m_key_set = false;
m_keccak.clear();
}

std::string KMAC256::name() const {
return std::string("KMAC256(" + std::to_string(m_output_bit_length) + ")");
}

std::unique_ptr<MessageAuthenticationCode> KMAC256::new_object() const {
return std::make_unique<KMAC256>(m_output_bit_length);
}

size_t KMAC256::output_length() const {
return m_output_bit_length / 8;
}

Key_Length_Specification KMAC256::key_spec() const {
// KMAC supports key lengths from zero up to 2²⁰⁴⁰ (2^(2040)) bits:
// https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf#page=28
// However, we restrict the key length to 64 bytes in order to avoid allocation of overly large memory stretches when client code works with the maximal key length.
return Key_Length_Specification(0, 64);
}

bool KMAC256::has_keying_material() const {
return m_key_set;
}

void KMAC256::start_msg(const uint8_t nonce[], size_t nonce_len) {
const uint8_t dom_sep[] = {'K', 'M', 'A', 'C'};
assert_key_material_set(m_key_set);
std::vector<uint8_t> t_input;
encode_string(dom_sep, sizeof(dom_sep), t_input);
encode_string(nonce, nonce_len, t_input);
std::vector<uint8_t> t;
byte_pad(&t_input[0], t_input.size(), m_pad_byte_length, t);
m_keccak.absorb(std::span(t));
secure_vector<uint8_t> key_input;
encode_string(&m_key[0], m_key.size(), key_input);
secure_vector<uint8_t> newX_head;
byte_pad(&key_input[0], key_input.size(), m_pad_byte_length, newX_head);
m_keccak.absorb(std::span(newX_head));
}

KMAC256::KMAC256(size_t output_bit_length) :
m_output_bit_length(output_bit_length), m_keccak(512, 00, 2), m_pad_byte_length(136) {
// ensure valid output length
byte_len_from_bit_len(m_output_bit_length);
}

void KMAC256::add_data(const uint8_t data[], size_t data_len) {
assert_key_material_set(m_key_set);
m_keccak.absorb(std::span(data, data_len));
}

void KMAC256::final_result(uint8_t output[]) {
assert_key_material_set(m_key_set);
std::vector<uint8_t> tail;
right_encode(m_output_bit_length, tail);
m_keccak.absorb(std::span(tail));

m_keccak.finish();
m_keccak.squeeze({output, m_output_bit_length / 8});
m_keccak.clear();
}

void KMAC256::key_schedule(const uint8_t key[], size_t key_length) {
m_keccak.clear();
zap(m_key);
m_key.insert(m_key.end(), &key[0], &key[key_length]);
m_key_set = true;
}
} // namespace Botan
53 changes: 53 additions & 0 deletions src/lib/mac/kmac/kmac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* KMAC
* (C) 2023 Falko Strenzke
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_KMAC_H_
#define BOTAN_KMAC_H_

#include <botan/hash.h>
#include <botan/mac.h>
#include <botan/internal/keccak_perm.h>

namespace Botan {

/**
* KMAC256
*/
class KMAC256 final : public MessageAuthenticationCode {
public:
void clear() override;
std::string name() const override;
std::unique_ptr<MessageAuthenticationCode> new_object() const override;

size_t output_length() const override;

Key_Length_Specification key_spec() const override;

void start_msg(const uint8_t nonce[], size_t nonce_len) override;
explicit KMAC256(size_t output_byte_length);

KMAC256(const KMAC256&) = delete;
KMAC256& operator=(const KMAC256&) = delete;

bool has_keying_material() const override;

private:
void add_data(const uint8_t[], size_t) override;
void final_result(uint8_t[]) override;
void key_schedule(const uint8_t[], size_t) override;

size_t m_output_bit_length;
secure_vector<uint8_t> m_key;
bool m_key_set = false;

Keccak_Permutation m_keccak;
size_t m_pad_byte_length;
};

} // namespace Botan

#endif
16 changes: 16 additions & 0 deletions src/lib/mac/mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
#include <botan/internal/blake2bmac.h>
#endif

#if defined(BOTAN_HAS_KMAC)
#include <botan/internal/kmac.h>
#endif

namespace Botan {

std::unique_ptr<MessageAuthenticationCode> MessageAuthenticationCode::create(std::string_view algo_spec,
Expand Down Expand Up @@ -107,6 +111,18 @@ std::unique_ptr<MessageAuthenticationCode> MessageAuthenticationCode::create(std
}
#endif

#if defined(BOTAN_HAS_KMAC)
if(req.algo_name() == "KMAC256") {
if(provider.empty() || provider == "base") {
if(req.arg_count() != 1) {
throw Invalid_Argument(
"invalid algorithm specification for KMAC: need exactly one argument for output bit length");
}
return std::make_unique<KMAC256>(req.arg_as_integer(0));
}
}
#endif

BOTAN_UNUSED(req);
BOTAN_UNUSED(provider);

Expand Down
28 changes: 28 additions & 0 deletions src/tests/data/mac/kmac.vec
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# The test vectors in this file are taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf

# sample #3 from [1]
[KMAC256(512)]
Key = 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F
In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7
# Out = 512 bits
Out = 75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69

# sample 4 from [1]
[KMAC256(512)]
Key = 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F
In = 00010203
# IV = "My Tagged Application"
IV = 4D7920546167676564204170706C69636174696F6E
# out len = 512 bit
Out = 20C570C31346F703C9AC36C61C03CB64C3970D0CFC787E9B79599D273A68D2F7F69D4CC3DE9D104A351689F27CF6F5951F0103F33F4F24871024D9C27773A8DD

# sample #5 from [1]
#
Key = 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F
In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7
# IV = "(null)" // means empty!
Out = 75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69


# [1] https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf

0 comments on commit e07e82d

Please sign in to comment.