@ 261106.9
current
#include "crypto/crypto_scrypt.h"
#include "async_wrap-inl.h"
#include "crypto/crypto_util.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "node_buffer.h"
#include "threadpoolwork-inl.h"
#include "v8.h"

namespace node {

using v8::FunctionCallbackInfo;
using v8::Int32;
using v8::JustVoid;
using v8::Maybe;
using v8::MaybeLocal;
using v8::Nothing;
using v8::Uint32;
using v8::Value;

namespace crypto {
#ifndef OPENSSL_NO_SCRYPT

ScryptConfig::ScryptConfig(ScryptConfig&& other) noexcept
    : pass(std::move(other.pass)),
      salt(std::move(other.salt)),
      N(other.N),
      r(other.r),
      p(other.p),
      maxmem(other.maxmem),
      length(other.length) {}

ScryptConfig& ScryptConfig::operator=(ScryptConfig&& other) noexcept {
  if (&other == this) return *this;
  this->~ScryptConfig();
  return *new (this) ScryptConfig(std::move(other));
}

void ScryptConfig::MemoryInfo(MemoryTracker* tracker) const {
  tracker->TraitTrackInline(pass, "pass");
  tracker->TraitTrackInline(salt, "salt");
}

MaybeLocal<Value> ScryptTraits::EncodeOutput(Environment* env,
                                             const ScryptConfig& params,
                                             ByteSource* out) {
  return out->ToArrayBuffer(env);
}

Maybe<void> ScryptTraits::AdditionalConfig(
    CryptoJobMode mode,
    const FunctionCallbackInfo<Value>& args,
    unsigned int offset,
    ScryptConfig* params) {
  Environment* env = Environment::GetCurrent(args);

  ArrayBufferOrViewContents<char> pass(args[offset]);
  ArrayBufferOrViewContents<char> salt(args[offset + 1]);

  if (!pass.CheckSizeInt32()) [[unlikely]] {
    THROW_ERR_OUT_OF_RANGE(env, "pass is too large");
    return Nothing<void>();
  }

  if (!salt.CheckSizeInt32()) [[unlikely]] {
    THROW_ERR_OUT_OF_RANGE(env, "salt is too large");
    return Nothing<void>();
  }

  params->pass = IsCryptoJobAsync(mode) ? pass.ToCopy() : pass.ToByteSource();

  params->salt = IsCryptoJobAsync(mode) ? salt.ToCopy() : salt.ToByteSource();

  CHECK(args[offset + 2]->IsUint32());  // N
  CHECK(args[offset + 3]->IsUint32());  // r
  CHECK(args[offset + 4]->IsUint32());  // p
  CHECK(args[offset + 5]->IsNumber());  // maxmem
  CHECK(args[offset + 6]->IsInt32());   // length

  params->N = args[offset + 2].As<Uint32>()->Value();
  params->r = args[offset + 3].As<Uint32>()->Value();
  params->p = args[offset + 4].As<Uint32>()->Value();
  params->maxmem = args[offset + 5]->IntegerValue(env->context()).ToChecked();

  params->length = args[offset + 6].As<Int32>()->Value();
  CHECK_GE(params->length, 0);

  ncrypto::MarkPopErrorOnReturn mark_pop_error_on_return;
  if (!ncrypto::checkScryptParams(
          params->N, params->r, params->p, params->maxmem)) {
    // Do not use CryptoErrorStore or ThrowCryptoError here in order to maintain
    // backward compatibility with ERR_CRYPTO_INVALID_SCRYPT_PARAMS.
    uint32_t err = ERR_peek_last_error();
    if (err != 0) {
      char buf[256];
      ERR_error_string_n(err, buf, sizeof(buf));
      THROW_ERR_CRYPTO_INVALID_SCRYPT_PARAMS(
          env, "Invalid scrypt params: %s", buf);
    } else {
      THROW_ERR_CRYPTO_INVALID_SCRYPT_PARAMS(env);
    }
    return Nothing<void>();
  }

  return JustVoid();
}

bool ScryptTraits::DeriveBits(Environment* env,
                              const ScryptConfig& params,
                              ByteSource* out,
                              CryptoJobMode mode,
                              CryptoErrorStore* errors) {
  // If the params.length is zero-length, just return an empty buffer.
  // It's useless, yes, but allowed via the API.
  if (params.length == 0) {
    *out = ByteSource();
    return true;
  }

  auto dp = ncrypto::scrypt(
      ncrypto::Buffer<const char>{
          .data = params.pass.data<char>(),
          .len = params.pass.size(),
      },
      ncrypto::Buffer<const unsigned char>{
          .data = params.salt.data<unsigned char>(),
          .len = params.salt.size(),
      },
      params.N,
      params.r,
      params.p,
      params.maxmem,
      params.length);

  if (!dp) {
    errors->Capture();
    errors->Insert(NodeCryptoError::SCRYPT_FAILED);
    return false;
  }
  DCHECK(!dp.isSecure());
  *out = ByteSource::Allocated(dp.release());
  return true;
}

#endif  // !OPENSSL_NO_SCRYPT

}  // namespace crypto
}  // namespace node