From f98bfb78e817d66e6122ffa495ce842a81b0e2ad Mon Sep 17 00:00:00 2001 From: Jochen <97750753+Jochengehtab@users.noreply.github.com> Date: Sun, 21 Jun 2026 23:35:46 +0200 Subject: [PATCH 01/14] first working slow draft --- CMakeLists.txt | 10 +++- src/decrypt.cpp | 14 +++--- src/decrypt.h | 10 +--- src/encrypt.cpp | 15 +++--- src/encrypt.h | 11 +---- src/keyPair.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++ src/keyPair.h | 19 ++++---- tests/CMakeLists.txt | 30 ++++++++++++ tests/test_rsa.cpp | 77 +++++++++++++++++++++++++++++ 9 files changed, 254 insertions(+), 44 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test_rsa.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d782b2..5cfeb8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,14 @@ set(CMAKE_CXX_EXTENSIONS OFF) add_subdirectory(lib) add_subdirectory(src) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + include(CTest) + if (BUILD_TESTING) + add_subdirectory(tests) + endif() +else() + message(STATUS "Skipping CTest/tests because CMAKE_BUILD_TYPE != Debug") +endif() -target_link_libraries(RSA PRIVATE Base256) +target_link_libraries(RSA PUBLIC Base256) target_include_directories(RSA PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/src/decrypt.cpp b/src/decrypt.cpp index 3d43bad..bc1b08f 100644 --- a/src/decrypt.cpp +++ b/src/decrypt.cpp @@ -1,14 +1,15 @@ #include "decrypt.h" +#include "math_utils.h" #include -namespace core { -Decryptor::Decryptor(PrivateKey privKey) : key(std::move(privKey)) {} +using namespace operations::math; -std::string Decryptor::decrypt(const std::vector& ciphertext) const { +namespace core::decryptor { +std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { std::string plaintext; - const size_t blockSize = key.n.getBytes().size(); + const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size(); if (blockSize == 0 || ciphertext.size() % blockSize != 0) { std::cerr << "Decryption error: Invalid ciphertext block size alignment." << std::endl; return plaintext; @@ -23,12 +24,11 @@ std::string Decryptor::decrypt(const std::vector& ciphertext) const { const operations::Base256 c_num(chunk); // 3. Perform RSA mathematical operation: M = C^d mod n - operations::Base256 m_num = operations::Base256::modPow(c_num, key.d, key.n); + operations::Base256 m_num = modPow(c_num, keyPair.getPrivateKey().d, keyPair.getPrivateKey().n); // 4. Retrieve the decrypted byte value and convert it back to a character const auto& m_bytes = m_num.getBytes(); if (!m_bytes.empty()) { - // Assuming little-endian layout where index 0 is the least significant byte plaintext.push_back(static_cast(m_bytes[0])); } else { plaintext.push_back('\0'); // Fallback for a zero-value block @@ -37,4 +37,4 @@ std::string Decryptor::decrypt(const std::vector& ciphertext) const { return plaintext; } -} // namespace core \ No newline at end of file +} diff --git a/src/decrypt.h b/src/decrypt.h index 1831624..ee61fe9 100644 --- a/src/decrypt.h +++ b/src/decrypt.h @@ -7,16 +7,10 @@ #include "keyPair.h" namespace core { -class Decryptor { - private: - PrivateKey key; - - public: - // Constructor binds the decryption process to a specific Private Key - explicit Decryptor(PrivateKey privKey); +namespace decryptor { // Performs RSA decryption on a ciphertext byte vector - [[nodiscard]] std::string decrypt(const std::vector& ciphertext) const; + [[nodiscard]] std::string decrypt(keyPair& keyPair, const std::vector& ciphertext); }; } // namespace core diff --git a/src/encrypt.cpp b/src/encrypt.cpp index 5e0644f..94c5687 100644 --- a/src/encrypt.cpp +++ b/src/encrypt.cpp @@ -1,13 +1,14 @@ #include "encrypt.h" +#include "math_utils.h" -namespace core { -Encryptor::Encryptor(PublicKey pubKey) : key(std::move(pubKey)) {} +using namespace operations::math; -std::vector Encryptor::encrypt(const std::string& plaintext) const { +namespace core::encryptor { +std::vector encrypt(keyPair& keyPair, const std::string& plaintext) { std::vector ciphertext; // The ciphertext block size is determined by the byte-length of the modulus n - const size_t blockSize = key.n.getBytes().size(); + const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size(); if (blockSize == 0) return ciphertext; for (const char c : plaintext) { @@ -15,14 +16,12 @@ std::vector Encryptor::encrypt(const std::string& plaintext) const { const operations::Base256 m(static_cast(c)); // 2. Perform RSA mathematical operation: C = M^e mod n - operations::Base256 c_num = operations::Base256::modPow(m, key.e, key.n); + operations::Base256 c_num = modPow(m, keyPair.getPublicKey().e, keyPair.getPublicKey().n); // 3. Extract the raw bytes from the computed ciphertext number std::vector c_bytes = c_num.getBytes(); // 4. Padding: Pad the byte vector with trailing zeros up to the required block size. - // (Assuming little-endian layout, where the most significant bytes are placed at the - // end) while (c_bytes.size() < blockSize) { c_bytes.push_back(0); } @@ -33,4 +32,4 @@ std::vector Encryptor::encrypt(const std::string& plaintext) const { return ciphertext; } -} // namespace core \ No newline at end of file +} diff --git a/src/encrypt.h b/src/encrypt.h index a66606d..614894d 100644 --- a/src/encrypt.h +++ b/src/encrypt.h @@ -7,16 +7,9 @@ #include "keyPair.h" namespace core { -class Encryptor { - private: - PublicKey key; - - public: - // Constructor binds the encryption process to a specific Public Key - explicit Encryptor(PublicKey pubKey); - +namespace encryptor { // Performs RSA encryption on a plaintext string - [[nodiscard]] std::vector encrypt(const std::string& plaintext) const; + [[nodiscard]] std::vector encrypt(keyPair& keyPair, const std::string& plaintext) ; }; } // namespace core diff --git a/src/keyPair.cpp b/src/keyPair.cpp index e7ee7e0..e174361 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -4,6 +4,118 @@ #include #include +#if defined(_WIN32) +#include +#include +#pragma comment(lib, "bcrypt.lib") +#else +#include +#endif + +#include "math_utils.h" + + +namespace { + // 256 bytes = 2048 bits for prime p and q. + // Resulting in a 4096-bit RSA modulus n = p * q. + constexpr size_t PRIME_SIZE_BYTES = 256; + + // Reads cryptographically secure random bytes from the operating system + std::vector getSecureRandomBytes(size_t size) { + std::vector buffer(size); + #if defined(_WIN32) + // Windows BCrypt API + BCryptGenRandom(nullptr, buffer.data(), static_cast(size), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + #else + // Linux/macOS: getentropy() with /dev/urandom as a fallback + #if defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) + if (getentropy(buffer.data(), size) == 0) { + return buffer; + } + #endif + std::ifstream urandom("/dev/urandom", std::ios::binary); + if (urandom.is_open()) { + urandom.read(reinterpret_cast(buffer.data()), size); + } + #endif + return buffer; + } + + // Generates a random odd candidate with the most significant bit set + std::vector generateCandidateBytes() { + std::vector candidate = getSecureRandomBytes(PRIME_SIZE_BYTES); + + // Ensure the most significant bit (MSB) is set (little endian) + candidate[PRIME_SIZE_BYTES - 1] |= 0x80; + + // Ensure the number is odd (least significant bit = 1) + candidate[0] |= 0x01; + + return candidate; + } + + // Generates a cryptographically secure 2048-bit prime number + operations::Base256 generateSecurePrime() { + std::vector candidateBytes = generateCandidateBytes(); + operations::Base256 candidate(candidateBytes); + + // Search sequentially for the next prime using the math_utils library + while (!operations::math::isPrime(candidate)) { + candidate += operations::Base256(2); + } + return candidate; + } +} + +// Default Constructor: Generates a new secure 4096-bit RSA keypair +keyPair::keyPair() { + const operations::Base256 p = generateSecurePrime(); + operations::Base256 q = generateSecurePrime(); + + // Ensure p and q are not identical + while (p == q) { + q = generateSecurePrime(); + } + + operations::Base256 phi = (p - operations::Base256(1)) * (q - operations::Base256(1)); + const operations::Base256 e(65537); + + // Ensure e and phi are coprime + while (operations::math::gcd(e, phi) != operations::Base256(1)) { + q = generateSecurePrime(); + while (p == q) { + q = generateSecurePrime(); + } + phi = (p - operations::Base256(1)) * (q - operations::Base256(1)); + } + + const operations::Base256 n = p * q; + const operations::Base256 d = operations::math::modInverse(e, phi); + + public_key.n = n; + public_key.e = e; + + private_key.n = n; + private_key.d = d; +} + +// Import Constructor: Imports keys from Base64 encoded serialized strings +keyPair::keyPair(const std::string& publicKey, const std::string& privateKey) { + const std::vector pubBytes = base64Decode(publicKey); + s_deserialize(pubBytes, public_key.n, public_key.e); + + const std::vector privBytes = base64Decode(privateKey); + s_deserialize(privBytes, private_key.n, private_key.d); +} + +std::vector PublicKey::serialize() const { + return keyPair::s_serialize(n, e); +} + +std::vector PrivateKey::serialize() const { + return keyPair::s_serialize(n, d); +} + // Serializes two 4 bytes Byte Arrays with Big endian std::vector keyPair::s_serialize(const operations::Base256 &first, const operations::Base256 &second) { diff --git a/src/keyPair.h b/src/keyPair.h index fe693d9..5079694 100644 --- a/src/keyPair.h +++ b/src/keyPair.h @@ -35,24 +35,21 @@ class keyPair { static constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - static std::vector s_serialize(const operations::Base256 &first, - const operations::Base256 &second); - static bool s_deserialize(const std::vector &data, operations::Base256 &outFirst, - operations::Base256 &outSecond); public: - keyPair() { - // Currently using dummy values - public_key.n = operations::Base256(937131); - public_key.e = operations::Base256(65537); + keyPair(); - private_key.n = operations::Base256(937131); - private_key.d = operations::Base256(129381); - } + keyPair(const std::string& publicKey, const std::string& privateKey); PublicKey getPublicKey() { return public_key; } PrivateKey getPrivateKey() { return private_key; } + static std::vector s_serialize(const operations::Base256 &first, + const operations::Base256 &second); + static bool s_deserialize(const std::vector + &data, operations::Base256 &outFirst, + operations::Base256 &outSecond); + // Base64 helper functions static std::string base64Encode(const std::vector &data); static std::vector base64Decode(std::string data); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..073ab84 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +# Prevent CMake from re-downloading Catch2 every time CLion reloads +set(FETCHCONTENT_UPDATES_DISCONNECTED ON CACHE BOOL "Don't update FetchContent on every config" FORCE) + +# Download Catch2 +include(FetchContent) +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.11.0 +) +FetchContent_MakeAvailable(Catch2) + +# Find Catch 2 CMakeList +list(APPEND CMAKE_MODULE_PATH ${Catch2_SOURCE_DIR}/extras) +include(Catch) + +add_executable(RSA-Tests + test_rsa.cpp +) +target_link_libraries(RSA-Tests + PRIVATE + RSA + Catch2::Catch2WithMain +) + +# Catch2 integration in CTest +catch_discover_tests(RSA-Tests + TEST_PREFIX rsa: + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/tests/test_rsa.cpp b/tests/test_rsa.cpp new file mode 100644 index 0000000..6a53d4c --- /dev/null +++ b/tests/test_rsa.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +#include "encrypt.h" +#include "decrypt.h" +#include "keyPair.h" + +using core::encryptor::encrypt; +using core::decryptor::decrypt; + +TEST_CASE("RSA Core: Basic Encryption and Decryption Roundtrip") { + static keyPair pair; + + SECTION("Decrypting encrypted normal string recovers the original text") { + std::string plaintext = "Hello, C++ World!"; + + std::vector ciphertext = encrypt(pair, plaintext); + + REQUIRE_FALSE(ciphertext.empty()); + + std::string recovered = decrypt(pair, ciphertext); + + REQUIRE(recovered == plaintext); + } + + SECTION("Decrypting encrypted empty string recovers empty string") { + std::string plaintext = ""; + + std::vector ciphertext = encrypt(pair, plaintext); + std::string recovered = decrypt(pair, ciphertext); + + REQUIRE(recovered == plaintext); + } + + SECTION("Correct handling of spaces, numbers, and special symbols") { + std::string plaintext = "RSA_4096_Test! @#$%^&*()_+ 12345"; + + std::vector ciphertext = encrypt(pair, plaintext); + std::string recovered = decrypt(pair, ciphertext); + + REQUIRE(recovered == plaintext); + } +} + +TEST_CASE("RSA Core: Security and Key Isolation") { + static keyPair pairA; + static keyPair pairB; + + std::string plaintext = "Highly Confidential Cryptographic Data"; + + std::vector ciphertext = encrypt(pairA, plaintext); + + SECTION("Decrypting with the incorrect keypair must not recover the plaintext") { + std::string recovered = decrypt(pairB, ciphertext); + + REQUIRE(recovered != plaintext); + } +} + +TEST_CASE("RSA Core: Key Serialization and Base64 Import/Export") { + static keyPair originalPair; + + std::string pubBase64 = keyPair::base64Encode(originalPair.getPublicKey().serialize()); + std::string privBase64 = keyPair::base64Encode(originalPair.getPrivateKey().serialize()); + + REQUIRE_FALSE(pubBase64.empty()); + REQUIRE_FALSE(privBase64.empty()); + + keyPair importedPair(pubBase64, privBase64); + + std::string plaintext = "Verification of Imported Keys"; + std::vector ciphertext = encrypt(importedPair, plaintext); + std::string recovered = decrypt(importedPair, ciphertext); + + REQUIRE(recovered == plaintext); +} \ No newline at end of file From 2d9d908d0f3221ea387bb0ae375ced6290f918e6 Mon Sep 17 00:00:00 2001 From: Jochen <97750753+Jochengehtab@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:53:36 +0200 Subject: [PATCH 02/14] build in release mode --- CMakeLists.txt | 6 ------ lib/Base256 | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cfeb8c..7801308 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,8 @@ set(CMAKE_CXX_EXTENSIONS OFF) add_subdirectory(lib) add_subdirectory(src) -if (CMAKE_BUILD_TYPE STREQUAL "Debug") include(CTest) - if (BUILD_TESTING) add_subdirectory(tests) - endif() -else() - message(STATUS "Skipping CTest/tests because CMAKE_BUILD_TYPE != Debug") -endif() target_link_libraries(RSA PUBLIC Base256) target_include_directories(RSA PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/lib/Base256 b/lib/Base256 index 3fa56dd..d3f446e 160000 --- a/lib/Base256 +++ b/lib/Base256 @@ -1 +1 @@ -Subproject commit 3fa56dd387614d8cf67438df2e28ec34636452eb +Subproject commit d3f446eb6237a0891f17eaebc939bf419ea42a7c From 1f2341ceda62a2449e0bbec601c600f137ae8b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:00:59 +0200 Subject: [PATCH 03/14] Make includes public --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7801308..cb6ef34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,4 +12,4 @@ add_subdirectory(src) add_subdirectory(tests) target_link_libraries(RSA PUBLIC Base256) -target_include_directories(RSA PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file +target_include_directories(RSA PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file From 924d1588270a59b09c3541654e4fb1fc5249c74a Mon Sep 17 00:00:00 2001 From: LordofGhost <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:56:51 +0000 Subject: [PATCH 04/14] Apply Clang formatting --- src/decrypt.cpp | 10 +++-- src/decrypt.h | 6 +-- src/encrypt.cpp | 5 ++- src/encrypt.h | 6 +-- src/keyPair.cpp | 94 ++++++++++++++++++++++------------------------ src/keyPair.h | 8 ++-- tests/test_rsa.cpp | 4 +- 7 files changed, 65 insertions(+), 68 deletions(-) diff --git a/src/decrypt.cpp b/src/decrypt.cpp index bc1b08f..00d47bb 100644 --- a/src/decrypt.cpp +++ b/src/decrypt.cpp @@ -1,12 +1,13 @@ #include "decrypt.h" -#include "math_utils.h" #include +#include "math_utils.h" + using namespace operations::math; namespace core::decryptor { -std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { +std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { std::string plaintext; const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size(); @@ -24,7 +25,8 @@ std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { const operations::Base256 c_num(chunk); // 3. Perform RSA mathematical operation: M = C^d mod n - operations::Base256 m_num = modPow(c_num, keyPair.getPrivateKey().d, keyPair.getPrivateKey().n); + operations::Base256 m_num = + modPow(c_num, keyPair.getPrivateKey().d, keyPair.getPrivateKey().n); // 4. Retrieve the decrypted byte value and convert it back to a character const auto& m_bytes = m_num.getBytes(); @@ -37,4 +39,4 @@ std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { return plaintext; } -} +} // namespace core::decryptor diff --git a/src/decrypt.h b/src/decrypt.h index ee61fe9..7884d13 100644 --- a/src/decrypt.h +++ b/src/decrypt.h @@ -9,9 +9,9 @@ namespace core { namespace decryptor { - // Performs RSA decryption on a ciphertext byte vector - [[nodiscard]] std::string decrypt(keyPair& keyPair, const std::vector& ciphertext); -}; +// Performs RSA decryption on a ciphertext byte vector +[[nodiscard]] std::string decrypt(keyPair& keyPair, const std::vector& ciphertext); +}; // namespace decryptor } // namespace core #endif \ No newline at end of file diff --git a/src/encrypt.cpp b/src/encrypt.cpp index 94c5687..7e1d15d 100644 --- a/src/encrypt.cpp +++ b/src/encrypt.cpp @@ -1,10 +1,11 @@ #include "encrypt.h" + #include "math_utils.h" using namespace operations::math; namespace core::encryptor { -std::vector encrypt(keyPair& keyPair, const std::string& plaintext) { +std::vector encrypt(keyPair& keyPair, const std::string& plaintext) { std::vector ciphertext; // The ciphertext block size is determined by the byte-length of the modulus n @@ -32,4 +33,4 @@ std::vector encrypt(keyPair& keyPair, const std::string& plaintext) { return ciphertext; } -} +} // namespace core::encryptor diff --git a/src/encrypt.h b/src/encrypt.h index 614894d..4ae913a 100644 --- a/src/encrypt.h +++ b/src/encrypt.h @@ -8,9 +8,9 @@ namespace core { namespace encryptor { - // Performs RSA encryption on a plaintext string - [[nodiscard]] std::vector encrypt(keyPair& keyPair, const std::string& plaintext) ; -}; +// Performs RSA encryption on a plaintext string +[[nodiscard]] std::vector encrypt(keyPair& keyPair, const std::string& plaintext); +}; // namespace encryptor } // namespace core #endif \ No newline at end of file diff --git a/src/keyPair.cpp b/src/keyPair.cpp index e174361..caa322e 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -5,8 +5,8 @@ #include #if defined(_WIN32) -#include #include +#include #pragma comment(lib, "bcrypt.lib") #else #include @@ -14,58 +14,58 @@ #include "math_utils.h" - namespace { - // 256 bytes = 2048 bits for prime p and q. - // Resulting in a 4096-bit RSA modulus n = p * q. - constexpr size_t PRIME_SIZE_BYTES = 256; - - // Reads cryptographically secure random bytes from the operating system - std::vector getSecureRandomBytes(size_t size) { - std::vector buffer(size); - #if defined(_WIN32) - // Windows BCrypt API - BCryptGenRandom(nullptr, buffer.data(), static_cast(size), BCRYPT_USE_SYSTEM_PREFERRED_RNG); - #else - // Linux/macOS: getentropy() with /dev/urandom as a fallback - #if defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) - if (getentropy(buffer.data(), size) == 0) { - return buffer; - } - #endif - std::ifstream urandom("/dev/urandom", std::ios::binary); - if (urandom.is_open()) { - urandom.read(reinterpret_cast(buffer.data()), size); - } - #endif +// 256 bytes = 2048 bits for prime p and q. +// Resulting in a 4096-bit RSA modulus n = p * q. +constexpr size_t PRIME_SIZE_BYTES = 256; + +// Reads cryptographically secure random bytes from the operating system +std::vector getSecureRandomBytes(size_t size) { + std::vector buffer(size); +#if defined(_WIN32) + // Windows BCrypt API + BCryptGenRandom(nullptr, buffer.data(), static_cast(size), + BCRYPT_USE_SYSTEM_PREFERRED_RNG); +#else +// Linux/macOS: getentropy() with /dev/urandom as a fallback +#if defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) + if (getentropy(buffer.data(), size) == 0) { return buffer; } +#endif + std::ifstream urandom("/dev/urandom", std::ios::binary); + if (urandom.is_open()) { + urandom.read(reinterpret_cast(buffer.data()), size); + } +#endif + return buffer; +} - // Generates a random odd candidate with the most significant bit set - std::vector generateCandidateBytes() { - std::vector candidate = getSecureRandomBytes(PRIME_SIZE_BYTES); +// Generates a random odd candidate with the most significant bit set +std::vector generateCandidateBytes() { + std::vector candidate = getSecureRandomBytes(PRIME_SIZE_BYTES); - // Ensure the most significant bit (MSB) is set (little endian) - candidate[PRIME_SIZE_BYTES - 1] |= 0x80; + // Ensure the most significant bit (MSB) is set (little endian) + candidate[PRIME_SIZE_BYTES - 1] |= 0x80; - // Ensure the number is odd (least significant bit = 1) - candidate[0] |= 0x01; + // Ensure the number is odd (least significant bit = 1) + candidate[0] |= 0x01; - return candidate; - } + return candidate; +} - // Generates a cryptographically secure 2048-bit prime number - operations::Base256 generateSecurePrime() { - std::vector candidateBytes = generateCandidateBytes(); - operations::Base256 candidate(candidateBytes); +// Generates a cryptographically secure 2048-bit prime number +operations::Base256 generateSecurePrime() { + std::vector candidateBytes = generateCandidateBytes(); + operations::Base256 candidate(candidateBytes); - // Search sequentially for the next prime using the math_utils library - while (!operations::math::isPrime(candidate)) { - candidate += operations::Base256(2); - } - return candidate; + // Search sequentially for the next prime using the math_utils library + while (!operations::math::isPrime(candidate)) { + candidate += operations::Base256(2); } + return candidate; } +} // namespace // Default Constructor: Generates a new secure 4096-bit RSA keypair keyPair::keyPair() { @@ -100,7 +100,7 @@ keyPair::keyPair() { } // Import Constructor: Imports keys from Base64 encoded serialized strings -keyPair::keyPair(const std::string& publicKey, const std::string& privateKey) { +keyPair::keyPair(const std::string &publicKey, const std::string &privateKey) { const std::vector pubBytes = base64Decode(publicKey); s_deserialize(pubBytes, public_key.n, public_key.e); @@ -108,13 +108,9 @@ keyPair::keyPair(const std::string& publicKey, const std::string& privateKey) { s_deserialize(privBytes, private_key.n, private_key.d); } -std::vector PublicKey::serialize() const { - return keyPair::s_serialize(n, e); -} +std::vector PublicKey::serialize() const { return keyPair::s_serialize(n, e); } -std::vector PrivateKey::serialize() const { - return keyPair::s_serialize(n, d); -} +std::vector PrivateKey::serialize() const { return keyPair::s_serialize(n, d); } // Serializes two 4 bytes Byte Arrays with Big endian std::vector keyPair::s_serialize(const operations::Base256 &first, diff --git a/src/keyPair.h b/src/keyPair.h index 5079694..b1570d0 100644 --- a/src/keyPair.h +++ b/src/keyPair.h @@ -35,19 +35,17 @@ class keyPair { static constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - public: keyPair(); - keyPair(const std::string& publicKey, const std::string& privateKey); + keyPair(const std::string &publicKey, const std::string &privateKey); PublicKey getPublicKey() { return public_key; } PrivateKey getPrivateKey() { return private_key; } static std::vector s_serialize(const operations::Base256 &first, - const operations::Base256 &second); - static bool s_deserialize(const std::vector - &data, operations::Base256 &outFirst, + const operations::Base256 &second); + static bool s_deserialize(const std::vector &data, operations::Base256 &outFirst, operations::Base256 &outSecond); // Base64 helper functions diff --git a/tests/test_rsa.cpp b/tests/test_rsa.cpp index 6a53d4c..8f335fb 100644 --- a/tests/test_rsa.cpp +++ b/tests/test_rsa.cpp @@ -2,12 +2,12 @@ #include #include -#include "encrypt.h" #include "decrypt.h" +#include "encrypt.h" #include "keyPair.h" -using core::encryptor::encrypt; using core::decryptor::decrypt; +using core::encryptor::encrypt; TEST_CASE("RSA Core: Basic Encryption and Decryption Roundtrip") { static keyPair pair; From 5619b9b4991676072367c3a6b5b8f85b52496833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:29:11 +0200 Subject: [PATCH 05/14] Update run unit tests only on release build --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dd610c..4953693 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,8 @@ jobs: - name: Build Project run: cmake --build build --config ${{ matrix.build-configuration }} - - name: Run Catch2 Unit Tests + - name: Run Catch2 Unit Tests (Release) + if: matrix.build-configuration == 'Release' # Runs the unit tests you specified run: ctest --test-dir build -C ${{ matrix.build-configuration }} --output-on-failure @@ -107,4 +108,4 @@ jobs: gh release create "$TAG_NAME" ./release-artifacts/*.zip \ --title "Development Build $TAG_NAME" \ --prerelease \ - --generate-notes \ No newline at end of file + --generate-notes From d9d17e5c24214c017f1e77892d923b34a83d2423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:33:07 +0200 Subject: [PATCH 06/14] Add windows.h for win platform --- src/keyPair.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/keyPair.h b/src/keyPair.h index b1570d0..6907935 100644 --- a/src/keyPair.h +++ b/src/keyPair.h @@ -5,6 +5,9 @@ #include #include #include +#ifdef _WIN32 +#include +#endif #include "base256.h" #include "key_fwd.h" From 8087ad0a6b6db180a22aaecf36c4cd5d42cb5efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:47:25 +0200 Subject: [PATCH 07/14] Link bcrypt in cmake on windows --- src/CMakeLists.txt | 4 ++++ src/keyPair.cpp | 14 ++++++++++---- src/keyPair.h | 3 --- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cc56a1..fef04ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,3 +9,7 @@ target_include_directories(RSA PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) + +if (WIN32) + target_link_libraries(RSA PRIVATE bcrypt) +endif () diff --git a/src/keyPair.cpp b/src/keyPair.cpp index caa322e..3552d13 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -2,12 +2,15 @@ #include #include +#include #include #if defined(_WIN32) -#include +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#endif #include -#pragma comment(lib, "bcrypt.lib") +#include #else #include #endif @@ -24,8 +27,11 @@ std::vector getSecureRandomBytes(size_t size) { std::vector buffer(size); #if defined(_WIN32) // Windows BCrypt API - BCryptGenRandom(nullptr, buffer.data(), static_cast(size), - BCRYPT_USE_SYSTEM_PREFERRED_RNG); + const NTSTATUS status = BCryptGenRandom(nullptr, buffer.data(), static_cast(size), + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (status < 0) { + throw std::runtime_error("BCryptGenRandom failed"); + } #else // Linux/macOS: getentropy() with /dev/urandom as a fallback #if defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) diff --git a/src/keyPair.h b/src/keyPair.h index 6907935..b1570d0 100644 --- a/src/keyPair.h +++ b/src/keyPair.h @@ -5,9 +5,6 @@ #include #include #include -#ifdef _WIN32 -#include -#endif #include "base256.h" #include "key_fwd.h" From dc2e7b92af2519e66cbad9bb827916faff5deddb Mon Sep 17 00:00:00 2001 From: LordofGhost <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 15:50:53 +0000 Subject: [PATCH 08/14] Apply Clang formatting --- src/keyPair.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyPair.cpp b/src/keyPair.cpp index 3552d13..249ba7a 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -9,8 +9,8 @@ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif -#include #include +#include #else #include #endif From c9f9bce5d8d552cf5ef4d4cb3223a746cd725d42 Mon Sep 17 00:00:00 2001 From: Jochen <97750753+Jochengehtab@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:37:22 +0200 Subject: [PATCH 09/14] use 64 bit chunks --- src/decrypt.cpp | 15 +++++++------- src/encrypt.cpp | 21 ++++++++----------- src/helper.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ src/keyPair.cpp | 21 +++++++++++-------- 4 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 src/helper.h diff --git a/src/decrypt.cpp b/src/decrypt.cpp index 00d47bb..c9cd01d 100644 --- a/src/decrypt.cpp +++ b/src/decrypt.cpp @@ -1,8 +1,8 @@ #include "decrypt.h" #include - #include "math_utils.h" +#include "helper.h" using namespace operations::math; @@ -10,7 +10,8 @@ namespace core::decryptor { std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { std::string plaintext; - const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size(); + // The block size in bytes is determined by the size of modulus n multiplied by 8 (64 bits per limb) + const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size() * 8; if (blockSize == 0 || ciphertext.size() % blockSize != 0) { std::cerr << "Decryption error: Invalid ciphertext block size alignment." << std::endl; return plaintext; @@ -19,17 +20,17 @@ std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { // Process the ciphertext block-by-block using the fixed block size for (size_t i = 0; i < ciphertext.size(); i += blockSize) { // 1. Extract a single block chunk - std::vector chunk(ciphertext.begin() + i, ciphertext.begin() + i + blockSize); + std::vector chunk(ciphertext.begin() + i, ciphertext.begin() + i + blockSize); - // 2. Construct a Base256 representation from the extracted block chunk - const operations::Base256 c_num(chunk); + // 2. Construct representation from the extracted block chunk (using 64-bit limbs) + const operations::Base256 c_num(bytesToByteArray(chunk)); // 3. Perform RSA mathematical operation: M = C^d mod n operations::Base256 m_num = modPow(c_num, keyPair.getPrivateKey().d, keyPair.getPrivateKey().n); // 4. Retrieve the decrypted byte value and convert it back to a character - const auto& m_bytes = m_num.getBytes(); + std::vector m_bytes = byteArrayToBytes(m_num.getBytes()); if (!m_bytes.empty()) { plaintext.push_back(static_cast(m_bytes[0])); } else { @@ -39,4 +40,4 @@ std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { return plaintext; } -} // namespace core::decryptor +} // namespace core::decryptor \ No newline at end of file diff --git a/src/encrypt.cpp b/src/encrypt.cpp index 7e1d15d..a88f706 100644 --- a/src/encrypt.cpp +++ b/src/encrypt.cpp @@ -1,6 +1,6 @@ #include "encrypt.h" - #include "math_utils.h" +#include "helper.h" using namespace operations::math; @@ -8,29 +8,24 @@ namespace core::encryptor { std::vector encrypt(keyPair& keyPair, const std::string& plaintext) { std::vector ciphertext; - // The ciphertext block size is determined by the byte-length of the modulus n - const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size(); + // The block size in bytes is determined by the size of modulus n multiplied by 8 (64 bits per limb) + const size_t blockSize = keyPair.getPublicKey().n.getBytes().size() * 8; if (blockSize == 0) return ciphertext; for (const char c : plaintext) { - // 1. Convert the character byte to an arbitrary-precision Base256 representation + // 1. Convert the character byte to the internal 64-bit limb representation const operations::Base256 m(static_cast(c)); // 2. Perform RSA mathematical operation: C = M^e mod n operations::Base256 c_num = modPow(m, keyPair.getPublicKey().e, keyPair.getPublicKey().n); - // 3. Extract the raw bytes from the computed ciphertext number - std::vector c_bytes = c_num.getBytes(); - - // 4. Padding: Pad the byte vector with trailing zeros up to the required block size. - while (c_bytes.size() < blockSize) { - c_bytes.push_back(0); - } + // 3. Extract raw bytes from the computed ciphertext number (padded to target block size) + std::vector c_bytes = byteArrayToBytes(c_num.getBytes(), blockSize); - // 5. Append the padded block to the final ciphertext vector + // 4. Append the padded block to the final ciphertext vector ciphertext.insert(ciphertext.end(), c_bytes.begin(), c_bytes.end()); } return ciphertext; } -} // namespace core::encryptor +} // namespace core::encryptor \ No newline at end of file diff --git a/src/helper.h b/src/helper.h new file mode 100644 index 0000000..8be61db --- /dev/null +++ b/src/helper.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include +#include + +#include "base256.h" +#include "key_fwd.h" + +// Converts a little-endian byte array to a 64-bit word array +[[nodiscard]] inline ByteArray bytesToByteArray(const std::vector& bytes) { + if (bytes.empty()) return {0}; + ByteArray data; + data.reserve((bytes.size() + 7) / 8); + for (size_t i = 0; i < bytes.size(); i += 8) { + uint64_t val = 0; + for (size_t j = 0; j < 8 && (i + j) < bytes.size(); ++j) { + val |= (static_cast(bytes[i + j]) << (j * 8)); + } + data.push_back(val); + } + // Remove trailing zeros to normalize representation + while (data.size() > 1 && data.back() == 0) { + data.pop_back(); + } + return data; +} + +// Converts a 64-bit word array to a little-endian byte array +[[nodiscard]] inline std::vector byteArrayToBytes(const ByteArray& data, size_t targetSize = 0) { + std::vector bytes; + bytes.reserve(data.size() * 8); + for (uint64_t word : data) { + for (int i = 0; i < 8; ++i) { + bytes.push_back(static_cast((word >> (i * 8)) & 0xFF)); + } + } + if (targetSize == 0) { + // Trim trailing zeros but keep at least 1 byte if the number is 0 + while (bytes.size() > 1 && bytes.back() == 0) { + bytes.pop_back(); + } + } else { + // Adjust array size to match the exact block or key size constraint + if (bytes.size() < targetSize) { + bytes.resize(targetSize, 0); + } else if (bytes.size() > targetSize) { + while (bytes.size() > targetSize && bytes.back() == 0) { + bytes.pop_back(); + } + } + } + return bytes; +} \ No newline at end of file diff --git a/src/keyPair.cpp b/src/keyPair.cpp index 249ba7a..960b07f 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -16,6 +16,7 @@ #endif #include "math_utils.h" +#include "helper.h" namespace { // 256 bytes = 2048 bits for prime p and q. @@ -63,7 +64,8 @@ std::vector generateCandidateBytes() { // Generates a cryptographically secure 2048-bit prime number operations::Base256 generateSecurePrime() { std::vector candidateBytes = generateCandidateBytes(); - operations::Base256 candidate(candidateBytes); + // Convert 256 byte vector to Base256 representation using 64-bit limbs + operations::Base256 candidate(bytesToByteArray(candidateBytes)); // Search sequentially for the next prime using the math_utils library while (!operations::math::isPrime(candidate)) { @@ -118,12 +120,14 @@ std::vector PublicKey::serialize() const { return keyPair::s_serialize( std::vector PrivateKey::serialize() const { return keyPair::s_serialize(n, d); } -// Serializes two 4 bytes Byte Arrays with Big endian +// Serializes two Base256 fields with Big-endian size headers std::vector keyPair::s_serialize(const operations::Base256 &first, const operations::Base256 &second) { std::vector serialized; - const auto &firstBytes = first.getBytes(); - const auto &secondBytes = second.getBytes(); + + // Safely extract the raw byte stream from the 64-bit limb vectors + std::vector firstBytes = byteArrayToBytes(first.getBytes()); + std::vector secondBytes = byteArrayToBytes(second.getBytes()); uint32_t firstSize = firstBytes.size(); uint32_t secondSize = secondBytes.size(); @@ -145,7 +149,7 @@ std::vector keyPair::s_serialize(const operations::Base256 &first, return serialized; } -// Deserializes two 4 bytes Byte Arrays with Big endian +// Deserializes two Base256 fields with Big-endian size headers bool keyPair::s_deserialize(const std::vector &data, operations::Base256 &outFirst, operations::Base256 &outSecond) { if (data.size() < 8) return false; @@ -177,8 +181,9 @@ bool keyPair::s_deserialize(const std::vector &data, operations::Base25 secondBegin + static_cast::difference_type>(secondSize); std::vector secondBytes(secondBegin, secondEnd); - outFirst = operations::Base256(firstBytes); - outSecond = operations::Base256(secondBytes); + // Convert deserialized byte streams back into 64-bit limb vectors + outFirst = operations::Base256(bytesToByteArray(firstBytes)); + outSecond = operations::Base256(bytesToByteArray(secondBytes)); return true; } @@ -242,4 +247,4 @@ uint8_t keyPair::getBase64Index(char letter) { } } return 0; -} +} \ No newline at end of file From 5b7d1ce7dc5092577d26fad6fa843d9367302da6 Mon Sep 17 00:00:00 2001 From: Jochen <97750753+Jochengehtab@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:53:59 +0200 Subject: [PATCH 10/14] fix order --- src/keyPair.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyPair.cpp b/src/keyPair.cpp index 960b07f..d3630e5 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -9,8 +9,8 @@ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif -#include #include +#include #else #include #endif From 1da646476638edfb9ae12b1504b0bca30fd1198e Mon Sep 17 00:00:00 2001 From: Jochengehtab <97750753+Jochengehtab@users.noreply.github.com> Date: Thu, 9 Jul 2026 16:58:30 +0000 Subject: [PATCH 11/14] Apply Clang formatting --- src/decrypt.cpp | 6 ++++-- src/encrypt.cpp | 6 ++++-- src/helper.h | 3 ++- src/keyPair.cpp | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/decrypt.cpp b/src/decrypt.cpp index c9cd01d..be9503d 100644 --- a/src/decrypt.cpp +++ b/src/decrypt.cpp @@ -1,8 +1,9 @@ #include "decrypt.h" #include -#include "math_utils.h" + #include "helper.h" +#include "math_utils.h" using namespace operations::math; @@ -10,7 +11,8 @@ namespace core::decryptor { std::string decrypt(keyPair& keyPair, const std::vector& ciphertext) { std::string plaintext; - // The block size in bytes is determined by the size of modulus n multiplied by 8 (64 bits per limb) + // The block size in bytes is determined by the size of modulus n multiplied by 8 (64 bits per + // limb) const size_t blockSize = keyPair.getPrivateKey().n.getBytes().size() * 8; if (blockSize == 0 || ciphertext.size() % blockSize != 0) { std::cerr << "Decryption error: Invalid ciphertext block size alignment." << std::endl; diff --git a/src/encrypt.cpp b/src/encrypt.cpp index a88f706..8813c29 100644 --- a/src/encrypt.cpp +++ b/src/encrypt.cpp @@ -1,6 +1,7 @@ #include "encrypt.h" -#include "math_utils.h" + #include "helper.h" +#include "math_utils.h" using namespace operations::math; @@ -8,7 +9,8 @@ namespace core::encryptor { std::vector encrypt(keyPair& keyPair, const std::string& plaintext) { std::vector ciphertext; - // The block size in bytes is determined by the size of modulus n multiplied by 8 (64 bits per limb) + // The block size in bytes is determined by the size of modulus n multiplied by 8 (64 bits per + // limb) const size_t blockSize = keyPair.getPublicKey().n.getBytes().size() * 8; if (blockSize == 0) return ciphertext; diff --git a/src/helper.h b/src/helper.h index 8be61db..27fabb8 100644 --- a/src/helper.h +++ b/src/helper.h @@ -27,7 +27,8 @@ } // Converts a 64-bit word array to a little-endian byte array -[[nodiscard]] inline std::vector byteArrayToBytes(const ByteArray& data, size_t targetSize = 0) { +[[nodiscard]] inline std::vector byteArrayToBytes(const ByteArray& data, + size_t targetSize = 0) { std::vector bytes; bytes.reserve(data.size() * 8); for (uint64_t word : data) { diff --git a/src/keyPair.cpp b/src/keyPair.cpp index d3630e5..c07edb0 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -9,14 +9,14 @@ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif -#include #include +#include #else #include #endif -#include "math_utils.h" #include "helper.h" +#include "math_utils.h" namespace { // 256 bytes = 2048 bits for prime p and q. From 71ddb523539b997f854f4d9af844fb2e3740075f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:12:21 +0200 Subject: [PATCH 12/14] fix clang format include error --- src/keyPair.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/keyPair.cpp b/src/keyPair.cpp index c07edb0..5c67b36 100644 --- a/src/keyPair.cpp +++ b/src/keyPair.cpp @@ -5,15 +5,17 @@ #include #include +// clang-format off #if defined(_WIN32) #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif -#include #include +#include #else #include #endif +// clang-format on #include "helper.h" #include "math_utils.h" From 74d712fdf67a00a7d73e7672b85224fc65520b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:22:12 +0200 Subject: [PATCH 13/14] Update submodule name to BigInt --- .gitmodules | 6 +++--- lib/{Base256 => BigInt} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename lib/{Base256 => BigInt} (100%) diff --git a/.gitmodules b/.gitmodules index 0f66571..1ab53f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "lib/Base256"] - path = lib/Base256 - url = https://github.com/ParallelEngineering/Base256.git +[submodule "lib/BigInt"] + path = lib/BigInt + url = https://github.com/ParallelEngineering/BigInt.git diff --git a/lib/Base256 b/lib/BigInt similarity index 100% rename from lib/Base256 rename to lib/BigInt From 49696c7bf795c04281df04fd09dc91144b9568da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:25:05 +0200 Subject: [PATCH 14/14] Update Cmake to BigInt --- lib/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 35063ce..d87f02c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/Base256/CMakeLists.txt") - message(FATAL_ERROR "Base256 submodule not initialized. Run: git submodule update --init --recursive") +if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/BigInt/CMakeLists.txt") + message(FATAL_ERROR "BigInt submodule not initialized. Run: git submodule update --init --recursive") endif() -add_subdirectory(Base256) \ No newline at end of file +add_subdirectory(BigInt) \ No newline at end of file