From dad22c5d5a598cb05240752af6c5e2cd9edf1e4f Mon Sep 17 00:00:00 2001 From: stelar7 Date: Wed, 29 Jul 2020 20:24:54 +0200 Subject: [PATCH] LibCrypto: Add some checksum algorithms Namely CRC32 and Adler32 --- Libraries/LibCrypto/CMakeLists.txt | 2 + Libraries/LibCrypto/Checksum/Adler32.cpp | 46 ++++++++++ Libraries/LibCrypto/Checksum/Adler32.h | 58 +++++++++++++ Libraries/LibCrypto/Checksum/CRC32.cpp | 45 ++++++++++ Libraries/LibCrypto/Checksum/CRC32.h | 85 +++++++++++++++++++ .../LibCrypto/Checksum/ChecksumFunction.h | 40 +++++++++ Userland/test-crypto.cpp | 82 ++++++++++++++++++ 7 files changed, 358 insertions(+) create mode 100644 Libraries/LibCrypto/Checksum/Adler32.cpp create mode 100644 Libraries/LibCrypto/Checksum/Adler32.h create mode 100644 Libraries/LibCrypto/Checksum/CRC32.cpp create mode 100644 Libraries/LibCrypto/Checksum/CRC32.h create mode 100644 Libraries/LibCrypto/Checksum/ChecksumFunction.h diff --git a/Libraries/LibCrypto/CMakeLists.txt b/Libraries/LibCrypto/CMakeLists.txt index c644b1eec4..428f99dc1c 100644 --- a/Libraries/LibCrypto/CMakeLists.txt +++ b/Libraries/LibCrypto/CMakeLists.txt @@ -1,6 +1,8 @@ set(SOURCES BigInt/UnsignedBigInteger.cpp BigInt/SignedBigInteger.cpp + Checksum/Adler32.cpp + Checksum/CRC32.cpp Cipher/AES.cpp Hash/MD5.cpp Hash/SHA1.cpp diff --git a/Libraries/LibCrypto/Checksum/Adler32.cpp b/Libraries/LibCrypto/Checksum/Adler32.cpp new file mode 100644 index 0000000000..8f88fe79ed --- /dev/null +++ b/Libraries/LibCrypto/Checksum/Adler32.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, the SerenityOS developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace Crypto::Checksum { + +void Adler32::update(ReadonlyBytes data) +{ + for (size_t i = 0; i < data.size(); i++) { + m_state_a = (m_state_a + data.at(i)) % 65521; + m_state_b = (m_state_b + m_state_a) % 65521; + } +}; + +u32 Adler32::digest() +{ + return (m_state_b << 16) | m_state_a; +} + +} diff --git a/Libraries/LibCrypto/Checksum/Adler32.h b/Libraries/LibCrypto/Checksum/Adler32.h new file mode 100644 index 0000000000..c7aa7107ce --- /dev/null +++ b/Libraries/LibCrypto/Checksum/Adler32.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, the SerenityOS developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +namespace Crypto::Checksum { + +class Adler32 : public ChecksumFunction { +public: + Adler32() {} + Adler32(ReadonlyBytes data) + { + update(data); + } + + Adler32(u32 initial_a, u32 initial_b, ReadonlyBytes data) + : m_state_a(initial_a) + , m_state_b(initial_b) + { + update(data); + } + + void update(ReadonlyBytes data); + u32 digest(); + +private: + u32 m_state_a { 1 }; + u32 m_state_b { 0 }; +}; + +} diff --git a/Libraries/LibCrypto/Checksum/CRC32.cpp b/Libraries/LibCrypto/Checksum/CRC32.cpp new file mode 100644 index 0000000000..2019e969f0 --- /dev/null +++ b/Libraries/LibCrypto/Checksum/CRC32.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020, the SerenityOS developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace Crypto::Checksum { + +void CRC32::update(ReadonlyBytes data) +{ + for (size_t i = 0; i < data.size(); i++) { + m_state = table[(m_state ^ data.at(i)) & 0xFF] ^ (m_state >> 8); + } +}; + +u32 CRC32::digest() +{ + return ~m_state; +} + +} diff --git a/Libraries/LibCrypto/Checksum/CRC32.h b/Libraries/LibCrypto/Checksum/CRC32.h new file mode 100644 index 0000000000..6c0f06e1b7 --- /dev/null +++ b/Libraries/LibCrypto/Checksum/CRC32.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, the SerenityOS developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace Crypto::Checksum { + +struct Table { + u32 data[256]; + + constexpr Table() + : data() + { + for (auto i = 0; i < 256; i++) { + u32 value = i; + + for (auto j = 0; j < 8; j++) { + if (value & 1) { + value = 0xEDB88320 ^ (value >> 1); + } else { + value = value >> 1; + } + } + + data[i] = value; + } + } + + constexpr u32 operator[](int index) const { + return data[index]; + } +}; + +constexpr static auto table = Table(); + +class CRC32 : public ChecksumFunction { +public: + CRC32() {} + CRC32(ReadonlyBytes data) + { + update(data); + } + + CRC32(u32 initial_state, ReadonlyBytes data) + : m_state(initial_state) + { + update(data); + } + + void update(ReadonlyBytes data); + u32 digest(); + +private: + u32 m_state { ~0u }; +}; + +} diff --git a/Libraries/LibCrypto/Checksum/ChecksumFunction.h b/Libraries/LibCrypto/Checksum/ChecksumFunction.h new file mode 100644 index 0000000000..c62ff1d4d9 --- /dev/null +++ b/Libraries/LibCrypto/Checksum/ChecksumFunction.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, the SerenityOS developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace Crypto::Checksum { + +template +class ChecksumFunction { +public: + virtual void update(ReadonlyBytes data) = 0; + virtual ChecksumType digest() = 0; +}; + +} diff --git a/Userland/test-crypto.cpp b/Userland/test-crypto.cpp index 54720c5d47..c89cab2ece 100644 --- a/Userland/test-crypto.cpp +++ b/Userland/test-crypto.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -61,6 +63,7 @@ static bool g_some_test_failed = false; static bool encrypting = true; constexpr const char* DEFAULT_DIGEST_SUITE { "HMAC-SHA256" }; +constexpr const char* DEFAULT_CHECKSUM_SUITE { "CRC32" }; constexpr const char* DEFAULT_HASH_SUITE { "SHA256" }; constexpr const char* DEFAULT_CIPHER_SUITE { "AES_CBC" }; constexpr const char* DEFAULT_SERVER { "www.google.com" }; @@ -90,6 +93,10 @@ int tls_tests(); // Big Integer int bigint_tests(); +// Checksum +int adler32_tests(); +int crc32_tests(); + // stop listing tests void print_buffer(const ByteBuffer& buffer, int split) @@ -204,6 +211,18 @@ void aes_cbc(const char* message, size_t len) } } +void adler32(const char* message, size_t len) +{ + auto checksum = Crypto::Checksum::Adler32({ (const u8*)message, len }); + printf("%#10X\n", checksum.digest()); +} + +void crc32(const char* message, size_t len) +{ + auto checksum = Crypto::Checksum::CRC32({ (const u8*)message, len }); + printf("%#10X\n", checksum.digest()); +} + void md5(const char* message, size_t len) { auto digest = Crypto::Hash::MD5::hash((const u8*)message, len); @@ -293,6 +312,7 @@ auto main(int argc, char** argv) -> int puts("test-crypto modes"); puts("\tdigest - Access digest (authentication) functions"); puts("\thash - Access hash functions"); + puts("\tchecksum - Access checksum functions"); puts("\tencrypt -- Access encryption functions"); puts("\tdecrypt -- Access decryption functions"); puts("\ttls -- Connect to a peer over TLS 1.2"); @@ -332,6 +352,24 @@ auto main(int argc, char** argv) -> int printf("unknown hash function '%s'\n", suite); return 1; } + if (mode_sv == "checksum") { + if (suite == nullptr) + suite = DEFAULT_CHECKSUM_SUITE; + StringView suite_sv { suite }; + + if (suite_sv == "CRC32") { + if (run_tests) + return crc32_tests(); + return run(crc32); + } + if (suite_sv == "Adler32") { + if (run_tests) + return adler32_tests(); + return run(adler32); + } + printf("unknown checksum function '%s'\n", suite); + return 1; + } if (mode_sv == "digest") { if (suite == nullptr) suite = DEFAULT_DIGEST_SUITE; @@ -1558,6 +1596,50 @@ void tls_test_client_hello() loop.exec(); } +int adler32_tests() +{ + auto do_test = [](ReadonlyBytes input, u32 expected_result) { + I_TEST((CRC32)); + + auto pass = Crypto::Checksum::Adler32(input).digest() == expected_result; + + if (pass) { + PASS; + } else { + FAIL(Incorrect Result); + } + }; + + do_test(String("").bytes(), 0x1); + do_test(String("a").bytes(), 0x00620062); + do_test(String("abc").bytes(), 0x024d0127); + do_test(String("message digest").bytes(), 0x29750586); + do_test(String("abcdefghijklmnopqrstuvwxyz").bytes(), 0x90860b20); + + return g_some_test_failed ? 1 : 0; +} + +int crc32_tests() +{ + auto do_test = [](ReadonlyBytes input, u32 expected_result) { + I_TEST((Adler32)); + + auto pass = Crypto::Checksum::CRC32(input).digest() == expected_result; + + if (pass) { + PASS; + } else { + FAIL(Incorrect Result); + } + }; + + do_test(String("").bytes(), 0x0); + do_test(String("The quick brown fox jumps over the lazy dog").bytes(), 0x414FA339); + do_test(String("various CRC algorithms input data").bytes(), 0x9BD366AE); + + return g_some_test_failed ? 1 : 0; +} + int bigint_tests() { bigint_test_fibo500();