upload codebase

Signed-off-by: Double Sine <xiao_ai_yu@live.cn>
This commit is contained in:
Double Sine
2022-02-14 15:47:41 +08:00
commit 9017721a5c
43 changed files with 4615 additions and 0 deletions

90
common/exception.hpp Normal file
View File

@@ -0,0 +1,90 @@
#pragma once
#include <exception>
#include <string>
#include <vector>
#include <utility>
namespace nkg {
class exception : public std::exception {
private:
int m_source_line;
std::string m_source_file;
std::string m_custom_message;
std::vector<std::string> m_hints;
public:
[[noreturn]]
static void trap_then_terminate() {
#if _MSC_VER
__debugbreak();
#elif defined(__GNUC__) || defined(__GNUG__) || defined(__clang__)
__builtin_trap();
#else
#error "exception.hpp: unknown compiler is detected."
#endif
std::terminate();
}
exception(std::string_view file, int line, std::string_view message) noexcept :
std::exception(), m_source_line(line), m_source_file(file), m_custom_message(message) {}
exception(const exception&) noexcept = default;
exception(exception&&) noexcept = default;
exception& operator=(const exception&) noexcept = default;
exception& operator=(exception&&) noexcept = default;
[[nodiscard]]
int source_line() const noexcept {
return m_source_line;
}
[[nodiscard]]
const std::string& source_file() const noexcept {
return m_source_file;
}
[[nodiscard]]
const std::string& custom_message() const noexcept {
return m_custom_message;
}
exception&& push_hint(std::string_view hint) noexcept {
m_hints.emplace_back(hint);
return std::move(*this);
}
exception&& pop_hint() noexcept {
m_hints.pop_back();
return std::move(*this);
}
const std::vector<std::string>& hints() const noexcept {
return m_hints;
}
virtual const char* what() const noexcept override {
return m_custom_message.c_str();
}
[[nodiscard]]
virtual bool error_code_exists() const noexcept {
return false;
}
[[nodiscard]]
virtual intptr_t error_code() const noexcept {
trap_then_terminate();
}
[[nodiscard]]
virtual const std::string& error_string() const noexcept {
trap_then_terminate();
}
virtual ~exception() override = default;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../exception.hpp"
namespace nkg::exceptions {
class index_exception : public ::nkg::exception {
public:
index_exception(std::string_view file, int line, std::string_view message) noexcept :
::nkg::exception(file, line, message) {}
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../exception.hpp"
namespace nkg::exceptions {
class key_exception : public ::nkg::exception {
public:
key_exception(std::string_view file, int line, std::string_view message) noexcept :
::nkg::exception(file, line, message) {}
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../exception.hpp"
namespace nkg::exceptions {
class operation_canceled_exception : public ::nkg::exception {
public:
operation_canceled_exception(std::string_view file, int line, std::string_view message) noexcept :
::nkg::exception(file, line, message) {}
};
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <errno.h>
#include <string.h>
#include "../exception.hpp"
namespace nkg::exceptions {
class unix_exception : public ::nkg::exception {
public:
using error_code_t = decltype(errno);
private:
error_code_t m_error_code;
std::string m_error_string;
public:
unix_exception(std::string_view file, int line, error_code_t unix_errno, std::string_view message) noexcept :
::nkg::exception(file, line, message), m_error_code(unix_errno), m_error_string(strerror(unix_errno)) {}
[[nodiscard]]
virtual bool error_code_exists() const noexcept override {
return true;
}
[[nodiscard]]
virtual intptr_t error_code() const noexcept override {
return m_error_code;
}
[[nodiscard]]
virtual const std::string& error_string() const noexcept override {
return m_error_string;
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
namespace nkg::resource_traits {
template<typename element_t>
struct cxx_dynamic_array_traits {
using handle_t = element_t*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) {
delete[] handle;
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
namespace nkg::resource_traits {
template<typename object_t>
struct cxx_object_traits {
using handle_t = object_t*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) {
delete handle;
}
};
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <keystone/keystone.h>
namespace nkg::resource_traits::keystone {
struct keystone_alloc {
using handle_t = unsigned char*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) noexcept {
ks_free(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <keystone/keystone.h>
namespace nkg::resource_traits::keystone {
struct keystone_handle {
using handle_t = ks_engine*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) {
ks_close(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <openssl/bn.h>
namespace nkg::resource_traits::openssl {
struct bignum {
using handle_t = BIGNUM*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) noexcept {
BN_free(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <openssl/bio.h>
namespace nkg::resource_traits::openssl {
struct bio {
using handle_t = BIO*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) noexcept {
BIO_free(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <openssl/bio.h>
namespace nkg::resource_traits::openssl {
struct bio_chain {
using handle_t = BIO*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) noexcept {
BIO_free_all(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <openssl/rsa.h>
namespace nkg::resource_traits::openssl {
struct rsa {
using handle_t = RSA*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) noexcept {
RSA_free(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <unicorn/unicorn.h>
namespace nkg::resource_traits::unicorn {
struct unicorn_alloc {
using handle_t = void*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) {
uc_free(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <unicorn/unicorn.h>
namespace nkg::resource_traits::unicorn {
struct unicorn_handle {
using handle_t = uc_engine*;
static constexpr handle_t invalid_value = nullptr;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) {
uc_close(handle);
}
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <unistd.h>
namespace nkg::resource_traits::unix_os {
struct file_descriptor {
using handle_t = int;
static constexpr handle_t invalid_value = -1;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
static void release(const handle_t& handle) {
close(handle);
}
};
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
namespace nkg::resource_traits::unix_os {
struct map_view {
using handle_t = void*;
static inline const handle_t invalid_value = MAP_FAILED;
[[nodiscard]]
static bool is_valid(const handle_t& handle) noexcept {
return handle != invalid_value;
}
};
}

238
common/resource_wrapper.hpp Normal file
View File

@@ -0,0 +1,238 @@
#pragma once
#include <type_traits>
#include <utility>
namespace nkg {
template<typename resource_traits_t, typename releaser_t = void>
class resource_wrapper {
public:
using handle_t = typename resource_traits_t::handle_t;
static_assert(std::is_trivial_v<handle_t> && std::is_standard_layout_v<handle_t>, "`resource_wrapper` requires a handle with POD type.");
private:
handle_t m_handle;
releaser_t m_releaser;
public:
template<typename releaser_arg_t>
resource_wrapper(releaser_arg_t&& releaser) noexcept :
m_handle(resource_traits_t::invalid_value),
m_releaser(std::forward<releaser_arg_t>(releaser)) {}
template<typename releaser_arg_t>
resource_wrapper(const handle_t& handle, releaser_arg_t&& releaser) noexcept :
m_handle(handle),
m_releaser(std::forward<releaser_arg_t>(releaser)) {}
template<typename releaser_arg_t>
resource_wrapper(resource_traits_t, releaser_arg_t&& releaser) noexcept :
m_handle(resource_traits_t::invalid_value),
m_releaser(std::forward<releaser_arg_t>(releaser)) {}
template<typename releaser_arg_t>
resource_wrapper(resource_traits_t, const handle_t& handle, releaser_arg_t&& releaser) noexcept :
m_handle(handle),
m_releaser(std::forward<releaser_t>(releaser)) {}
//
// `resource_wrapper` does not allow copy-construct
//
resource_wrapper(const resource_wrapper& other) = delete;
//
// `resource_wrapper` allows move-construct.
//
resource_wrapper(resource_wrapper&& other) noexcept :
m_handle(other.m_handle),
m_releaser(std::move(other.m_releaser))
{
other.m_handle = resource_traits_t::invalid_value;
}
//
// `resource_wrapper` does not allow to copy.
//
resource_wrapper& operator=(const resource_wrapper& other) = delete;
//
// `resource_wrapper` allows to move.
//
resource_wrapper& operator=(resource_wrapper&& other) noexcept {
if (this != std::addressof(other)) {
m_handle = other.m_handle;
m_releaser = std::move(other.m_releaser);
other.m_handle = resource_traits_t::invalid_value;
}
return *this;
}
template<typename ptr_t = handle_t, std::enable_if_t<std::is_pointer_v<handle_t>, ptr_t> = nullptr>
[[nodiscard]]
ptr_t operator->() const noexcept {
return m_handle;
}
template<typename as_t>
[[nodiscard]]
as_t as() const noexcept {
return reinterpret_cast<as_t>(m_handle);
}
[[nodiscard]]
bool is_valid() const noexcept {
return resource_traits_t::is_valid(m_handle);
}
[[nodiscard]]
const handle_t& get() const noexcept {
return m_handle;
}
template<typename as_t = handle_t>
[[nodiscard]]
as_t* unsafe_addressof() noexcept {
return reinterpret_cast<as_t*>(std::addressof(m_handle));
}
void set(const handle_t& handle) {
if (is_valid()) {
m_releaser(m_handle);
}
m_handle = handle;
}
void discard() noexcept {
m_handle = resource_traits_t::invalid_value;
}
[[nodiscard]]
handle_t transfer() noexcept {
handle_t t = m_handle;
m_handle = resource_traits_t::invalid_value;
return t;
}
void release() {
if (is_valid()) {
m_releaser(m_handle);
m_handle = resource_traits_t::invalid_value;
}
}
~resource_wrapper() {
release();
}
};
template<typename resource_traits_t>
class resource_wrapper<resource_traits_t, void> {
public:
using handle_t = typename resource_traits_t::handle_t;
static_assert(std::is_trivial_v<handle_t>&& std::is_standard_layout_v<handle_t>, "`resource_wrapper` requires a handle with POD type.");
private:
handle_t m_handle;
public:
resource_wrapper() noexcept :
m_handle(resource_traits_t::invalid_value) {}
resource_wrapper(const handle_t& handle) noexcept :
m_handle(handle) {}
resource_wrapper(resource_traits_t) noexcept :
m_handle(resource_traits_t::invalid_value) {}
resource_wrapper(resource_traits_t, const handle_t& handle) noexcept :
m_handle(handle) {}
resource_wrapper(const resource_wrapper& other) = delete;
resource_wrapper(resource_wrapper&& other) noexcept :
m_handle(other.m_handle)
{
other.m_handle = resource_traits_t::invalid_value;
}
resource_wrapper& operator=(const resource_wrapper& other) = delete;
resource_wrapper& operator=(resource_wrapper&& other) noexcept {
if (this != std::addressof(other)) {
m_handle = other.m_handle;
other.m_handle = resource_traits_t::invalid_value;
}
return *this;
}
template<typename ptr_t = handle_t, std::enable_if_t<std::is_pointer_v<handle_t>, ptr_t> = nullptr>
[[nodiscard]]
ptr_t operator->() const noexcept {
return m_handle;
}
template<typename as_t>
[[nodiscard]]
as_t as() const noexcept {
return reinterpret_cast<as_t>(m_handle);
}
[[nodiscard]]
bool is_valid() const noexcept {
return resource_traits_t::is_valid(m_handle);
}
[[nodiscard]]
const handle_t& get() const noexcept {
return m_handle;
}
template<typename as_t = handle_t>
[[nodiscard]]
as_t* unsafe_addressof() noexcept {
return reinterpret_cast<as_t*>(std::addressof(m_handle));
}
void set(const handle_t& handle) {
if (is_valid()) {
resource_traits_t::release(m_handle);
}
m_handle = handle;
}
void discard() noexcept {
m_handle = resource_traits_t::invalid_value;
}
[[nodiscard]]
handle_t transfer() noexcept {
handle_t t = m_handle;
m_handle = resource_traits_t::invalid_value;
return t;
}
void release() {
if (is_valid()) {
resource_traits_t::release(m_handle);
m_handle = resource_traits_t::invalid_value;
}
}
~resource_wrapper() {
release();
}
};
template<typename resource_traits_t>
resource_wrapper(resource_traits_t) ->
resource_wrapper<resource_traits_t, void>;
template<typename resource_traits_t, typename arg_t>
resource_wrapper(resource_traits_t, arg_t&&) ->
resource_wrapper<resource_traits_t, std::conditional_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<arg_t>>, typename resource_traits_t::handle_t>, std::remove_reference_t<arg_t>, void>>;
template<typename resource_traits_t, typename releaser_t, typename handle_t = typename resource_traits_t::handle_t>
resource_wrapper(resource_traits_t, const handle_t&, releaser_t&&) ->
resource_wrapper<resource_traits_t, std::remove_reference_t<releaser_t>>;
}

327
common/rsa_cipher.cpp Normal file
View File

@@ -0,0 +1,327 @@
#include "rsa_cipher.hpp"
#include <openssl/pem.h>
#include <openssl/bio.h>
#include "resource_traits/openssl/bio.hpp"
#include "resource_traits/openssl/bignum.hpp"
#define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.cpp"
#define NKG_CURRENT_SOURCE_LINE() __LINE__
namespace nkg {
RSA* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) {
resource_wrapper new_rsa
{ resource_traits::openssl::rsa{}, PEM_read_bio_RSAPrivateKey(p_bio, nullptr, nullptr, nullptr) };
if (new_rsa.is_valid()) {
return new_rsa.transfer();
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPrivateKey failed.")
.push_hint(u8"Are you sure that you DO provide a valid RSA private key file?");
}
}
RSA* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) {
resource_wrapper new_rsa
{ resource_traits::openssl::rsa{}, PEM_read_bio_RSA_PUBKEY(p_bio, nullptr, nullptr, nullptr) };
if (new_rsa.is_valid()) {
return new_rsa.transfer();
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSA_PUBKEY failed.")
.push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PEM format?");
}
}
RSA* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) {
resource_wrapper new_rsa
{ resource_traits::openssl::rsa{}, PEM_read_bio_RSAPublicKey(p_bio, nullptr, nullptr, nullptr) };
if (new_rsa.is_valid()) {
return new_rsa.transfer();
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPublicKey failed.")
.push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PKCS1 format?");
}
}
void rsa_cipher::_write_private_key_to_bio(RSA* p_rsa, BIO* p_bio) {
auto r = PEM_write_bio_RSAPrivateKey(p_bio, p_rsa, nullptr, nullptr, 0, nullptr, nullptr);
if (r == 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPrivateKey failed.");
};
}
void rsa_cipher::_write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio) {
auto r = PEM_write_bio_RSA_PUBKEY(p_bio, p_rsa);
if (r == 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSA_PUBKEY failed.");
}
}
void rsa_cipher::_write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio) {
auto r = PEM_write_bio_RSAPublicKey(p_bio, p_rsa);
if (r == 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPublicKey failed.");
}
}
rsa_cipher::rsa_cipher() : m_rsa(RSA_new()) {
if (!m_rsa.is_valid()) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_new failed.");
}
}
[[nodiscard]]
size_t rsa_cipher::bits() const {
#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x
if (m_rsa->n) {
return BN_num_bits(m_rsa->n);
} else {
throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA modulus has not been set.");
}
#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x
return RSA_bits(m_rsa.get());
#else
#error "rsa_cipher.cpp: uexpected OpenSSL version"
#endif
}
void rsa_cipher::generate_key(int bits, unsigned int e) {
resource_wrapper bn_e{ resource_traits::openssl::bignum{}, BN_new() };
if (!bn_e.is_valid()) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"BN_new failed.");
}
if (BN_set_word(bn_e.get(), e) == 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BN_set_word failed.");
}
if (RSA_generate_key_ex(m_rsa.get(), bits, bn_e.get(), nullptr) == 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_generate_key_ex failed.");
}
}
void rsa_cipher::export_private_key_file(std::string_view file_path) const {
resource_wrapper bio_file
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")};
if (bio_file.is_valid()) {
_write_private_key_to_bio(m_rsa.get(), bio_file.get());
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
}
}
void rsa_cipher::export_public_key_file_pem(std::string_view file_path) const {
resource_wrapper bio_file
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")};
if (bio_file.is_valid()) {
_write_public_key_pem_to_bio(m_rsa.get(), bio_file.get());
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
}
}
void rsa_cipher::export_public_key_file_pkcs1(std::string_view file_path) const {
resource_wrapper bio_file
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")};
if (bio_file.is_valid()) {
_write_public_key_pkcs1_to_bio(m_rsa.get(), bio_file.get());
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
}
}
void rsa_cipher::import_private_key_file(std::string_view file_path) {
resource_wrapper bio_file
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") };
if (bio_file.is_valid()) {
m_rsa.set(_read_private_key_from_bio(bio_file.get()));
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
}
}
void rsa_cipher::import_public_key_file_pem(std::string_view file_path) {
resource_wrapper bio_file
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") };
if (bio_file.is_valid()) {
m_rsa.set(_read_public_key_pem_from_bio(bio_file.get()));
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
}
}
void rsa_cipher::import_public_key_file_pkcs1(std::string_view file_path) {
resource_wrapper bio_file
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") };
if (bio_file.is_valid()) {
m_rsa.set(_read_public_key_pkcs1_from_bio(bio_file.get()));
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
}
}
[[nodiscard]]
std::string rsa_cipher::export_private_key_string() const {
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
if (bio_memory.is_valid()) {
_write_private_key_to_bio(m_rsa.get(), bio_memory.get());
const char* pch = nullptr;
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
return std::string(pch, lch);
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
}
}
[[nodiscard]]
std::string rsa_cipher::export_public_key_string_pem() const {
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
if (bio_memory.is_valid()) {
_write_public_key_pem_to_bio(m_rsa.get(), bio_memory.get());
const char* pch = nullptr;
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
return std::string(pch, lch);
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
}
}
[[nodiscard]]
std::string rsa_cipher::export_public_key_string_pkcs1() const {
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
if (bio_memory.is_valid()) {
_write_public_key_pkcs1_to_bio(m_rsa.get(), bio_memory.get());
const char* pch = nullptr;
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
return std::string(pch, lch);
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
}
}
void rsa_cipher::import_private_key_string(std::string_view key_string) {
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
if (!bio_memory.is_valid()) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
}
if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
}
m_rsa.set(_read_private_key_from_bio(bio_memory.get()));
}
void rsa_cipher::import_public_key_string_pem(std::string_view key_string) {
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
if (!bio_memory.is_valid()) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
}
if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
}
m_rsa.set(_read_public_key_pem_from_bio(bio_memory.get()));
}
void rsa_cipher::import_public_key_string_pkcs1(std::string_view key_string) {
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
if (!bio_memory.is_valid()) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
}
if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
}
m_rsa.set(_read_public_key_pkcs1_from_bio(bio_memory.get()));
}
size_t rsa_cipher::public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
if (plaintext_size <= INT_MAX) {
int bytes_written =
RSA_public_encrypt(static_cast<int>(plaintext_size), reinterpret_cast<const unsigned char*>(plaintext), reinterpret_cast<unsigned char*>(ciphertext), m_rsa.get(), padding);
if (bytes_written != -1) {
return bytes_written;
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
}
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
}
}
size_t rsa_cipher::private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
if (plaintext_size <= INT_MAX) {
int bytes_written =
RSA_private_encrypt(static_cast<int>(plaintext_size), reinterpret_cast<const unsigned char*>(plaintext), reinterpret_cast<unsigned char*>(ciphertext), m_rsa.get(), padding);
if (bytes_written != -1) {
return bytes_written;
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
}
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
}
}
size_t rsa_cipher::public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
if (ciphertext_size <= INT_MAX) {
int bytes_written =
RSA_public_decrypt(static_cast<int>(ciphertext_size), reinterpret_cast<const unsigned char*>(ciphertext), reinterpret_cast<unsigned char*>(plaintext), m_rsa.get(), padding);
if (bytes_written != -1) {
return bytes_written;
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
.push_hint(u8"Are your sure you DO provide a correct public key?");
}
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
}
}
size_t rsa_cipher::private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
if (ciphertext_size <= INT_MAX) {
int bytes_written =
RSA_private_decrypt(static_cast<int>(ciphertext_size), reinterpret_cast<const unsigned char*>(ciphertext), reinterpret_cast<unsigned char*>(plaintext), m_rsa.get(), padding);
if (bytes_written != -1) {
return bytes_written;
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
.push_hint(u8"Are your sure you DO provide a correct private key?");
}
} else {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
}
}
}
#undef NKG_CURRENT_SOURCE_FILE
#undef NKG_CURRENT_SOURCE_LINE

123
common/rsa_cipher.hpp Normal file
View File

@@ -0,0 +1,123 @@
#pragma once
#include <string>
#include <optional>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include "resource_wrapper.hpp"
#include "resource_traits/openssl/rsa.hpp"
#include "exception.hpp"
#define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.hpp"
#define NKG_CURRENT_SOURCE_LINE() __LINE__
namespace nkg {
class rsa_cipher {
public:
class no_key_assigned_error : public ::nkg::exception {
public:
no_key_assigned_error(std::string_view file, int line, std::string_view message) noexcept :
::nkg::exception(file, line, message) {}
};
class backend_error : public ::nkg::exception {
public:
using error_code_t = decltype(ERR_get_error());
private:
std::optional<error_code_t> m_error_code;
std::string m_error_string;
public:
backend_error(std::string_view file, int line, std::string_view message) noexcept :
::nkg::exception(file, line, message) {}
backend_error(std::string_view file, int line, error_code_t openssl_errno, std::string_view message) noexcept :
::nkg::exception(file, line, message), m_error_code(openssl_errno) {}
[[nodiscard]]
virtual bool error_code_exists() const noexcept override {
return m_error_code.has_value();
}
[[nodiscard]]
virtual intptr_t error_code() const noexcept override {
if (error_code_exists()) { return m_error_code.value(); } else { trap_then_terminate(); }
}
[[nodiscard]]
virtual const std::string& error_string() const noexcept override {
if (error_code_exists()) { return m_error_string; } else { trap_then_terminate(); }
}
};
private:
resource_wrapper<resource_traits::openssl::rsa> m_rsa;
[[nodiscard]]
static RSA* _read_private_key_from_bio(BIO* p_bio);
[[nodiscard]]
static RSA* _read_public_key_pem_from_bio(BIO* p_bio);
[[nodiscard]]
static RSA* _read_public_key_pkcs1_from_bio(BIO* p_bio);
static void _write_private_key_to_bio(RSA* p_rsa, BIO* p_bio);
static void _write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio);
static void _write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio);
public:
rsa_cipher();
[[nodiscard]]
size_t bits() const;
void generate_key(int bits, unsigned int e = RSA_F4);
void export_private_key_file(std::string_view file_path) const;
void export_public_key_file_pem(std::string_view file_path) const;
void export_public_key_file_pkcs1(std::string_view file_path) const;
void import_private_key_file(std::string_view file_path);
void import_public_key_file_pem(std::string_view file_path);
void import_public_key_file_pkcs1(std::string_view file_path);
[[nodiscard]]
std::string export_private_key_string() const;
[[nodiscard]]
std::string export_public_key_string_pem() const;
[[nodiscard]]
std::string export_public_key_string_pkcs1() const;
void import_private_key_string(std::string_view key_string);
void import_public_key_string_pem(std::string_view key_string);
void import_public_key_string_pkcs1(std::string_view key_string);
size_t public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const;
size_t private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const;
size_t public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const;
size_t private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const;
};
}
#undef NKG_CURRENT_SOURCE_FILE
#undef NKG_CURRENT_SOURCE_LINE