Program Listing for File kdf.hpp
↰ Return to documentation for file (libspookyaction/include/desfire/kdf.hpp
)
//
// Created by spak on 9/15/22.
//
#ifndef LIBSPOOKYACTION_KDF_HPP
#define LIBSPOOKYACTION_KDF_HPP
#include <cstdint>
#include <desfire/cmac_provider.hpp>
#include <desfire/crypto.hpp>
#include <desfire/keys.hpp>
#include <desfire/log.h>
#include <mlab/bin_data.hpp>
namespace desfire {
class any_key;
struct cipher_provider;
template <cipher_type>
struct key;
template <std::size_t BlockSize, std::size_t NBlocks>
[[nodiscard]] key_body<BlockSize * NBlocks> kdf_an10922(
cmac_keychain const &keychain,
crypto &crypto,
mlab::bin_data &diversify_input,
std::array<std::uint8_t, NBlocks> data_prepend_const);
[[nodiscard]] any_key kdf_an10922(crypto &crypto, mlab::bin_data &diversify_input, std::uint8_t key_version);
[[nodiscard]] any_key kdf_an10922(any_key const &key, cipher_provider &provider, mlab::bin_data &diversify_input);
[[nodiscard]] key<cipher_type::des> kdf_an10922(key<cipher_type::des> const &key, cipher_provider &provider, mlab::bin_data &diversify_input);
[[nodiscard]] key<cipher_type::des3_2k> kdf_an10922(key<cipher_type::des3_2k> const &key, cipher_provider &provider, mlab::bin_data &diversify_input);
[[nodiscard]] key<cipher_type::des3_3k> kdf_an10922(key<cipher_type::des3_3k> const &key, cipher_provider &provider, mlab::bin_data &diversify_input);
[[nodiscard]] key<cipher_type::aes128> kdf_an10922(key<cipher_type::aes128> const &key, cipher_provider &provider, mlab::bin_data &diversify_input);
key_body<8> kdf_an10922(crypto_des_base &crypto, mlab::bin_data &diversify_input);
key_body<16> kdf_an10922(crypto_2k3des_base &crypto, mlab::bin_data &diversify_input);
key_body<24> kdf_an10922(crypto_3k3des_base &crypto, mlab::bin_data &diversify_input);
key_body<16> kdf_an10922(crypto_aes_base &crypto, mlab::bin_data &diversify_input);
key_body<8> kdf_an10922(crypto_des_base &crypto, mlab::bin_data &diversify_input, std::uint8_t key_version);
key_body<16> kdf_an10922(crypto_2k3des_base &crypto, mlab::bin_data &diversify_input, std::uint8_t key_version);
key_body<24> kdf_an10922(crypto_3k3des_base &crypto, mlab::bin_data &diversify_input, std::uint8_t key_version);
}// namespace desfire
namespace desfire {
template <std::size_t BlockSize, std::size_t NBlocks>
key_body<BlockSize * NBlocks> kdf_an10922(
cmac_keychain const &keychain,
crypto &crypto,
mlab::bin_data &diversify_input,
std::array<std::uint8_t, NBlocks> data_prepend_const) {
static constexpr auto MaxDiversifyLength = 2 * BlockSize - 1;
static constexpr auto KeyLength = BlockSize * NBlocks;
// This will be the final key returned.
key_body<KeyLength> diversified_key{};
std::fill_n(std::begin(diversified_key), KeyLength, 0);
if (keychain.block_size() != BlockSize) {
DESFIRE_LOGE("The keychain block size differs to the block size required by the ciphers: %u != %u.", keychain.block_size(), BlockSize);
return diversified_key;
}
// We use at most 15 bits of the diversification data
if (diversify_input.size() > MaxDiversifyLength) {
DESFIRE_LOGW("Too long diversification input, %d > %u bytes. Will truncate.", diversify_input.size(), MaxDiversifyLength);
diversify_input.resize(MaxDiversifyLength);
}
// The CMAC procedure will process a total of 2 blocks of data. We use the diversification input as a buffer:
diversify_input.reserve(2 * BlockSize);
// For each block, we need to insert a different constant in front of the diversification data. For now, put zero.
diversify_input.insert(std::begin(diversify_input), 0);
// Preprocess the diversification input. It should never alter the first block:
keychain.prepare_cmac_data(diversify_input, 2 * BlockSize);
assert(diversify_input.size() == 2 * BlockSize);
assert(diversify_input[0] == 0);
// Each block is always processed in the same way:
auto process_one_block = [&](std::uint8_t block_data_prepend_const, std::size_t offset_in_diversified_key) {
// Set the first constant to be the requested one
diversify_input[0] = block_data_prepend_const;
// The new piece of the final key is now at zero, so we can use it as an IV and then copy over it
const range<std::uint8_t *> iv{
std::begin(diversified_key) + offset_in_diversified_key,
std::begin(diversified_key) + offset_in_diversified_key + BlockSize};
// Perform crypto in CMAC mode with a zero block IV.
crypto.do_crypto(diversify_input.data_view(), iv, crypto_operation::mac);
// Copy the last block of the diversified data onto the key
std::copy(std::begin(diversify_input) + BlockSize, std::end(diversify_input), std::begin(iv));
};
if constexpr (NBlocks == 1) {
// Just process the single block
process_one_block(data_prepend_const[0], 0);
} else {
// We will need to do NBlock interations, so we will need a copy of the current data.
std::array<std::uint8_t, 2 * BlockSize> diversify_input_backup{};
std::copy(std::begin(diversify_input), std::end(diversify_input), std::begin(diversify_input_backup));
// Now loop once for each block
for (std::uint_fast8_t block_idx = 0; block_idx < NBlocks; ++block_idx) {
if (block_idx > 0) {
// We need to restore the original data
std::copy(std::begin(diversify_input_backup), std::end(diversify_input_backup), std::begin(diversify_input));
}
process_one_block(data_prepend_const[block_idx], BlockSize * block_idx);
}
}
return diversified_key;
}
}// namespace desfire
#endif//LIBSPOOKYACTION_KDF_HPP