blob: 27fe25a2457ae37f18541ec8dd30c24c320d8322 [file] [log] [blame] [edit]
#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
: mode(other.mode),
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 {
if (mode == kCryptoJobAsync) {
tracker->TrackFieldWithSize("pass", pass.size());
tracker->TrackFieldWithSize("salt", salt.size());
}
}
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);
params->mode = mode;
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 = mode == kCryptoJobAsync
? pass.ToCopy()
: pass.ToByteSource();
params->salt = mode == kCryptoJobAsync
? 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);
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) {
// 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) return false;
DCHECK(!dp.isSecure());
*out = ByteSource::Allocated(dp.release());
return true;
}
#endif // !OPENSSL_NO_SCRYPT
} // namespace crypto
} // namespace node