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