1#include "crypto/crypto_scrypt.h"
2#include "async_wrap-inl.h"
3#include "crypto/crypto_util.h"
4#include "env-inl.h"
5#include "memory_tracker-inl.h"
6#include "node_buffer.h"
7#include "threadpoolwork-inl.h"
8#include "v8.h"
9
10namespace node {
11
12using v8::FunctionCallbackInfo;
13using v8::Int32;
14using v8::Just;
15using v8::Maybe;
16using v8::Nothing;
17using v8::Uint32;
18using v8::Value;
19
20namespace crypto {
21#ifndef OPENSSL_NO_SCRYPT
22
23ScryptConfig::ScryptConfig(ScryptConfig&& other) noexcept
24  : mode(other.mode),
25    pass(std::move(other.pass)),
26    salt(std::move(other.salt)),
27    N(other.N),
28    r(other.r),
29    p(other.p),
30    maxmem(other.maxmem),
31    length(other.length) {}
32
33ScryptConfig& ScryptConfig::operator=(ScryptConfig&& other) noexcept {
34  if (&other == this) return *this;
35  this->~ScryptConfig();
36  return *new (this) ScryptConfig(std::move(other));
37}
38
39void ScryptConfig::MemoryInfo(MemoryTracker* tracker) const {
40  if (mode == kCryptoJobAsync) {
41    tracker->TrackFieldWithSize("pass", pass.size());
42    tracker->TrackFieldWithSize("salt", salt.size());
43  }
44}
45
46Maybe<bool> ScryptTraits::EncodeOutput(
47    Environment* env,
48    const ScryptConfig& params,
49    ByteSource* out,
50    v8::Local<v8::Value>* result) {
51  *result = out->ToArrayBuffer(env);
52  return Just(!result->IsEmpty());
53}
54
55Maybe<bool> ScryptTraits::AdditionalConfig(
56    CryptoJobMode mode,
57    const FunctionCallbackInfo<Value>& args,
58    unsigned int offset,
59    ScryptConfig* params) {
60  Environment* env = Environment::GetCurrent(args);
61
62  params->mode = mode;
63
64  ArrayBufferOrViewContents<char> pass(args[offset]);
65  ArrayBufferOrViewContents<char> salt(args[offset + 1]);
66
67  if (UNLIKELY(!pass.CheckSizeInt32())) {
68    THROW_ERR_OUT_OF_RANGE(env, "pass is too large");
69    return Nothing<bool>();
70  }
71
72  if (UNLIKELY(!salt.CheckSizeInt32())) {
73    THROW_ERR_OUT_OF_RANGE(env, "salt is too large");
74    return Nothing<bool>();
75  }
76
77  params->pass = mode == kCryptoJobAsync
78      ? pass.ToCopy()
79      : pass.ToByteSource();
80
81  params->salt = mode == kCryptoJobAsync
82      ? salt.ToCopy()
83      : salt.ToByteSource();
84
85  CHECK(args[offset + 2]->IsUint32());  // N
86  CHECK(args[offset + 3]->IsUint32());  // r
87  CHECK(args[offset + 4]->IsUint32());  // p
88  CHECK(args[offset + 5]->IsNumber());  // maxmem
89  CHECK(args[offset + 6]->IsInt32());  // length
90
91  params->N = args[offset + 2].As<Uint32>()->Value();
92  params->r = args[offset + 3].As<Uint32>()->Value();
93  params->p = args[offset + 4].As<Uint32>()->Value();
94  params->maxmem = args[offset + 5]->IntegerValue(env->context()).ToChecked();
95
96  if (EVP_PBE_scrypt(
97          nullptr,
98          0,
99          nullptr,
100          0,
101          params->N,
102          params->r,
103          params->p,
104          params->maxmem,
105          nullptr,
106          0) != 1) {
107    THROW_ERR_CRYPTO_INVALID_SCRYPT_PARAMS(env);
108    return Nothing<bool>();
109  }
110
111  params->length = args[offset + 6].As<Int32>()->Value();
112  CHECK_GE(params->length, 0);
113
114  return Just(true);
115}
116
117bool ScryptTraits::DeriveBits(
118    Environment* env,
119    const ScryptConfig& params,
120    ByteSource* out) {
121  ByteSource::Builder buf(params.length);
122
123  // Both the pass and salt may be zero-length at this point
124
125  if (!EVP_PBE_scrypt(params.pass.data<char>(),
126                      params.pass.size(),
127                      params.salt.data<unsigned char>(),
128                      params.salt.size(),
129                      params.N,
130                      params.r,
131                      params.p,
132                      params.maxmem,
133                      buf.data<unsigned char>(),
134                      params.length)) {
135    return false;
136  }
137  *out = std::move(buf).release();
138  return true;
139}
140
141#endif  // !OPENSSL_NO_SCRYPT
142
143}  // namespace crypto
144}  // namespace node
145