From fb37587efe867af8fff8676496d7216b02afd036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kleines=20Filmr=C3=B6llchen?= Date: Fri, 30 Jun 2023 20:03:42 +0200 Subject: [PATCH] LibCrypto: Implement a generic 16-bit CRC This is mostly a 16-bit version of the 8-bit CRC, using the same general byte-LUT algorithm. --- AK/Endian.h | 9 ++ Userland/Libraries/LibCrypto/Checksum/CRC16.h | 82 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 Userland/Libraries/LibCrypto/Checksum/CRC16.h diff --git a/AK/Endian.h b/AK/Endian.h index 3b4b34aa94..05743cb119 100644 --- a/AK/Endian.h +++ b/AK/Endian.h @@ -129,10 +129,19 @@ struct Traits> : public GenericTraits> { static constexpr bool is_trivially_serializable() { return Traits::is_trivially_serializable(); } }; +constexpr u16 bitswap(u16 v) +{ + v = ((v >> 1) & 0x5555) | ((v & 0x5555) << 1); // even & odd bits + v = ((v >> 2) & 0x3333) | ((v & 0x3333) << 2); // pairs + v = ((v >> 4) & 0x0F0F) | ((v & 0x0F0F) << 4); // nibbles + return ((v >> 8) & 0x00FF) | ((v & 0x00FF) << 8); // bytes +} + } #if USING_AK_GLOBALLY using AK::BigEndian; +using AK::bitswap; using AK::LittleEndian; using AK::NetworkOrdered; #endif diff --git a/Userland/Libraries/LibCrypto/Checksum/CRC16.h b/Userland/Libraries/LibCrypto/Checksum/CRC16.h new file mode 100644 index 0000000000..22112be7ba --- /dev/null +++ b/Userland/Libraries/LibCrypto/Checksum/CRC16.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, kleines Filmröllchen . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Crypto::Checksum { + +// A generic 16-bit Cyclic Redundancy Check. +// Just like CRC32, this class receives its polynomial little-endian. +// For example, the polynomial x¹⁶ + x¹² + x⁵ + 1 is represented as 0x8408. +template +class CRC16 : public ChecksumFunction { +public: + static constexpr u16 be_polynomial = bitswap(polynomial); + + // This is a big endian table, while CRC-32 uses a little endian table. + static constexpr auto generate_table() + { + Array data {}; + data[0] = 0; + u16 value = 0x8000; + auto i = 1u; + do { + if ((value & 0x8000) != 0) { + value = be_polynomial ^ (value << 1); + } else { + value = value << 1; + } + + for (auto j = 0u; j < i; ++j) { + data[i + j] = value ^ data[j]; + } + i <<= 1; + } while (i < 256); + + return data; + } + + static constexpr auto table = generate_table(); + + virtual ~CRC16() = default; + + CRC16() = default; + CRC16(ReadonlyBytes data) + { + update(data); + } + + CRC16(u16 initial_state, ReadonlyBytes data) + : m_state(initial_state) + { + update(data); + } + + // FIXME: This implementation is naive and slow. + // Figure out how to adopt the slicing-by-8 algorithm (see CRC32) for 16-bit polynomials. + virtual void update(ReadonlyBytes data) override + { + for (size_t i = 0; i < data.size(); i++) { + size_t table_index = ((m_state >> 8) ^ data.at(i)) & 0xFF; + m_state = (table[table_index] ^ (static_cast(m_state) << 8)) & 0xFFFF; + } + } + + virtual u16 digest() override + { + return m_state; + } + +private: + u16 m_state { 0 }; +}; + +}