| #ifndef SRC_CRYPTO_CRYPTO_CIPHER_H_ |
| #define SRC_CRYPTO_CRYPTO_CIPHER_H_ |
| |
| #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
| |
| #include "crypto/crypto_keys.h" |
| #include "crypto/crypto_util.h" |
| #include "base_object.h" |
| #include "env.h" |
| #include "memory_tracker.h" |
| #include "v8.h" |
| |
| #include <string> |
| |
| namespace node { |
| namespace crypto { |
| class CipherBase : public BaseObject { |
| public: |
| static void GetSSLCiphers(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void GetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static void Initialize(Environment* env, v8::Local<v8::Object> target); |
| static void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
| |
| void MemoryInfo(MemoryTracker* tracker) const override; |
| SET_MEMORY_INFO_NAME(CipherBase) |
| SET_SELF_SIZE(CipherBase) |
| |
| protected: |
| enum CipherKind { |
| kCipher, |
| kDecipher |
| }; |
| enum UpdateResult { |
| kSuccess, |
| kErrorMessageSize, |
| kErrorState |
| }; |
| enum AuthTagState { |
| kAuthTagUnknown, |
| kAuthTagKnown, |
| kAuthTagPassedToOpenSSL |
| }; |
| static const unsigned kNoAuthTagLength = static_cast<unsigned>(-1); |
| |
| void CommonInit(const char* cipher_type, |
| const EVP_CIPHER* cipher, |
| const unsigned char* key, |
| int key_len, |
| const unsigned char* iv, |
| int iv_len, |
| unsigned int auth_tag_len); |
| void Init(const char* cipher_type, |
| const ArrayBufferOrViewContents<unsigned char>& key_buf, |
| unsigned int auth_tag_len); |
| void InitIv(const char* cipher_type, |
| const ByteSource& key_buf, |
| const ArrayBufferOrViewContents<unsigned char>& iv_buf, |
| unsigned int auth_tag_len); |
| bool InitAuthenticated(const char* cipher_type, int iv_len, |
| unsigned int auth_tag_len); |
| bool CheckCCMMessageLength(int message_len); |
| UpdateResult Update(const char* data, size_t len, |
| std::unique_ptr<v8::BackingStore>* out); |
| bool Final(std::unique_ptr<v8::BackingStore>* out); |
| bool SetAutoPadding(bool auto_padding); |
| |
| bool IsAuthenticatedMode() const; |
| bool SetAAD( |
| const ArrayBufferOrViewContents<unsigned char>& data, |
| int plaintext_len); |
| bool MaybePassAuthTagToOpenSSL(); |
| |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void Init(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void InitIv(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void Update(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void Final(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void SetAutoPadding(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static void GetAuthTag(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void SetAuthTag(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void SetAAD(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| CipherBase(Environment* env, v8::Local<v8::Object> wrap, CipherKind kind); |
| |
| private: |
| DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> ctx_; |
| const CipherKind kind_; |
| AuthTagState auth_tag_state_; |
| unsigned int auth_tag_len_; |
| char auth_tag_[EVP_GCM_TLS_TAG_LEN]; |
| bool pending_auth_failed_; |
| int max_message_size_; |
| }; |
| |
| class PublicKeyCipher { |
| public: |
| typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX* ctx); |
| typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX* ctx, |
| unsigned char* out, size_t* outlen, |
| const unsigned char* in, size_t inlen); |
| |
| enum Operation { |
| kPublic, |
| kPrivate |
| }; |
| |
| template <Operation operation, |
| EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init, |
| EVP_PKEY_cipher_t EVP_PKEY_cipher> |
| static bool Cipher(Environment* env, |
| const ManagedEVPPKey& pkey, |
| int padding, |
| const EVP_MD* digest, |
| const ArrayBufferOrViewContents<unsigned char>& oaep_label, |
| const ArrayBufferOrViewContents<unsigned char>& data, |
| std::unique_ptr<v8::BackingStore>* out); |
| |
| template <Operation operation, |
| EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init, |
| EVP_PKEY_cipher_t EVP_PKEY_cipher> |
| static void Cipher(const v8::FunctionCallbackInfo<v8::Value>& args); |
| }; |
| |
| enum WebCryptoCipherMode { |
| kWebCryptoCipherEncrypt, |
| kWebCryptoCipherDecrypt |
| }; |
| |
| enum class WebCryptoCipherStatus { |
| OK, |
| INVALID_KEY_TYPE, |
| FAILED |
| }; |
| |
| // CipherJob is a base implementation class for implementations of |
| // one-shot sync and async ciphers. It has been added primarily to |
| // support the AES and RSA ciphers underlying the WebCrypt API. |
| // |
| // See the crypto_aes and crypto_rsa headers for examples of how to |
| // use CipherJob. |
| template <typename CipherTraits> |
| class CipherJob final : public CryptoJob<CipherTraits> { |
| public: |
| using AdditionalParams = typename CipherTraits::AdditionalParameters; |
| |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| CHECK(args.IsConstructCall()); |
| |
| CryptoJobMode mode = GetCryptoJobMode(args[0]); |
| |
| CHECK(args[1]->IsUint32()); // Cipher Mode |
| |
| uint32_t cmode = args[1].As<v8::Uint32>()->Value(); |
| CHECK_LE(cmode, WebCryptoCipherMode::kWebCryptoCipherDecrypt); |
| WebCryptoCipherMode cipher_mode = static_cast<WebCryptoCipherMode>(cmode); |
| |
| CHECK(args[2]->IsObject()); // KeyObject |
| KeyObjectHandle* key; |
| ASSIGN_OR_RETURN_UNWRAP(&key, args[2]); |
| CHECK_NOT_NULL(key); |
| |
| ArrayBufferOrViewContents<char> data(args[3]); // data to operate on |
| if (!data.CheckSizeInt32()) |
| return THROW_ERR_OUT_OF_RANGE(env, "data is too large"); |
| |
| AdditionalParams params; |
| if (CipherTraits::AdditionalConfig(mode, args, 4, cipher_mode, ¶ms) |
| .IsNothing()) { |
| // The CipherTraits::AdditionalConfig is responsible for |
| // calling an appropriate THROW_CRYPTO_* variant reporting |
| // whatever error caused initialization to fail. |
| return; |
| } |
| |
| new CipherJob<CipherTraits>( |
| env, |
| args.This(), |
| mode, |
| key, |
| cipher_mode, |
| data, |
| std::move(params)); |
| } |
| |
| static void Initialize( |
| Environment* env, |
| v8::Local<v8::Object> target) { |
| CryptoJob<CipherTraits>::Initialize(New, env, target); |
| } |
| |
| static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
| CryptoJob<CipherTraits>::RegisterExternalReferences(New, registry); |
| } |
| |
| CipherJob( |
| Environment* env, |
| v8::Local<v8::Object> object, |
| CryptoJobMode mode, |
| KeyObjectHandle* key, |
| WebCryptoCipherMode cipher_mode, |
| const ArrayBufferOrViewContents<char>& data, |
| AdditionalParams&& params) |
| : CryptoJob<CipherTraits>( |
| env, |
| object, |
| AsyncWrap::PROVIDER_CIPHERREQUEST, |
| mode, |
| std::move(params)), |
| key_(key->Data()), |
| cipher_mode_(cipher_mode), |
| in_(mode == kCryptoJobAsync |
| ? data.ToCopy() |
| : data.ToByteSource()) {} |
| |
| std::shared_ptr<KeyObjectData> key() const { return key_; } |
| |
| WebCryptoCipherMode cipher_mode() const { return cipher_mode_; } |
| |
| void DoThreadPoolWork() override { |
| const WebCryptoCipherStatus status = |
| CipherTraits::DoCipher( |
| AsyncWrap::env(), |
| key(), |
| cipher_mode_, |
| *CryptoJob<CipherTraits>::params(), |
| in_, |
| &out_); |
| if (status == WebCryptoCipherStatus::OK) { |
| // Success! |
| return; |
| } |
| CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors(); |
| errors->Capture(); |
| if (errors->Empty()) { |
| switch (status) { |
| case WebCryptoCipherStatus::OK: |
| UNREACHABLE(); |
| break; |
| case WebCryptoCipherStatus::INVALID_KEY_TYPE: |
| errors->Insert(NodeCryptoError::INVALID_KEY_TYPE); |
| break; |
| case WebCryptoCipherStatus::FAILED: |
| errors->Insert(NodeCryptoError::CIPHER_JOB_FAILED); |
| break; |
| } |
| } |
| } |
| |
| v8::Maybe<bool> ToResult( |
| v8::Local<v8::Value>* err, |
| v8::Local<v8::Value>* result) override { |
| Environment* env = AsyncWrap::env(); |
| CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors(); |
| |
| if (errors->Empty()) |
| errors->Capture(); |
| |
| if (out_.size() > 0 || errors->Empty()) { |
| CHECK(errors->Empty()); |
| *err = v8::Undefined(env->isolate()); |
| *result = out_.ToArrayBuffer(env); |
| return v8::Just(!result->IsEmpty()); |
| } |
| |
| *result = v8::Undefined(env->isolate()); |
| return v8::Just(errors->ToException(env).ToLocal(err)); |
| } |
| |
| SET_SELF_SIZE(CipherJob) |
| void MemoryInfo(MemoryTracker* tracker) const override { |
| if (CryptoJob<CipherTraits>::mode() == kCryptoJobAsync) |
| tracker->TrackFieldWithSize("in", in_.size()); |
| tracker->TrackFieldWithSize("out", out_.size()); |
| CryptoJob<CipherTraits>::MemoryInfo(tracker); |
| } |
| |
| private: |
| std::shared_ptr<KeyObjectData> key_; |
| WebCryptoCipherMode cipher_mode_; |
| ByteSource in_; |
| ByteSource out_; |
| }; |
| |
| } // namespace crypto |
| } // namespace node |
| |
| #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
| #endif // SRC_CRYPTO_CRYPTO_CIPHER_H_ |