From a9bfaa02fcf19bbaf963ea2847ba43839c30e3ad Mon Sep 17 00:00:00 2001 From: fallenoak Date: Thu, 23 Mar 2023 12:48:16 -0500 Subject: [PATCH] feat(crypto): add ARC4 implementation --- storm/CMakeLists.txt | 1 + storm/Crypto.cpp | 57 +++++++++++++++++++++++++++++++++++++++ storm/Crypto.hpp | 10 +++++++ storm/crypto/SARC4Key.hpp | 12 +++++++++ test/Crypto.cpp | 47 ++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 storm/Crypto.cpp create mode 100644 storm/Crypto.hpp create mode 100644 storm/crypto/SARC4Key.hpp create mode 100644 test/Crypto.cpp diff --git a/storm/CMakeLists.txt b/storm/CMakeLists.txt index cf09951..c6f429a 100644 --- a/storm/CMakeLists.txt +++ b/storm/CMakeLists.txt @@ -4,6 +4,7 @@ check_cxx_compiler_flag(-Wno-invalid-offsetof HAS_NO_INVALID_OFFSETOF) file(GLOB STORM_SOURCES "*.cpp" "big/*.cpp" + "crypto/*.cpp" "hash/*.cpp" "queue/*.cpp" "string/*.cpp" diff --git a/storm/Crypto.cpp b/storm/Crypto.cpp new file mode 100644 index 0000000..0b8cae9 --- /dev/null +++ b/storm/Crypto.cpp @@ -0,0 +1,57 @@ +#include "storm/Crypto.hpp" +#include "storm/Error.hpp" +#include + +void SARC4PrepareKey(const void* data, uint32_t len, SARC4Key* key) { + STORM_ASSERT(data); + STORM_VALIDATE(data, ERROR_INVALID_PARAMETER); + + key->x = 0; + key->y = 0; + + // ARC4 key-scheduling algorithm + + for (uint32_t i = 0; i < 256; i++) { + key->state[i] = i; + } + + uint32_t j = 0; + for (uint32_t i = 0; i < 256; i++) { + j = (j + key->state[i] + static_cast(data)[i % len]) % 256; + + // Swap values + auto si = key->state[i]; + auto sj = key->state[j]; + key->state[i] = sj; + key->state[j] = si; + } +} + +void SARC4ProcessBuffer(void* data, uint32_t len, const SARC4Key* inKey, SARC4Key* outKey) { + if (inKey != outKey) { + memcpy(outKey, inKey, sizeof(SARC4Key)); + } + + // Note: The original implementation uses two loops. The first loop decrypts/encrypts data + // 4-bytes-at-a-time. The second loop decrypts/encrypts all bytes outside of that alignment + // 1-byte-at-a-time. For simplicity's sake, our implementation handles 1-byte-at-a-time. + + auto x = outKey->x; + auto y = outKey->y; + + for (uint32_t i = 0; i < len; i++) { + x += 1; + auto sx = outKey->state[x]; + + y += sx; + auto sy = outKey->state[y]; + + outKey->state[x] = sy; + outKey->state[y] = sx; + + static_cast(data)[i] ^= outKey->state[static_cast(sx + sy)]; + } + + outKey->x = x; + outKey->y = y; +} diff --git a/storm/Crypto.hpp b/storm/Crypto.hpp new file mode 100644 index 0000000..87e155e --- /dev/null +++ b/storm/Crypto.hpp @@ -0,0 +1,10 @@ +#ifndef STORM_CRYPTO_HPP +#define STORM_CRYPTO_HPP + +#include "storm/crypto/SARC4Key.hpp" + +void SARC4PrepareKey(const void* data, uint32_t len, SARC4Key* key); + +void SARC4ProcessBuffer(void* data, uint32_t len, const SARC4Key* inKey, SARC4Key* outKey); + +#endif diff --git a/storm/crypto/SARC4Key.hpp b/storm/crypto/SARC4Key.hpp new file mode 100644 index 0000000..f33d3e3 --- /dev/null +++ b/storm/crypto/SARC4Key.hpp @@ -0,0 +1,12 @@ +#ifndef STORM_CRYPTO_S_ARC4_KEY_HPP +#define STORM_CRYPTO_S_ARC4_KEY_HPP + +#include + +struct SARC4Key { + uint8_t state[256]; + uint8_t x; + uint8_t y; +}; + +#endif diff --git a/test/Crypto.cpp b/test/Crypto.cpp new file mode 100644 index 0000000..1a8e1f6 --- /dev/null +++ b/test/Crypto.cpp @@ -0,0 +1,47 @@ +#include "storm/Crypto.hpp" +#include "test/Test.hpp" +#include + +TEST_CASE("SARC4ProcessBuffer", "[crypto]") { + SECTION("correctly processes given non-zero input for given non-zero key") { + uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + + SARC4Key rc4Key; + SARC4PrepareKey(key, sizeof(key), &rc4Key); + + uint8_t input[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + uint8_t processed[] = { 0x75, 0xB7, 0x87, 0x80, 0x99, 0xE0, 0xC5, 0x96 }; + + SARC4ProcessBuffer(input, sizeof(input), &rc4Key, &rc4Key); + + REQUIRE(!memcmp(input, processed, sizeof(input))); + } + + SECTION("correctly processes given all-zero input for given non-zero key") { + uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + + SARC4Key rc4Key; + SARC4PrepareKey(key, sizeof(key), &rc4Key); + + uint8_t input[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t processed[] = { 0x74, 0x94, 0xC2, 0xE7, 0x10, 0x4B, 0x08, 0x79 }; + + SARC4ProcessBuffer(input, sizeof(input), &rc4Key, &rc4Key); + + REQUIRE(!memcmp(input, processed, sizeof(input))); + } + + SECTION("correctly processes given all-zero input for given all-zero key") { + uint8_t key[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + SARC4Key rc4Key; + SARC4PrepareKey(key, sizeof(key), &rc4Key); + + uint8_t input[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t processed[] = { 0xDE, 0x18, 0x89, 0x41, 0xA3, 0x37, 0x5D, 0x3A }; + + SARC4ProcessBuffer(input, sizeof(input), &rc4Key, &rc4Key); + + REQUIRE(!memcmp(input, processed, sizeof(input))); + } +}