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, ¶ms) 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