Program Listing for File data.hpp
↰ Return to documentation for file (libspookyaction/include/desfire/data.hpp
)
//
// Created by Pietro Saccardi on 03/01/2021.
//
#ifndef DESFIRE_DATA_HPP
#define DESFIRE_DATA_HPP
#include <desfire/bits.hpp>
#include <desfire/key_actor.hpp>
#include <desfire/log.h>
#include <memory>
#include <mlab/any_of.hpp>
#include <mlab/bin_data.hpp>
#include <type_traits>
namespace desfire {
struct app_id_tag {};
using app_id = mlab::tagged_array<app_id_tag, bits::app_id_length>;
static constexpr app_id root_app{0x0, 0x0, 0x0};
using file_id = std::uint8_t;
enum struct error : std::uint8_t {
out_of_eeprom = static_cast<std::uint8_t>(bits::status::out_of_eeprom),
illegal_command = static_cast<std::uint8_t>(bits::status::illegal_command),
integrity_error = static_cast<std::uint8_t>(bits::status::integrity_error),
no_such_key = static_cast<std::uint8_t>(bits::status::no_such_key),
length_error = static_cast<std::uint8_t>(bits::status::length_error),
permission_denied = static_cast<std::uint8_t>(bits::status::permission_denied),
parameter_error = static_cast<std::uint8_t>(bits::status::parameter_error),
app_not_found = static_cast<std::uint8_t>(bits::status::app_not_found),
app_integrity_error = static_cast<std::uint8_t>(bits::status::app_integrity_error),
authentication_error = static_cast<std::uint8_t>(bits::status::authentication_error),
boundary_error = static_cast<std::uint8_t>(bits::status::boundary_error),
picc_integrity_error = static_cast<std::uint8_t>(bits::status::picc_integrity_error),
command_aborted = static_cast<std::uint8_t>(bits::status::command_aborted),
picc_disabled_error = static_cast<std::uint8_t>(bits::status::picc_disabled_error),
count_error = static_cast<std::uint8_t>(bits::status::count_error),
duplicate_error = static_cast<std::uint8_t>(bits::status::duplicate_error),
eeprom_error = static_cast<std::uint8_t>(bits::status::eeprom_error),
file_not_found = static_cast<std::uint8_t>(bits::status::file_not_found),
file_integrity_error = static_cast<std::uint8_t>(bits::status::file_integrity_error),
controller_error,
malformed,
crypto_error
};
[[nodiscard]] error error_from_status(bits::status s);
struct same_key_t {};
static constexpr same_key_t same_key{};
struct key_rights {
key_actor<same_key_t> allowed_to_change_keys{same_key};
bool master_key_changeable = true;
bool dir_access_without_auth = true;
bool create_delete_without_master_key = false;
bool config_changeable = true;
[[nodiscard]] inline bool operator==(desfire::key_rights const &other) const;
[[nodiscard]] inline bool operator!=(desfire::key_rights const &other) const;
constexpr key_rights() = default;
constexpr key_rights(key_actor<same_key_t> allowed_to_change_keys_,
bool master_key_changeable_,
bool dir_access_without_auth_,
bool create_delete_without_master_key_,
bool config_changeable_)
: allowed_to_change_keys{allowed_to_change_keys_},
master_key_changeable{master_key_changeable_},
dir_access_without_auth{dir_access_without_auth_},
create_delete_without_master_key{create_delete_without_master_key_},
config_changeable{config_changeable_} {}
};
struct free_access_t {};
static constexpr free_access_t free_access{};
enum struct file_access {
change,
read,
write
};
struct file_access_rights {
key_actor<free_access_t> change;
key_actor<free_access_t> read_write;
key_actor<free_access_t> write;
key_actor<free_access_t> read;
constexpr file_access_rights() = default;
constexpr file_access_rights(no_key_t);
constexpr file_access_rights(free_access_t);
constexpr explicit file_access_rights(std::uint8_t single_key);
explicit file_access_rights(bool) = delete;
constexpr file_access_rights(key_actor<free_access_t> rw, key_actor<free_access_t> chg);
constexpr file_access_rights(key_actor<free_access_t> rw, key_actor<free_access_t> chg, key_actor<free_access_t> r, key_actor<free_access_t> w);
inline void set_word(std::uint16_t v);
[[nodiscard]] inline std::uint16_t get_word() const;
[[nodiscard]] inline static file_access_rights from_word(std::uint16_t word);
[[nodiscard]] bool is_free(file_access access) const;
[[nodiscard]] inline bool operator==(file_access_rights const &other) const;
[[nodiscard]] inline bool operator!=(file_access_rights const &other) const;
};
struct common_file_settings {
file_security security = file_security::none;
file_access_rights rights;
constexpr common_file_settings() = default;
constexpr common_file_settings(file_security security_, file_access_rights rights_);
};
struct data_file_settings {
std::uint32_t size = 0;
constexpr data_file_settings() = default;
explicit constexpr data_file_settings(std::uint32_t size_) : size{size_} {}
};
struct value_file_settings {
std::int32_t lower_limit = 0;
std::int32_t upper_limit = 0;
std::int32_t value = 0;
bool limited_credit_enabled = false;
constexpr value_file_settings() = default;
constexpr value_file_settings(std::int32_t lowlim, std::int32_t uplim, std::int32_t v, bool enable_lim_credit = false)
: lower_limit{lowlim},
upper_limit{uplim},
value{v},
limited_credit_enabled{enable_lim_credit} {}
};
struct record_file_settings {
std::uint32_t record_size = 0;
std::uint32_t max_record_count = 0;
std::uint32_t record_count = 0;
constexpr record_file_settings() = default;
constexpr record_file_settings(std::uint32_t rec_size, std::uint32_t max_rec_count, std::uint32_t rec_count = 0)
: record_size{rec_size},
max_record_count{max_rec_count},
record_count{rec_count} {}
};
template <file_type>
struct file_settings {};
template <>
struct file_settings<file_type::standard> : public common_file_settings, public data_file_settings {
using specific_file_settings = data_file_settings;
constexpr file_settings() : common_file_settings{}, data_file_settings{} {}
constexpr file_settings(common_file_settings generic, data_file_settings specific)
: common_file_settings{generic}, data_file_settings{specific} {}
constexpr file_settings(file_security security, file_access_rights rights, std::uint32_t size)
: common_file_settings{security, rights}, data_file_settings{size} {}
constexpr file_settings(common_file_settings generic, std::uint32_t size)
: common_file_settings{generic}, data_file_settings{size} {}
constexpr file_settings(file_security security, file_access_rights rights, data_file_settings specific)
: common_file_settings{security, rights}, data_file_settings{specific} {}
};
template <>
struct file_settings<file_type::backup> : public common_file_settings, public data_file_settings {
using specific_file_settings = data_file_settings;
constexpr file_settings() : common_file_settings{}, data_file_settings{} {}
constexpr file_settings(common_file_settings generic, data_file_settings specific)
: common_file_settings{generic}, data_file_settings{specific} {}
constexpr file_settings(file_security security, file_access_rights rights, std::uint32_t size)
: common_file_settings{security, rights}, data_file_settings{size} {}
constexpr file_settings(common_file_settings generic, std::uint32_t size)
: common_file_settings{generic}, data_file_settings{size} {}
constexpr file_settings(file_security security, file_access_rights rights, data_file_settings specific)
: common_file_settings{security, rights}, data_file_settings{specific} {}
};
template <>
struct file_settings<file_type::value> : public common_file_settings, public value_file_settings {
using specific_file_settings = value_file_settings;
constexpr file_settings()
: common_file_settings{},
value_file_settings{0, 0, 0, false} {}
constexpr file_settings(common_file_settings generic, value_file_settings specific)
: common_file_settings{generic}, value_file_settings{specific} {}
constexpr file_settings(file_security security, file_access_rights rights,
std::int32_t lowlim, std::int32_t uplim, std::int32_t v, bool enable_lim_credit = false)
: common_file_settings{security, rights},
value_file_settings{lowlim, uplim, v, enable_lim_credit} {}
constexpr file_settings(common_file_settings generic,
std::int32_t lowlim, std::int32_t uplim, std::int32_t v, bool enable_lim_credit = false)
: common_file_settings{generic},
value_file_settings{lowlim, uplim, v, enable_lim_credit} {}
constexpr file_settings(file_security security, file_access_rights rights,
value_file_settings specific)
: common_file_settings{security, rights},
value_file_settings{specific} {}
};
template <>
struct file_settings<file_type::linear_record> : public common_file_settings, public record_file_settings {
using specific_file_settings = record_file_settings;
constexpr file_settings()
: common_file_settings{},
record_file_settings{0, 0, 0} {}
constexpr file_settings(common_file_settings generic, record_file_settings specific)
: common_file_settings{generic}, record_file_settings{specific} {}
constexpr file_settings(file_security security, file_access_rights rights,
std::uint32_t rec_size, std::uint32_t max_rec_count, std::uint32_t rec_count = 0)
: common_file_settings{security, rights},
record_file_settings{rec_size, max_rec_count, rec_count} {}
constexpr file_settings(common_file_settings generic,
std::uint32_t rec_size, std::uint32_t max_rec_count, std::uint32_t rec_count = 0)
: common_file_settings{generic},
record_file_settings{rec_size, max_rec_count, rec_count} {}
constexpr file_settings(file_security security, file_access_rights rights,
record_file_settings specific)
: common_file_settings{security, rights},
record_file_settings{specific} {}
};
template <>
struct file_settings<file_type::cyclic_record> : public common_file_settings, public record_file_settings {
using specific_file_settings = record_file_settings;
constexpr file_settings()
: common_file_settings{},
record_file_settings{0, 0, 0} {}
constexpr file_settings(common_file_settings generic, record_file_settings specific)
: common_file_settings{generic}, record_file_settings{specific} {}
constexpr file_settings(file_security security, file_access_rights rights,
std::uint32_t rec_size, std::uint32_t max_rec_count, std::uint32_t rec_count = 0)
: common_file_settings{security, rights},
record_file_settings{rec_size, max_rec_count, rec_count} {}
constexpr file_settings(common_file_settings generic,
std::uint32_t rec_size, std::uint32_t max_rec_count, std::uint32_t rec_count = 0)
: common_file_settings{generic},
record_file_settings{rec_size, max_rec_count, rec_count} {}
constexpr file_settings(file_security security, file_access_rights rights,
record_file_settings specific)
: common_file_settings{security, rights},
record_file_settings{specific} {}
};
class any_file_settings : public mlab::any_of<file_type, file_settings, file_type::standard> {
public:
using mlab::any_of<file_type, file_settings, file_type::standard>::any_of;
[[nodiscard]] common_file_settings const &common_settings() const;
[[nodiscard]] data_file_settings const &data_settings() const;
[[nodiscard]] record_file_settings const &record_settings() const;
[[nodiscard]] value_file_settings const &value_settings() const;
[[nodiscard]] common_file_settings &common_settings();
[[nodiscard]] data_file_settings &data_settings();
[[nodiscard]] record_file_settings &record_settings();
[[nodiscard]] value_file_settings &value_settings();
};
struct app_settings {
key_rights rights;
std::uint8_t max_num_keys;
app_crypto crypto;
constexpr explicit app_settings(app_crypto crypto_ = app_crypto::legacy_des_2k3des,
key_rights rights_ = key_rights{},
std::uint8_t max_num_keys_ = bits::max_keys_per_app);
constexpr explicit app_settings(cipher_type cipher,
key_rights rights_ = key_rights{},
std::uint8_t max_num_keys_ = bits::max_keys_per_app);
};
class storage_size {
std::uint8_t _flag;
[[nodiscard]] inline unsigned exponent() const;
[[nodiscard]] inline bool approx() const;
public:
explicit storage_size(std::size_t nbytes = 0);
[[nodiscard]] inline std::size_t bytes_lower_bound() const;
[[nodiscard]] inline std::size_t bytes_upper_bound() const;
mlab::bin_stream &operator>>(mlab::bin_stream &s);
mlab::bin_data &operator<<(mlab::bin_data &s) const;
};
struct ware_info {
std::uint8_t vendor_id = 0;
std::uint8_t type = 0;
std::uint8_t subtype = 0;
std::uint8_t version_major = 0;
std::uint8_t version_minor = 0;
storage_size size;
std::uint8_t comm_protocol_type = 0;
};
struct manufacturing_info {
ware_info hardware;
ware_info software;
std::array<std::uint8_t, 7> serial_no{};
std::array<std::uint8_t, 5> batch_no{};
std::uint8_t production_week = 0;
std::uint8_t production_year = 0;
};
}// namespace desfire
namespace mlab {
#ifndef DOXYGEN_SHOULD_SKIP_THIS
bin_stream &operator>>(bin_stream &s, desfire::key_rights &kr);
bin_stream &operator>>(bin_stream &s, desfire::app_settings &ks);
bin_stream &operator>>(bin_stream &s, std::vector<desfire::app_id> &ids);
bin_stream &operator>>(bin_stream &s, desfire::ware_info &wi);
bin_stream &operator>>(bin_stream &s, desfire::manufacturing_info &mi);
bin_stream &operator>>(bin_stream &s, desfire::file_access_rights &ar);
bin_stream &operator>>(bin_stream &s, desfire::common_file_settings &fs);
bin_stream &operator>>(bin_stream &s, desfire::data_file_settings &fs);
bin_stream &operator>>(bin_stream &s, desfire::value_file_settings &fs);
bin_stream &operator>>(bin_stream &s, desfire::record_file_settings &fs);
bin_stream &operator>>(bin_stream &s, desfire::any_file_settings &fs);
template <desfire::file_type Type>
bin_stream &operator>>(bin_stream &s, desfire::file_settings<Type> &fs);
bin_data &operator<<(bin_data &bd, desfire::key_rights const &kr);
bin_data &operator<<(bin_data &bd, desfire::app_settings const &ks);
bin_data &operator<<(bin_data &bd, desfire::file_access_rights const &ar);
bin_data &operator<<(bin_data &bd, desfire::common_file_settings const &fs);
bin_data &operator<<(bin_data &bd, desfire::data_file_settings const &fs);
bin_data &operator<<(bin_data &bd, desfire::value_file_settings const &fs);
bin_data &operator<<(bin_data &bd, desfire::record_file_settings const &fs);
bin_data &operator<<(bin_data &bd, desfire::any_file_settings const &fs);
template <desfire::file_type Type>
bin_data &operator<<(bin_data &bd, desfire::file_settings<Type> const &fs);
#endif
}// namespace mlab
namespace desfire {
constexpr app_settings::app_settings(app_crypto crypto_, key_rights rights_, std::uint8_t max_num_keys_) : rights{rights_}, max_num_keys{max_num_keys_}, crypto{crypto_} {}
constexpr app_settings::app_settings(cipher_type cipher, key_rights rights_, std::uint8_t max_num_keys_) : rights{rights_}, max_num_keys{max_num_keys_}, crypto{app_crypto_from_cipher(cipher)} {}
unsigned storage_size::exponent() const {
return _flag >> bits::storage_size_exponent_shift;
}
bool storage_size::approx() const {
return 0 != (_flag & bits::storage_size_approx_bit);
}
std::size_t storage_size::bytes_lower_bound() const {
return 1 << exponent();
}
std::size_t storage_size::bytes_upper_bound() const {
return 1 << (approx() ? exponent() + 1 : exponent());
}
constexpr file_access_rights::file_access_rights(std::uint8_t single_key) : change{single_key}, read_write{single_key}, write{single_key}, read{single_key} {
// TODO: when C++20 is enabled, used is_constant_evaluated to issue a warning if single_key is out of range
}
constexpr file_access_rights::file_access_rights(no_key_t) : change{no_key}, read_write{no_key}, write{no_key}, read{no_key} {}
constexpr file_access_rights::file_access_rights(free_access_t) : change{free_access}, read_write{free_access}, write{free_access}, read{free_access} {}
constexpr file_access_rights::file_access_rights(key_actor<free_access_t> rw, key_actor<free_access_t> chg) : file_access_rights{no_key} {
read_write = rw;
change = chg;
}
constexpr file_access_rights::file_access_rights(key_actor<free_access_t> rw, key_actor<free_access_t> chg, key_actor<free_access_t> r, key_actor<free_access_t> w)
: file_access_rights{no_key} {
read_write = rw;
change = chg;
read = r;
write = w;
}
std::uint16_t file_access_rights::get_word() const {
return (std::uint16_t(read_write.get_nibble()) << bits::file_access_rights_read_write_shift) |
(std::uint16_t(change.get_nibble()) << bits::file_access_rights_change_shift) |
(std::uint16_t(read.get_nibble()) << bits::file_access_rights_read_shift) |
(std::uint16_t(write.get_nibble()) << bits::file_access_rights_write_shift);
}
void file_access_rights::set_word(std::uint16_t v) {
read_write.set_nibble(std::uint8_t((v >> bits::file_access_rights_read_write_shift) & 0b1111));
change.set_nibble(std::uint8_t((v >> bits::file_access_rights_change_shift) & 0b1111));
read.set_nibble(std::uint8_t((v >> bits::file_access_rights_read_shift) & 0b1111));
write.set_nibble(std::uint8_t((v >> bits::file_access_rights_write_shift) & 0b1111));
}
file_access_rights file_access_rights::from_word(std::uint16_t word) {
file_access_rights retval;
retval.set_word(word);
return retval;
}
bool file_access_rights::operator==(file_access_rights const &other) const {
return change == other.change and
read_write == other.read_write and
write == other.write and
read == other.read;
}
bool file_access_rights::operator!=(file_access_rights const &other) const {
return change != other.change or
read_write != other.read_write or
write != other.write or
read != other.read;
}
constexpr common_file_settings::common_file_settings(file_security security_, file_access_rights rights_) : security{security_}, rights{rights_} {}
bool desfire::key_rights::operator==(desfire::key_rights const &other) const {
return other.allowed_to_change_keys == allowed_to_change_keys and
other.create_delete_without_master_key == create_delete_without_master_key and
other.dir_access_without_auth == dir_access_without_auth and
other.config_changeable == config_changeable and
other.master_key_changeable == master_key_changeable;
}
bool desfire::key_rights::operator!=(desfire::key_rights const &other) const {
return not operator==(other);
}
}// namespace desfire
namespace mlab {
template <desfire::file_type Type>
bin_stream &operator>>(bin_stream &s, desfire::file_settings<Type> &fs) {
if (not s.bad()) {
s >> static_cast<desfire::common_file_settings &>(fs);
}
if (not s.bad()) {
s >> static_cast<typename desfire::file_settings<Type>::specific_file_settings &>(fs);
}
return s;
}
template <desfire::file_type Type>
bin_data &operator<<(bin_data &bd, desfire::file_settings<Type> const &fs) {
return bd
<< static_cast<desfire::common_file_settings const &>(fs)
<< static_cast<typename desfire::file_settings<Type>::specific_file_settings const &>(fs);
}
}// namespace mlab
#endif//DESFIRE_DATA_HPP