Fix CRC and hash calculations (#321)

* Fix CRC calculation, add more CRC parameters

Use the Boost CRC module to calculate the CRC values.
Add options for final xor value, reflectIn and reflectOut.
Fixes #320

* Cleanup Hash view combo box, add CRC8

* Use offset/size consistently

* Cleanup: unify processing data by chunks

* Change CRC algorithm back, drop boost dependency

This is mostly the original algorithm, with a few fixes and small
additions (support for reflect In / Out, final XOR value).

* Use size_t for file read size consistently
This commit is contained in:
RADICS Áron
2021-10-26 17:21:48 +02:00
committed by GitHub
parent cab1089e22
commit a6b8597f5a
5 changed files with 365 additions and 242 deletions

View File

@@ -15,6 +15,11 @@
#include <array>
#include <span>
#include <concepts>
#include <functional>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#if MBEDTLS_VERSION_MAJOR <= 2
@@ -37,77 +42,139 @@
#endif
namespace hex::crypt {
using namespace std::placeholders;
u16 crc16(prv::Provider* &data, u64 offset, size_t size, u16 polynomial, u16 init) {
const auto table = [polynomial] {
std::array<u16, 256> table;
for (u16 i = 0; i < 256; i++) {
u16 crc = 0;
u16 c = i;
for (u16 j = 0; j < 8; j++) {
if (((crc ^ c) & 0x0001U) != 0)
crc = (crc >> 1U) ^ polynomial;
else
crc >>= 1U;
c >>= 1U;
}
table[i] = crc;
}
return table;
}();
u16 crc = init;
template<std::invocable<unsigned char*, size_t> Func>
void processDataByChunks(prv::Provider* data, u64 offset, size_t size, Func func)
{
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
for (size_t bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const auto readSize = std::min(buffer.size(), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
for (size_t i = 0; i < readSize; i++) {
crc = (crc >> 8) ^ table[(crc ^ u16(buffer[i])) & 0x00FF];
}
func(buffer.data(), readSize);
}
return crc;
}
u32 crc32(prv::Provider* &data, u64 offset, size_t size, u32 polynomial, u32 init) {
const auto table = [polynomial] {
std::array<uint32_t, 256> table = {0};
template<typename T>
T reflect(T in, std::size_t bits)
{
T out{};
for (uint32_t i = 0; i < 256; i++) {
uint32_t c = i;
for (size_t j = 0; j < 8; j++) {
if (c & 1)
c = polynomial ^ (c >> 1);
else
c >>= 1;
for(std::size_t i = 0; i < bits; i++)
{
out <<= 1;
if (in & 0b1)
out |= 1;
in >>= 1;
}
return out;
}
template<typename T>
T reflect(T in)
{
if constexpr (sizeof(T) == 1)
{
T out{in};
out = ((out & 0xf0u) >> 4) | ((out & 0x0fu) << 4);
out = ((out & 0xccu) >> 2) | ((out & 0x33u) << 2);
out = ((out & 0xaau) >> 1) | ((out & 0x55u) << 1);
return out;
}
else
{
return reflect(in, sizeof(T) *8 );
}
}
class Crc {
// use reflected algorithm, so we reflect only if refin / refout is FALSE
// mask values, 0b1 << 64 is UB, so use 0b10 << 63
public:
using calc_type = uint64_t;
Crc(int bits, calc_type polynomial, calc_type init, calc_type xorout, bool refin, bool refout) :
m_bits(bits),
m_init(init & ((0b10ull << (bits-1)) - 1)),
m_xorout(xorout & ((0b10ull << (bits-1)) - 1)),
m_refin(refin),
m_refout(refout),
table([polynomial, bits](){
auto reflectedpoly= reflect(polynomial & ((0b10ull << (bits-1)) - 1), bits);
std::array<uint64_t, 256> table = {0};
for (uint32_t i = 0; i < 256; i++) {
uint64_t c = i;
for (std::size_t j = 0; j < 8; j++) {
if (c & 0b1)
c = reflectedpoly ^ (c >> 1);
else
c >>= 1;
}
table[i] = c;
}
table[i] = c;
}
return table;
}();
return table;
}()) {
reset();
};
uint32_t c = init;
std::array<u8, 512> buffer = { 0 };
void reset() {
c = reflect(m_init, m_bits);
}
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
void processBytes(const unsigned char *data, std::size_t size) {
for (std::size_t i = 0; i < size; i++) {
unsigned char d;
if (m_refin)
d = data[i];
else
d = reflect(data[i]);
for (size_t i = 0; i < readSize; i++) {
c = table[(c ^ buffer[i]) & 0xFF] ^ (c >> 8);
c = table[(c ^ d) & 0xFFL] ^ (c >> 8);
}
}
return ~c;
calc_type checksum() const {
if (m_refout)
return c ^ m_xorout;
else
return reflect(c, m_bits) ^ m_xorout;
}
private:
const int m_bits;
const calc_type m_init;
const calc_type m_xorout;
const bool m_refin;
const bool m_refout;
const std::array<uint64_t, 256> table;
calc_type c;
};
template<int bits>
auto calcCrc(prv::Provider* data, u64 offset, std::size_t size, u32 polynomial, u32 init, u32 xorout, bool reflectIn, bool reflectOut) {
Crc crc(bits, polynomial, init, xorout, reflectIn, reflectOut);
processDataByChunks(data, offset, size, std::bind(&Crc::processBytes, &crc, _1, _2));
return crc.checksum();
}
u16 crc8(prv::Provider* &data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorout, bool reflectIn, bool reflectOut) {
return calcCrc<8>(data, offset, size, polynomial, init, xorout, reflectIn, reflectOut);
}
u16 crc16(prv::Provider* &data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorout, bool reflectIn, bool reflectOut) {
return calcCrc<16>(data, offset, size, polynomial, init, xorout, reflectIn, reflectOut);
}
u32 crc32(prv::Provider* &data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorout, bool reflectIn, bool reflectOut) {
return calcCrc<32>(data, offset, size, polynomial, init, xorout, reflectIn, reflectOut);
}
@@ -119,12 +186,7 @@ namespace hex::crypt {
mbedtls_md5_starts(&ctx);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
mbedtls_md5_update(&ctx, buffer.data(), readSize);
}
processDataByChunks(data, offset, size, std::bind(mbedtls_md5_update, &ctx, _1, _2));
mbedtls_md5_finish(&ctx, result.data());
@@ -156,12 +218,7 @@ namespace hex::crypt {
mbedtls_sha1_starts(&ctx);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
mbedtls_sha1_update(&ctx, buffer.data(), readSize);
}
processDataByChunks(data, offset, size, std::bind(mbedtls_sha1_update, &ctx, _1, _2));
mbedtls_sha1_finish(&ctx, result.data());
@@ -193,12 +250,7 @@ namespace hex::crypt {
mbedtls_sha256_starts(&ctx, true);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
mbedtls_sha256_update(&ctx, buffer.data(), readSize);
}
processDataByChunks(data, offset, size, std::bind(mbedtls_sha256_update, &ctx, _1, _2));
mbedtls_sha256_finish(&ctx, result.data());
@@ -230,12 +282,7 @@ namespace hex::crypt {
mbedtls_sha256_starts(&ctx, false);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
mbedtls_sha256_update(&ctx, buffer.data(), readSize);
}
processDataByChunks(data, offset, size, std::bind(mbedtls_sha256_update, &ctx, _1, _2));
mbedtls_sha256_finish(&ctx, result.data());
@@ -267,12 +314,7 @@ namespace hex::crypt {
mbedtls_sha512_starts(&ctx, true);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
mbedtls_sha512_update(&ctx, buffer.data(), readSize);
}
processDataByChunks(data, offset, size, std::bind(mbedtls_sha512_update, &ctx, _1, _2));
mbedtls_sha512_finish(&ctx, result.data());
@@ -304,12 +346,7 @@ namespace hex::crypt {
mbedtls_sha512_starts(&ctx, false);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
mbedtls_sha512_update(&ctx, buffer.data(), readSize);
}
processDataByChunks(data, offset, size, std::bind(mbedtls_sha512_update, &ctx, _1, _2));
mbedtls_sha512_finish(&ctx, result.data());
@@ -445,4 +482,4 @@ namespace hex::crypt {
return aes(type, MBEDTLS_DECRYPT, key, nonce, iv, input);
}
}
}