Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ add_library(RSA
decrypt.cpp
encrypt.cpp
keyPair.cpp
signature.cpp
)

# Add src directory to path for header includes
Expand Down
54 changes: 54 additions & 0 deletions src/signature.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "signature.h"

#include "helper.h"
#include "math_utils.h"

namespace core::signature {
namespace {

[[nodiscard]] operations::BigInt digestToInteger(const std::vector<std::uint8_t>& digest) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this helper necessary? Isn't this just a wrapper for a helper class?

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<std::uint8_t> signDigest(const PrivateKey& privateKey,
const std::vector<std::uint8_t>& 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<std::uint8_t>& digest,
const std::vector<std::uint8_t>& 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
19 changes: 19 additions & 0 deletions src/signature.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef RSA_SIGNATURE_H
#define RSA_SIGNATURE_H

#include <cstdint>
#include <vector>

#include "keyPair.h"

namespace core::signature {

[[nodiscard]] std::vector<std::uint8_t> signDigest(const PrivateKey& privateKey,
const std::vector<std::uint8_t>& digest);

[[nodiscard]] bool verifyDigest(const PublicKey& publicKey, const std::vector<std::uint8_t>& digest,
const std::vector<std::uint8_t>& signature);

} // namespace core::signature

#endif // RSA_SIGNATURE_H
39 changes: 38 additions & 1 deletion tests/test_rsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "decrypt.h"
#include "encrypt.h"
#include "keyPair.h"
#include "signature.h"

using core::decryptor::decrypt;
using core::encryptor::encrypt;
Expand Down Expand Up @@ -125,4 +126,40 @@ 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);
}
}
}

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<std::uint8_t> 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));
}
Comment on lines +148 to +153

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, {}));
}
}
Loading