react-native-quick-crypto 1.0.0-beta.21 → 1.0.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/QuickCrypto.podspec +11 -1
- package/android/CMakeLists.txt +2 -0
- package/cpp/cipher/GCMCipher.cpp +68 -0
- package/cpp/cipher/GCMCipher.hpp +14 -0
- package/cpp/cipher/HybridCipherFactory.hpp +8 -0
- package/cpp/cipher/HybridRsaCipher.cpp +229 -0
- package/cpp/cipher/HybridRsaCipher.hpp +23 -0
- package/cpp/keys/HybridKeyObjectHandle.cpp +508 -9
- package/cpp/keys/HybridKeyObjectHandle.hpp +10 -1
- package/cpp/utils/base64.h +309 -0
- package/lib/commonjs/ec.js +85 -17
- package/lib/commonjs/ec.js.map +1 -1
- package/lib/commonjs/specs/rsaCipher.nitro.js +6 -0
- package/lib/commonjs/specs/rsaCipher.nitro.js.map +1 -0
- package/lib/commonjs/subtle.js +420 -17
- package/lib/commonjs/subtle.js.map +1 -1
- package/lib/commonjs/utils/conversion.js +1 -1
- package/lib/commonjs/utils/conversion.js.map +1 -1
- package/lib/module/ec.js +86 -18
- package/lib/module/ec.js.map +1 -1
- package/lib/module/specs/rsaCipher.nitro.js +4 -0
- package/lib/module/specs/rsaCipher.nitro.js.map +1 -0
- package/lib/module/subtle.js +421 -18
- package/lib/module/subtle.js.map +1 -1
- package/lib/module/utils/conversion.js +1 -1
- package/lib/module/utils/conversion.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/typescript/ec.d.ts.map +1 -1
- package/lib/typescript/specs/keyObjectHandle.nitro.d.ts +1 -0
- package/lib/typescript/specs/keyObjectHandle.nitro.d.ts.map +1 -1
- package/lib/typescript/specs/rsaCipher.nitro.d.ts +26 -0
- package/lib/typescript/specs/rsaCipher.nitro.d.ts.map +1 -0
- package/lib/typescript/subtle.d.ts.map +1 -1
- package/lib/typescript/utils/conversion.d.ts.map +1 -1
- package/lib/typescript/utils/types.d.ts +1 -1
- package/lib/typescript/utils/types.d.ts.map +1 -1
- package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +1 -0
- package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +10 -0
- package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +10 -0
- package/nitrogen/generated/shared/c++/AsymmetricKeyType.hpp +104 -0
- package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.hpp +5 -4
- package/nitrogen/generated/shared/c++/HybridRsaCipherSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridRsaCipherSpec.hpp +70 -0
- package/package.json +1 -1
- package/src/ec.ts +122 -20
- package/src/specs/keyObjectHandle.nitro.ts +1 -0
- package/src/specs/rsaCipher.nitro.ts +35 -0
- package/src/subtle.ts +550 -45
- package/src/utils/conversion.ts +3 -1
- package/src/utils/types.ts +6 -6
- package/nitrogen/generated/shared/c++/CFRGKeyPairType.hpp +0 -84
package/QuickCrypto.podspec
CHANGED
|
@@ -145,6 +145,14 @@ Pod::Spec.new do |s|
|
|
|
145
145
|
"CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES" => "YES"
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
# Add cpp subdirectories to header search paths
|
|
149
|
+
cpp_headers = [
|
|
150
|
+
"\"$(PODS_TARGET_SRCROOT)/cpp/utils\"",
|
|
151
|
+
"\"$(PODS_TARGET_SRCROOT)/deps/ncrypto\"",
|
|
152
|
+
"\"$(PODS_TARGET_SRCROOT)/deps/blake3/c\"",
|
|
153
|
+
"\"$(PODS_TARGET_SRCROOT)/deps/fastpbkdf2\""
|
|
154
|
+
]
|
|
155
|
+
|
|
148
156
|
if sodium_enabled
|
|
149
157
|
sodium_headers = [
|
|
150
158
|
"\"$(PODS_TARGET_SRCROOT)/ios/libsodium-stable/src/libsodium/include\"",
|
|
@@ -153,8 +161,10 @@ Pod::Spec.new do |s|
|
|
|
153
161
|
"\"$(PODS_ROOT)/../../packages/react-native-quick-crypto/ios/libsodium-stable/src/libsodium/include\"",
|
|
154
162
|
"\"$(PODS_ROOT)/../../packages/react-native-quick-crypto/ios/libsodium-stable/src/libsodium/include/sodium\""
|
|
155
163
|
]
|
|
156
|
-
xcconfig["HEADER_SEARCH_PATHS"] = sodium_headers.join(' ')
|
|
164
|
+
xcconfig["HEADER_SEARCH_PATHS"] = (cpp_headers + sodium_headers).join(' ')
|
|
157
165
|
xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) BLSALLOC_SODIUM=1"
|
|
166
|
+
else
|
|
167
|
+
xcconfig["HEADER_SEARCH_PATHS"] = cpp_headers.join(' ')
|
|
158
168
|
end
|
|
159
169
|
|
|
160
170
|
s.pod_target_xcconfig = xcconfig
|
package/android/CMakeLists.txt
CHANGED
|
@@ -27,7 +27,9 @@ add_library(
|
|
|
27
27
|
src/main/cpp/cpp-adapter.cpp
|
|
28
28
|
../cpp/blake3/HybridBlake3.cpp
|
|
29
29
|
../cpp/cipher/CCMCipher.cpp
|
|
30
|
+
../cpp/cipher/GCMCipher.cpp
|
|
30
31
|
../cpp/cipher/HybridCipher.cpp
|
|
32
|
+
../cpp/cipher/HybridRsaCipher.cpp
|
|
31
33
|
../cpp/cipher/OCBCipher.cpp
|
|
32
34
|
../cpp/cipher/XSalsa20Cipher.cpp
|
|
33
35
|
../cpp/cipher/ChaCha20Cipher.cpp
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#include "GCMCipher.hpp"
|
|
2
|
+
#include "Utils.hpp"
|
|
3
|
+
#include <openssl/err.h>
|
|
4
|
+
#include <openssl/evp.h>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::crypto {
|
|
8
|
+
|
|
9
|
+
void GCMCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std::shared_ptr<ArrayBuffer> iv) {
|
|
10
|
+
// Clean up any existing context
|
|
11
|
+
if (ctx) {
|
|
12
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
13
|
+
ctx = nullptr;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 1. Get cipher implementation by name
|
|
17
|
+
const EVP_CIPHER* cipher = EVP_get_cipherbyname(cipher_type.c_str());
|
|
18
|
+
if (!cipher) {
|
|
19
|
+
throw std::runtime_error("Unknown cipher " + cipher_type);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 2. Create a new context
|
|
23
|
+
ctx = EVP_CIPHER_CTX_new();
|
|
24
|
+
if (!ctx) {
|
|
25
|
+
throw std::runtime_error("Failed to create cipher context");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 3. Initialize with cipher type only (no key/IV yet)
|
|
29
|
+
if (EVP_CipherInit_ex(ctx, cipher, nullptr, nullptr, nullptr, is_cipher) != 1) {
|
|
30
|
+
unsigned long err = ERR_get_error();
|
|
31
|
+
char err_buf[256];
|
|
32
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
33
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
34
|
+
ctx = nullptr;
|
|
35
|
+
throw std::runtime_error("GCMCipher: Failed initial CipherInit setup: " + std::string(err_buf));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 4. Set IV length for non-standard IV sizes (GCM default is 96 bits/12 bytes)
|
|
39
|
+
auto native_iv = ToNativeArrayBuffer(iv);
|
|
40
|
+
size_t iv_len = native_iv->size();
|
|
41
|
+
|
|
42
|
+
if (iv_len != 12) { // Only set if not the default length
|
|
43
|
+
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(iv_len), nullptr) != 1) {
|
|
44
|
+
unsigned long err = ERR_get_error();
|
|
45
|
+
char err_buf[256];
|
|
46
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
47
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
48
|
+
ctx = nullptr;
|
|
49
|
+
throw std::runtime_error("GCMCipher: Failed to set IV length: " + std::string(err_buf));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 5. Now set the key and IV
|
|
54
|
+
auto native_key = ToNativeArrayBuffer(cipher_key);
|
|
55
|
+
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(native_key->data());
|
|
56
|
+
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(native_iv->data());
|
|
57
|
+
|
|
58
|
+
if (EVP_CipherInit_ex(ctx, nullptr, nullptr, key_ptr, iv_ptr, is_cipher) != 1) {
|
|
59
|
+
unsigned long err = ERR_get_error();
|
|
60
|
+
char err_buf[256];
|
|
61
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
62
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
63
|
+
ctx = nullptr;
|
|
64
|
+
throw std::runtime_error("GCMCipher: Failed to set key/IV: " + std::string(err_buf));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
} // namespace margelo::nitro::crypto
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "HybridCipher.hpp"
|
|
4
|
+
|
|
5
|
+
namespace margelo::nitro::crypto {
|
|
6
|
+
|
|
7
|
+
class GCMCipher : public HybridCipher {
|
|
8
|
+
public:
|
|
9
|
+
GCMCipher() : HybridObject(TAG) {}
|
|
10
|
+
|
|
11
|
+
void init(const std::shared_ptr<ArrayBuffer> cipher_key, const std::shared_ptr<ArrayBuffer> iv) override;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
} // namespace margelo::nitro::crypto
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include "CCMCipher.hpp"
|
|
8
8
|
#include "ChaCha20Cipher.hpp"
|
|
9
9
|
#include "ChaCha20Poly1305Cipher.hpp"
|
|
10
|
+
#include "GCMCipher.hpp"
|
|
10
11
|
#include "HybridCipherFactorySpec.hpp"
|
|
11
12
|
#include "OCBCipher.hpp"
|
|
12
13
|
#include "Utils.hpp"
|
|
@@ -50,6 +51,13 @@ class HybridCipherFactory : public HybridCipherFactorySpec {
|
|
|
50
51
|
EVP_CIPHER_free(cipher);
|
|
51
52
|
return cipherInstance;
|
|
52
53
|
}
|
|
54
|
+
case EVP_CIPH_GCM_MODE: {
|
|
55
|
+
cipherInstance = std::make_shared<GCMCipher>();
|
|
56
|
+
cipherInstance->setArgs(args);
|
|
57
|
+
cipherInstance->init(args.cipherKey, args.iv);
|
|
58
|
+
EVP_CIPHER_free(cipher);
|
|
59
|
+
return cipherInstance;
|
|
60
|
+
}
|
|
53
61
|
case EVP_CIPH_STREAM_CIPHER: {
|
|
54
62
|
// Check for ChaCha20 variants specifically
|
|
55
63
|
std::string cipherName = toLower(args.cipherType);
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#include "HybridRsaCipher.hpp"
|
|
2
|
+
#include "../keys/HybridKeyObjectHandle.hpp"
|
|
3
|
+
#include "Utils.hpp"
|
|
4
|
+
|
|
5
|
+
#include <cstring>
|
|
6
|
+
#include <openssl/err.h>
|
|
7
|
+
#include <openssl/evp.h>
|
|
8
|
+
#include <openssl/rsa.h>
|
|
9
|
+
|
|
10
|
+
namespace margelo::nitro::crypto {
|
|
11
|
+
|
|
12
|
+
using margelo::nitro::NativeArrayBuffer;
|
|
13
|
+
|
|
14
|
+
// Helper to get OpenSSL digest from hash algorithm name
|
|
15
|
+
const EVP_MD* getDigestByName(const std::string& hashAlgorithm) {
|
|
16
|
+
if (hashAlgorithm == "SHA-1" || hashAlgorithm == "SHA1" || hashAlgorithm == "sha1" || hashAlgorithm == "sha-1") {
|
|
17
|
+
return EVP_sha1();
|
|
18
|
+
} else if (hashAlgorithm == "SHA-256" || hashAlgorithm == "SHA256" || hashAlgorithm == "sha256" || hashAlgorithm == "sha-256") {
|
|
19
|
+
return EVP_sha256();
|
|
20
|
+
} else if (hashAlgorithm == "SHA-384" || hashAlgorithm == "SHA384" || hashAlgorithm == "sha384" || hashAlgorithm == "sha-384") {
|
|
21
|
+
return EVP_sha384();
|
|
22
|
+
} else if (hashAlgorithm == "SHA-512" || hashAlgorithm == "SHA512" || hashAlgorithm == "sha512" || hashAlgorithm == "sha-512") {
|
|
23
|
+
return EVP_sha512();
|
|
24
|
+
}
|
|
25
|
+
throw std::runtime_error("Unsupported hash algorithm: " + hashAlgorithm);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
std::shared_ptr<ArrayBuffer> HybridRsaCipher::encrypt(const std::shared_ptr<HybridKeyObjectHandleSpec>& keyHandle,
|
|
29
|
+
const std::shared_ptr<ArrayBuffer>& data, const std::string& hashAlgorithm,
|
|
30
|
+
const std::optional<std::shared_ptr<ArrayBuffer>>& label) {
|
|
31
|
+
// Get the EVP_PKEY from the key handle
|
|
32
|
+
auto keyHandleImpl = std::static_pointer_cast<HybridKeyObjectHandle>(keyHandle);
|
|
33
|
+
EVP_PKEY* pkey = keyHandleImpl->getKeyObjectData().GetAsymmetricKey().get();
|
|
34
|
+
|
|
35
|
+
if (!pkey) {
|
|
36
|
+
throw std::runtime_error("Invalid key for RSA encryption");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create context for encryption
|
|
40
|
+
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, nullptr);
|
|
41
|
+
if (!ctx) {
|
|
42
|
+
throw std::runtime_error("Failed to create EVP_PKEY_CTX");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Initialize encryption
|
|
46
|
+
if (EVP_PKEY_encrypt_init(ctx) <= 0) {
|
|
47
|
+
EVP_PKEY_CTX_free(ctx);
|
|
48
|
+
unsigned long err = ERR_get_error();
|
|
49
|
+
char err_buf[256];
|
|
50
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
51
|
+
throw std::runtime_error("Failed to initialize encryption: " + std::string(err_buf));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Set padding to OAEP
|
|
55
|
+
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
|
|
56
|
+
EVP_PKEY_CTX_free(ctx);
|
|
57
|
+
throw std::runtime_error("Failed to set RSA OAEP padding");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Set OAEP hash algorithm
|
|
61
|
+
const EVP_MD* md = getDigestByName(hashAlgorithm);
|
|
62
|
+
if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) {
|
|
63
|
+
EVP_PKEY_CTX_free(ctx);
|
|
64
|
+
throw std::runtime_error("Failed to set OAEP hash algorithm");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Set MGF1 hash (same as OAEP hash per WebCrypto spec)
|
|
68
|
+
if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) <= 0) {
|
|
69
|
+
EVP_PKEY_CTX_free(ctx);
|
|
70
|
+
throw std::runtime_error("Failed to set MGF1 hash algorithm");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Set OAEP label if provided
|
|
74
|
+
if (label.has_value() && label.value()->size() > 0) {
|
|
75
|
+
auto native_label = ToNativeArrayBuffer(label.value());
|
|
76
|
+
// OpenSSL takes ownership of the label, so we need to allocate a copy
|
|
77
|
+
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(native_label->size());
|
|
78
|
+
if (!label_copy) {
|
|
79
|
+
EVP_PKEY_CTX_free(ctx);
|
|
80
|
+
throw std::runtime_error("Failed to allocate memory for label");
|
|
81
|
+
}
|
|
82
|
+
std::memcpy(label_copy, native_label->data(), native_label->size());
|
|
83
|
+
|
|
84
|
+
if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, native_label->size()) <= 0) {
|
|
85
|
+
OPENSSL_free(label_copy);
|
|
86
|
+
EVP_PKEY_CTX_free(ctx);
|
|
87
|
+
throw std::runtime_error("Failed to set OAEP label");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get input data
|
|
92
|
+
auto native_data = ToNativeArrayBuffer(data);
|
|
93
|
+
const unsigned char* in = native_data->data();
|
|
94
|
+
size_t inlen = native_data->size();
|
|
95
|
+
|
|
96
|
+
// Determine output length
|
|
97
|
+
size_t outlen;
|
|
98
|
+
if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, in, inlen) <= 0) {
|
|
99
|
+
EVP_PKEY_CTX_free(ctx);
|
|
100
|
+
unsigned long err = ERR_get_error();
|
|
101
|
+
char err_buf[256];
|
|
102
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
103
|
+
throw std::runtime_error("Failed to determine output length: " + std::string(err_buf));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Allocate output buffer
|
|
107
|
+
auto out_buf = std::make_unique<uint8_t[]>(outlen);
|
|
108
|
+
|
|
109
|
+
// Perform encryption
|
|
110
|
+
if (EVP_PKEY_encrypt(ctx, out_buf.get(), &outlen, in, inlen) <= 0) {
|
|
111
|
+
EVP_PKEY_CTX_free(ctx);
|
|
112
|
+
unsigned long err = ERR_get_error();
|
|
113
|
+
char err_buf[256];
|
|
114
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
115
|
+
throw std::runtime_error("Encryption failed: " + std::string(err_buf));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
EVP_PKEY_CTX_free(ctx);
|
|
119
|
+
|
|
120
|
+
// Create ArrayBuffer from result
|
|
121
|
+
uint8_t* raw_ptr = out_buf.get();
|
|
122
|
+
return std::make_shared<NativeArrayBuffer>(out_buf.release(), outlen, [raw_ptr]() { delete[] raw_ptr; });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
std::shared_ptr<ArrayBuffer> HybridRsaCipher::decrypt(const std::shared_ptr<HybridKeyObjectHandleSpec>& keyHandle,
|
|
126
|
+
const std::shared_ptr<ArrayBuffer>& data, const std::string& hashAlgorithm,
|
|
127
|
+
const std::optional<std::shared_ptr<ArrayBuffer>>& label) {
|
|
128
|
+
// Get the EVP_PKEY from the key handle
|
|
129
|
+
auto keyHandleImpl = std::static_pointer_cast<HybridKeyObjectHandle>(keyHandle);
|
|
130
|
+
EVP_PKEY* pkey = keyHandleImpl->getKeyObjectData().GetAsymmetricKey().get();
|
|
131
|
+
|
|
132
|
+
if (!pkey) {
|
|
133
|
+
throw std::runtime_error("Invalid key for RSA decryption");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Create context for decryption
|
|
137
|
+
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, nullptr);
|
|
138
|
+
if (!ctx) {
|
|
139
|
+
throw std::runtime_error("Failed to create EVP_PKEY_CTX");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Initialize decryption
|
|
143
|
+
if (EVP_PKEY_decrypt_init(ctx) <= 0) {
|
|
144
|
+
EVP_PKEY_CTX_free(ctx);
|
|
145
|
+
unsigned long err = ERR_get_error();
|
|
146
|
+
char err_buf[256];
|
|
147
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
148
|
+
throw std::runtime_error("Failed to initialize decryption: " + std::string(err_buf));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Set padding to OAEP
|
|
152
|
+
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
|
|
153
|
+
EVP_PKEY_CTX_free(ctx);
|
|
154
|
+
throw std::runtime_error("Failed to set RSA OAEP padding");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Set OAEP hash algorithm
|
|
158
|
+
const EVP_MD* md = getDigestByName(hashAlgorithm);
|
|
159
|
+
if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) {
|
|
160
|
+
EVP_PKEY_CTX_free(ctx);
|
|
161
|
+
throw std::runtime_error("Failed to set OAEP hash algorithm");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Set MGF1 hash (same as OAEP hash per WebCrypto spec)
|
|
165
|
+
if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) <= 0) {
|
|
166
|
+
EVP_PKEY_CTX_free(ctx);
|
|
167
|
+
throw std::runtime_error("Failed to set MGF1 hash algorithm");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Set OAEP label if provided
|
|
171
|
+
if (label.has_value() && label.value()->size() > 0) {
|
|
172
|
+
auto native_label = ToNativeArrayBuffer(label.value());
|
|
173
|
+
// OpenSSL takes ownership of the label, so we need to allocate a copy
|
|
174
|
+
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(native_label->size());
|
|
175
|
+
if (!label_copy) {
|
|
176
|
+
EVP_PKEY_CTX_free(ctx);
|
|
177
|
+
throw std::runtime_error("Failed to allocate memory for label");
|
|
178
|
+
}
|
|
179
|
+
std::memcpy(label_copy, native_label->data(), native_label->size());
|
|
180
|
+
|
|
181
|
+
if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, native_label->size()) <= 0) {
|
|
182
|
+
OPENSSL_free(label_copy);
|
|
183
|
+
EVP_PKEY_CTX_free(ctx);
|
|
184
|
+
throw std::runtime_error("Failed to set OAEP label");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Get input data
|
|
189
|
+
auto native_data = ToNativeArrayBuffer(data);
|
|
190
|
+
const unsigned char* in = native_data->data();
|
|
191
|
+
size_t inlen = native_data->size();
|
|
192
|
+
|
|
193
|
+
// Determine output length
|
|
194
|
+
size_t outlen;
|
|
195
|
+
if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, in, inlen) <= 0) {
|
|
196
|
+
EVP_PKEY_CTX_free(ctx);
|
|
197
|
+
unsigned long err = ERR_get_error();
|
|
198
|
+
char err_buf[256];
|
|
199
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
200
|
+
throw std::runtime_error("Failed to determine output length: " + std::string(err_buf));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Allocate output buffer
|
|
204
|
+
auto out_buf = std::make_unique<uint8_t[]>(outlen);
|
|
205
|
+
|
|
206
|
+
// Perform decryption
|
|
207
|
+
if (EVP_PKEY_decrypt(ctx, out_buf.get(), &outlen, in, inlen) <= 0) {
|
|
208
|
+
EVP_PKEY_CTX_free(ctx);
|
|
209
|
+
unsigned long err = ERR_get_error();
|
|
210
|
+
char err_buf[256];
|
|
211
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
212
|
+
throw std::runtime_error("Decryption failed: " + std::string(err_buf));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
EVP_PKEY_CTX_free(ctx);
|
|
216
|
+
|
|
217
|
+
// Create ArrayBuffer from result
|
|
218
|
+
uint8_t* raw_ptr = out_buf.get();
|
|
219
|
+
return std::make_shared<NativeArrayBuffer>(out_buf.release(), outlen, [raw_ptr]() { delete[] raw_ptr; });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
void HybridRsaCipher::loadHybridMethods() {
|
|
223
|
+
registerHybrids(this, [](Prototype& prototype) {
|
|
224
|
+
prototype.registerHybridMethod("encrypt", &HybridRsaCipher::encrypt);
|
|
225
|
+
prototype.registerHybridMethod("decrypt", &HybridRsaCipher::decrypt);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
} // namespace margelo::nitro::crypto
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "HybridRsaCipherSpec.hpp"
|
|
4
|
+
#include <memory>
|
|
5
|
+
|
|
6
|
+
namespace margelo::nitro::crypto {
|
|
7
|
+
|
|
8
|
+
class HybridRsaCipher : public HybridRsaCipherSpec {
|
|
9
|
+
public:
|
|
10
|
+
HybridRsaCipher() : HybridObject(TAG) {}
|
|
11
|
+
|
|
12
|
+
std::shared_ptr<ArrayBuffer> encrypt(const std::shared_ptr<HybridKeyObjectHandleSpec>& keyHandle,
|
|
13
|
+
const std::shared_ptr<ArrayBuffer>& data, const std::string& hashAlgorithm,
|
|
14
|
+
const std::optional<std::shared_ptr<ArrayBuffer>>& label) override;
|
|
15
|
+
|
|
16
|
+
std::shared_ptr<ArrayBuffer> decrypt(const std::shared_ptr<HybridKeyObjectHandleSpec>& keyHandle,
|
|
17
|
+
const std::shared_ptr<ArrayBuffer>& data, const std::string& hashAlgorithm,
|
|
18
|
+
const std::optional<std::shared_ptr<ArrayBuffer>>& label) override;
|
|
19
|
+
|
|
20
|
+
void loadHybridMethods() override;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
} // namespace margelo::nitro::crypto
|