From 5882ada702b2a05353cf905893003b2288e68697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:47:12 +0200 Subject: [PATCH 1/3] Implement RSA signature and verification functions --- src/signature.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++ src/signature.h | 22 ++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/signature.cpp create mode 100644 src/signature.h diff --git a/src/signature.cpp b/src/signature.cpp new file mode 100644 index 0000000..38fac83 --- /dev/null +++ b/src/signature.cpp @@ -0,0 +1,59 @@ +#include "signature.h" + +#include "helper.h" +#include "math_utils.h" + +namespace core::signature { +namespace { + +[[nodiscard]] operations::BigInt digestToInteger( + const std::vector& digest) { + return operations::BigInt(bytesToByteArray(digest)); +} + +[[nodiscard]] std::size_t modulusSize(const operations::BigInt& modulus) { + return modulus.getBytes().size() * sizeof(std::uint64_t); +} + +} // namespace + +std::vector signDigest( + const PrivateKey& privateKey, + const std::vector& digest) { + const operations::BigInt one(1); + if (digest.empty() || privateKey.n <= one || privateKey.d <= one) { + return {}; + } + + const auto digestInteger = digestToInteger(digest); + if (digestInteger >= privateKey.n) { + return {}; + } + + const auto signature = operations::math::modPow( + digestInteger, privateKey.d, privateKey.n); + return byteArrayToBytes(signature.getBytes(), modulusSize(privateKey.n)); +} + +bool verifyDigest( + const PublicKey& publicKey, + const std::vector& digest, + const std::vector& signature) { + const operations::BigInt one(1); + if (digest.empty() || publicKey.n <= one || publicKey.e <= one + || signature.size() != modulusSize(publicKey.n)) { + return false; + } + + const auto digestInteger = digestToInteger(digest); + const operations::BigInt signatureInteger(bytesToByteArray(signature)); + if (digestInteger >= publicKey.n || signatureInteger >= publicKey.n) { + return false; + } + + const auto verifiedDigest = operations::math::modPow( + signatureInteger, publicKey.e, publicKey.n); + return verifiedDigest == digestInteger; +} + +} // namespace core::signature diff --git a/src/signature.h b/src/signature.h new file mode 100644 index 0000000..229b30e --- /dev/null +++ b/src/signature.h @@ -0,0 +1,22 @@ +#ifndef RSA_SIGNATURE_H +#define RSA_SIGNATURE_H + +#include +#include + +#include "keyPair.h" + +namespace core::signature { + +[[nodiscard]] std::vector signDigest( + const PrivateKey& privateKey, + const std::vector& digest); + +[[nodiscard]] bool verifyDigest( + const PublicKey& publicKey, + const std::vector& digest, + const std::vector& signature); + +} // namespace core::signature + +#endif // RSA_SIGNATURE_H From 012301aae7e77832b5c034c23dc57797f2f3276c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B6hm?= <134922046+LordofGhost@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:47:42 +0200 Subject: [PATCH 2/3] Add signature handling and tests for RSA digest verification --- src/CMakeLists.txt | 1 + tests/test_rsa.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fef04ae..a7e6484 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(RSA decrypt.cpp encrypt.cpp keyPair.cpp + signature.cpp ) # Add src directory to path for header includes diff --git a/tests/test_rsa.cpp b/tests/test_rsa.cpp index 32dd851..91e9ebf 100644 --- a/tests/test_rsa.cpp +++ b/tests/test_rsa.cpp @@ -5,6 +5,7 @@ #include "decrypt.h" #include "encrypt.h" #include "keyPair.h" +#include "signature.h" using core::decryptor::decrypt; using core::encryptor::encrypt; @@ -125,4 +126,42 @@ TEST_CASE("RSA Core: Key Creation and Struct-based Initialization") { // Expect std::runtime_error as specified in keyPair::create REQUIRE_THROWS_AS(keyPair::create(invalidPub, invalidPriv), std::runtime_error); } -} \ No newline at end of file +} + +TEST_CASE("RSA Core: Digest signatures prove private-key possession") { + keyPair pairA( + PublicKey{operations::BigInt(3233), operations::BigInt(17)}, + PrivateKey{operations::BigInt(3233), operations::BigInt(2753)}); + keyPair pairB( + PublicKey{operations::BigInt(2773), operations::BigInt(17)}, + PrivateKey{operations::BigInt(2773), operations::BigInt(157)}); + const std::vector digest = {0x2A}; + + const auto signature = core::signature::signDigest(pairA.getPrivateKey(), digest); + REQUIRE_FALSE(signature.empty()); + REQUIRE(core::signature::verifyDigest(pairA.getPublicKey(), digest, signature)); + + SECTION("A different public key does not verify the signature") { + REQUIRE_FALSE(core::signature::verifyDigest(pairB.getPublicKey(), digest, signature)); + } + + SECTION("A modified digest does not verify the signature") { + auto modifiedDigest = digest; + modifiedDigest.front() ^= 0x01; + REQUIRE_FALSE(core::signature::verifyDigest( + pairA.getPublicKey(), modifiedDigest, signature)); + } + + SECTION("A modified signature is rejected") { + auto modifiedSignature = signature; + modifiedSignature.back() ^= 0x01; + REQUIRE_FALSE(core::signature::verifyDigest( + pairA.getPublicKey(), digest, modifiedSignature)); + } + + SECTION("Malformed inputs are rejected") { + REQUIRE(core::signature::signDigest(pairA.getPrivateKey(), {}).empty()); + REQUIRE_FALSE(core::signature::verifyDigest(pairA.getPublicKey(), {}, signature)); + REQUIRE_FALSE(core::signature::verifyDigest(pairA.getPublicKey(), digest, {})); + } +} From 8836b3e360be3aa866b7459c6a39f7f951f3450a Mon Sep 17 00:00:00 2001 From: LordofGhost <134922046+LordofGhost@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:33:44 +0000 Subject: [PATCH 3/3] Apply Clang formatting --- src/signature.cpp | 25 ++++++++++--------------- src/signature.h | 11 ++++------- tests/test_rsa.cpp | 18 ++++++++---------- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/src/signature.cpp b/src/signature.cpp index 38fac83..ba9eabc 100644 --- a/src/signature.cpp +++ b/src/signature.cpp @@ -6,8 +6,7 @@ namespace core::signature { namespace { -[[nodiscard]] operations::BigInt digestToInteger( - const std::vector& digest) { +[[nodiscard]] operations::BigInt digestToInteger(const std::vector& digest) { return operations::BigInt(bytesToByteArray(digest)); } @@ -17,9 +16,8 @@ namespace { } // namespace -std::vector signDigest( - const PrivateKey& privateKey, - const std::vector& digest) { +std::vector signDigest(const PrivateKey& privateKey, + const std::vector& digest) { const operations::BigInt one(1); if (digest.empty() || privateKey.n <= one || privateKey.d <= one) { return {}; @@ -30,18 +28,15 @@ std::vector signDigest( return {}; } - const auto signature = operations::math::modPow( - digestInteger, privateKey.d, privateKey.n); + const auto signature = operations::math::modPow(digestInteger, privateKey.d, privateKey.n); return byteArrayToBytes(signature.getBytes(), modulusSize(privateKey.n)); } -bool verifyDigest( - const PublicKey& publicKey, - const std::vector& digest, - const std::vector& signature) { +bool verifyDigest(const PublicKey& publicKey, const std::vector& digest, + const std::vector& signature) { const operations::BigInt one(1); - if (digest.empty() || publicKey.n <= one || publicKey.e <= one - || signature.size() != modulusSize(publicKey.n)) { + if (digest.empty() || publicKey.n <= one || publicKey.e <= one || + signature.size() != modulusSize(publicKey.n)) { return false; } @@ -51,8 +46,8 @@ bool verifyDigest( return false; } - const auto verifiedDigest = operations::math::modPow( - signatureInteger, publicKey.e, publicKey.n); + const auto verifiedDigest = + operations::math::modPow(signatureInteger, publicKey.e, publicKey.n); return verifiedDigest == digestInteger; } diff --git a/src/signature.h b/src/signature.h index 229b30e..d974f73 100644 --- a/src/signature.h +++ b/src/signature.h @@ -8,14 +8,11 @@ namespace core::signature { -[[nodiscard]] std::vector signDigest( - const PrivateKey& privateKey, - const std::vector& digest); +[[nodiscard]] std::vector signDigest(const PrivateKey& privateKey, + const std::vector& digest); -[[nodiscard]] bool verifyDigest( - const PublicKey& publicKey, - const std::vector& digest, - const std::vector& signature); +[[nodiscard]] bool verifyDigest(const PublicKey& publicKey, const std::vector& digest, + const std::vector& signature); } // namespace core::signature diff --git a/tests/test_rsa.cpp b/tests/test_rsa.cpp index 91e9ebf..374d28d 100644 --- a/tests/test_rsa.cpp +++ b/tests/test_rsa.cpp @@ -129,12 +129,10 @@ TEST_CASE("RSA Core: Key Creation and Struct-based Initialization") { } TEST_CASE("RSA Core: Digest signatures prove private-key possession") { - keyPair pairA( - PublicKey{operations::BigInt(3233), operations::BigInt(17)}, - PrivateKey{operations::BigInt(3233), operations::BigInt(2753)}); - keyPair pairB( - PublicKey{operations::BigInt(2773), operations::BigInt(17)}, - PrivateKey{operations::BigInt(2773), operations::BigInt(157)}); + keyPair pairA(PublicKey{operations::BigInt(3233), operations::BigInt(17)}, + PrivateKey{operations::BigInt(3233), operations::BigInt(2753)}); + keyPair pairB(PublicKey{operations::BigInt(2773), operations::BigInt(17)}, + PrivateKey{operations::BigInt(2773), operations::BigInt(157)}); const std::vector digest = {0x2A}; const auto signature = core::signature::signDigest(pairA.getPrivateKey(), digest); @@ -148,15 +146,15 @@ TEST_CASE("RSA Core: Digest signatures prove private-key possession") { SECTION("A modified digest does not verify the signature") { auto modifiedDigest = digest; modifiedDigest.front() ^= 0x01; - REQUIRE_FALSE(core::signature::verifyDigest( - pairA.getPublicKey(), modifiedDigest, signature)); + REQUIRE_FALSE( + core::signature::verifyDigest(pairA.getPublicKey(), modifiedDigest, signature)); } SECTION("A modified signature is rejected") { auto modifiedSignature = signature; modifiedSignature.back() ^= 0x01; - REQUIRE_FALSE(core::signature::verifyDigest( - pairA.getPublicKey(), digest, modifiedSignature)); + REQUIRE_FALSE( + core::signature::verifyDigest(pairA.getPublicKey(), digest, modifiedSignature)); } SECTION("Malformed inputs are rejected") {