/src/node/src/crypto/crypto_keys.h
Line | Count | Source |
1 | | #ifndef SRC_CRYPTO_CRYPTO_KEYS_H_ |
2 | | #define SRC_CRYPTO_CRYPTO_KEYS_H_ |
3 | | |
4 | | #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
5 | | |
6 | | #include "crypto/crypto_util.h" |
7 | | #include "base_object.h" |
8 | | #include "env.h" |
9 | | #include "memory_tracker.h" |
10 | | #include "node_buffer.h" |
11 | | #include "node_worker.h" |
12 | | #include "v8.h" |
13 | | |
14 | | #include <openssl/evp.h> |
15 | | |
16 | | #include <memory> |
17 | | #include <string> |
18 | | |
19 | | namespace node::crypto { |
20 | | enum KeyType { |
21 | | kKeyTypeSecret, |
22 | | kKeyTypePublic, |
23 | | kKeyTypePrivate |
24 | | }; |
25 | | |
26 | | enum KeyEncodingContext { |
27 | | kKeyContextInput, |
28 | | kKeyContextExport, |
29 | | kKeyContextGenerate |
30 | | }; |
31 | | |
32 | | enum class ParseKeyResult { |
33 | | kParseKeyNotRecognized = |
34 | | static_cast<int>(ncrypto::EVPKeyPointer::PKParseError::NOT_RECOGNIZED), |
35 | | kParseKeyNeedPassphrase = |
36 | | static_cast<int>(ncrypto::EVPKeyPointer::PKParseError::NEED_PASSPHRASE), |
37 | | kParseKeyFailed = |
38 | | static_cast<int>(ncrypto::EVPKeyPointer::PKParseError::FAILED), |
39 | | kParseKeyOk, |
40 | | }; |
41 | | |
42 | | // Objects of this class can safely be shared among threads. |
43 | | class KeyObjectData final : public MemoryRetainer { |
44 | | public: |
45 | | static KeyObjectData CreateSecret(ByteSource key); |
46 | | |
47 | | static KeyObjectData CreateAsymmetric(KeyType type, |
48 | | ncrypto::EVPKeyPointer&& pkey); |
49 | | |
50 | | KeyObjectData(std::nullptr_t = nullptr); |
51 | | |
52 | 0 | inline operator bool() const { return data_ != nullptr; } |
53 | | |
54 | | KeyType GetKeyType() const; |
55 | | |
56 | | // These functions allow unprotected access to the raw key material and should |
57 | | // only be used to implement cryptographic operations requiring the key. |
58 | | const ncrypto::EVPKeyPointer& GetAsymmetricKey() const; |
59 | | const char* GetSymmetricKey() const; |
60 | | size_t GetSymmetricKeySize() const; |
61 | | |
62 | | void MemoryInfo(MemoryTracker* tracker) const override; |
63 | | SET_MEMORY_INFO_NAME(KeyObjectData) |
64 | | SET_SELF_SIZE(KeyObjectData) |
65 | | |
66 | | Mutex& mutex() const; |
67 | | |
68 | | static v8::Maybe<ncrypto::EVPKeyPointer::PublicKeyEncodingConfig> |
69 | | GetPublicKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args, |
70 | | unsigned int* offset, |
71 | | KeyEncodingContext context); |
72 | | |
73 | | static KeyObjectData GetPrivateKeyFromJs( |
74 | | const v8::FunctionCallbackInfo<v8::Value>& args, |
75 | | unsigned int* offset, |
76 | | bool allow_key_object); |
77 | | |
78 | | static KeyObjectData GetPublicOrPrivateKeyFromJs( |
79 | | const v8::FunctionCallbackInfo<v8::Value>& args, unsigned int* offset); |
80 | | |
81 | | static v8::Maybe<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig> |
82 | | GetPrivateKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args, |
83 | | unsigned int* offset, |
84 | | KeyEncodingContext context); |
85 | | |
86 | | bool ToEncodedPublicKey( |
87 | | Environment* env, |
88 | | const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config, |
89 | | v8::Local<v8::Value>* out); |
90 | | |
91 | | bool ToEncodedPrivateKey( |
92 | | Environment* env, |
93 | | const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config, |
94 | | v8::Local<v8::Value>* out); |
95 | | |
96 | 0 | inline KeyObjectData addRef() const { |
97 | 0 | return KeyObjectData(key_type_, mutex_, data_); |
98 | 0 | } |
99 | | |
100 | 0 | inline KeyObjectData addRefWithType(KeyType type) const { |
101 | 0 | return KeyObjectData(type, mutex_, data_); |
102 | 0 | } |
103 | | |
104 | | private: |
105 | | explicit KeyObjectData(ByteSource symmetric_key); |
106 | | explicit KeyObjectData(KeyType type, ncrypto::EVPKeyPointer&& pkey); |
107 | | |
108 | | static KeyObjectData GetParsedKey(KeyType type, |
109 | | Environment* env, |
110 | | ncrypto::EVPKeyPointer&& pkey, |
111 | | ParseKeyResult ret, |
112 | | const char* default_msg); |
113 | | |
114 | | KeyType key_type_; |
115 | | mutable std::shared_ptr<Mutex> mutex_; |
116 | | |
117 | | struct Data { |
118 | | const ByteSource symmetric_key; |
119 | | const ncrypto::EVPKeyPointer asymmetric_key; |
120 | | explicit Data(ByteSource symmetric_key) |
121 | 0 | : symmetric_key(std::move(symmetric_key)) {} |
122 | | explicit Data(ncrypto::EVPKeyPointer asymmetric_key) |
123 | 0 | : asymmetric_key(std::move(asymmetric_key)) {} |
124 | | }; |
125 | | std::shared_ptr<Data> data_; |
126 | | |
127 | | KeyObjectData(KeyType type, |
128 | | std::shared_ptr<Mutex> mutex, |
129 | | std::shared_ptr<Data> data) |
130 | 0 | : key_type_(type), mutex_(std::move(mutex)), data_(std::move(data)) {} |
131 | | }; |
132 | | |
133 | | class KeyObjectHandle : public BaseObject { |
134 | | public: |
135 | | static bool HasInstance(Environment* env, v8::Local<v8::Value> value); |
136 | | static v8::Local<v8::Function> Initialize(Environment* env); |
137 | | static void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
138 | | |
139 | | static v8::MaybeLocal<v8::Object> Create(Environment* env, |
140 | | const KeyObjectData& data); |
141 | | |
142 | | // TODO(tniessen): track the memory used by OpenSSL types |
143 | | SET_NO_MEMORY_INFO() |
144 | | SET_MEMORY_INFO_NAME(KeyObjectHandle) |
145 | | SET_SELF_SIZE(KeyObjectHandle) |
146 | | |
147 | | const KeyObjectData& Data(); |
148 | | |
149 | | protected: |
150 | | static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
151 | | |
152 | | static void Init(const v8::FunctionCallbackInfo<v8::Value>& args); |
153 | | static void InitECRaw(const v8::FunctionCallbackInfo<v8::Value>& args); |
154 | | static void InitEDRaw(const v8::FunctionCallbackInfo<v8::Value>& args); |
155 | | static void InitJWK(const v8::FunctionCallbackInfo<v8::Value>& args); |
156 | | static void GetKeyDetail(const v8::FunctionCallbackInfo<v8::Value>& args); |
157 | | static void Equals(const v8::FunctionCallbackInfo<v8::Value>& args); |
158 | | |
159 | | static void ExportJWK(const v8::FunctionCallbackInfo<v8::Value>& args); |
160 | | |
161 | | static void GetAsymmetricKeyType( |
162 | | const v8::FunctionCallbackInfo<v8::Value>& args); |
163 | | v8::Local<v8::Value> GetAsymmetricKeyType() const; |
164 | | |
165 | | static void CheckEcKeyData(const v8::FunctionCallbackInfo<v8::Value>& args); |
166 | | bool CheckEcKeyData() const; |
167 | | |
168 | | static void GetSymmetricKeySize( |
169 | | const v8::FunctionCallbackInfo<v8::Value>& args); |
170 | | |
171 | | static void Export(const v8::FunctionCallbackInfo<v8::Value>& args); |
172 | | |
173 | | #if OPENSSL_WITH_PQC |
174 | | static void InitPqcRaw(const v8::FunctionCallbackInfo<v8::Value>& args); |
175 | | static void RawPublicKey(const v8::FunctionCallbackInfo<v8::Value>& args); |
176 | | static void RawSeed(const v8::FunctionCallbackInfo<v8::Value>& args); |
177 | | #endif |
178 | | |
179 | | v8::MaybeLocal<v8::Value> ExportSecretKey() const; |
180 | | v8::MaybeLocal<v8::Value> ExportPublicKey( |
181 | | const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const; |
182 | | v8::MaybeLocal<v8::Value> ExportPrivateKey( |
183 | | const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) const; |
184 | | |
185 | | KeyObjectHandle(Environment* env, |
186 | | v8::Local<v8::Object> wrap); |
187 | | |
188 | | private: |
189 | | KeyObjectData data_; |
190 | | }; |
191 | | |
192 | | class NativeKeyObject : public BaseObject { |
193 | | public: |
194 | | static void Initialize(Environment* env, v8::Local<v8::Object> target); |
195 | | static void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
196 | | |
197 | | static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
198 | | static void CreateNativeKeyObjectClass( |
199 | | const v8::FunctionCallbackInfo<v8::Value>& args); |
200 | | |
201 | | SET_NO_MEMORY_INFO() |
202 | | SET_MEMORY_INFO_NAME(NativeKeyObject) |
203 | | SET_SELF_SIZE(NativeKeyObject) |
204 | | |
205 | | class KeyObjectTransferData : public worker::TransferData { |
206 | | public: |
207 | | explicit KeyObjectTransferData(const KeyObjectData& data) |
208 | 0 | : data_(data.addRef()) {} |
209 | | |
210 | | BaseObjectPtr<BaseObject> Deserialize( |
211 | | Environment* env, |
212 | | v8::Local<v8::Context> context, |
213 | | std::unique_ptr<worker::TransferData> self) override; |
214 | | |
215 | | SET_MEMORY_INFO_NAME(KeyObjectTransferData) |
216 | | SET_SELF_SIZE(KeyObjectTransferData) |
217 | | SET_NO_MEMORY_INFO() |
218 | | |
219 | | private: |
220 | | KeyObjectData data_; |
221 | | }; |
222 | | |
223 | | BaseObject::TransferMode GetTransferMode() const override; |
224 | | std::unique_ptr<worker::TransferData> CloneForMessaging() const override; |
225 | | |
226 | | private: |
227 | | NativeKeyObject(Environment* env, |
228 | | v8::Local<v8::Object> wrap, |
229 | | const KeyObjectData& handle_data) |
230 | 0 | : BaseObject(env, wrap), handle_data_(handle_data.addRef()) { |
231 | 0 | MakeWeak(); |
232 | 0 | } |
233 | | |
234 | | KeyObjectData handle_data_; |
235 | | }; |
236 | | |
237 | | enum WebCryptoKeyFormat { |
238 | | kWebCryptoKeyFormatRaw, |
239 | | kWebCryptoKeyFormatPKCS8, |
240 | | kWebCryptoKeyFormatSPKI, |
241 | | kWebCryptoKeyFormatJWK |
242 | | }; |
243 | | |
244 | | enum class WebCryptoKeyExportStatus { |
245 | | OK, |
246 | | INVALID_KEY_TYPE, |
247 | | FAILED |
248 | | }; |
249 | | |
250 | | template <typename KeyExportTraits> |
251 | | class KeyExportJob final : public CryptoJob<KeyExportTraits> { |
252 | | public: |
253 | | using AdditionalParams = typename KeyExportTraits::AdditionalParameters; |
254 | | |
255 | 0 | static void New(const v8::FunctionCallbackInfo<v8::Value>& args) { |
256 | 0 | Environment* env = Environment::GetCurrent(args); |
257 | 0 | CHECK(args.IsConstructCall()); |
258 | | |
259 | 0 | CryptoJobMode mode = GetCryptoJobMode(args[0]); |
260 | |
|
261 | 0 | CHECK(args[1]->IsUint32()); // Export Type |
262 | 0 | CHECK(args[2]->IsObject()); // KeyObject |
263 | | |
264 | 0 | WebCryptoKeyFormat format = |
265 | 0 | static_cast<WebCryptoKeyFormat>(args[1].As<v8::Uint32>()->Value()); |
266 | |
|
267 | 0 | KeyObjectHandle* key; |
268 | 0 | ASSIGN_OR_RETURN_UNWRAP(&key, args[2]); |
269 | | |
270 | 0 | CHECK_NOT_NULL(key); |
271 | | |
272 | 0 | AdditionalParams params; |
273 | 0 | if (KeyExportTraits::AdditionalConfig(args, 3, ¶ms).IsNothing()) { |
274 | | // The KeyExportTraits::AdditionalConfig is responsible for |
275 | | // calling an appropriate THROW_CRYPTO_* variant reporting |
276 | | // whatever error caused initialization to fail. |
277 | 0 | return; |
278 | 0 | } |
279 | | |
280 | 0 | new KeyExportJob<KeyExportTraits>( |
281 | 0 | env, |
282 | 0 | args.This(), |
283 | 0 | mode, |
284 | 0 | key->Data(), |
285 | 0 | format, |
286 | 0 | std::move(params)); |
287 | 0 | } Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::New(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::New(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::New(v8::FunctionCallbackInfo<v8::Value> const&) |
288 | | |
289 | | static void Initialize( |
290 | | Environment* env, |
291 | 0 | v8::Local<v8::Object> target) { |
292 | 0 | CryptoJob<KeyExportTraits>::Initialize(New, env, target); |
293 | 0 | } Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::Initialize(node::Environment*, v8::Local<v8::Object>) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::Initialize(node::Environment*, v8::Local<v8::Object>) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::Initialize(node::Environment*, v8::Local<v8::Object>) |
294 | | |
295 | 0 | static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
296 | 0 | CryptoJob<KeyExportTraits>::RegisterExternalReferences(New, registry); |
297 | 0 | } Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::RegisterExternalReferences(node::ExternalReferenceRegistry*) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::RegisterExternalReferences(node::ExternalReferenceRegistry*) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::RegisterExternalReferences(node::ExternalReferenceRegistry*) |
298 | | |
299 | | KeyExportJob(Environment* env, |
300 | | v8::Local<v8::Object> object, |
301 | | CryptoJobMode mode, |
302 | | const KeyObjectData& key, |
303 | | WebCryptoKeyFormat format, |
304 | | AdditionalParams&& params) |
305 | 0 | : CryptoJob<KeyExportTraits>(env, |
306 | 0 | object, |
307 | 0 | AsyncWrap::PROVIDER_KEYEXPORTREQUEST, |
308 | 0 | mode, |
309 | 0 | std::move(params)), |
310 | 0 | key_(key.addRef()), |
311 | 0 | format_(format) {}Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::KeyExportJob(node::Environment*, v8::Local<v8::Object>, node::crypto::CryptoJobMode, node::crypto::KeyObjectData const&, node::crypto::WebCryptoKeyFormat, node::crypto::ECKeyExportConfig&&) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::KeyExportJob(node::Environment*, v8::Local<v8::Object>, node::crypto::CryptoJobMode, node::crypto::KeyObjectData const&, node::crypto::WebCryptoKeyFormat, node::crypto::RSAKeyExportConfig&&) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::KeyExportJob(node::Environment*, v8::Local<v8::Object>, node::crypto::CryptoJobMode, node::crypto::KeyObjectData const&, node::crypto::WebCryptoKeyFormat, node::crypto::DHKeyExportConfig&&) |
312 | | |
313 | | WebCryptoKeyFormat format() const { return format_; } |
314 | | |
315 | 0 | void DoThreadPoolWork() override { |
316 | 0 | const WebCryptoKeyExportStatus status = |
317 | 0 | KeyExportTraits::DoExport( |
318 | 0 | key_, |
319 | 0 | format_, |
320 | 0 | *CryptoJob<KeyExportTraits>::params(), |
321 | 0 | &out_); |
322 | 0 | if (status == WebCryptoKeyExportStatus::OK) { |
323 | | // Success! |
324 | 0 | return; |
325 | 0 | } |
326 | 0 | CryptoErrorStore* errors = CryptoJob<KeyExportTraits>::errors(); |
327 | 0 | errors->Capture(); |
328 | 0 | if (errors->Empty()) { |
329 | 0 | switch (status) { |
330 | 0 | case WebCryptoKeyExportStatus::OK: |
331 | 0 | UNREACHABLE(); |
332 | 0 | break; |
333 | 0 | case WebCryptoKeyExportStatus::INVALID_KEY_TYPE: |
334 | 0 | errors->Insert(NodeCryptoError::INVALID_KEY_TYPE); |
335 | 0 | break; |
336 | 0 | case WebCryptoKeyExportStatus::FAILED: |
337 | 0 | errors->Insert(NodeCryptoError::CIPHER_JOB_FAILED); |
338 | 0 | break; |
339 | 0 | } |
340 | 0 | } |
341 | 0 | } Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::DoThreadPoolWork() Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::DoThreadPoolWork() Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::DoThreadPoolWork() |
342 | | |
343 | | v8::Maybe<void> ToResult(v8::Local<v8::Value>* err, |
344 | 0 | v8::Local<v8::Value>* result) override { |
345 | 0 | Environment* env = AsyncWrap::env(); |
346 | 0 | CryptoErrorStore* errors = CryptoJob<KeyExportTraits>::errors(); |
347 | 0 | if (out_.size() > 0) { |
348 | 0 | CHECK(errors->Empty()); |
349 | 0 | *err = v8::Undefined(env->isolate()); |
350 | 0 | *result = out_.ToArrayBuffer(env); |
351 | 0 | if (result->IsEmpty()) { |
352 | 0 | return v8::Nothing<void>(); |
353 | 0 | } |
354 | 0 | } else { |
355 | 0 | if (errors->Empty()) errors->Capture(); |
356 | 0 | CHECK(!errors->Empty()); |
357 | 0 | *result = v8::Undefined(env->isolate()); |
358 | 0 | if (!errors->ToException(env).ToLocal(err)) { |
359 | 0 | return v8::Nothing<void>(); |
360 | 0 | } |
361 | 0 | } |
362 | 0 | CHECK(!result->IsEmpty()); |
363 | 0 | CHECK(!err->IsEmpty()); |
364 | 0 | return v8::JustVoid(); |
365 | 0 | } Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::ToResult(v8::Local<v8::Value>*, v8::Local<v8::Value>*) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::ToResult(v8::Local<v8::Value>*, v8::Local<v8::Value>*) Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::ToResult(v8::Local<v8::Value>*, v8::Local<v8::Value>*) |
366 | | |
367 | | SET_SELF_SIZE(KeyExportJob) |
368 | 0 | void MemoryInfo(MemoryTracker* tracker) const override { |
369 | 0 | tracker->TrackFieldWithSize("out", out_.size()); |
370 | 0 | CryptoJob<KeyExportTraits>::MemoryInfo(tracker); |
371 | 0 | } Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::ECKeyExportTraits>::MemoryInfo(node::MemoryTracker*) const Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::RSAKeyExportTraits>::MemoryInfo(node::MemoryTracker*) const Unexecuted instantiation: node::crypto::KeyExportJob<node::crypto::DHKeyExportTraits>::MemoryInfo(node::MemoryTracker*) const |
372 | | |
373 | | private: |
374 | | KeyObjectData key_; |
375 | | WebCryptoKeyFormat format_; |
376 | | ByteSource out_; |
377 | | }; |
378 | | |
379 | | WebCryptoKeyExportStatus PKEY_SPKI_Export(const KeyObjectData& key_data, |
380 | | ByteSource* out); |
381 | | |
382 | | WebCryptoKeyExportStatus PKEY_PKCS8_Export(const KeyObjectData& key_data, |
383 | | ByteSource* out); |
384 | | |
385 | | namespace Keys { |
386 | | void Initialize(Environment* env, v8::Local<v8::Object> target); |
387 | | void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
388 | | } // namespace Keys |
389 | | } // namespace node::crypto |
390 | | |
391 | | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
392 | | #endif // SRC_CRYPTO_CRYPTO_KEYS_H_ |