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