Program Listing for File keys.hpp

Return to documentation for file (libspookyaction/include/desfire/keys.hpp)

//
// Created by spak on 11/24/22.
//

#ifndef DESFIRE_KEYS_HPP
#define DESFIRE_KEYS_HPP

#include <desfire/bits.hpp>
#include <desfire/crypto_algo.hpp>
#include <mlab/any_of.hpp>
#include <mlab/bin_data.hpp>

namespace desfire {
    using mlab::bin_data;

    struct random_oracle {
        using fn_t = void (*)(void *, std::size_t);
        fn_t fn = nullptr;

        explicit random_oracle(fn_t fn_) : fn{fn_} {}

        void operator()(void *ptr, std::size_t len) const { fn(ptr, len); }
    };

    template <cipher_type Cipher>
    struct key {
        static constexpr cipher_type cipher = Cipher;//<! Alias for the template parm.
    };

    class any_key : public mlab::any_of<cipher_type, key, cipher_type::none> {
    public:
        using mlab::any_of<cipher_type, key, cipher_type::none>::any_of;

        any_key() = default;

        template <cipher_type Cipher>
        any_key(key<Cipher> obj);

        any_key(any_key const &other);
        any_key &operator=(any_key const &other);
        any_key(any_key &&other) noexcept = default;
        any_key &operator=(any_key &&other) noexcept = default;
        explicit any_key(cipher_type cipher);

        any_key(cipher_type cipher, mlab::range<std::uint8_t const *> k, std::uint8_t key_no = 0);

        any_key(cipher_type cipher, mlab::range<std::uint8_t const *> k, std::uint8_t key_no, std::uint8_t v);

        any_key(cipher_type cipher, random_oracle rng, std::uint8_t key_no = 0);

        any_key(cipher_type cipher, random_oracle rng, std::uint8_t key_no, std::uint8_t v);

        [[nodiscard]] std::uint8_t key_number() const;

        [[nodiscard]] std::uint8_t version() const;

        [[nodiscard]] mlab::range<std::uint8_t const *> body() const;

        void set_key_number(std::uint8_t v);

        void set_version(std::uint8_t v);

        void set_body(mlab::range<std::uint8_t const *> k);

        void randomize(random_oracle rng);

        [[nodiscard]] any_key with_key_number(std::uint8_t key_no) const;

        [[nodiscard]] std::size_t size() const;

        [[nodiscard]] bool parity_bits_are_version() const;

        [[nodiscard]] bin_data get_packed_key_body() const;

        [[nodiscard]] bin_data xored_with(any_key const &key_to_xor_with) const;

        [[nodiscard]] bool operator==(any_key const &other) const;

        [[nodiscard]] bool operator!=(any_key const &other) const;
    };

    struct key_tag {};

    template <std::size_t Length>
    using key_body = mlab::tagged_array<key_tag, Length>;

    template <std::size_t KeyLength, bool ParityBitsAreVersion>
    class key_storage;

    template <std::size_t KeyLength>
    class key_storage<KeyLength, true /* ParityBitsAreVersion */> {
    public:
        static constexpr std::size_t size = KeyLength;

        using key_body_t = key_body<KeyLength>;

        [[nodiscard]] constexpr mlab::range<std::uint8_t const *> as_range() const;

        key_storage() = default;

        inline explicit key_storage(random_oracle rng);

        key_storage(random_oracle rng, std::uint8_t v);

        constexpr explicit key_storage(key_body_t k);

        constexpr key_storage(key_body_t k, std::uint8_t v);

        [[nodiscard]] constexpr std::uint8_t version() const;


        inline void set_version(std::uint8_t v);

        [[nodiscard]] constexpr key_body_t const &body() const;

        inline void set_body(key_body_t k);

        void randomize(random_oracle rng);

    protected:
        key_body_t _body{};
    };

    template <std::size_t KeyLength>
    class key_storage<KeyLength, false /* ParityBitsAreVersion */> : private key_storage<KeyLength, true> {
    public:
        using key_body_t = typename key_storage<KeyLength, true>::key_body_t;

        using key_storage<KeyLength, true>::size;

        using key_storage<KeyLength, true>::as_range;

        constexpr key_storage() = default;

