-
Notifications
You must be signed in to change notification settings - Fork 578
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1b089e1
commit 45bd450
Showing
5 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_fips | ||
</requires> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
/* | ||
* 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_hash.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_hash.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_hash.absorb(std::span(newX_head)); | ||
} | ||
|
||
KMAC256::KMAC256(size_t output_bit_length) : | ||
m_output_bit_length(output_bit_length), m_hash(output_bit_length, 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_hash.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_hash.absorb(std::span(tail)); | ||
|
||
m_hash.finish(std::span(output, m_output_bit_length / 8)); | ||
} | ||
|
||
void KMAC256::key_schedule(const uint8_t key[], size_t key_length) { | ||
m_hash.clear(); | ||
zap(m_key); | ||
m_key.insert(m_key.end(), &key[0], &key[key_length]); | ||
m_key_set = true; | ||
} | ||
} // namespace Botan |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_fips.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_FIPS m_hash; | ||
size_t m_pad_byte_length; | ||
}; | ||
|
||
} // namespace Botan | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|