| #ifndef SRC_CRYPTO_CRYPTO_KEYS_H_ |
| #define SRC_CRYPTO_CRYPTO_KEYS_H_ |
| |
| #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
| |
| #include "crypto/crypto_util.h" |
| #include "base_object.h" |
| #include "env.h" |
| #include "memory_tracker.h" |
| #include "node_buffer.h" |
| #include "node_worker.h" |
| #include "v8.h" |
| |
| #include <openssl/evp.h> |
| |
| #include <memory> |
| #include <string> |
| |
| namespace node { |
| namespace crypto { |
| enum PKEncodingType { |
| // RSAPublicKey / RSAPrivateKey according to PKCS#1. |
| kKeyEncodingPKCS1, |
| // PrivateKeyInfo or EncryptedPrivateKeyInfo according to PKCS#8. |
| kKeyEncodingPKCS8, |
| // SubjectPublicKeyInfo according to X.509. |
| kKeyEncodingSPKI, |
| // ECPrivateKey according to SEC1. |
| kKeyEncodingSEC1 |
| }; |
| |
| enum PKFormatType { |
| kKeyFormatDER, |
| kKeyFormatPEM, |
| kKeyFormatJWK |
| }; |
| |
| enum KeyType { |
| kKeyTypeSecret, |
| kKeyTypePublic, |
| kKeyTypePrivate |
| }; |
| |
| enum KeyEncodingContext { |
| kKeyContextInput, |
| kKeyContextExport, |
| kKeyContextGenerate |
| }; |
| |
| enum class ParseKeyResult { |
| kParseKeyOk, |
| kParseKeyNotRecognized, |
| kParseKeyNeedPassphrase, |
| kParseKeyFailed |
| }; |
| |
| struct AsymmetricKeyEncodingConfig { |
| bool output_key_object_ = false; |
| PKFormatType format_ = kKeyFormatDER; |
| v8::Maybe<PKEncodingType> type_ = v8::Nothing<PKEncodingType>(); |
| }; |
| |
| using PublicKeyEncodingConfig = AsymmetricKeyEncodingConfig; |
| |
| struct PrivateKeyEncodingConfig : public AsymmetricKeyEncodingConfig { |
| const EVP_CIPHER* cipher_; |
| // The ByteSource alone is not enough to distinguish between "no passphrase" |
| // and a zero-length passphrase (which can be a null pointer), therefore, we |
| // use a NonCopyableMaybe. |
| NonCopyableMaybe<ByteSource> passphrase_; |
| }; |
| |
| // This uses the built-in reference counter of OpenSSL to manage an EVP_PKEY |
| // which is slightly more efficient than using a shared pointer and easier to |
| // use. |
| class ManagedEVPPKey : public MemoryRetainer { |
| public: |
| ManagedEVPPKey() : mutex_(std::make_shared<Mutex>()) {} |
| explicit ManagedEVPPKey(EVPKeyPointer&& pkey); |
| ManagedEVPPKey(const ManagedEVPPKey& that); |
| ManagedEVPPKey& operator=(const ManagedEVPPKey& that); |
| |
| operator bool() const; |
| EVP_PKEY* get() const; |
| Mutex* mutex() const; |
| |
| void MemoryInfo(MemoryTracker* tracker) const override; |
| SET_MEMORY_INFO_NAME(ManagedEVPPKey) |
| SET_SELF_SIZE(ManagedEVPPKey) |
| |
| static PublicKeyEncodingConfig GetPublicKeyEncodingFromJs( |
| const v8::FunctionCallbackInfo<v8::Value>& args, |
| unsigned int* offset, |
| KeyEncodingContext context); |
| |
| static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs( |
| const v8::FunctionCallbackInfo<v8::Value>& args, |
| unsigned int* offset, |
| KeyEncodingContext context); |
| |
| static ManagedEVPPKey GetParsedKey(Environment* env, |
| EVPKeyPointer&& pkey, |
| ParseKeyResult ret, |
| const char* default_msg); |
| |
| static ManagedEVPPKey GetPublicOrPrivateKeyFromJs( |
| const v8::FunctionCallbackInfo<v8::Value>& args, |
| unsigned int* offset); |
| |
| static ManagedEVPPKey GetPrivateKeyFromJs( |
| const v8::FunctionCallbackInfo<v8::Value>& args, |
| unsigned int* offset, |
| bool allow_key_object); |
| |
| static v8::Maybe<bool> ToEncodedPublicKey( |
| Environment* env, |
| ManagedEVPPKey key, |
| const PublicKeyEncodingConfig& config, |
| v8::Local<v8::Value>* out); |
| |
| static v8::Maybe<bool> ToEncodedPrivateKey( |
| Environment* env, |
| ManagedEVPPKey key, |
| const PrivateKeyEncodingConfig& config, |
| v8::Local<v8::Value>* out); |
| |
| private: |
| size_t size_of_private_key() const; |
| size_t size_of_public_key() const; |
| |
| EVPKeyPointer pkey_; |
| std::shared_ptr<Mutex> mutex_; |
| }; |
| |
| // Objects of this class can safely be shared among threads. |
| class KeyObjectData : public MemoryRetainer { |
| public: |
| static std::shared_ptr<KeyObjectData> CreateSecret(ByteSource key); |
| |
| static std::shared_ptr<KeyObjectData> CreateAsymmetric( |
| KeyType type, |
| const ManagedEVPPKey& pkey); |
| |
| KeyType GetKeyType() const; |
| |
| // These functions allow unprotected access to the raw key material and should |
| // only be used to implement cryptographic operations requiring the key. |
| ManagedEVPPKey GetAsymmetricKey() const; |
| const char* GetSymmetricKey() const; |
| size_t GetSymmetricKeySize() const; |
| |
| void MemoryInfo(MemoryTracker* tracker) const override; |
| SET_MEMORY_INFO_NAME(KeyObjectData) |
| SET_SELF_SIZE(KeyObjectData) |
| |
| private: |
| explicit KeyObjectData(ByteSource symmetric_key); |
| |
| KeyObjectData( |
| KeyType type, |
| const ManagedEVPPKey& pkey); |
| |
| const KeyType key_type_; |
| const ByteSource symmetric_key_; |
| const unsigned int symmetric_key_len_; |
| const ManagedEVPPKey asymmetric_key_; |
| }; |
| |
| class KeyObjectHandle : public BaseObject { |
| public: |
| static v8::Local<v8::Function> Initialize(Environment* env); |
| static void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
| |
| static v8::MaybeLocal<v8::Object> Create(Environment* env, |
| std::shared_ptr<KeyObjectData> data); |
| |
| // TODO(tniessen): track the memory used by OpenSSL types |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(KeyObjectHandle) |
| SET_SELF_SIZE(KeyObjectHandle) |
| |
| const std::shared_ptr<KeyObjectData>& Data(); |
| |
| protected: |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static void Init(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void InitECRaw(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void InitEDRaw(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void InitJWK(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void GetKeyDetail(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void Equals(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static void ExportJWK(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static void GetAsymmetricKeyType( |
| const v8::FunctionCallbackInfo<v8::Value>& args); |
| v8::Local<v8::Value> GetAsymmetricKeyType() const; |
| |
| static void GetSymmetricKeySize( |
| const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static void Export(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| v8::MaybeLocal<v8::Value> ExportSecretKey() const; |
| v8::MaybeLocal<v8::Value> ExportPublicKey( |
| const PublicKeyEncodingConfig& config) const; |
| v8::MaybeLocal<v8::Value> ExportPrivateKey( |
| const PrivateKeyEncodingConfig& config) const; |
| |
| KeyObjectHandle(Environment* env, |
| v8::Local<v8::Object> wrap); |
| |
| private: |
| std::shared_ptr<KeyObjectData> data_; |
| }; |
| |
| class NativeKeyObject : public BaseObject { |
| public: |
| static void Initialize(Environment* env, v8::Local<v8::Object> target); |
| static void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
| |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void CreateNativeKeyObjectClass( |
| const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| SET_NO_MEMORY_INFO() |
| SET_MEMORY_INFO_NAME(NativeKeyObject) |
| SET_SELF_SIZE(NativeKeyObject) |
| |
| class KeyObjectTransferData : public worker::TransferData { |
| public: |
| explicit KeyObjectTransferData(const std::shared_ptr<KeyObjectData>& data) |
| : data_(data) {} |
| |
| BaseObjectPtr<BaseObject> Deserialize( |
| Environment* env, |
| v8::Local<v8::Context> context, |
| std::unique_ptr<worker::TransferData> self) override; |
| |
| SET_MEMORY_INFO_NAME(KeyObjectTransferData) |
| SET_SELF_SIZE(KeyObjectTransferData) |
| SET_NO_MEMORY_INFO() |
| |
| private: |
| std::shared_ptr<KeyObjectData> data_; |
| }; |
| |
| BaseObject::TransferMode GetTransferMode() const override; |
| std::unique_ptr<worker::TransferData> CloneForMessaging() const override; |
| |
| private: |
| NativeKeyObject(Environment* env, |
| v8::Local<v8::Object> wrap, |
| const std::shared_ptr<KeyObjectData>& handle_data) |
| : BaseObject(env, wrap), |
| handle_data_(handle_data) { |
| MakeWeak(); |
| } |
| |
| std::shared_ptr<KeyObjectData> handle_data_; |
| }; |
| |
| enum WebCryptoKeyFormat { |
| kWebCryptoKeyFormatRaw, |
| kWebCryptoKeyFormatPKCS8, |
| kWebCryptoKeyFormatSPKI, |
| kWebCryptoKeyFormatJWK |
| }; |
| |
| enum class WebCryptoKeyExportStatus { |
| OK, |
| INVALID_KEY_TYPE, |
| FAILED |
| }; |
| |
| template <typename KeyExportTraits> |
| class KeyExportJob final : public CryptoJob<KeyExportTraits> { |
| public: |
| using AdditionalParams = typename KeyExportTraits::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()); // Export Type |
| CHECK(args[2]->IsObject()); // KeyObject |
| |
| WebCryptoKeyFormat format = |
| static_cast<WebCryptoKeyFormat>(args[1].As<v8::Uint32>()->Value()); |
| |
| KeyObjectHandle* key; |
| ASSIGN_OR_RETURN_UNWRAP(&key, args[2]); |
| |
| CHECK_NOT_NULL(key); |
| |
| AdditionalParams params; |
| if (KeyExportTraits::AdditionalConfig(args, 3, ¶ms).IsNothing()) { |
| // The KeyExportTraits::AdditionalConfig is responsible for |
| // calling an appropriate THROW_CRYPTO_* variant reporting |
| // whatever error caused initialization to fail. |
| return; |
| } |
| |
| new KeyExportJob<KeyExportTraits>( |
| env, |
| args.This(), |
| mode, |
| key->Data(), |
| format, |
| std::move(params)); |
| } |
| |
| static void Initialize( |
| Environment* env, |
| v8::Local<v8::Object> target) { |
| CryptoJob<KeyExportTraits>::Initialize(New, env, target); |
| } |
| |
| static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
| CryptoJob<KeyExportTraits>::RegisterExternalReferences(New, registry); |
| } |
| |
| KeyExportJob( |
| Environment* env, |
| v8::Local<v8::Object> object, |
| CryptoJobMode mode, |
| std::shared_ptr<KeyObjectData> key, |
| WebCryptoKeyFormat format, |
| AdditionalParams&& params) |
| : CryptoJob<KeyExportTraits>( |
| env, |
| object, |
| AsyncWrap::PROVIDER_KEYEXPORTREQUEST, |
| mode, |
| std::move(params)), |
| key_(key), |
| format_(format) {} |
| |
| WebCryptoKeyFormat format() const { return format_; } |
| |
| void DoThreadPoolWork() override { |
| const WebCryptoKeyExportStatus status = |
| KeyExportTraits::DoExport( |
| key_, |
| format_, |
| *CryptoJob<KeyExportTraits>::params(), |
| &out_); |
| if (status == WebCryptoKeyExportStatus::OK) { |
| // Success! |
| return; |
| } |
| CryptoErrorStore* errors = CryptoJob<KeyExportTraits>::errors(); |
| errors->Capture(); |
| if (errors->Empty()) { |
| switch (status) { |
| case WebCryptoKeyExportStatus::OK: |
| UNREACHABLE(); |
| break; |
| case WebCryptoKeyExportStatus::INVALID_KEY_TYPE: |
| errors->Insert(NodeCryptoError::INVALID_KEY_TYPE); |
| break; |
| case WebCryptoKeyExportStatus::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<KeyExportTraits>::errors(); |
| if (out_.size() > 0) { |
| CHECK(errors->Empty()); |
| *err = v8::Undefined(env->isolate()); |
| *result = out_.ToArrayBuffer(env); |
| return v8::Just(!result->IsEmpty()); |
| } |
| |
| if (errors->Empty()) |
| errors->Capture(); |
| CHECK(!errors->Empty()); |
| *result = v8::Undefined(env->isolate()); |
| return v8::Just(errors->ToException(env).ToLocal(err)); |
| } |
| |
| SET_SELF_SIZE(KeyExportJob) |
| void MemoryInfo(MemoryTracker* tracker) const override { |
| tracker->TrackFieldWithSize("out", out_.size()); |
| CryptoJob<KeyExportTraits>::MemoryInfo(tracker); |
| } |
| |
| private: |
| std::shared_ptr<KeyObjectData> key_; |
| WebCryptoKeyFormat format_; |
| ByteSource out_; |
| }; |
| |
| WebCryptoKeyExportStatus PKEY_SPKI_Export( |
| KeyObjectData* key_data, |
| ByteSource* out); |
| |
| WebCryptoKeyExportStatus PKEY_PKCS8_Export( |
| KeyObjectData* key_data, |
| ByteSource* out); |
| |
| namespace Keys { |
| void Initialize(Environment* env, v8::Local<v8::Object> target); |
| void RegisterExternalReferences(ExternalReferenceRegistry* registry); |
| } // namespace Keys |
| |
| } // namespace crypto |
| } // namespace node |
| |
| #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
| #endif // SRC_CRYPTO_CRYPTO_KEYS_H_ |