        explicit key_storage(random_oracle rng, std::uint8_t v = 0);

        constexpr explicit key_storage(key_body_t k, std::uint8_t v = 0);

        [[nodiscard]] constexpr std::uint8_t version() const;

        inline void set_version(std::uint8_t v);

        void randomize(random_oracle rng);

        using key_storage<KeyLength, true>::body;

        using key_storage<KeyLength, true>::set_body;

    private:
        std::uint8_t _version{};
    };


    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    class key_base : public key_storage<KeyLength, ParityBitsAreVersion> {
    public:
        using storage = key_storage<KeyLength, ParityBitsAreVersion>;

        static constexpr bool parity_bits_are_version = ParityBitsAreVersion;

        using typename storage::key_body_t;

        using storage::as_range;
        using storage::body;
        using storage::randomize;
        using storage::set_body;
        using storage::set_version;
        using storage::size;
        using storage::version;

        constexpr key_base() = default;

        explicit key_base(random_oracle rng);

        key_base(std::uint8_t key_no, random_oracle rng);

        constexpr key_base(std::uint8_t key_no, key_body_t k);

        key_base(std::uint8_t key_no, random_oracle rng, std::uint8_t v);

        constexpr key_base(std::uint8_t key_no, key_body_t k, std::uint8_t v);
        [[nodiscard]] CRTPSubclass with_key_number(std::uint8_t key_no) const;

        [[nodiscard]] constexpr std::uint8_t key_number() const;

        inline void set_key_number(std::uint8_t key_no);

        [[nodiscard]] inline bool operator==(key_base const &other) const;
        [[nodiscard]] inline bool operator!=(key_base const &other) const;
    private:
        std::uint8_t _key_no{0};
    };

    template <>
    struct key<cipher_type::des> : public key_base<8, true, key<cipher_type::des>> {
        using key_base = key_base<8, true, key<cipher_type::des>>;
        static constexpr cipher_type cipher = cipher_type::des;
        using key_base::body;
        using key_base::key_base;
        using key_base::key_number;
        using key_base::randomize;
        using key_base::set_body;
        using key_base::set_key_number;
        using key_base::set_version;
        using key_base::size;
        using key_base::version;
        using key_base::with_key_number;
    };

    template <>
    struct key<cipher_type::des3_2k> : public key_base<16, true, key<cipher_type::des3_2k>> {
        using key_base = key_base<16, true, key<cipher_type::des3_2k>>;
        static constexpr cipher_type cipher = cipher_type::des3_2k;
        using key_base::body;
        using key_base::key_base;
        using key_base::key_number;
        using key_base::randomize;
        using key_base::set_body;
        using key_base::set_key_number;
        using key_base::set_version;
        using key_base::size;
        using key_base::version;
        using key_base::with_key_number;
    };

    template <>
    struct key<cipher_type::des3_3k> : public key_base<24, true, key<cipher_type::des3_3k>> {
        using key_base = key_base<24, true, key<cipher_type::des3_3k>>;
        static constexpr cipher_type cipher = cipher_type::des3_3k;
        using key_base::body;
        using key_base::key_base;
        using key_base::key_number;
        using key_base::randomize;
        using key_base::set_body;
        using key_base::set_key_number;
        using key_base::set_version;
        using key_base::size;
        using key_base::version;
        using key_base::with_key_number;
    };

    template <>
    struct key<cipher_type::aes128> : public key_base<16, false, key<cipher_type::aes128>> {
        using key_base = key_base<16, false, key<cipher_type::aes128>>;
        static constexpr cipher_type cipher = cipher_type::aes128;
        using key_base::body;
        using key_base::key_base;
        using key_base::key_number;
        using key_base::randomize;
        using key_base::set_body;
        using key_base::set_key_number;
        using key_base::set_version;
        using key_base::size;
        using key_base::version;
        using key_base::with_key_number;
    };

}// namespace desfire

namespace mlab {
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    bin_data &operator<<(bin_data &bd, desfire::any_key const &k);
#endif
}// namespace mlab

namespace desfire {

    template <std::size_t KeyLength>
    constexpr key_storage<KeyLength, false>::key_storage(key_body_t k, std::uint8_t v) : key_storage<KeyLength, true>{k}, _version{v} {}

