Program Listing for File crypto_algo.hpp
↰ Return to documentation for file (libspookyaction/include/desfire/crypto_algo.hpp
)
//
// Created by Pietro Saccardi on 02/01/2021.
//
#ifndef DESFIRE_CRYPTO_ALGO_HPP
#define DESFIRE_CRYPTO_ALGO_HPP
#include <algorithm>
#include <cstdint>
#include <desfire/log.h>
#include <iterator>
#include <mlab/bin_data.hpp>
#include <mlab/mathutils.hpp>
#include <utility>
namespace desfire {
struct randbytes {
std::size_t n;
constexpr explicit randbytes(std::size_t len) : n{len} {}
};
static constexpr std::uint16_t crc16_init = 0x6363;
static constexpr std::uint32_t crc32_init = 0xffffffff;
[[nodiscard]] std::uint16_t compute_crc16(mlab::range<std::uint8_t const *> data, std::uint16_t init = crc16_init);
[[nodiscard]] std::uint32_t compute_crc32(mlab::range<std::uint8_t const *> data, std::uint32_t init = crc32_init);
[[nodiscard]] inline std::uint16_t compute_crc16(mlab::bin_data const &data, std::uint16_t init = crc16_init);
[[nodiscard]] inline std::uint32_t compute_crc32(mlab::bin_data const &data, std::uint32_t init = crc32_init);
[[nodiscard]] std::uint16_t compute_crc16(std::uint8_t data, std::uint16_t init = crc16_init);
[[nodiscard]] std::uint32_t compute_crc32(std::uint8_t data, std::uint32_t init = crc32_init);
template <class It>
void lshift_sequence(It begin, It end, unsigned lshift);
template <class Container>
void set_key_version(Container &c, std::uint8_t v);
template <class Container>
[[nodiscard]] std::uint8_t get_key_version(Container const &c);
template <class ByteIterator, class N, class Fn, class BytesContainer>
[[nodiscard]] std::pair<ByteIterator, bool> find_crc_tail(ByteIterator begin, ByteIterator end, Fn &&crc_fn, N init, std::size_t block_size,
bool incremental_crc, BytesContainer const &valid_padding_bytes);
template <class ByteIterator, class N, class Fn>
[[nodiscard]] std::pair<ByteIterator, bool> find_crc_tail(ByteIterator begin, ByteIterator end, Fn &&crc_fn, N init, std::size_t block_size,
bool incremental_crc);
}// namespace desfire
namespace mlab {
#ifndef DOXYGEN_SHOULD_SKIP_THIS
bin_data &operator<<(bin_data &bd, desfire::randbytes const &rndb);
#endif
}// namespace mlab
namespace desfire {
template <class It>
void lshift_sequence(It begin, It end, unsigned lshift) {
using value_type = typename std::iterator_traits<It>::value_type;
static_assert(std::is_integral_v<value_type> and std::is_unsigned_v<value_type>);
static constexpr unsigned value_nbits = sizeof(value_type) * 8;
const unsigned complementary_rshift = value_nbits - std::min(value_nbits, lshift);
if (begin != end) {
It prev = begin++;
for (; begin != end; prev = begin++) {
*prev = ((*prev) << lshift) | ((*begin) >> complementary_rshift);
}
*prev <<= lshift;
}
}
template <class ByteIterator, class N, class Fn, class BytesContainer>
std::pair<ByteIterator, bool> find_crc_tail(ByteIterator begin, ByteIterator end, Fn &&crc_fn, N init,
std::size_t block_size, bool incremental_crc,
BytesContainer const &valid_padding_bytes) {
static const auto nonzero_byte_pred = [&](std::uint8_t b) -> bool {
return std::find(std::begin(valid_padding_bytes), std::end(valid_padding_bytes), b) == std::end(valid_padding_bytes);
};
const bool multiple_of_block_size = std::distance(begin, end) % block_size == 0;
if (not multiple_of_block_size) {
DESFIRE_LOGE("Cannot scan for CRC tail if data length is not a multiple of the block size.");
}
if (begin != end and multiple_of_block_size) {
// Find the last nonzero byte, get and iterator to the element past that.
// You just have to scan the last block, and in the worst case, the last non-padding byte is the first
// byte of the last block.
// This is achieved by reverse scanning for a nonzero byte, and getting the underlying iterator.
// Since the reverse iterator holds an underlying iterator to the next element (in the normal traversal
// sense), we can just get that.
const auto rev_end = std::reverse_iterator<ByteIterator>(end);
auto end_payload = std::find_if(rev_end, rev_end + block_size, nonzero_byte_pred).base();
// Compute the crc until the supposed end of the payload
N crc = crc_fn(begin, end_payload, init);
while (crc != N(0) and end_payload != end) {
if (incremental_crc) {
// Update the crc with one byte at a time
crc = crc_fn(end_payload, std::next(end_payload), crc);
} else {
// Recalculate the crc on the whole new sequence
crc = crc_fn(begin, std::next(end_payload), init);
}
// Keep advancing the supposed end of the payload until end
++end_payload;
}
return {end_payload, crc == N(0)};
}
return {end, false};
}
template <class ByteIterator, class N, class Fn>
std::pair<ByteIterator, bool> find_crc_tail(ByteIterator begin, ByteIterator end, Fn &&crc_fn, N init, std::size_t block_size,
bool incremental_crc) {
static constexpr std::array<std::uint8_t, 2> default_padding_bytes = {0x00, 0x80};
return find_crc_tail(begin, end, std::forward<Fn>(crc_fn), init, block_size, incremental_crc, default_padding_bytes);
}
template <class Container>
void set_key_version(Container &c, std::uint8_t v) {
std::uint_fast8_t i = 0;
for (auto &b : c) {
static_assert(std::is_same_v<decltype(b), std::uint8_t &>);
if (++i > 8) {
break;
}
b = (b & 0b11111110) | (v >> 7);
v <<= 1;
}
}
template <class Container>
std::uint8_t get_key_version(Container const &c) {
std::uint8_t v = 0x0;
std::uint_fast8_t i = 0;
for (std::uint8_t b : c) {
if (++i > 8) {
break;
}
v = (v << 1) | (b & 0b00000001);
}
return v;
}
std::uint16_t compute_crc16(mlab::bin_data const &data, std::uint16_t init) {
return compute_crc16(data.data_view(), init);
}
std::uint32_t compute_crc32(mlab::bin_data const &data, std::uint32_t init) {
return compute_crc32(data.data_view(), init);
}
}// namespace desfire
#endif//DESFIRE_CRYPTO_ALGO_HPP