-
Daniel Lyubomirov authoredDaniel Lyubomirov authored
rsa.cc 4.59 KiB
#include <vereign/crypto/rsa.hh>
#include <vereign/bytes/view.hh>
#include <vereign/crypto/errors.hh>
#include <vereign/bytes/buffer.hh>
#include <vereign/crypto/bio.hh>
#include <openssl/base.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/mem.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pem.h>
namespace vereign::crypto::rsa {
auto GenerateKey(int bits) -> bssl::UniquePtr<EVP_PKEY> {
bssl::UniquePtr<BIGNUM> bn{BN_new()};
if (!bn) {
throw OpenSSLError("rsa key generation failed");
}
auto r = BN_set_word(bn.get(), RSA_F4);
if (r != 1) {
throw OpenSSLError("rsa key generation failed");
}
bssl::UniquePtr<RSA> rsa{RSA_new()};
r = RSA_generate_key_ex(rsa.get(), bits, bn.get(), nullptr);
if (r != 1) {
throw OpenSSLError("rsa key generation failed");
}
bssl::UniquePtr<EVP_PKEY> key(EVP_PKEY_new());
if (key == nullptr) {
throw OpenSSLError("creating key failed");
}
r = EVP_PKEY_assign_RSA(key.get(), rsa.release());
if (r != 1) {
throw OpenSSLError("rsa key assign to evp failed");
}
return key;
}
void PublicKeyEncrypt(EVP_PKEY* key, bytes::View src, bytes::Buffer& encrypted) {
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key, nullptr));
if (!ctx) {
throw OpenSSLError("creating evp ctx failed");
}
auto r = EVP_PKEY_encrypt_init(ctx.get());
if (r != 1) {
throw OpenSSLError("encrypt init failed");
}
r = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING);
if (r != 1) {
throw OpenSSLError("pkey padding init failed");
}
std::size_t outlen = 0;
r = EVP_PKEY_encrypt(ctx.get(), nullptr, &outlen, src.Data(), src.Size());
if (r != 1) {
throw OpenSSLError("determining ciphertext size failed");
}
encrypted.Reserve(outlen);
r = EVP_PKEY_encrypt(ctx.get(), encrypted.end(), &outlen, src.Data(), src.Size());
if (r != 1) {
throw OpenSSLError("encrypting failed");
}
encrypted.IncSize(outlen);
}
void PrivateKeyDecrypt(EVP_PKEY* key, bytes::View src, bytes::Buffer& decrypted) {
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key, nullptr));
if (!ctx) {
throw OpenSSLError("creating evp ctx failed");
}
auto r = EVP_PKEY_decrypt_init(ctx.get());
if (r != 1) {
throw OpenSSLError("decrypt init failed");
}
r = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING);
if (r != 1) {
throw OpenSSLError("init pkey padding failed");
}
std::size_t outlen = 0;
r = EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen, src.Data(), src.Size());
if (r != 1) {
throw OpenSSLError("determining decrypted buffer size failed");
}
decrypted.Reserve(outlen);
r = EVP_PKEY_decrypt(ctx.get(), decrypted.end(), &outlen, src.Data(), src.Size());
if (r != 1) {
throw OpenSSLError("decrypting failed");
}
decrypted.IncSize(outlen);
}
auto ExportPublicKeyToPEM(EVP_PKEY* key) -> bssl::UniquePtr<BIO> {
bssl::UniquePtr<BIO> mem(BIO_new(BIO_s_mem()));
if (!mem) {
throw OpenSSLError("creating memory buffer failed");
}
auto r = PEM_write_bio_PUBKEY(mem.get(), key);
if (r != 1) {
throw OpenSSLError("exporting public key to PEM failed");
}
return mem;
}
auto ImportPublicKeyFromPEM(bytes::View pem) -> bssl::UniquePtr<EVP_PKEY> {
bssl::UniquePtr<BIO> mem(BIO_new_mem_buf(pem.Data(), pem.Size()));
if (mem == nullptr) {
throw OpenSSLError("creating memory buffer failed");
}
bssl::UniquePtr<EVP_PKEY> key(PEM_read_bio_PUBKEY(mem.get(), nullptr, nullptr, nullptr));
if (key == nullptr) {
throw OpenSSLError("importing public key from PEM failed");
}
return key;
}
auto ExportPrivateKeyToPEM(EVP_PKEY* key) -> bssl::UniquePtr<BIO> {
bssl::UniquePtr<BIO> mem(BIO_new(BIO_s_mem()));
if (!mem) {
throw OpenSSLError("creating memory buffer failed");
}
auto r = PEM_write_bio_PrivateKey(mem.get(), key, nullptr, nullptr, 0, nullptr, nullptr);
if (r != 1) {
throw OpenSSLError("exporting private key to PEM failed");
}
return mem;
}
auto ExportPrivateKeyToPEMString(EVP_PKEY* key) -> std::string {
auto key_bio = ExportPublicKeyToPEM(key);
return std::string{crypto::bio::View(key_bio.get()).String()};
}
auto ImportPrivateKeyFromPEM(bytes::View pem) -> bssl::UniquePtr<EVP_PKEY> {
bssl::UniquePtr<BIO> mem(BIO_new_mem_buf(pem.Data(), pem.Size()));
if (mem == nullptr) {
throw OpenSSLError("creating memory buffer failed");
}
auto key_ptr = PEM_read_bio_PrivateKey(mem.get(), nullptr, nullptr, nullptr);
if (key_ptr == nullptr) {
throw OpenSSLError("importing private key from PEM failed");
}
return bssl::UniquePtr<EVP_PKEY>(key_ptr);
}
} // vereign::crypto::rsa