• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef SRC_CRYPTO_CRYPTO_CIPHER_H_
2 #define SRC_CRYPTO_CRYPTO_CIPHER_H_
3 
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5 
6 #include "crypto/crypto_keys.h"
7 #include "crypto/crypto_util.h"
8 #include "base_object.h"
9 #include "env.h"
10 #include "memory_tracker.h"
11 #include "v8.h"
12 
13 #include <string>
14 
15 namespace node {
16 namespace crypto {
17 class CipherBase : public BaseObject {
18  public:
19   static void GetSSLCiphers(const v8::FunctionCallbackInfo<v8::Value>& args);
20   static void GetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args);
21 
22   static void Initialize(Environment* env, v8::Local<v8::Object> target);
23   static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
24 
25   void MemoryInfo(MemoryTracker* tracker) const override;
26   SET_MEMORY_INFO_NAME(CipherBase)
27   SET_SELF_SIZE(CipherBase)
28 
29  protected:
30   enum CipherKind {
31     kCipher,
32     kDecipher
33   };
34   enum UpdateResult {
35     kSuccess,
36     kErrorMessageSize,
37     kErrorState
38   };
39   enum AuthTagState {
40     kAuthTagUnknown,
41     kAuthTagKnown,
42     kAuthTagPassedToOpenSSL
43   };
44   static const unsigned kNoAuthTagLength = static_cast<unsigned>(-1);
45 
46   void CommonInit(const char* cipher_type,
47                   const EVP_CIPHER* cipher,
48                   const unsigned char* key,
49                   int key_len,
50                   const unsigned char* iv,
51                   int iv_len,
52                   unsigned int auth_tag_len);
53   void Init(const char* cipher_type,
54             const ArrayBufferOrViewContents<unsigned char>& key_buf,
55             unsigned int auth_tag_len);
56   void InitIv(const char* cipher_type,
57               const ByteSource& key_buf,
58               const ArrayBufferOrViewContents<unsigned char>& iv_buf,
59               unsigned int auth_tag_len);
60   bool InitAuthenticated(const char* cipher_type, int iv_len,
61                          unsigned int auth_tag_len);
62   bool CheckCCMMessageLength(int message_len);
63   UpdateResult Update(const char* data, size_t len,
64                       std::unique_ptr<v8::BackingStore>* out);
65   bool Final(std::unique_ptr<v8::BackingStore>* out);
66   bool SetAutoPadding(bool auto_padding);
67 
68   bool IsAuthenticatedMode() const;
69   bool SetAAD(
70       const ArrayBufferOrViewContents<unsigned char>& data,
71       int plaintext_len);
72   bool MaybePassAuthTagToOpenSSL();
73 
74   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
75   static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
76   static void InitIv(const v8::FunctionCallbackInfo<v8::Value>& args);
77   static void Update(const v8::FunctionCallbackInfo<v8::Value>& args);
78   static void Final(const v8::FunctionCallbackInfo<v8::Value>& args);
79   static void SetAutoPadding(const v8::FunctionCallbackInfo<v8::Value>& args);
80 
81   static void GetAuthTag(const v8::FunctionCallbackInfo<v8::Value>& args);
82   static void SetAuthTag(const v8::FunctionCallbackInfo<v8::Value>& args);
83   static void SetAAD(const v8::FunctionCallbackInfo<v8::Value>& args);
84 
85   CipherBase(Environment* env, v8::Local<v8::Object> wrap, CipherKind kind);
86 
87  private:
88   DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> ctx_;
89   const CipherKind kind_;
90   AuthTagState auth_tag_state_;
91   unsigned int auth_tag_len_;
92   char auth_tag_[EVP_GCM_TLS_TAG_LEN];
93   bool pending_auth_failed_;
94   int max_message_size_;
95 };
96 
97 class PublicKeyCipher {
98  public:
99   typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX* ctx);
100   typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX* ctx,
101                                    unsigned char* out, size_t* outlen,
102                                    const unsigned char* in, size_t inlen);
103 
104   enum Operation {
105     kPublic,
106     kPrivate
107   };
108 
109   template <Operation operation,
110             EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
111             EVP_PKEY_cipher_t EVP_PKEY_cipher>
112   static bool Cipher(Environment* env,
113                      const ManagedEVPPKey& pkey,
114                      int padding,
115                      const EVP_MD* digest,
116                      const ArrayBufferOrViewContents<unsigned char>& oaep_label,
117                      const ArrayBufferOrViewContents<unsigned char>& data,
118                      std::unique_ptr<v8::BackingStore>* out);
119 
120   template <Operation operation,
121             EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
122             EVP_PKEY_cipher_t EVP_PKEY_cipher>
123   static void Cipher(const v8::FunctionCallbackInfo<v8::Value>& args);
124 };
125 
126 enum WebCryptoCipherMode {
127   kWebCryptoCipherEncrypt,
128   kWebCryptoCipherDecrypt
129 };
130 
131 enum class WebCryptoCipherStatus {
132   OK,
133   INVALID_KEY_TYPE,
134   FAILED
135 };
136 
137 // CipherJob is a base implementation class for implementations of
138 // one-shot sync and async ciphers. It has been added primarily to
139 // support the AES and RSA ciphers underlying the WebCrypt API.
140 //
141 // See the crypto_aes and crypto_rsa headers for examples of how to
142 // use CipherJob.
143 template <typename CipherTraits>
144 class CipherJob final : public CryptoJob<CipherTraits> {
145  public:
146   using AdditionalParams = typename CipherTraits::AdditionalParameters;
147 
New(const v8::FunctionCallbackInfo<v8::Value> & args)148   static void New(const v8::FunctionCallbackInfo<v8::Value>& args) {
149     Environment* env = Environment::GetCurrent(args);
150     CHECK(args.IsConstructCall());
151 
152     CryptoJobMode mode = GetCryptoJobMode(args[0]);
153 
154     CHECK(args[1]->IsUint32());  // Cipher Mode
155 
156     uint32_t cmode = args[1].As<v8::Uint32>()->Value();
157     CHECK_LE(cmode, WebCryptoCipherMode::kWebCryptoCipherDecrypt);
158     WebCryptoCipherMode cipher_mode = static_cast<WebCryptoCipherMode>(cmode);
159 
160     CHECK(args[2]->IsObject());  // KeyObject
161     KeyObjectHandle* key;
162     ASSIGN_OR_RETURN_UNWRAP(&key, args[2]);
163     CHECK_NOT_NULL(key);
164 
165     ArrayBufferOrViewContents<char> data(args[3]);  // data to operate on
166     if (!data.CheckSizeInt32())
167       return THROW_ERR_OUT_OF_RANGE(env, "data is too large");
168 
169     AdditionalParams params;
170     if (CipherTraits::AdditionalConfig(mode, args, 4, cipher_mode, &params)
171             .IsNothing()) {
172       // The CipherTraits::AdditionalConfig is responsible for
173       // calling an appropriate THROW_CRYPTO_* variant reporting
174       // whatever error caused initialization to fail.
175       return;
176     }
177 
178     new CipherJob<CipherTraits>(
179         env,
180         args.This(),
181         mode,
182         key,
183         cipher_mode,
184         data,
185         std::move(params));
186   }
187 
Initialize(Environment * env,v8::Local<v8::Object> target)188   static void Initialize(
189       Environment* env,
190       v8::Local<v8::Object> target) {
191     CryptoJob<CipherTraits>::Initialize(New, env, target);
192   }
193 
RegisterExternalReferences(ExternalReferenceRegistry * registry)194   static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
195     CryptoJob<CipherTraits>::RegisterExternalReferences(New, registry);
196   }
197 
CipherJob(Environment * env,v8::Local<v8::Object> object,CryptoJobMode mode,KeyObjectHandle * key,WebCryptoCipherMode cipher_mode,const ArrayBufferOrViewContents<char> & data,AdditionalParams && params)198   CipherJob(
199       Environment* env,
200       v8::Local<v8::Object> object,
201       CryptoJobMode mode,
202       KeyObjectHandle* key,
203       WebCryptoCipherMode cipher_mode,
204       const ArrayBufferOrViewContents<char>& data,
205       AdditionalParams&& params)
206       : CryptoJob<CipherTraits>(
207             env,
208             object,
209             AsyncWrap::PROVIDER_CIPHERREQUEST,
210             mode,
211             std::move(params)),
212         key_(key->Data()),
213         cipher_mode_(cipher_mode),
214         in_(mode == kCryptoJobAsync
215             ? data.ToCopy()
216             : data.ToByteSource()) {}
217 
key()218   std::shared_ptr<KeyObjectData> key() const { return key_; }
219 
cipher_mode()220   WebCryptoCipherMode cipher_mode() const { return cipher_mode_; }
221 
DoThreadPoolWork()222   void DoThreadPoolWork() override {
223     const WebCryptoCipherStatus status =
224         CipherTraits::DoCipher(
225             AsyncWrap::env(),
226             key(),
227             cipher_mode_,
228             *CryptoJob<CipherTraits>::params(),
229             in_,
230             &out_);
231     if (status == WebCryptoCipherStatus::OK) {
232       // Success!
233       return;
234     }
235     CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors();
236     errors->Capture();
237     if (errors->Empty()) {
238       switch (status) {
239         case WebCryptoCipherStatus::OK:
240           UNREACHABLE();
241           break;
242         case WebCryptoCipherStatus::INVALID_KEY_TYPE:
243           errors->Insert(NodeCryptoError::INVALID_KEY_TYPE);
244           break;
245         case WebCryptoCipherStatus::FAILED:
246           errors->Insert(NodeCryptoError::CIPHER_JOB_FAILED);
247           break;
248       }
249     }
250   }
251 
ToResult(v8::Local<v8::Value> * err,v8::Local<v8::Value> * result)252   v8::Maybe<bool> ToResult(
253       v8::Local<v8::Value>* err,
254       v8::Local<v8::Value>* result) override {
255     Environment* env = AsyncWrap::env();
256     CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors();
257 
258     if (errors->Empty())
259       errors->Capture();
260 
261     if (out_.size() > 0 || errors->Empty()) {
262       CHECK(errors->Empty());
263       *err = v8::Undefined(env->isolate());
264       *result = out_.ToArrayBuffer(env);
265       return v8::Just(!result->IsEmpty());
266     }
267 
268     *result = v8::Undefined(env->isolate());
269     return v8::Just(errors->ToException(env).ToLocal(err));
270   }
271 
SET_SELF_SIZE(CipherJob)272   SET_SELF_SIZE(CipherJob)
273   void MemoryInfo(MemoryTracker* tracker) const override {
274     if (CryptoJob<CipherTraits>::mode() == kCryptoJobAsync)
275       tracker->TrackFieldWithSize("in", in_.size());
276     tracker->TrackFieldWithSize("out", out_.size());
277     CryptoJob<CipherTraits>::MemoryInfo(tracker);
278   }
279 
280  private:
281   std::shared_ptr<KeyObjectData> key_;
282   WebCryptoCipherMode cipher_mode_;
283   ByteSource in_;
284   ByteSource out_;
285 };
286 
287 }  // namespace crypto
288 }  // namespace node
289 
290 #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
291 #endif  // SRC_CRYPTO_CRYPTO_CIPHER_H_
292