    template <std::size_t KeyLength>
    key_storage<KeyLength, false>::key_storage(random_oracle rng, std::uint8_t v) : key_storage<KeyLength, true>{rng}, _version{v} {}

    template <std::size_t KeyLength>
    constexpr std::uint8_t key_storage<KeyLength, false>::version() const {
        return _version;
    }

    template <std::size_t KeyLength>
    void key_storage<KeyLength, false>::set_version(std::uint8_t v) {
        _version = v;
    }

    template <std::size_t KeyLength>
    void key_storage<KeyLength, false>::randomize(random_oracle rng) {
        rng(key_storage<KeyLength, true>::_body.data(), key_storage<KeyLength, true>::_body.size());
    }

    template <std::size_t KeyLength>
    constexpr mlab::range<std::uint8_t const *> key_storage<KeyLength, true>::as_range() const {
        return mlab::make_range(_body);
    }

    template <std::size_t KeyLength>
    constexpr key_storage<KeyLength, true>::key_storage(key_body_t k) : _body{k} {}

    template <std::size_t KeyLength>
    key_storage<KeyLength, true>::key_storage(random_oracle rng) : key_storage{} {
        rng(_body.data(), _body.size());
    }

    template <std::size_t KeyLength>
    key_storage<KeyLength, true>::key_storage(random_oracle rng, std::uint8_t v) : key_storage{} {
        rng(_body.data(), _body.size());
        set_version(v);
    }

    template <std::size_t KeyLength>
    constexpr key_storage<KeyLength, true>::key_storage(key_body_t k, std::uint8_t v) : _body{k} {
        set_version(v);
    }

    template <std::size_t KeyLength>
    constexpr std::uint8_t key_storage<KeyLength, true>::version() const {
        return get_key_version(_body);
    }

    template <std::size_t KeyLength>
    void key_storage<KeyLength, true>::set_version(std::uint8_t v) {
        set_key_version(_body, v);
    }

    template <std::size_t KeyLength>
    void key_storage<KeyLength, true>::randomize(random_oracle rng) {
        const auto v = version();
        rng(_body.data(), _body.size());
        set_version(v);
    }

    template <std::size_t KeyLength>
    constexpr typename key_storage<KeyLength, true>::key_body_t const &key_storage<KeyLength, true>::body() const {
        return _body;
    }

    template <std::size_t KeyLength>
    void key_storage<KeyLength, true>::set_body(key_body_t k) {
        _body = k;
    }

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    constexpr key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::key_base(std::uint8_t key_no, key_body_t k_) : storage{k_}, _key_no{key_no} {}

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    constexpr key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::key_base(std::uint8_t key_no, key_body_t k_, std::uint8_t v_) : storage{k_, v_}, _key_no{key_no} {}

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::key_base(random_oracle rng) : storage{rng}, _key_no{0} {}

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::key_base(std::uint8_t key_no, random_oracle rng) : storage{rng}, _key_no{key_no} {}

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::key_base(std::uint8_t key_no, random_oracle rng, std::uint8_t v) : storage{rng, v}, _key_no{key_no} {}

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    CRTPSubclass key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::with_key_number(std::uint8_t key_no) const {
        return CRTPSubclass{key_no, body(), version()};
    }

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    constexpr std::uint8_t key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::key_number() const {
        return _key_no;
    }

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    void key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::set_key_number(std::uint8_t key_no) {
        _key_no = key_no;
    }

    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    bool key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::operator==(key_base const &other) const {
        return key_number() == other.key_number() and version() == other.version() and body() == other.body();
    }
    template <std::size_t KeyLength, bool ParityBitsAreVersion, class CRTPSubclass>
    bool key_base<KeyLength, ParityBitsAreVersion, CRTPSubclass>::operator!=(key_base const &other) const {
        return key_number() != other.key_number() or version() != other.version() or body() != other.body();
    }

    template <cipher_type Cipher>
    any_key::any_key(key<Cipher> obj) : mlab::any_of<cipher_type, key, cipher_type::none>{std::move(obj)} {}

}// namespace desfire

#endif//DESFIRE_KEYS_HPP