@ 261106.3
current
#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 <cstdint>
#include <memory>
#include <string>

namespace node::crypto {
enum KeyType {
  kKeyTypeSecret,
  kKeyTypePublic,
  kKeyTypePrivate
};

enum KeyEncodingContext {
  kKeyContextInput,
  kKeyContextExport,
  kKeyContextGenerate
};

enum class ParseKeyResult {
  kParseKeyNotRecognized =
      static_cast<int>(ncrypto::EVPKeyPointer::PKParseError::NOT_RECOGNIZED),
  kParseKeyNeedPassphrase =
      static_cast<int>(ncrypto::EVPKeyPointer::PKParseError::NEED_PASSPHRASE),
  kParseKeyFailed =
      static_cast<int>(ncrypto::EVPKeyPointer::PKParseError::FAILED),
  kParseKeyOk,
};

// Objects of this class can safely be shared among threads.
class KeyObjectData final : public MemoryRetainer {
 public:
  static KeyObjectData CreateSecret(ByteSource key);

  static KeyObjectData CreateAsymmetric(KeyType type,
                                        ncrypto::EVPKeyPointer&& pkey);

  KeyObjectData(std::nullptr_t = nullptr);

  inline operator bool() const { return data_ != nullptr; }

  KeyType GetKeyType() const;

  // The key material is immutable and can be used concurrently by operations
  // with separate contexts.
  const ncrypto::EVPKeyPointer& 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)

  static v8::Maybe<ncrypto::EVPKeyPointer::PublicKeyEncodingConfig>
  GetPublicKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
                             unsigned int* offset,
                             KeyEncodingContext context);

  static KeyObjectData GetPrivateKeyFromJs(
      const v8::FunctionCallbackInfo<v8::Value>& args,
      unsigned int* offset,
      bool allow_key_object);

  static KeyObjectData GetPublicOrPrivateKeyFromJs(
      const v8::FunctionCallbackInfo<v8::Value>& args,
      unsigned int* offset,
      bool allow_private_key_store = false);

  static v8::Maybe<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>
  GetPrivateKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
                              unsigned int* offset,
                              KeyEncodingContext context);

  bool ToEncodedPublicKey(
      Environment* env,
      const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config,
      v8::Local<v8::Value>* out);

  bool ToEncodedPrivateKey(
      Environment* env,
      const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
      v8::Local<v8::Value>* out);

  inline KeyObjectData addRef() const {
    return KeyObjectData(key_type_, data_);
  }

  inline KeyObjectData addRefWithType(KeyType type) const {
    return KeyObjectData(type, data_);
  }

 private:
  explicit KeyObjectData(ByteSource symmetric_key);
  explicit KeyObjectData(KeyType type, ncrypto::EVPKeyPointer&& pkey);

  static KeyObjectData GetParsedKey(KeyType type,
                                    Environment* env,
                                    ncrypto::EVPKeyPointer&& pkey,
                                    ParseKeyResult ret,
                                    const char* default_msg);

  KeyType key_type_;

  struct Data final : public MemoryRetainer {
    const ByteSource symmetric_key;
    const ncrypto::EVPKeyPointer asymmetric_key;
    explicit Data(ByteSource symmetric_key)
        : symmetric_key(std::move(symmetric_key)) {}
    explicit Data(ncrypto::EVPKeyPointer asymmetric_key)
        : asymmetric_key(std::move(asymmetric_key)) {}

    void MemoryInfo(MemoryTracker* tracker) const override;
    SET_MEMORY_INFO_NAME(KeyObjectData::Data)
    SET_SELF_SIZE(Data)
  };
  std::shared_ptr<Data> data_;

  KeyObjectData(KeyType type, std::shared_ptr<Data> data)
      : key_type_(type), data_(std::move(data)) {}
};

class KeyObjectHandle : public BaseObject {
 public:
  static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
  static v8::Local<v8::Function> Initialize(Environment* env);
  static void RegisterExternalReferences(ExternalReferenceRegistry* registry);

