Files
CryptoSHIT/main.cpp
2026-05-23 23:57:37 +03:00

225 lines
9.1 KiB
C++

#include "CryptEpstein.h"
using PRIVATE_KEY = std::unique_ptr<EVP_PKEY, PKEYDeleter>;
using PUBLIC_KEY_CONTEXT = std::unique_ptr<EVP_PKEY_CTX, CTXDeleter>;
using KEY_BIO = std::unique_ptr<BIO, BIODeleter>;
using CIPHER_CONTEXT = std::unique_ptr<EVP_CIPHER_CTX, CipherDeleter>;
#define DATA_WRITE(data) reinterpret_cast<const char*>(data)
#define DATA_READ(data) reinterpret_cast<char*>(data)
constexpr size_t BUFFER_SIZE = 4096;
void generate_rsa_keypair(const std::string& private_key_path, const std::string& public_key_path) {
// Initialize the context for key generation
PUBLIC_KEY_CONTEXT ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr));
if (!ctx) {
throw std::runtime_error("Failed to create keygen context.");
}
if (EVP_PKEY_keygen_init(ctx.get()) <= 0) {
throw std::runtime_error("Failed to initialize keygen context.");
}
// Set the RSA key size
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), 4096) <= 0) {
throw std::runtime_error("Failed to set RSA key size.");
}
// Generate key structure
EVP_PKEY* raw_pkey = nullptr;
if (EVP_PKEY_keygen(ctx.get(), &raw_pkey) <= 0) {
throw std::runtime_error("Failed to generate key pair.");
}
PRIVATE_KEY pkey(raw_pkey);
// Save the Private Key
KEY_BIO priv_bio(BIO_new_file(private_key_path.c_str(), "w"));
if (!priv_bio) {
throw std::runtime_error("Failed to create private key file.");
}
// TODO: use private key with password
if (PEM_write_bio_PrivateKey(priv_bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr) <= 0) {
throw std::runtime_error("Failed to write private key to file.");
}
// Save the Public Key
KEY_BIO pub_bio(BIO_new_file(public_key_path.c_str(), "w"));
if (!pub_bio)
throw std::runtime_error("Failed to create public key file.");
if (PEM_write_bio_PUBKEY(pub_bio.get(), pkey.get()) <= 0)
throw std::runtime_error("Failed to write public key to file.");
}
// Load Public Key from file
PRIVATE_KEY load_public_key(const std::string& path) {
KEY_BIO file(BIO_new_file(path.c_str(), "r"));
if (!file)
throw std::runtime_error("Cannot open public key file.");
EVP_PKEY* pkey = PEM_read_bio_PUBKEY(file.get(), nullptr, nullptr, nullptr);
if (!pkey)
throw std::runtime_error("Failed to read public key.");
return PRIVATE_KEY(pkey);
}
// Load Private Key from file
PRIVATE_KEY load_private_key(const std::string& path) {
KEY_BIO file(BIO_new_file(path.c_str(), "r"));
if (!file)
throw std::runtime_error("Cannot open private key file.");
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(file.get(), nullptr, nullptr, nullptr);
if (!pkey)
throw std::runtime_error("Failed to read private key.");
return PRIVATE_KEY(pkey);
}
// ENCRYPTION
void hybrid_encrypt(const std::string& input_path, const std::string& output_path, const std::string& pub_key_path) {
auto pub_key = load_public_key(pub_key_path);
// Generate ephemeral AES key and IV
unsigned char aes_key[32];
unsigned char iv[16];
if (RAND_bytes(aes_key, sizeof(aes_key)) != 1 || RAND_bytes(iv, sizeof(iv)) != 1) {
throw std::runtime_error("Failed to generate random AES key/IV.");
}
// Encrypt the AES key using the RSA Public Key
PUBLIC_KEY_CONTEXT rsa_ctx(EVP_PKEY_CTX_new(pub_key.get(), nullptr));
if (!rsa_ctx || EVP_PKEY_encrypt_init(rsa_ctx.get()) <= 0)
throw std::runtime_error("RSA init failed.");
size_t encrypted_key_len = 0;
if (EVP_PKEY_encrypt(rsa_ctx.get(), nullptr, &encrypted_key_len, aes_key, sizeof(aes_key)) <= 0) {
throw std::runtime_error("RSA encrypted key size calculation failed.");
}
std::vector<unsigned char> encrypted_key(encrypted_key_len);
if (EVP_PKEY_encrypt(rsa_ctx.get(), encrypted_key.data(), &encrypted_key_len, aes_key, sizeof(aes_key)) <= 0)
throw std::runtime_error("RSA encryption failed.");
// Open file streams
std::ifstream in_file(input_path, std::ios::binary);
std::ofstream out_file(output_path, std::ios::binary);
if (!in_file || !out_file) throw std::runtime_error("File stream error.");
// Write metadata header
uint32_t key_len_header = static_cast<uint32_t>(encrypted_key_len);
out_file.write(DATA_WRITE(&key_len_header), sizeof(key_len_header));
out_file.write(DATA_WRITE(encrypted_key.data()), encrypted_key_len);
out_file.write(DATA_WRITE(iv), sizeof(iv));
// Stream encrypt the actual file data via AES
CIPHER_CONTEXT aes_ctx(EVP_CIPHER_CTX_new());
if (!aes_ctx || EVP_EncryptInit_ex(aes_ctx.get(), EVP_aes_256_cbc(), nullptr, aes_key, iv) != 1) {
throw std::runtime_error("AES init failed.");
}
std::vector<char> in_buf(BUFFER_SIZE);
std::vector<unsigned char> out_buf(BUFFER_SIZE + EVP_MAX_BLOCK_LENGTH);
int out_len = 0;
while (in_file.read(in_buf.data(), BUFFER_SIZE) || in_file.gcount() > 0) {
if (EVP_EncryptUpdate(aes_ctx.get(), out_buf.data(), &out_len,
reinterpret_cast<const unsigned char*>(in_buf.data()), in_file.gcount()) != 1) {
throw std::runtime_error("AES update failed.");
}
out_file.write(DATA_WRITE(out_buf.data()), out_len);
}
if (EVP_EncryptFinal_ex(aes_ctx.get(), out_buf.data(), &out_len) != 1)
throw std::runtime_error("AES final failed.");
out_file.write(DATA_WRITE(out_buf.data()), out_len);
}
// DECRYPTION
void hybrid_decrypt(const std::string& input_path, const std::string& output_path, const std::string& priv_key_path) {
auto priv_key = load_private_key(priv_key_path);
std::ifstream input_file(input_path, std::ios::binary);
std::ofstream output_file(output_path, std::ios::binary);
if (!input_file || !output_file) throw std::runtime_error("File stream error.");
// Read metadata header
uint32_t encrypted_key_len = 0;
input_file.read(DATA_READ(&encrypted_key_len), sizeof(encrypted_key_len));
std::vector<unsigned char> encrypted_key(encrypted_key_len);
input_file.read(DATA_READ(encrypted_key.data()), encrypted_key_len);
unsigned char iv[16]{};
input_file.read(DATA_READ(iv), sizeof(iv));
// Decrypt the secret AES key using the RSA Private Key
PUBLIC_KEY_CONTEXT rsa_ctx(EVP_PKEY_CTX_new(priv_key.get(), nullptr));
if (!rsa_ctx || EVP_PKEY_decrypt_init(rsa_ctx.get()) <= 0) throw std::runtime_error("RSA decrypt init failed.");
size_t aes_key_len = 0;
if (EVP_PKEY_decrypt(rsa_ctx.get(), nullptr, &aes_key_len, encrypted_key.data(), encrypted_key_len) <= 0)
throw std::runtime_error("RSA decrypted key size calculation failed.");
std::vector<unsigned char> aes_key(aes_key_len);
if (EVP_PKEY_decrypt(rsa_ctx.get(), aes_key.data(), &aes_key_len, encrypted_key.data(), encrypted_key_len) <= 0) {
throw std::runtime_error("RSA decryption failed. Key might be wrong or corrupted.");
}
if (aes_key_len != 32)
throw std::runtime_error("RSA decryption produced an unexpected AES key size.");
aes_key.resize(aes_key_len);
// Stream decrypt the file data using the recovered AES key
CIPHER_CONTEXT aes_ctx(EVP_CIPHER_CTX_new());
if (!aes_ctx || EVP_DecryptInit_ex(aes_ctx.get(), EVP_aes_256_cbc(), nullptr, aes_key.data(), iv) != 1) {
throw std::runtime_error("AES decrypt init failed.");
}
std::vector<char> input_buf(BUFFER_SIZE);
std::vector<unsigned char> output_buf(BUFFER_SIZE + EVP_MAX_BLOCK_LENGTH);
int out_len = 0;
while (input_file.read(input_buf.data(), BUFFER_SIZE) || input_file.gcount() > 0) {
if (EVP_DecryptUpdate(aes_ctx.get(), output_buf.data(), &out_len,
reinterpret_cast<const unsigned char*>(input_buf.data()), input_file.gcount()) != 1) {
throw std::runtime_error("AES decrypt update failed.");
}
output_file.write(DATA_READ(output_buf.data()), out_len);
}
if (EVP_DecryptFinal_ex(aes_ctx.get(), output_buf.data(), &out_len) != 1) {
throw std::runtime_error("Decryption integrity check failed.");
}
output_file.write(DATA_READ(output_buf.data()), out_len);
}
int main() {
try {
const std::string input_path = "important_data.txt";
const std::string encrypted_path = "important_data.enc";
const std::string restored_path = "restored_test.txt";
std::ifstream input_check(input_path, std::ios::binary);
if (!input_check) {
throw std::runtime_error("Cannot open file.");
}
std::cout << "Generating RSA-4096 key pair...\n";
generate_rsa_keypair("private.pem", "public.pem");
std::cout << "Success! Saved 'private.pem' and 'public.pem'.\n";
std::cout << "Encrypting file with public key...\n";
hybrid_encrypt(input_path, encrypted_path, "public.pem");
std::cout << "Decrypting file with private key...\n";
hybrid_decrypt(encrypted_path, restored_path, "private.pem");
std::cout << "Done! Verify '" << restored_path << "' matching original inputs.\n";
} catch (const std::exception& e) {
std::cerr << "Pipeline failure: " << e.what() << "\n";
return 1;
}
return 0;
}