RyanHub – file viewer
filename: src/utils/crypto/CryptoUtils.cpp
branch: feature/world
back to repo
/*
 *
 *               _____  _                 _
 *              /  ___|| |               | |
 *              \ `--. | |_  _ __   __ _ | |_   ___   ___
 *               `--. \| __|| '__| / _` || __| / _ \ / __|
 *              /\__/ /| |_ | |   | (_| || |_ | (_) |\__ \
 *              \____/  \__||_|    \__,_| \__| \___/ |___/
 *
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Copyright (C) 2025 Armen Deroian
 *
 */

#include "CryptoUtils.h"

#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <stdexcept>

stratos::EVPKeyPtr stratos::generateEncryptionKey() {
    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr);
    if (!ctx) throw std::runtime_error("Failed to create EVP_PKEY_CTX");

    if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 1024) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize RSA key generation");
    }

    EVP_PKEY* pkey = nullptr;
    if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Failed to generate RSA key");
    }
    EVP_PKEY_CTX_free(ctx);

    return {pkey, &EVP_PKEY_free};
}
std::vector<uint8_t> stratos::encodeServerPublicKey(const EVPKeyPtr* key) {
    std::vector<uint8_t> result;
    const int            len = i2d_PUBKEY(key->get(), nullptr);
    if (len <= 0) {
        throw std::runtime_error("Failed to determine DER length");
    }

    result.resize(len);
    unsigned char* ptr = result.data();
    if (i2d_PUBKEY(key->get(), &ptr) <= 0) {
        throw std::runtime_error("Failed to encode DER public key");
    }

    return result;
}
std::vector<uint8_t> stratos::rsaEncrypt(const EVPKeyPtr* key, const std::vector<uint8_t>& payload) {
    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(key->get(), nullptr);
    if (!ctx) throw std::runtime_error("Failed to create encrypt context");

    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize encryption");
    }

    size_t outLen = 0;
    if (EVP_PKEY_encrypt(ctx, nullptr, &outLen, payload.data(), payload.size()) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Failed to determine encrypted length");
    }

    std::vector<uint8_t> ciphertext(outLen);
    if (EVP_PKEY_encrypt(ctx, ciphertext.data(), &outLen, payload.data(), payload.size()) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Encryption failed");
    }

    ciphertext.resize(outLen);
    EVP_PKEY_CTX_free(ctx);
    return ciphertext;
}
std::vector<uint8_t> stratos::rsaDecrypt(const EVPKeyPtr* key, const std::vector<uint8_t>& payload) {
    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(key->get(), nullptr);
    if (!ctx) throw std::runtime_error("Failed to create decrypt context");

    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize decryption");
    }

    size_t outLen = 0;
    if (EVP_PKEY_decrypt(ctx, nullptr, &outLen, payload.data(), payload.size()) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Failed to determine decrypted length");
    }

    std::vector<uint8_t> plaintext(outLen);
    if (EVP_PKEY_decrypt(ctx, plaintext.data(), &outLen, payload.data(), payload.size()) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        throw std::runtime_error("Decryption failed");
    }

    plaintext.resize(outLen);
    EVP_PKEY_CTX_free(ctx);
    return plaintext;
}
std::vector<uint8_t> stratos::aesEncryptCFB8(const std::vector<uint8_t>& key, const std::vector<uint8_t>& iv, const std::vector<uint8_t>& plaintext) {
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) throw std::runtime_error("Failed to create AES encryption context");

    if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cfb8(), nullptr, key.data(), iv.data()) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize AES encryption");
    }

    std::vector<uint8_t> ciphertext(plaintext.size());
    int outlen = 0;
    if (EVP_EncryptUpdate(ctx, ciphertext.data(), &outlen, plaintext.data(), static_cast<int>(plaintext.size())) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("AES encryption failed");
    }

    EVP_CIPHER_CTX_free(ctx);
    return ciphertext;
}

std::vector<uint8_t> stratos::aesDecryptCFB8(const std::vector<uint8_t>& key, const std::vector<uint8_t>& iv, const std::vector<uint8_t>& ciphertext) {
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) throw std::runtime_error("Failed to create AES decryption context");

    if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cfb8(), nullptr, key.data(), iv.data()) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize AES decryption");
    }

    std::vector<uint8_t> plaintext(ciphertext.size());
    int                  outlen = 0;
    if (EVP_DecryptUpdate(ctx, plaintext.data(), &outlen, ciphertext.data(), static_cast<int>(ciphertext.size())) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("AES decryption failed");
    }

    EVP_CIPHER_CTX_free(ctx);
    return plaintext;
}
std::vector<uint8_t> stratos::generateRandomBytes(const size_t size) {
    std::vector<uint8_t> randomBytes(size);
    if (RAND_bytes(randomBytes.data(), static_cast<int>(size)) != 1)
        throw std::runtime_error("Failed to generate random bytes");
    return randomBytes;
}