  static v8::MaybeLocal<v8::Object> Create(Environment* env,
                                           const 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 KeyObjectData& Data();

 protected:
  static void New(const v8::FunctionCallbackInfo<v8::Value>& args);

  static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void GetKeyType(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 CheckEcKeyData(const v8::FunctionCallbackInfo<v8::Value>& args);
  bool CheckEcKeyData() const;

  static void GetSymmetricKeySize(
      const v8::FunctionCallbackInfo<v8::Value>& args);

  static void Export(const v8::FunctionCallbackInfo<v8::Value>& args);

  static void RawPublicKey(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void RawPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void ExportECPublicRaw(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void ExportECPrivateRaw(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void ExportECPrivatePkcs8(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void RawSeed(const v8::FunctionCallbackInfo<v8::Value>& args);

  v8::MaybeLocal<v8::Value> ExportSecretKey() const;
  v8::MaybeLocal<v8::Value> ExportPublicKey(
      const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const;
  v8::MaybeLocal<v8::Value> ExportPrivateKey(
      const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) const;

  KeyObjectHandle(Environment* env,
                  v8::Local<v8::Object> wrap);

 private:
  KeyObjectData data_;
};

// NativeKeyObject is the native base class for the Node.js-specific
// `KeyObject`. It holds the underlying KeyObjectData for structured
// cloning and exposes the native hidden slot tuple that JS needs:
// [type enum, KeyObjectHandle]. JS primes a per-instance private-field
// cache from that result and lazily appends derived metadata there.
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);
  static void IsKeyObject(const v8::FunctionCallbackInfo<v8::Value>& args);

  // True if `value` is a real NativeKeyObject instance. Uses the
  // FunctionTemplate stored on the Environment as a brand check.
  // Used by `GetSlots` to validate its receiver.
  static bool HasInstance(Environment* env, v8::Local<v8::Value> value);

  // Returns [type, handle] in one call so JS can prime a per-instance cache
  // on first access. Derived metadata is not returned from native here.
  static void GetSlots(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 KeyObjectData& data)
        : data_(data.addRef()) {}

    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:
    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 KeyObjectData& handle_data)
      : BaseObject(env, wrap), handle_data_(handle_data.addRef()) {
    MakeWeak();
  }

  KeyObjectData handle_data_;
};

// NativeCryptoKey is the native base class for the Web Crypto
// `CryptoKey`. It holds the internal slots - `[[type]]` as an enum,
// `[[extractable]]`, `[[algorithm]]`, `[[usages]]` as a mask, the
// primary underlying KeyObjectData. Hybrid KEM CryptoKeys may also store an
// optional secondary KeyObjectData and optional seed_data, both of which are
// only used to reconstruct their hybrid key material. The public `type`,
// `extractable`, `algorithm`, and `usages` accessors on `CryptoKey.prototype`
// are
// user-configurable per Web IDL, so internal consumers read these
// values directly from the C++ side via a single `GetSlots` call
// which returns all slots at once; JS primes a per-instance cache
// from that result.
class NativeCryptoKey : public BaseObject {
 public:
  enum InternalFields {
    kAlgorithmField = BaseObject::kInternalFieldCount,
    kInternalFieldCount,
  };

  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 CreateCryptoKeyClass(
      const v8::FunctionCallbackInfo<v8::Value>& args);
  static void IsCryptoKey(const v8::FunctionCallbackInfo<v8::Value>& args);

  static v8::MaybeLocal<v8::Value> Create(Environment* env,
                                          const KeyObjectData& data,
                                          v8::Local<v8::Value> algorithm,
                                          uint32_t usages_mask,
                                          bool extractable);

  // True if `value` is a real NativeCryptoKey instance. Uses the
  // FunctionTemplate stored on the Environment as a brand check.
  // Used by `GetSlots` to validate its receiver.
  static bool HasInstance(Environment* env, v8::Local<v8::Value> value);

  // Returns [type, extractable, algorithm, usages mask, handle,
  // secondary handle, seed data] in one call so JS can prime a per-instance
  // cache on first access. The secondary handle and seed data slots are only
  // used by Hybrid KEM CryptoKeys.
  static void GetSlots(const v8::FunctionCallbackInfo<v8::Value>& args);

  void MemoryInfo(MemoryTracker* tracker) const override;
  SET_MEMORY_INFO_NAME(NativeCryptoKey)
  SET_SELF_SIZE(NativeCryptoKey)

  class CryptoKeyTransferData : public worker::TransferData {
   public:
    CryptoKeyTransferData(const KeyObjectData& data,
                          const KeyObjectData& secondary_data,
                          ByteSource&& seed_data,
                          v8::Global<v8::Object>&& algorithm,
                          uint32_t usages_mask,
                          bool extractable)
        : data_(data.addRef()),
          secondary_data_(secondary_data ? secondary_data.addRef()
                                         : KeyObjectData()),
          seed_data_(std::move(seed_data)),
          algorithm_(std::move(algorithm)),
          usages_mask_(usages_mask),
          extractable_(extractable) {}

    BaseObjectPtr<BaseObject> Deserialize(
        Environment* env,
        v8::Local<v8::Context> context,
        std::unique_ptr<worker::TransferData> self) override;

    v8::Maybe<bool> FinalizeTransferWrite(
        v8::Local<v8::Context> context,
        v8::ValueSerializer* serializer) override;

    void MemoryInfo(MemoryTracker* tracker) const override;
    SET_MEMORY_INFO_NAME(CryptoKeyTransferData)
    SET_SELF_SIZE(CryptoKeyTransferData)

   private:
    KeyObjectData data_;
    KeyObjectData secondary_data_;
    ByteSource seed_data_;
    v8::Global<v8::Object> algorithm_;
    uint32_t usages_mask_;
    bool extractable_;
  };

  BaseObject::TransferMode GetTransferMode() const override;
  std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
  v8::Maybe<void> FinalizeTransferRead(
      v8::Local<v8::Context> context,
      v8::ValueDeserializer* deserializer) override;

  const KeyObjectData& handle_data() const { return handle_data_; }
  const KeyObjectData& secondary_handle_data() const {
    return secondary_handle_data_;
  }

 private:
  NativeCryptoKey(Environment* env,
                  v8::Local<v8::Object> wrap,
                  const KeyObjectData& handle_data,
                  const KeyObjectData& secondary_handle_data,
                  ByteSource&& seed_data)
      : BaseObject(env, wrap),
        handle_data_(handle_data.addRef()),
        secondary_handle_data_(secondary_handle_data
                                   ? secondary_handle_data.addRef()
                                   : KeyObjectData()),
        seed_data_(std::move(seed_data)) {
    MakeWeak();
  }

  KeyObjectData handle_data_;
  KeyObjectData secondary_handle_data_;
  ByteSource seed_data_;
  uint32_t usages_mask_ = 0;
  bool extractable_ = false;
};

enum WebCryptoKeyFormat {
  kWebCryptoKeyFormatRaw,
  kWebCryptoKeyFormatPKCS8,
  kWebCryptoKeyFormatSPKI,
  kWebCryptoKeyFormatJWK
};

namespace Keys {
void Initialize(Environment* env, v8::Local<v8::Object> target);
void RegisterExternalReferences(ExternalReferenceRegistry* registry);
}  // namespace Keys
}  // namespace node::crypto

#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif  // SRC_CRYPTO_CRYPTO_KEYS_H_