current commit
260923.2 · crypto: use current FIPS state for availability
#include "crypto/crypto_keygen.h"
#include "async_wrap-inl.h"
#include "base_object-inl.h"
#include "debug_utils-inl.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "ncrypto.h"
#include "threadpoolwork-inl.h"
#include "v8.h"

#include <cmath>
#include <string_view>

namespace node {

using ncrypto::DataPointer;
using ncrypto::EVPKeyCtxPointer;
using v8::FunctionCallbackInfo;
using v8::JustVoid;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
using v8::Nothing;
using v8::Object;
using v8::Uint32;
using v8::Value;

namespace crypto {
// NamedKeyPairGenJob input arguments:
//   1. CryptoJobMode
//   2. Algorithm name
//   3. Public Format
//   4. Public Type
//   5. Private Format
//   6. Private Type
//   7. Cipher
//   8. Passphrase
Maybe<void> NamedKeyPairGenTraits::AdditionalConfig(
    CryptoJobMode mode,
    const FunctionCallbackInfo<Value>& args,
    unsigned int* offset,
    NamedKeyPairGenConfig* params) {
  CHECK(args[*offset]->IsString());
  Utf8Value name(args.GetIsolate(), args[*offset]);
  const auto* algorithm = ncrypto::KeyAlgorithm::FromName(*name);
  // Traditional key generation accepts lowercase key types; Web Crypto passes
  // normalized algorithm names and checks availability during normalization.
  if (algorithm == nullptr || (!algorithm->isOkp() && !algorithm->isPqc()) ||
      (mode != kCryptoJobWebCrypto &&
       (std::string_view(*name, name.length()) != algorithm->keyTypeName() ||
        !algorithm->isAvailable()))) {
    THROW_ERR_INVALID_ARG_VALUE(
        Environment::GetCurrent(args),
        "The argument 'type' must be a supported key type. Received '%s'",
        *name);
    return Nothing<void>();
  }
  params->params.algorithm = algorithm;

  *offset += 1;

  return JustVoid();
}

EVPKeyCtxPointer NamedKeyPairGenTraits::Setup(NamedKeyPairGenConfig* params) {
  auto ctx = EVPKeyCtxPointer::NewFromAlgorithm(*params->params.algorithm);
  if (!ctx || !ctx.initForKeygen()) return {};
  return ctx;
}

void SecretKeyGenConfig::MemoryInfo(MemoryTracker* tracker) const {
  tracker->TraitTrackInline(out, "out");
}

Maybe<void> SecretKeyGenTraits::AdditionalConfig(
    CryptoJobMode mode,
    const FunctionCallbackInfo<Value>& args,
    unsigned int* offset,
    SecretKeyGenConfig* params) {
  CHECK(args[*offset]->IsUint32());
  uint32_t bits = args[*offset].As<Uint32>()->Value();
  params->length_bits = bits;
  if (mode == kCryptoJobWebCrypto) {
    params->length = NumBitsToBytes(static_cast<size_t>(bits));
    params->truncate_to_bit_length = bits % CHAR_BIT != 0;
  } else {
    params->length = bits / CHAR_BIT;
  }
  *offset += 1;
  return JustVoid();
}

KeyGenJobStatus SecretKeyGenTraits::DoKeyGen(Environment* env,
                                             SecretKeyGenConfig* params) {
  auto bytes = DataPointer::Alloc(params->length);
  if (!ncrypto::CSPRNG(static_cast<unsigned char*>(bytes.get()),
                       params->length)) {
    return KeyGenJobStatus::FAILED;
  }
  params->out = ByteSource::Allocated(bytes.release());
  if (params->truncate_to_bit_length)
    TruncateToBitLength(params->length_bits, &params->out);
  return KeyGenJobStatus::OK;
}

MaybeLocal<Value> SecretKeyGenTraits::EncodeKey(Environment* env,
                                                SecretKeyGenConfig* params) {
  auto data = KeyObjectData::CreateSecret(std::move(params->out));
  return KeyObjectHandle::Create(env, data).FromMaybe(Local<Value>());
}

namespace Keygen {
void Initialize(Environment* env, Local<Object> target) {
  NamedKeyPairGenJob::Initialize(env, target);
  SecretKeyGenJob::Initialize(env, target);
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
  NamedKeyPairGenJob::RegisterExternalReferences(registry);
  SecretKeyGenJob::RegisterExternalReferences(registry);
}
}  // namespace Keygen
}  // namespace crypto
}  // namespace node