diff --git a/common/common.vcxitems b/common/common.vcxitems
index f4354a3..c6f9817 100644
--- a/common/common.vcxitems
+++ b/common/common.vcxitems
@@ -27,6 +27,11 @@
+
+
+
+
+
diff --git a/common/resource_traits/openssl/decoder_ctx.hpp b/common/resource_traits/openssl/decoder_ctx.hpp
new file mode 100644
index 0000000..7bd7dd7
--- /dev/null
+++ b/common/resource_traits/openssl/decoder_ctx.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include
+
+namespace nkg::resource_traits::openssl {
+
+ struct decoder_ctx {
+ using handle_t = OSSL_DECODER_CTX*;
+
+ 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 {
+ OSSL_DECODER_CTX_free(handle);
+ }
+ };
+
+}
diff --git a/common/resource_traits/openssl/encoder_ctx.hpp b/common/resource_traits/openssl/encoder_ctx.hpp
new file mode 100644
index 0000000..2d484dd
--- /dev/null
+++ b/common/resource_traits/openssl/encoder_ctx.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include
+
+namespace nkg::resource_traits::openssl {
+
+ struct encoder_ctx {
+ using handle_t = OSSL_ENCODER_CTX*;
+
+ 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 {
+ OSSL_ENCODER_CTX_free(handle);
+ }
+ };
+
+}
diff --git a/common/resource_traits/openssl/evp_cipher_ctx.hpp b/common/resource_traits/openssl/evp_cipher_ctx.hpp
new file mode 100644
index 0000000..b38c8c9
--- /dev/null
+++ b/common/resource_traits/openssl/evp_cipher_ctx.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include
+
+namespace nkg::resource_traits::openssl {
+
+ struct evp_cipher_ctx {
+ using handle_t = EVP_CIPHER_CTX*;
+
+ 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 {
+ EVP_CIPHER_CTX_free(handle);
+ }
+ };
+
+}
diff --git a/common/resource_traits/openssl/evp_pkey.hpp b/common/resource_traits/openssl/evp_pkey.hpp
new file mode 100644
index 0000000..52d70e3
--- /dev/null
+++ b/common/resource_traits/openssl/evp_pkey.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include
+
+namespace nkg::resource_traits::openssl {
+
+ struct evp_pkey {
+ using handle_t = EVP_PKEY*;
+
+ 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 {
+ EVP_PKEY_free(handle);
+ }
+ };
+
+}
diff --git a/common/resource_traits/openssl/evp_pkey_ctx.hpp b/common/resource_traits/openssl/evp_pkey_ctx.hpp
new file mode 100644
index 0000000..96c9f0d
--- /dev/null
+++ b/common/resource_traits/openssl/evp_pkey_ctx.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include
+
+namespace nkg::resource_traits::openssl {
+
+ struct evp_pkey_ctx {
+ using handle_t = EVP_PKEY_CTX*;
+
+ 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 {
+ EVP_PKEY_CTX_free(handle);
+ }
+ };
+
+}
diff --git a/common/rsa_cipher.cpp b/common/rsa_cipher.cpp
index 4dfaf82..f6aba0b 100644
--- a/common/rsa_cipher.cpp
+++ b/common/rsa_cipher.cpp
@@ -1,16 +1,22 @@
#include "rsa_cipher.hpp"
+#include
-#include
#include
#include
#include "resource_traits/openssl/bio.hpp"
#include "resource_traits/openssl/bignum.hpp"
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+#include
+#include
+#include "resource_traits/openssl/encoder_ctx.hpp"
+#include "resource_traits/openssl/decoder_ctx.hpp"
+#endif
+
#include "cp_converter.hpp"
#include "exceptions/overflow_exception.hpp"
-#include "exceptions/openssl_exception.hpp"
#pragma comment(lib, "libcrypto")
#pragma comment(lib, "crypt32") // required by libcrypto.lib
@@ -21,6 +27,7 @@
namespace nkg {
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
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) };
@@ -77,41 +84,172 @@ namespace nkg {
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPublicKey failed.");
}
}
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ [[nodiscard]]
+ EVP_PKEY* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) {
+ resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
- rsa_cipher::rsa_cipher() : m_rsa(RSA_new()) {
- if (!m_rsa.is_valid()) {
- throw exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_new failed.");
+ resource_wrapper decoder_context
+ { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "pkcs1", "RSA", OSSL_KEYMGMT_SELECT_PRIVATE_KEY, nullptr, nullptr) };
+
+ if (!decoder_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed.");
}
+
+ if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed.");
+ }
+
+ return new_rsa.transfer();
+}
+
+ [[nodiscard]]
+ EVP_PKEY* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) {
+ resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
+
+ resource_wrapper decoder_context
+ { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "SubjectPublicKeyInfo", "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, nullptr, nullptr) };
+
+ if (!decoder_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed.");
+ }
+
+ if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed.");
+ }
+
+ return new_rsa.transfer();
}
[[nodiscard]]
- size_t rsa_cipher::bits() const {
-#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x
- if (m_rsa->n == nullptr) {
- throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA modulus has not been set.");
+ EVP_PKEY* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) {
+ resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
+
+ resource_wrapper decoder_context
+ { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "pkcs1", "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, nullptr, nullptr) };
+
+ if (!decoder_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed.");
}
- return BN_num_bits(m_rsa->n);
-#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x
- return RSA_bits(m_rsa.get());
+
+ if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed.");
+ }
+
+ return new_rsa.transfer();
+ }
+
+ void rsa_cipher::_write_private_key_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) {
+ resource_wrapper encoder_context
+ { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, "PEM", "pkcs1", nullptr) };
+
+ if (!encoder_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed.");
+ }
+
+ if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed.");
+ }
+ }
+
+ void rsa_cipher::_write_public_key_pem_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) {
+ resource_wrapper encoder_context
+ { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", "SubjectPublicKeyInfo", nullptr) };
+
+ if (!encoder_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed.");
+ }
+
+ if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed.");
+ }
+ }
+
+ void rsa_cipher::_write_public_key_pkcs1_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) {
+ resource_wrapper encoder_context
+ { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", "pkcs1", nullptr) };
+
+ if (!encoder_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed.");
+ }
+
+ if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed.");
+ }
+ }
+#else
+#error "rsa_cipher.cpp: Unexpected OpenSSL version."
+#endif
+
+ rsa_cipher::rsa_cipher() = default;
+
+ [[nodiscard]]
+ size_t rsa_cipher::bits() const {
+ if (m_rsa.get()) {
+#if (OPENSSL_VERSION_NUMBER & 0xfff00000) == 0x10000000 // openssl 1.0.x
+ return BN_num_bits(m_rsa->n);
+#elif (OPENSSL_VERSION_NUMBER & 0xfff00000) == 0x10100000 // openssl 1.1.x
+ return RSA_bits(m_rsa.get());
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // openssl 3.x.x
+ return EVP_PKEY_get_bits(m_rsa.get());
#else
#error "rsa_cipher.cpp: uexpected OpenSSL version"
#endif
+ } else {
+ throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA key has not been assigned yet.");
+ }
}
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() == false) {
- throw exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"BN_new failed.");
+ 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 exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_generate_key_ex failed.");
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
+ resource_wrapper new_rsa{ resource_traits::openssl::rsa{}, RSA_new() };
+ if (!new_rsa.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_new failed.");
}
+
+ if (RSA_generate_key_ex(new_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.");
+ }
+
+ m_rsa = std::move(new_rsa);
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) };
+ if (!evp_pkey_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new_id failed.");
+ }
+
+ if (EVP_PKEY_keygen_init(evp_pkey_context.get()) <= 0) { // 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_keygen_init failed.");
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(evp_pkey_context.get(), bits) <= 0) { // return a positive value for success and 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_keygen_bits failed.");
+ }
+
+ if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(evp_pkey_context.get(), bn_e.get()) <= 0) { // return a positive value for success and 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set1_rsa_keygen_pubexp failed.");
+ }
+
+ resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} };
+
+ if (EVP_PKEY_keygen(evp_pkey_context.get(), new_rsa.unsafe_addressof()) <= 0) { // 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_keygen failed.");
+ }
+
+ m_rsa = std::move(new_rsa);
+#else
+#error "rsa_cipher.cpp: Unexpected OpenSSL version."
+#endif
}
void rsa_cipher::export_private_key_file(std::wstring_view file_path) const {
@@ -295,6 +433,7 @@ namespace nkg {
}
size_t rsa_cipher::public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
if (plaintext_size <= INT_MAX) {
int bytes_written =
RSA_public_encrypt(static_cast(plaintext_size), reinterpret_cast(plaintext), reinterpret_cast(ciphertext), m_rsa.get(), padding);
@@ -302,14 +441,42 @@ namespace nkg {
if (bytes_written != -1) {
return bytes_written;
} else {
- throw exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
}
} else {
throw exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
}
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
+ if (!evp_pkey_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
+ }
+
+ if (EVP_PKEY_encrypt_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt_init failed.");
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
+ }
+
+ size_t ciphertext_size = 0;
+ if (EVP_PKEY_encrypt(evp_pkey_context.get(), nullptr, &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt failed.");
+ }
+
+ if (EVP_PKEY_encrypt(evp_pkey_context.get(), reinterpret_cast(ciphertext), &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt failed.");
+ }
+
+ return ciphertext_size;
+#else
+#error "rsa_cipher.cpp: Unexpected OpenSSL version."
+#endif
}
size_t rsa_cipher::private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
if (plaintext_size <= INT_MAX) {
int bytes_written =
RSA_private_encrypt(static_cast(plaintext_size), reinterpret_cast(plaintext), reinterpret_cast(ciphertext), m_rsa.get(), padding);
@@ -317,14 +484,42 @@ namespace nkg {
if (bytes_written != -1) {
return bytes_written;
} else {
- throw exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
}
} else {
throw exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
}
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
+ if (!evp_pkey_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
+ }
+
+ if (EVP_PKEY_sign_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign_init failed.");
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
+ }
+
+ size_t ciphertext_size = 0;
+ if (EVP_PKEY_sign(evp_pkey_context.get(), nullptr, &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign failed.");
+ }
+
+ if (EVP_PKEY_sign(evp_pkey_context.get(), reinterpret_cast(ciphertext), &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign failed.");
+ }
+
+ return ciphertext_size;
+#else
+#error "rsa_cipher.cpp: Unexpected OpenSSL version."
+#endif
}
size_t rsa_cipher::public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
if (ciphertext_size <= INT_MAX) {
int bytes_written =
RSA_public_decrypt(static_cast(ciphertext_size), reinterpret_cast(ciphertext), reinterpret_cast(plaintext), m_rsa.get(), padding);
@@ -332,15 +527,44 @@ namespace nkg {
if (bytes_written != -1) {
return bytes_written;
} else {
- throw exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
+ 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 exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
}
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
+ if (!evp_pkey_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
+ }
+
+ if (EVP_PKEY_verify_recover_init(evp_pkey_context.get())) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover_init failed.");
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
+ }
+
+ size_t plaintext_size = 0;
+ if (EVP_PKEY_verify_recover(evp_pkey_context.get(), nullptr, &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover failed.")
+ .push_hint(u8"Are your sure you DO provide a correct public key?");
+ }
+
+ if (EVP_PKEY_verify_recover(evp_pkey_context.get(), reinterpret_cast(plaintext), &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover failed.");
+ }
+
+ return plaintext_size;
+#else
+#error "rsa_cipher.cpp: Unexpected OpenSSL version."
+#endif
}
size_t rsa_cipher::private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
if (ciphertext_size <= INT_MAX) {
int bytes_written =
RSA_private_decrypt(static_cast(ciphertext_size), reinterpret_cast(ciphertext), reinterpret_cast(plaintext), m_rsa.get(), padding);
@@ -348,12 +572,52 @@ namespace nkg {
if (bytes_written != -1) {
return bytes_written;
} else {
- throw exceptions::openssl_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
+ 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 exceptions::overflow_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
}
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) };
+ if (!evp_pkey_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed.");
+ }
+
+ if (EVP_PKEY_decrypt_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt_init failed.");
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed.");
+ }
+
+ size_t plaintext_size = 0;
+ if (EVP_PKEY_decrypt(evp_pkey_context.get(), nullptr, &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt failed.")
+ .push_hint(u8"Are your sure you DO provide a correct private key?");
+ }
+
+ if (EVP_PKEY_decrypt(evp_pkey_context.get(), reinterpret_cast(plaintext), &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt failed.");
+ }
+
+ return plaintext_size;
+#else
+#error "rsa_cipher.cpp: Unexpected OpenSSL version."
+#endif
+ }
+
+ rsa_cipher::backend_error::backend_error(std::string_view file, int line, std::string_view message) noexcept:
+ ::nkg::exception::exception(file, line, message), m_error_code(0) {}
+
+ rsa_cipher::backend_error::backend_error(std::string_view file, int line, error_code_t openssl_errno, std::string_view message) noexcept:
+ ::nkg::exception::exception(file, line, message), m_error_code(openssl_errno)
+ {
+ static std::once_flag onceflag_load_crypto_strings;
+ std::call_once(onceflag_load_crypto_strings, []() { ERR_load_crypto_strings(); });
+
+ m_error_string = ERR_reason_error_string(m_error_code);
}
}
diff --git a/common/rsa_cipher.hpp b/common/rsa_cipher.hpp
index 39a9bc7..2188248 100644
--- a/common/rsa_cipher.hpp
+++ b/common/rsa_cipher.hpp
@@ -1,10 +1,19 @@
#pragma once
#include
#include
+
+#include
#include
#include "resource_wrapper.hpp"
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
#include "resource_traits/openssl/rsa.hpp"
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+#include "resource_traits/openssl/evp_pkey_ctx.hpp"
+#include "resource_traits/openssl/evp_pkey.hpp"
+#else
+#error "rsa_cipher.hpp: Unexpected OpenSSL version."
+#endif
#include "exception.hpp"
@@ -15,19 +24,11 @@ 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:
- backend_error(std::string_view file, int line, std::string_view message) noexcept :
- ::nkg::exception(file, line, message) {}
- };
+ class backend_error;
+ class no_key_assigned_error;
private:
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0
resource_wrapper m_rsa;
[[nodiscard]]
@@ -44,9 +45,28 @@ namespace nkg {
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);
+#elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ resource_wrapper m_rsa;
+
+ [[nodiscard]]
+ static EVP_PKEY* _read_private_key_from_bio(BIO* p_bio);
+
+ [[nodiscard]]
+ static EVP_PKEY* _read_public_key_pem_from_bio(BIO* p_bio);
+
+ [[nodiscard]]
+ static EVP_PKEY* _read_public_key_pkcs1_from_bio(BIO* p_bio);
+
+ static void _write_private_key_to_bio(EVP_PKEY* p_rsa, BIO* p_bio);
+
+ static void _write_public_key_pem_to_bio(EVP_PKEY* p_rsa, BIO* p_bio);
+
+ static void _write_public_key_pkcs1_to_bio(EVP_PKEY* p_rsa, BIO* p_bio);
+#else
+#error "rsa_cipher.hpp: Unexpected OpenSSL version."
+#endif
public:
-
rsa_cipher();
[[nodiscard]]
@@ -102,6 +122,39 @@ namespace nkg {
size_t private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const;
};
+ class rsa_cipher::backend_error : public ::nkg::exception {
+ public:
+ using error_code_t = decltype(ERR_get_error());
+
+ private:
+ 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;
+
+ backend_error(std::string_view file, int line, error_code_t openssl_errno, std::string_view message) noexcept;
+
+ [[nodiscard]]
+ virtual bool error_code_exists() const noexcept override {
+ return m_error_code != 0;
+ }
+
+ [[nodiscard]]
+ virtual intptr_t error_code() const noexcept override {
+ if (error_code_exists()) { return m_error_code; } 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(); }
+ }
+ };
+
+ class rsa_cipher::no_key_assigned_error : public ::nkg::exception {
+ using ::nkg::exception::exception;
+ };
+
}
#undef NKG_CURRENT_SOURCE_FILE
diff --git a/navicat-keygen/navicat_serial_generator.cpp b/navicat-keygen/navicat_serial_generator.cpp
index cb60764..6a4feff 100644
--- a/navicat-keygen/navicat_serial_generator.cpp
+++ b/navicat-keygen/navicat_serial_generator.cpp
@@ -1,15 +1,33 @@
#include "navicat_serial_generator.hpp"
-#include "exception.hpp"
-#include "base32_rfc4648.hpp"
-#include
-#include
#include
+#include
+#include
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+#include
+#endif
+
+#include "resource_wrapper.hpp"
+#include "resource_traits/openssl/evp_cipher_ctx.hpp"
+
+#include
+#include "base32_rfc4648.hpp"
+
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\navicat_serial_generator.cpp"
#define NKG_CURRENT_SOURCE_LINE() __LINE__
namespace nkg {
+ char navicat_serial_generator::_replace_confusing_chars(char c) noexcept {
+ if (c == 'I') {
+ return '8';
+ } else if (c == 'O') {
+ return '9';
+ } else {
+ return c;
+ }
+ };
+
navicat_serial_generator::navicat_serial_generator() noexcept :
m_data{ 0x68 , 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32 }, m_des_key{} {}
@@ -112,36 +130,46 @@ namespace nkg {
void navicat_serial_generator::set_software_version(int ver) {
if (11 <= ver && ver < 16) {
+ static_assert(sizeof(m_des_key) == sizeof(s_des_key0));
+
m_data[8] = static_cast((ver << 4) | (m_data[8] & 0x0f));
memcpy(m_des_key, s_des_key0, sizeof(s_des_key0));
} else if (16 <= ver && ver < 32) {
+ static_assert(sizeof(m_des_key) == sizeof(s_des_key1));
+
m_data[8] = static_cast(((ver - 16) << 4) | (m_data[8] & 0x0f));
memcpy(m_des_key, s_des_key1, sizeof(s_des_key1));
} else {
- throw exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid navicat version.");
+ throw version_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid navicat version.");
}
}
void navicat_serial_generator::generate() {
RAND_bytes(m_data + 2, 3);
- DES_key_schedule schedule;
- DES_set_key_unchecked(&m_des_key, &schedule);
- DES_ecb_encrypt(reinterpret_cast(m_data + 2), reinterpret_cast(m_data + 2), &schedule, DES_ENCRYPT);
+#if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x
+ if (!OSSL_PROVIDER_available(nullptr, "legacy")) {
+ if (OSSL_PROVIDER_load(nullptr, "legacy") == nullptr) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_PROVIDER_load failed.");
+ }
+ }
+#endif
+
+ resource_wrapper evp_cipher_context{ resource_traits::openssl::evp_cipher_ctx{}, EVP_CIPHER_CTX_new() };
+ if (!evp_cipher_context.is_valid()) {
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_CIPHER_CTX_new failed.");
+ }
+
+ if (EVP_EncryptInit_ex(evp_cipher_context.get(), EVP_des_ecb(), nullptr, m_des_key, nullptr) <= 0) { // return 1 for success and 0 for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_EncryptInit failed.");
+ }
+
+ if (int _; EVP_EncryptUpdate(evp_cipher_context.get(), m_data + 2, &_, m_data + 2, 8) <= 0) { // return 1 for success and 0 for failure
+ throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_EncryptUpdate failed.");
+ }
m_serial_number = base32_rfc4648::encode(m_data, sizeof(m_data));
- std::transform(
- m_serial_number.begin(), m_serial_number.end(), m_serial_number.begin(),
- [](char c) -> char {
- if (c == 'I') {
- return '8';
- } else if (c == 'O') {
- return '9';
- } else {
- return c;
- }
- }
- );
+ std::transform(m_serial_number.begin(), m_serial_number.end(), m_serial_number.begin(), _replace_confusing_chars);
std::string_view sn = m_serial_number;
m_serial_number_formatted = fmt::format("{}-{}-{}-{}", sn.substr(0, 4), sn.substr(4, 4), sn.substr(8, 4), sn.substr(12, 4));
diff --git a/navicat-keygen/navicat_serial_generator.hpp b/navicat-keygen/navicat_serial_generator.hpp
index 6a220ef..07b0847 100644
--- a/navicat-keygen/navicat_serial_generator.hpp
+++ b/navicat-keygen/navicat_serial_generator.hpp
@@ -1,7 +1,7 @@
#pragma once
#include
#include
-#include
+#include "exception.hpp"
namespace nkg {
@@ -33,15 +33,21 @@ namespace nkg {
};
class navicat_serial_generator {
+ public:
+ class version_error;
+ class backend_error;
+
private:
- static inline const DES_cblock s_des_key0 = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
- static inline const DES_cblock s_des_key1 = { 0xE9, 0x7F, 0xB0, 0x60, 0x77, 0x45, 0x90, 0xAE };
+ static inline const uint8_t s_des_key0[8] = {0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27};
+ static inline const uint8_t s_des_key1[8] = {0xE9, 0x7F, 0xB0, 0x60, 0x77, 0x45, 0x90, 0xAE};
uint8_t m_data[10];
- DES_cblock m_des_key;
+ uint8_t m_des_key[8];
std::string m_serial_number;
std::string m_serial_number_formatted;
+ static char _replace_confusing_chars(char c) noexcept;
+
public:
navicat_serial_generator() noexcept;
@@ -62,5 +68,13 @@ namespace nkg {
const std::string& serial_number_formatted() const noexcept;
};
+ class navicat_serial_generator::version_error : public ::nkg::exception {
+ using ::nkg::exception::exception;
+ };
+
+ class navicat_serial_generator::backend_error : public ::nkg::exception {
+ using ::nkg::exception::exception;
+ };
+
}