Skip to content

Commit

Permalink
Merge pull request #38 from HLRichardson-Git/RSA
Browse files Browse the repository at this point in the history
Adds RSA key encryption and signature scheme
  • Loading branch information
HLRichardson-Git authored Nov 13, 2024
2 parents 6004653 + 4ce02ac commit 031c6ca
Show file tree
Hide file tree
Showing 39 changed files with 2,413 additions and 106 deletions.
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,25 @@ appropriate release branch.
Gestalt Releases
----------------

- [Gestalt 0.7](#gestalt-07)
- [Gestalt 0.6](#gestalt-06)
- [Gestalt 0.5](#gestalt-05)
- [Gestalt 0.4](#gestalt-04)
- [Gestalt 0.3](#gestalt-03)
- [Gestalt 0.2](#gestalt-02)
- [Gestalt 0.1](#gestalt-01)

Gestalt 0.7
-----------

### Changes between 0.6.2 and 0.7 [12 Nov 2024]

* Adds RSA Key Generation with provable or probable primes with key sizes of 1024 to 15360.
* Adds RSA key encryption with Raw or OAEP padding schemes.
* Adds RSA message Signing Scheme with Raw or PSS padding schemes.
* Refactors the `BigInt` class outside of ECC to its own file in `tools/bigint/bigint.h` to be used anywhere.
* Refactors the hash utilities in ECC to its own file in `tools/hash_utils/hash_utils.h` to be used anywhere.

Gestalt 0.6
-----------

Expand Down
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16.3)

# Project setup
project (Gestalt VERSION 0.6.2 LANGUAGES C CXX)
project (Gestalt VERSION 0.7 LANGUAGES C CXX)
set (CMAKE_C_STANDARD 99)
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
Expand Down Expand Up @@ -30,7 +30,15 @@ set (Sources
src/ecc/ecc.cpp
src/ecc/ecdsa/ecdsa.cpp
src/ecc/ecdh/ecdh.cpp
src/rsa/rsa.cpp
src/rsa/padding_schemes/oaep/oaep.cpp
src/rsa/padding_schemes/pss/pss.cpp
src/rsa/padding_schemes/pkcs1v15/pkcs1v15.cpp
src/rsa/padding_schemes/rsa_padding.cpp
src/rsa/prime_generation/prime_generation.cpp
src/rsa/rsa_key_generation/rsaKeyGen.cpp
tools/utils.cpp
tools/hash_utils/hash_utils.cpp
)

add_library (${PROJECT_NAME} STATIC ${Sources})
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ To see more about the supported algorithms check out our [website](https://gesta
| SHA-2 | Hash Function | Secure Hash Algorithm |
| HMAC-SHA1 | Message Authentication Code | HMAC using SHA-1 |
| HMAC-SHA2 | Message Authentication Code | HMAC using SHA-2 |
| ECDSA | Asymmetric Encryption| Elliptic Curve Signature Algorithm |
| ECDH | Asymmetric Encryption| Elliptic Curve Shared Secret computation |
| ECDSA | Asymmetric Signature Scheme| Elliptic Curve Digital Signature Algorithm signing |
| RSA | Asymmetric Encryption| Rivest–Shamir–Adleman key encryption with Raw or OAEP padding |
| RSA | Asymmetric Signature Scheme| Rivest–Shamir–Adleman message signing with Raw or PSS padding |
| ECDH | Asymmetric Key Agreement| Elliptic Curve Diffie-Hellman Shared Secret computation |
*More algorithms are being implemented very often, see [open issues](https://github.com/HLRichardson-Git/Gestalt/issues) to see algorithms in devlopment*
<p align="right">(<a href="#readme-top">back to top</a>)</p>
Expand Down
12 changes: 1 addition & 11 deletions include/gestalt/ecdsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,8 @@

#pragma once

#include <functional>
#include "../src/ecc/ecc.h"

enum class HashAlgorithm {
None, // No hash function
SHA1,
SHA224,
SHA256,
SHA384,
SHA512
};
#include "hash_utils/hash_utils.h"

class ECDSA : public ECC {
private:
Expand All @@ -45,7 +36,6 @@ class ECDSA : public ECC {
bool isInvalidSignature(Signature S);

Signature generateSignature(const mpz_t& e, mpz_t& k);
std::function<std::string(const std::string&)> getHashFunction(HashAlgorithm hashAlg);

friend class ECDSA_Test;
public:
Expand Down
1 change: 1 addition & 0 deletions include/gestalt/gestalt.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
#include "hmac_sha1.h"
#include "hmac_sha2.h"
#include "ecdsa.h"
#include "rsa.h"
#include "ecdh.h"
56 changes: 56 additions & 0 deletions include/gestalt/rsa.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2023-2024 The Gestalt Project Authors. All Rights Reserved.
*
* Licensed under the MIT License. See the file LICENSE for the full text.
*/

/*
* rsa.h
*
* RSA is a widely used asymmetric encryption algorithm that relies on the difficulty of factoring
* large numbers. The class provides both basic RSA operations (without padding) and secure
* operations with padding schemes for enhanced security.
*
* This file defines the RSA class, which provides functionality for RSA encryption,
* decryption, digital signatures, and signature verification. The RSA class supports
* both raw RSA operations and padded encryption/signature schemes (e.g., OAEP and PSS).
*
*/

# pragma once

#include "rsa/rsa_key_generation/rsaKeyGen.h"
#include "rsa/padding_schemes/rsa_padding.h"

class RSA {
private:
RSAKeyPair keyPair;

BigInt rawEncrypt(const BigInt& plaintext, const RSAPublicKey& recipientPublicKey) const;
BigInt rawDecrypt(const BigInt& ciphertext) const;

BigInt rawSignatureGen(const BigInt& messageHash) const;
BigInt rawSignatureVer(const BigInt& signature, const RSAPublicKey& recipientPublicKey) const;

public:
RSA() {};
RSA(RSAKeyGenOptions keyGenerationOptions) // TODO: Make a unit test for this constructor
: keyPair(keyGenerationOptions) {}
RSA(RSASecurityStrength specifiedStength, const RSAPrivateKey& privateKey, const RSAPublicKey& publicKey)
: keyPair(specifiedStength, privateKey, publicKey) {}

RSAPrivateKey getPrivateKey() const { return keyPair.getPrivateKey(); };
RSAPublicKey getPublicKey() const { return keyPair.getPublicKey(); };

std::string encrypt(const std::string& plaintext, const RSAPublicKey& recipientPublicKey);
std::string encrypt(const std::string& plaintext, const RSAPublicKey& recipientPublicKey, const OAEPParams& parameters);

std::string decrypt(const std::string& ciphertext);
std::string decrypt(const std::string& ciphertext, const OAEPParams& parameters);

std::string signMessage(const std::string& message, HashAlgorithm hashAlg = HashAlgorithm::None);
std::string signMessage(const std::string& message, const PSSParams& parameters, HashAlgorithm hashAlg = HashAlgorithm::None);

bool verifySignature(const std::string& message, const std::string& signature, const RSAPublicKey& recipientPublicKey, HashAlgorithm hashAlg = HashAlgorithm::None);
bool verifySignature(const std::string& message, const std::string& signature, const RSAPublicKey& recipientPublicKey, const PSSParams& parameters, HashAlgorithm hashAlg = HashAlgorithm::None);
};
55 changes: 1 addition & 54 deletions src/ecc/eccObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,60 +12,7 @@
*/
#pragma once

//#include "standardCurves.h"

#include <string>
#include <gmp.h>

inline void stringToGMP(const std::string& str, mpz_t& result) {
if (str.substr(0, 2) == "0x") {
std::string truncatedStr = str.substr(2, str.length());
mpz_set_str(result, truncatedStr.c_str(), 16);
} else {
mpz_set_str(result, str.c_str(), 10);
}
}

class BigInt {
public:
mpz_t n;

BigInt() { mpz_init(n); }
BigInt(const std::string& strN) {
mpz_init(n);
stringToGMP(strN, n);
}

BigInt(const BigInt& other) {
mpz_init_set(n, other.n);
}

BigInt(const char* strN) {
mpz_init(n);
stringToGMP(strN, n);
}

BigInt& operator=(const BigInt& other) {
if (this != &other) {
mpz_set(n, other.n);
}
return *this;
}

BigInt& operator=(const std::string& strN) {
stringToGMP(strN, n);
return *this;
}

BigInt& operator=(const char* strN) {
stringToGMP(strN, n);
return *this;
}

~BigInt() {
mpz_clear(n);
}
};
#include "bigInt/bigInt.h"

class Point {
public:
Expand Down
29 changes: 3 additions & 26 deletions src/ecc/ecdsa/ecdsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,7 @@
*
*/

#include <gmp.h>

#include <gestalt/ecdsa.h>
#include <gestalt/sha1.h>
#include <gestalt/sha2.h>

std::function<std::string(const std::string&)> ECDSA::getHashFunction(HashAlgorithm hashAlg) {
switch (hashAlg) {
case HashAlgorithm::None:
return [](const std::string& in) { return in; };
case HashAlgorithm::SHA1:
return hashSHA1;
case HashAlgorithm::SHA224:
return hashSHA224;
case HashAlgorithm::SHA256:
return hashSHA256;
case HashAlgorithm::SHA384:
return hashSHA384;
case HashAlgorithm::SHA512:
return hashSHA512;
default:
throw std::invalid_argument("Unsupported hash function");
}
}

void ECDSA::prepareMessage(const std::string& messageHash, mpz_t& result) {
std::string hashWithoutPrefix = messageHash;
Expand All @@ -70,7 +47,7 @@ bool ECDSA::isInvalidSignature(Signature S) {
}

Signature ECDSA::signMessage(const std::string& message, HashAlgorithm hashAlg) {
std::string messageHash = getHashFunction(hashAlg)(message);
std::string messageHash = hash(hashAlg)(message);

mpz_t e;
mpz_init(e);
Expand All @@ -92,7 +69,7 @@ Signature ECDSA::signMessage(const std::string& message, HashAlgorithm hashAlg)
}

Signature ECDSA::signMessage(const std::string& message, BigInt& K, HashAlgorithm hashAlg) {
std::string messageHash = getHashFunction(hashAlg)(message);
std::string messageHash = hash(hashAlg)(message);

mpz_t e;
mpz_init(e);
Expand Down Expand Up @@ -138,7 +115,7 @@ Signature ECDSA::generateSignature(const mpz_t& e, mpz_t& k) {
}

bool ECDSA::verifySignature(const std::string& message, const ECDSAPublicKey& peerPublicKey, const Signature& signature, HashAlgorithm hashAlg) {
std::string messageHash = getHashFunction(hashAlg)(message);
std::string messageHash = hash(hashAlg)(message);

mpz_t e;
mpz_init(e);
Expand Down
108 changes: 108 additions & 0 deletions src/rsa/padding_schemes/oaep/oaep.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2023-2024 The Gestalt Project Authors. All Rights Reserved.
*
* Licensed under the MIT License. See the file LICENSE for the full text.
*/

/*
* oaep.cpp
*
* This file provides the implementation for the Optimal Asymmetric Encryption Padding (OAEP) scheme used in RSA
* encryption. It includes functions for applying and removing OAEP padding with configurable hash functions and mask
* generation functions (MGF1).
*
* This file provides functions for applying and removing OAEP padding, based on PKCS #1 v2.2
* (see https://tools.ietf.org/html/rfc8017). The implementation includes hash-based mask generation using MGF1,
* supporting configurable hash functions and label handling.
*
*/

#include <iostream>
#include <string>

#include "oaep.h"
#include <gestalt/sha1.h>
#include <gestalt/sha2.h>
#include "utils.h"

std::string applyOAEP_Padding(const std::string& input, const OAEPParams& params, unsigned int modulusSizeBytes) {
unsigned int hashLength = static_cast<unsigned int>(params.hashFunc);
unsigned int inputLength = input.length(); // This inheriently means it can only handle ASCII for now
int psLen = modulusSizeBytes - inputLength - (2 * hashLength) - 2;

if (psLen < 0) {
throw std::invalid_argument("Message too long for RSA modulus");
}
std::string PS(psLen, 0x00);

// TODO: convert the output of hashSHA256 to bytes because you will find that the DB length is 255
// because it is doing it correctly, but since the output of hashSHA256 is in hex, and the length of DB
// is expected as bytes its "counting the byte twice", so its adding 32 to the length
// Maybe this is an overall flaw of the SHA implementations I have...
std::string DB = hexToBytes(hash(params.hashFunc)(params.label)) + PS + "\x01" + input;

std::string seed = params.seed;
if (seed.empty()) {
seed = generateRandomHexData(hashLength);
}

std::string dbMask = hexToBytes(mgf1(hexToBytes(seed), modulusSizeBytes - hashLength - 1, params.hashFunc));
std::string maskedDB;
for (size_t i = 0; i < DB.length(); ++i) {
maskedDB += DB[i] ^ dbMask[i];
}

std::string seedMask = hexToBytes(mgf1(maskedDB, hashLength, params.hashFunc));
std::string maskedSeed;
seed = hexToBytes(seed);
for (size_t i = 0; i < hashLength; ++i) {
maskedSeed += seed[i] ^ seedMask[i];
}

return std::string(1, 0x00) + maskedSeed + maskedDB; // EM
}

std::string removeOAEP_Padding(const std::string& input, const OAEPParams& params, unsigned int modulusSizeBytes) {
if (static_cast<unsigned char>(input[0]) != 0x00) {
throw std::invalid_argument("Given OAEP message does not begin with 0x00");
}

unsigned int hashLength = static_cast<unsigned int>(params.hashFunc);

std::string maskedSeed = input.substr(1, hashLength);
std::string maskedDB = input.substr(hashLength + 1, input.length());

std::string seedMask = hexToBytes(mgf1(maskedDB, hashLength, params.hashFunc));
std::string seed;
for (size_t i = 0; i < seedMask.length(); ++i) {
seed += maskedSeed[i] ^ seedMask[i];
}

std::string dbMask = hexToBytes(mgf1(seed, modulusSizeBytes - hashLength - 1, params.hashFunc));

std::string DB;
for (size_t i = 0; i < maskedDB.length(); ++i) {
DB += maskedDB[i] ^ dbMask[i];
}

std::string lhash = hexToBytes(hash(params.hashFunc)(params.label));
if (DB.substr(0, hashLength) != lhash) {
throw std::invalid_argument("OAEP Decode Error: The encoded lhash and computed lhash are not the same.");
}

int psStartPos = hashLength + 1; // Padding starts after lhash and the 0x01 delimiter
int psEndPos = DB.find(0x01, psStartPos); // Look for the 0x01 byte which ends PS

if (psEndPos == std::string::npos || psEndPos <= psStartPos) {
throw std::invalid_argument("OAEP Decode Error: Padding 0x01 byte not found.");
}

// Ensure all bytes from psStartPos to psEndPos-1 are zero (the PS)
for (int i = psStartPos; i < psEndPos; i++) {
if (DB[i] != 0x00) {
throw std::invalid_argument("OAEP Decode Error: Non-zero byte found in padding (PS).");
}
}

return DB.substr(psEndPos + 1, DB.length());
}
Loading

0 comments on commit 031c6ca

Please sign in to comment.