Coverage Report

Created: 2026-01-21 08:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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, &params).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_