#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 namespace node { namespace crypto { class CipherBase : public BaseObject { public: static void GetSSLCiphers(const v8::FunctionCallbackInfo& args); static void GetCiphers(const v8::FunctionCallbackInfo& args); static void Initialize(Environment* env, v8::Local 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(-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& key_buf, unsigned int auth_tag_len); void InitIv(const char* cipher_type, const ByteSource& key_buf, const ArrayBufferOrViewContents& 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* out); bool Final(std::unique_ptr* out); bool SetAutoPadding(bool auto_padding); bool IsAuthenticatedMode() const; bool SetAAD( const ArrayBufferOrViewContents& data, int plaintext_len); bool MaybePassAuthTagToOpenSSL(); static void New(const v8::FunctionCallbackInfo& args); static void Init(const v8::FunctionCallbackInfo& args); static void InitIv(const v8::FunctionCallbackInfo& args); static void Update(const v8::FunctionCallbackInfo& args); static void Final(const v8::FunctionCallbackInfo& args); static void SetAutoPadding(const v8::FunctionCallbackInfo& args); static void GetAuthTag(const v8::FunctionCallbackInfo& args); static void SetAuthTag(const v8::FunctionCallbackInfo& args); static void SetAAD(const v8::FunctionCallbackInfo& args); CipherBase(Environment* env, v8::Local wrap, CipherKind kind); private: DeleteFnPtr 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 static bool Cipher(Environment* env, const ManagedEVPPKey& pkey, int padding, const EVP_MD* digest, const ArrayBufferOrViewContents& oaep_label, const ArrayBufferOrViewContents& data, std::unique_ptr* out); template static void Cipher(const v8::FunctionCallbackInfo& 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 class CipherJob final : public CryptoJob { public: using AdditionalParams = typename CipherTraits::AdditionalParameters; static void New(const v8::FunctionCallbackInfo& 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()->Value(); CHECK_LE(cmode, WebCryptoCipherMode::kWebCryptoCipherDecrypt); WebCryptoCipherMode cipher_mode = static_cast(cmode); CHECK(args[2]->IsObject()); // KeyObject KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[2]); CHECK_NOT_NULL(key); ArrayBufferOrViewContents 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( env, args.This(), mode, key, cipher_mode, data, std::move(params)); } static void Initialize( Environment* env, v8::Local target) { CryptoJob::Initialize(New, env, target); } static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { CryptoJob::RegisterExternalReferences(New, registry); } CipherJob( Environment* env, v8::Local object, CryptoJobMode mode, KeyObjectHandle* key, WebCryptoCipherMode cipher_mode, const ArrayBufferOrViewContents& data, AdditionalParams&& params) : CryptoJob( 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 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::params(), in_, &out_); if (status == WebCryptoCipherStatus::OK) { // Success! return; } CryptoErrorStore* errors = CryptoJob::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 ToResult( v8::Local* err, v8::Local* result) override { Environment* env = AsyncWrap::env(); CryptoErrorStore* errors = CryptoJob::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::mode() == kCryptoJobAsync) tracker->TrackFieldWithSize("in", in_.size()); tracker->TrackFieldWithSize("out", out_.size()); CryptoJob::MemoryInfo(tracker); } private: std::shared_ptr 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_