@hxa-rn/react-native-quick-crypto 1.1.0-beta.1 → 1.1.1
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.
|
@@ -1,284 +1,284 @@
|
|
|
1
|
-
#include "KeyObjectData.hpp"
|
|
2
|
-
#include "QuickCryptoUtils.hpp"
|
|
3
|
-
#include <cstdio>
|
|
4
|
-
#include <optional>
|
|
5
|
-
#include <string_view>
|
|
6
|
-
|
|
7
|
-
namespace margelo::nitro::crypto {
|
|
8
|
-
|
|
9
|
-
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig GetPrivateKeyEncodingConfig(KFormatType format, KeyEncoding type) {
|
|
10
|
-
auto pk_format = static_cast<ncrypto::EVPKeyPointer::PKFormatType>(format);
|
|
11
|
-
auto pk_type = static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(type);
|
|
12
|
-
|
|
13
|
-
auto config = ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig(false, pk_format, pk_type);
|
|
14
|
-
return config;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
ncrypto::EVPKeyPointer::PublicKeyEncodingConfig GetPublicKeyEncodingConfig(KFormatType format, KeyEncoding type) {
|
|
18
|
-
auto pk_format = static_cast<ncrypto::EVPKeyPointer::PKFormatType>(format);
|
|
19
|
-
auto pk_type = static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(type);
|
|
20
|
-
|
|
21
|
-
auto config = ncrypto::EVPKeyPointer::PublicKeyEncodingConfig(false, pk_format, pk_type);
|
|
22
|
-
return config;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
KeyObjectData TryParsePrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format, std::optional<KeyEncoding> type,
|
|
26
|
-
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
|
|
27
|
-
// For PEM format, use PKCS8 as default encoding
|
|
28
|
-
KeyEncoding actualType = type.value_or(KeyEncoding::PKCS8);
|
|
29
|
-
auto config = GetPrivateKeyEncodingConfig(format.value(), actualType);
|
|
30
|
-
|
|
31
|
-
if (passphrase.has_value()) {
|
|
32
|
-
auto& passphrase_ptr = passphrase.value();
|
|
33
|
-
config.passphrase = std::make_optional(
|
|
34
|
-
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
auto buffer = ncrypto::Buffer<const unsigned char>{key->data(), key->size()};
|
|
38
|
-
|
|
39
|
-
// Clear any existing OpenSSL errors before parsing
|
|
40
|
-
ERR_clear_error();
|
|
41
|
-
|
|
42
|
-
auto res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
43
|
-
if (res) {
|
|
44
|
-
return KeyObjectData::CreateAsymmetric(KeyType::PRIVATE, std::move(res.value));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (res.error.has_value() && res.error.value() == ncrypto::EVPKeyPointer::PKParseError::NEED_PASSPHRASE) {
|
|
48
|
-
throw std::runtime_error("Passphrase required for encrypted key");
|
|
49
|
-
} else {
|
|
50
|
-
// Get OpenSSL error details
|
|
51
|
-
unsigned long err = ERR_get_error();
|
|
52
|
-
char err_buf[256];
|
|
53
|
-
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
54
|
-
throw std::runtime_error("Failed to read private key: " + std::string(err_buf));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
KeyObjectData::KeyObjectData(std::nullptr_t) : key_type_(KeyType::SECRET) {}
|
|
59
|
-
|
|
60
|
-
KeyObjectData::KeyObjectData(std::shared_ptr<ArrayBuffer> symmetric_key)
|
|
61
|
-
: key_type_(KeyType::SECRET), data_(std::make_shared<Data>(std::move(symmetric_key))) {}
|
|
62
|
-
|
|
63
|
-
KeyObjectData::KeyObjectData(KeyType type, ncrypto::EVPKeyPointer&& pkey)
|
|
64
|
-
: key_type_(type), data_(std::make_shared<Data>(std::move(pkey))) {}
|
|
65
|
-
|
|
66
|
-
KeyObjectData KeyObjectData::CreateSecret(std::shared_ptr<ArrayBuffer> key) {
|
|
67
|
-
return KeyObjectData(std::move(key));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
KeyObjectData KeyObjectData::CreateAsymmetric(KeyType key_type, ncrypto::EVPKeyPointer&& pkey) {
|
|
71
|
-
CHECK(pkey);
|
|
72
|
-
return KeyObjectData(key_type, std::move(pkey));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
KeyType KeyObjectData::GetKeyType() const {
|
|
76
|
-
if (!data_) {
|
|
77
|
-
throw std::runtime_error("Invalid key object: no key data available");
|
|
78
|
-
}
|
|
79
|
-
return key_type_;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const ncrypto::EVPKeyPointer& KeyObjectData::GetAsymmetricKey() const {
|
|
83
|
-
if (key_type_ == KeyType::SECRET) {
|
|
84
|
-
throw std::runtime_error("Cannot get asymmetric key from secret key object");
|
|
85
|
-
}
|
|
86
|
-
if (!data_) {
|
|
87
|
-
throw std::runtime_error("Invalid key object: no key data available");
|
|
88
|
-
}
|
|
89
|
-
return data_->asymmetric_key;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
std::shared_ptr<ArrayBuffer> KeyObjectData::GetSymmetricKey() const {
|
|
93
|
-
if (key_type_ != KeyType::SECRET) {
|
|
94
|
-
throw std::runtime_error("Cannot get symmetric key from asymmetric key object");
|
|
95
|
-
}
|
|
96
|
-
if (!data_) {
|
|
97
|
-
throw std::runtime_error("Invalid key object: no key data available");
|
|
98
|
-
}
|
|
99
|
-
return data_->symmetric_key;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
size_t KeyObjectData::GetSymmetricKeySize() const {
|
|
103
|
-
if (key_type_ != KeyType::SECRET) {
|
|
104
|
-
throw std::runtime_error("Cannot get symmetric key size from asymmetric key object");
|
|
105
|
-
}
|
|
106
|
-
if (!data_) {
|
|
107
|
-
throw std::runtime_error("Invalid key object: no key data available");
|
|
108
|
-
}
|
|
109
|
-
return data_->symmetric_key->size();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
KeyObjectData KeyObjectData::GetPublicOrPrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
|
|
113
|
-
std::optional<KeyEncoding> type,
|
|
114
|
-
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
|
|
115
|
-
if (key->size() > static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
|
|
116
|
-
throw std::runtime_error("key is too big");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
KFormatType actualFormat = format.value_or(KFormatType::DER);
|
|
120
|
-
|
|
121
|
-
if (actualFormat == KFormatType::PEM || actualFormat == KFormatType::DER) {
|
|
122
|
-
auto buffer = ncrypto::Buffer<const unsigned char>{key->data(), key->size()};
|
|
123
|
-
|
|
124
|
-
if (actualFormat == KFormatType::PEM) {
|
|
125
|
-
if (type.has_value() && type.value() == KeyEncoding::SPKI) {
|
|
126
|
-
auto res = ncrypto::EVPKeyPointer::TryParsePublicKeyPEM(buffer);
|
|
127
|
-
if (res) {
|
|
128
|
-
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
|
|
129
|
-
}
|
|
130
|
-
throw std::runtime_error("Failed to read PEM public key: key is not in SPKI format");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (type.has_value() &&
|
|
134
|
-
(type.value() == KeyEncoding::PKCS8 || type.value() == KeyEncoding::SEC1 || type.value() == KeyEncoding::PKCS1)) {
|
|
135
|
-
auto config = GetPrivateKeyEncodingConfig(actualFormat, type.value());
|
|
136
|
-
if (passphrase.has_value()) {
|
|
137
|
-
auto& passphrase_ptr = passphrase.value();
|
|
138
|
-
config.passphrase = std::make_optional(
|
|
139
|
-
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
140
|
-
}
|
|
141
|
-
ERR_clear_error();
|
|
142
|
-
auto private_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
143
|
-
if (private_res) {
|
|
144
|
-
return CreateAsymmetric(KeyType::PRIVATE, std::move(private_res.value));
|
|
145
|
-
}
|
|
146
|
-
unsigned long err = ERR_get_error();
|
|
147
|
-
char err_buf[256];
|
|
148
|
-
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
149
|
-
throw std::runtime_error("Failed to read PEM private key: " + std::string(err_buf));
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
auto res = ncrypto::EVPKeyPointer::TryParsePublicKeyPEM(buffer);
|
|
153
|
-
if (res) {
|
|
154
|
-
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
KeyEncoding actualType = KeyEncoding::PKCS8;
|
|
158
|
-
auto config = GetPrivateKeyEncodingConfig(actualFormat, actualType);
|
|
159
|
-
if (passphrase.has_value()) {
|
|
160
|
-
auto& passphrase_ptr = passphrase.value();
|
|
161
|
-
config.passphrase = std::make_optional(
|
|
162
|
-
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
ERR_clear_error();
|
|
166
|
-
|
|
167
|
-
auto private_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
168
|
-
if (private_res) {
|
|
169
|
-
return CreateAsymmetric(KeyType::PRIVATE, std::move(private_res.value));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
unsigned long err = ERR_get_error();
|
|
173
|
-
char err_buf[256];
|
|
174
|
-
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
175
|
-
throw std::runtime_error("Failed to read PEM asymmetric key: " + std::string(err_buf));
|
|
176
|
-
} else if (actualFormat == KFormatType::DER) {
|
|
177
|
-
// For DER, try parsing as public key first
|
|
178
|
-
if (type.has_value() && type.value() == KeyEncoding::SPKI) {
|
|
179
|
-
auto public_config = GetPublicKeyEncodingConfig(actualFormat, type.value());
|
|
180
|
-
auto res = ncrypto::EVPKeyPointer::TryParsePublicKey(public_config, buffer);
|
|
181
|
-
if (res) {
|
|
182
|
-
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
|
|
183
|
-
}
|
|
184
|
-
} else if (type.has_value() && type.value() == KeyEncoding::PKCS8) {
|
|
185
|
-
auto private_config = GetPrivateKeyEncodingConfig(actualFormat, type.value());
|
|
186
|
-
if (passphrase.has_value()) {
|
|
187
|
-
auto& passphrase_ptr = passphrase.value();
|
|
188
|
-
private_config.passphrase = std::make_optional(
|
|
189
|
-
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
190
|
-
}
|
|
191
|
-
auto res = ncrypto::EVPKeyPointer::TryParsePrivateKey(private_config, buffer);
|
|
192
|
-
if (res) {
|
|
193
|
-
return CreateAsymmetric(KeyType::PRIVATE, std::move(res.value));
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
// If no encoding type specified, try both SPKI and PKCS8
|
|
197
|
-
auto public_config = GetPublicKeyEncodingConfig(actualFormat, KeyEncoding::SPKI);
|
|
198
|
-
auto public_res = ncrypto::EVPKeyPointer::TryParsePublicKey(public_config, buffer);
|
|
199
|
-
if (public_res) {
|
|
200
|
-
return CreateAsymmetric(KeyType::PUBLIC, std::move(public_res.value));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
auto private_config = GetPrivateKeyEncodingConfig(actualFormat, KeyEncoding::PKCS8);
|
|
204
|
-
auto private_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(private_config, buffer);
|
|
205
|
-
if (private_res) {
|
|
206
|
-
return CreateAsymmetric(KeyType::PRIVATE, std::move(private_res.value));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
throw std::runtime_error("Failed to read DER asymmetric key");
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
throw std::runtime_error("Unsupported key format for GetPublicOrPrivateKey. Only PEM and DER are supported.");
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
KeyObjectData KeyObjectData::GetPrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
|
|
217
|
-
std::optional<KeyEncoding> type, const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase,
|
|
218
|
-
bool /* isPublic */) {
|
|
219
|
-
// Check if key size fits in int32_t without using double conversion
|
|
220
|
-
if (key->size() > static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
|
|
221
|
-
std::string error_msg = "key is too big (int32): size=" + std::to_string(key->size()) +
|
|
222
|
-
", max_int32=" + std::to_string(std::numeric_limits<int32_t>::max());
|
|
223
|
-
throw std::runtime_error(error_msg);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// If no format is specified, assume DER format for binary data
|
|
227
|
-
KFormatType actualFormat = format.has_value() ? format.value() : KFormatType::DER;
|
|
228
|
-
|
|
229
|
-
// Auto-detect PEM when caller omitted format but payload looks like PEM text
|
|
230
|
-
if (!format.has_value() && key->size() >= 11) {
|
|
231
|
-
const char* data = reinterpret_cast<const char*>(key->data());
|
|
232
|
-
std::string_view head(data, std::min(key->size(), size_t(64)));
|
|
233
|
-
if (head.find("-----BEGIN") != std::string_view::npos) {
|
|
234
|
-
actualFormat = KFormatType::PEM;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (actualFormat == KFormatType::PEM || actualFormat == KFormatType::DER) {
|
|
239
|
-
auto buffer = ncrypto::Buffer<const unsigned char>{key->data(), key->size()};
|
|
240
|
-
|
|
241
|
-
if (actualFormat == KFormatType::PEM) {
|
|
242
|
-
return TryParsePrivateKey(key, format, type, passphrase);
|
|
243
|
-
} else if (actualFormat == KFormatType::DER) {
|
|
244
|
-
// Try the specified encoding first, or PKCS8 as default
|
|
245
|
-
KeyEncoding primaryEncoding = type.value_or(KeyEncoding::PKCS8);
|
|
246
|
-
auto private_config = GetPrivateKeyEncodingConfig(actualFormat, primaryEncoding);
|
|
247
|
-
if (passphrase.has_value()) {
|
|
248
|
-
auto& passphrase_ptr = passphrase.value();
|
|
249
|
-
private_config.passphrase = std::make_optional(
|
|
250
|
-
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Clear any existing OpenSSL errors before parsing
|
|
254
|
-
ERR_clear_error();
|
|
255
|
-
|
|
256
|
-
auto res = ncrypto::EVPKeyPointer::TryParsePrivateKey(private_config, buffer);
|
|
257
|
-
if (res) {
|
|
258
|
-
return CreateAsymmetric(KeyType::PRIVATE, std::move(res.value));
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// If no specific encoding was provided, try other encodings as fallback
|
|
262
|
-
if (!type.has_value()) {
|
|
263
|
-
std::vector<KeyEncoding> fallbackEncodings = {KeyEncoding::SEC1, KeyEncoding::PKCS1};
|
|
264
|
-
for (auto encoding : fallbackEncodings) {
|
|
265
|
-
auto config = GetPrivateKeyEncodingConfig(actualFormat, encoding);
|
|
266
|
-
if (passphrase.has_value()) {
|
|
267
|
-
auto& passphrase_ptr = passphrase.value();
|
|
268
|
-
config.passphrase = std::make_optional(
|
|
269
|
-
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
270
|
-
}
|
|
271
|
-
auto fallback_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
272
|
-
if (fallback_res) {
|
|
273
|
-
return CreateAsymmetric(KeyType::PRIVATE, std::move(fallback_res.value));
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
throw std::runtime_error("Failed to read DER private key");
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
throw std::runtime_error("Unsupported key format for GetPrivateKey. Only PEM and DER are supported.");
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
} // namespace margelo::nitro::crypto
|
|
1
|
+
#include "KeyObjectData.hpp"
|
|
2
|
+
#include "QuickCryptoUtils.hpp"
|
|
3
|
+
#include <cstdio>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <string_view>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::crypto {
|
|
8
|
+
|
|
9
|
+
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig GetPrivateKeyEncodingConfig(KFormatType format, KeyEncoding type) {
|
|
10
|
+
auto pk_format = static_cast<ncrypto::EVPKeyPointer::PKFormatType>(format);
|
|
11
|
+
auto pk_type = static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(type);
|
|
12
|
+
|
|
13
|
+
auto config = ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig(false, pk_format, pk_type);
|
|
14
|
+
return config;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
ncrypto::EVPKeyPointer::PublicKeyEncodingConfig GetPublicKeyEncodingConfig(KFormatType format, KeyEncoding type) {
|
|
18
|
+
auto pk_format = static_cast<ncrypto::EVPKeyPointer::PKFormatType>(format);
|
|
19
|
+
auto pk_type = static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(type);
|
|
20
|
+
|
|
21
|
+
auto config = ncrypto::EVPKeyPointer::PublicKeyEncodingConfig(false, pk_format, pk_type);
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
KeyObjectData TryParsePrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format, std::optional<KeyEncoding> type,
|
|
26
|
+
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
|
|
27
|
+
// For PEM format, use PKCS8 as default encoding
|
|
28
|
+
KeyEncoding actualType = type.value_or(KeyEncoding::PKCS8);
|
|
29
|
+
auto config = GetPrivateKeyEncodingConfig(format.value(), actualType);
|
|
30
|
+
|
|
31
|
+
if (passphrase.has_value()) {
|
|
32
|
+
auto& passphrase_ptr = passphrase.value();
|
|
33
|
+
config.passphrase = std::make_optional(
|
|
34
|
+
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
auto buffer = ncrypto::Buffer<const unsigned char>{key->data(), key->size()};
|
|
38
|
+
|
|
39
|
+
// Clear any existing OpenSSL errors before parsing
|
|
40
|
+
ERR_clear_error();
|
|
41
|
+
|
|
42
|
+
auto res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
43
|
+
if (res) {
|
|
44
|
+
return KeyObjectData::CreateAsymmetric(KeyType::PRIVATE, std::move(res.value));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (res.error.has_value() && res.error.value() == ncrypto::EVPKeyPointer::PKParseError::NEED_PASSPHRASE) {
|
|
48
|
+
throw std::runtime_error("Passphrase required for encrypted key");
|
|
49
|
+
} else {
|
|
50
|
+
// Get OpenSSL error details
|
|
51
|
+
unsigned long err = ERR_get_error();
|
|
52
|
+
char err_buf[256];
|
|
53
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
54
|
+
throw std::runtime_error("Failed to read private key: " + std::string(err_buf));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
KeyObjectData::KeyObjectData(std::nullptr_t) : key_type_(KeyType::SECRET) {}
|
|
59
|
+
|
|
60
|
+
KeyObjectData::KeyObjectData(std::shared_ptr<ArrayBuffer> symmetric_key)
|
|
61
|
+
: key_type_(KeyType::SECRET), data_(std::make_shared<Data>(std::move(symmetric_key))) {}
|
|
62
|
+
|
|
63
|
+
KeyObjectData::KeyObjectData(KeyType type, ncrypto::EVPKeyPointer&& pkey)
|
|
64
|
+
: key_type_(type), data_(std::make_shared<Data>(std::move(pkey))) {}
|
|
65
|
+
|
|
66
|
+
KeyObjectData KeyObjectData::CreateSecret(std::shared_ptr<ArrayBuffer> key) {
|
|
67
|
+
return KeyObjectData(std::move(key));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
KeyObjectData KeyObjectData::CreateAsymmetric(KeyType key_type, ncrypto::EVPKeyPointer&& pkey) {
|
|
71
|
+
CHECK(pkey);
|
|
72
|
+
return KeyObjectData(key_type, std::move(pkey));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
KeyType KeyObjectData::GetKeyType() const {
|
|
76
|
+
if (!data_) {
|
|
77
|
+
throw std::runtime_error("Invalid key object: no key data available");
|
|
78
|
+
}
|
|
79
|
+
return key_type_;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const ncrypto::EVPKeyPointer& KeyObjectData::GetAsymmetricKey() const {
|
|
83
|
+
if (key_type_ == KeyType::SECRET) {
|
|
84
|
+
throw std::runtime_error("Cannot get asymmetric key from secret key object");
|
|
85
|
+
}
|
|
86
|
+
if (!data_) {
|
|
87
|
+
throw std::runtime_error("Invalid key object: no key data available");
|
|
88
|
+
}
|
|
89
|
+
return data_->asymmetric_key;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
std::shared_ptr<ArrayBuffer> KeyObjectData::GetSymmetricKey() const {
|
|
93
|
+
if (key_type_ != KeyType::SECRET) {
|
|
94
|
+
throw std::runtime_error("Cannot get symmetric key from asymmetric key object");
|
|
95
|
+
}
|
|
96
|
+
if (!data_) {
|
|
97
|
+
throw std::runtime_error("Invalid key object: no key data available");
|
|
98
|
+
}
|
|
99
|
+
return data_->symmetric_key;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
size_t KeyObjectData::GetSymmetricKeySize() const {
|
|
103
|
+
if (key_type_ != KeyType::SECRET) {
|
|
104
|
+
throw std::runtime_error("Cannot get symmetric key size from asymmetric key object");
|
|
105
|
+
}
|
|
106
|
+
if (!data_) {
|
|
107
|
+
throw std::runtime_error("Invalid key object: no key data available");
|
|
108
|
+
}
|
|
109
|
+
return data_->symmetric_key->size();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
KeyObjectData KeyObjectData::GetPublicOrPrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
|
|
113
|
+
std::optional<KeyEncoding> type,
|
|
114
|
+
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
|
|
115
|
+
if (key->size() > static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
|
|
116
|
+
throw std::runtime_error("key is too big");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
KFormatType actualFormat = format.value_or(KFormatType::DER);
|
|
120
|
+
|
|
121
|
+
if (actualFormat == KFormatType::PEM || actualFormat == KFormatType::DER) {
|
|
122
|
+
auto buffer = ncrypto::Buffer<const unsigned char>{key->data(), key->size()};
|
|
123
|
+
|
|
124
|
+
if (actualFormat == KFormatType::PEM) {
|
|
125
|
+
if (type.has_value() && type.value() == KeyEncoding::SPKI) {
|
|
126
|
+
auto res = ncrypto::EVPKeyPointer::TryParsePublicKeyPEM(buffer);
|
|
127
|
+
if (res) {
|
|
128
|
+
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
|
|
129
|
+
}
|
|
130
|
+
throw std::runtime_error("Failed to read PEM public key: key is not in SPKI format");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (type.has_value() &&
|
|
134
|
+
(type.value() == KeyEncoding::PKCS8 || type.value() == KeyEncoding::SEC1 || type.value() == KeyEncoding::PKCS1)) {
|
|
135
|
+
auto config = GetPrivateKeyEncodingConfig(actualFormat, type.value());
|
|
136
|
+
if (passphrase.has_value()) {
|
|
137
|
+
auto& passphrase_ptr = passphrase.value();
|
|
138
|
+
config.passphrase = std::make_optional(
|
|
139
|
+
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
140
|
+
}
|
|
141
|
+
ERR_clear_error();
|
|
142
|
+
auto private_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
143
|
+
if (private_res) {
|
|
144
|
+
return CreateAsymmetric(KeyType::PRIVATE, std::move(private_res.value));
|
|
145
|
+
}
|
|
146
|
+
unsigned long err = ERR_get_error();
|
|
147
|
+
char err_buf[256];
|
|
148
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
149
|
+
throw std::runtime_error("Failed to read PEM private key: " + std::string(err_buf));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
auto res = ncrypto::EVPKeyPointer::TryParsePublicKeyPEM(buffer);
|
|
153
|
+
if (res) {
|
|
154
|
+
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
KeyEncoding actualType = KeyEncoding::PKCS8;
|
|
158
|
+
auto config = GetPrivateKeyEncodingConfig(actualFormat, actualType);
|
|
159
|
+
if (passphrase.has_value()) {
|
|
160
|
+
auto& passphrase_ptr = passphrase.value();
|
|
161
|
+
config.passphrase = std::make_optional(
|
|
162
|
+
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
ERR_clear_error();
|
|
166
|
+
|
|
167
|
+
auto private_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
168
|
+
if (private_res) {
|
|
169
|
+
return CreateAsymmetric(KeyType::PRIVATE, std::move(private_res.value));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
unsigned long err = ERR_get_error();
|
|
173
|
+
char err_buf[256];
|
|
174
|
+
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
175
|
+
throw std::runtime_error("Failed to read PEM asymmetric key: " + std::string(err_buf));
|
|
176
|
+
} else if (actualFormat == KFormatType::DER) {
|
|
177
|
+
// For DER, try parsing as public key first
|
|
178
|
+
if (type.has_value() && type.value() == KeyEncoding::SPKI) {
|
|
179
|
+
auto public_config = GetPublicKeyEncodingConfig(actualFormat, type.value());
|
|
180
|
+
auto res = ncrypto::EVPKeyPointer::TryParsePublicKey(public_config, buffer);
|
|
181
|
+
if (res) {
|
|
182
|
+
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
|
|
183
|
+
}
|
|
184
|
+
} else if (type.has_value() && type.value() == KeyEncoding::PKCS8) {
|
|
185
|
+
auto private_config = GetPrivateKeyEncodingConfig(actualFormat, type.value());
|
|
186
|
+
if (passphrase.has_value()) {
|
|
187
|
+
auto& passphrase_ptr = passphrase.value();
|
|
188
|
+
private_config.passphrase = std::make_optional(
|
|
189
|
+
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
190
|
+
}
|
|
191
|
+
auto res = ncrypto::EVPKeyPointer::TryParsePrivateKey(private_config, buffer);
|
|
192
|
+
if (res) {
|
|
193
|
+
return CreateAsymmetric(KeyType::PRIVATE, std::move(res.value));
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
// If no encoding type specified, try both SPKI and PKCS8
|
|
197
|
+
auto public_config = GetPublicKeyEncodingConfig(actualFormat, KeyEncoding::SPKI);
|
|
198
|
+
auto public_res = ncrypto::EVPKeyPointer::TryParsePublicKey(public_config, buffer);
|
|
199
|
+
if (public_res) {
|
|
200
|
+
return CreateAsymmetric(KeyType::PUBLIC, std::move(public_res.value));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
auto private_config = GetPrivateKeyEncodingConfig(actualFormat, KeyEncoding::PKCS8);
|
|
204
|
+
auto private_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(private_config, buffer);
|
|
205
|
+
if (private_res) {
|
|
206
|
+
return CreateAsymmetric(KeyType::PRIVATE, std::move(private_res.value));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
throw std::runtime_error("Failed to read DER asymmetric key");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
throw std::runtime_error("Unsupported key format for GetPublicOrPrivateKey. Only PEM and DER are supported.");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
KeyObjectData KeyObjectData::GetPrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
|
|
217
|
+
std::optional<KeyEncoding> type, const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase,
|
|
218
|
+
bool /* isPublic */) {
|
|
219
|
+
// Check if key size fits in int32_t without using double conversion
|
|
220
|
+
if (key->size() > static_cast<size_t>(std::numeric_limits<int32_t>::max())) {
|
|
221
|
+
std::string error_msg = "key is too big (int32): size=" + std::to_string(key->size()) +
|
|
222
|
+
", max_int32=" + std::to_string(std::numeric_limits<int32_t>::max());
|
|
223
|
+
throw std::runtime_error(error_msg);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// If no format is specified, assume DER format for binary data
|
|
227
|
+
KFormatType actualFormat = format.has_value() ? format.value() : KFormatType::DER;
|
|
228
|
+
|
|
229
|
+
// Auto-detect PEM when caller omitted format but payload looks like PEM text
|
|
230
|
+
if (!format.has_value() && key->size() >= 11) {
|
|
231
|
+
const char* data = reinterpret_cast<const char*>(key->data());
|
|
232
|
+
std::string_view head(data, std::min(key->size(), size_t(64)));
|
|
233
|
+
if (head.find("-----BEGIN") != std::string_view::npos) {
|
|
234
|
+
actualFormat = KFormatType::PEM;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (actualFormat == KFormatType::PEM || actualFormat == KFormatType::DER) {
|
|
239
|
+
auto buffer = ncrypto::Buffer<const unsigned char>{key->data(), key->size()};
|
|
240
|
+
|
|
241
|
+
if (actualFormat == KFormatType::PEM) {
|
|
242
|
+
return TryParsePrivateKey(key, format, type, passphrase);
|
|
243
|
+
} else if (actualFormat == KFormatType::DER) {
|
|
244
|
+
// Try the specified encoding first, or PKCS8 as default
|
|
245
|
+
KeyEncoding primaryEncoding = type.value_or(KeyEncoding::PKCS8);
|
|
246
|
+
auto private_config = GetPrivateKeyEncodingConfig(actualFormat, primaryEncoding);
|
|
247
|
+
if (passphrase.has_value()) {
|
|
248
|
+
auto& passphrase_ptr = passphrase.value();
|
|
249
|
+
private_config.passphrase = std::make_optional(
|
|
250
|
+
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Clear any existing OpenSSL errors before parsing
|
|
254
|
+
ERR_clear_error();
|
|
255
|
+
|
|
256
|
+
auto res = ncrypto::EVPKeyPointer::TryParsePrivateKey(private_config, buffer);
|
|
257
|
+
if (res) {
|
|
258
|
+
return CreateAsymmetric(KeyType::PRIVATE, std::move(res.value));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// If no specific encoding was provided, try other encodings as fallback
|
|
262
|
+
if (!type.has_value()) {
|
|
263
|
+
std::vector<KeyEncoding> fallbackEncodings = {KeyEncoding::SEC1, KeyEncoding::PKCS1};
|
|
264
|
+
for (auto encoding : fallbackEncodings) {
|
|
265
|
+
auto config = GetPrivateKeyEncodingConfig(actualFormat, encoding);
|
|
266
|
+
if (passphrase.has_value()) {
|
|
267
|
+
auto& passphrase_ptr = passphrase.value();
|
|
268
|
+
config.passphrase = std::make_optional(
|
|
269
|
+
ncrypto::DataPointer::Copy(ncrypto::Buffer<const void>{passphrase_ptr->data(), passphrase_ptr->size()}));
|
|
270
|
+
}
|
|
271
|
+
auto fallback_res = ncrypto::EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
|
272
|
+
if (fallback_res) {
|
|
273
|
+
return CreateAsymmetric(KeyType::PRIVATE, std::move(fallback_res.value));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
throw std::runtime_error("Failed to read DER private key");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
throw std::runtime_error("Unsupported key format for GetPrivateKey. Only PEM and DER are supported.");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
} // namespace margelo::nitro::crypto
|