1 #include "crypto/crypto_pbkdf2.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
10 namespace node {
11
12 using v8::FunctionCallbackInfo;
13 using v8::Int32;
14 using v8::Just;
15 using v8::Maybe;
16 using v8::Nothing;
17 using v8::Value;
18
19 namespace crypto {
PBKDF2Config(PBKDF2Config && other)20 PBKDF2Config::PBKDF2Config(PBKDF2Config&& other) noexcept
21 : mode(other.mode),
22 pass(std::move(other.pass)),
23 salt(std::move(other.salt)),
24 iterations(other.iterations),
25 length(other.length),
26 digest(other.digest) {}
27
operator =(PBKDF2Config && other)28 PBKDF2Config& PBKDF2Config::operator=(PBKDF2Config&& other) noexcept {
29 if (&other == this) return *this;
30 this->~PBKDF2Config();
31 return *new (this) PBKDF2Config(std::move(other));
32 }
33
MemoryInfo(MemoryTracker * tracker) const34 void PBKDF2Config::MemoryInfo(MemoryTracker* tracker) const {
35 // The job is sync, the PBKDF2Config does not own the data.
36 if (mode == kCryptoJobAsync) {
37 tracker->TrackFieldWithSize("pass", pass.size());
38 tracker->TrackFieldWithSize("salt", salt.size());
39 }
40 }
41
EncodeOutput(Environment * env,const PBKDF2Config & params,ByteSource * out,v8::Local<v8::Value> * result)42 Maybe<bool> PBKDF2Traits::EncodeOutput(
43 Environment* env,
44 const PBKDF2Config& params,
45 ByteSource* out,
46 v8::Local<v8::Value>* result) {
47 *result = out->ToArrayBuffer(env);
48 return Just(!result->IsEmpty());
49 }
50
51 // The input arguments for the job are:
52 // 1. CryptoJobMode
53 // 2. The password
54 // 3. The salt
55 // 4. The number of iterations
56 // 5. The number of bytes to generate
57 // 6. The digest algorithm name
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int offset,PBKDF2Config * params)58 Maybe<bool> PBKDF2Traits::AdditionalConfig(
59 CryptoJobMode mode,
60 const FunctionCallbackInfo<Value>& args,
61 unsigned int offset,
62 PBKDF2Config* params) {
63 Environment* env = Environment::GetCurrent(args);
64
65 params->mode = mode;
66
67 ArrayBufferOrViewContents<char> pass(args[offset]);
68 ArrayBufferOrViewContents<char> salt(args[offset + 1]);
69
70 if (UNLIKELY(!pass.CheckSizeInt32())) {
71 THROW_ERR_OUT_OF_RANGE(env, "pass is too large");
72 return Nothing<bool>();
73 }
74
75 if (UNLIKELY(!salt.CheckSizeInt32())) {
76 THROW_ERR_OUT_OF_RANGE(env, "salt is too large");
77 return Nothing<bool>();
78 }
79
80 params->pass = mode == kCryptoJobAsync
81 ? pass.ToCopy()
82 : pass.ToByteSource();
83
84 params->salt = mode == kCryptoJobAsync
85 ? salt.ToCopy()
86 : salt.ToByteSource();
87
88 CHECK(args[offset + 2]->IsInt32()); // iteration_count
89 CHECK(args[offset + 3]->IsInt32()); // length
90 CHECK(args[offset + 4]->IsString()); // digest_name
91
92 params->iterations = args[offset + 2].As<Int32>()->Value();
93 if (params->iterations < 0) {
94 THROW_ERR_OUT_OF_RANGE(env, "iterations must be <= %d", INT_MAX);
95 return Nothing<bool>();
96 }
97
98 params->length = args[offset + 3].As<Int32>()->Value();
99 if (params->length < 0) {
100 THROW_ERR_OUT_OF_RANGE(env, "length must be <= %d", INT_MAX);
101 return Nothing<bool>();
102 }
103
104 Utf8Value name(args.GetIsolate(), args[offset + 4]);
105 params->digest = EVP_get_digestbyname(*name);
106 if (params->digest == nullptr) {
107 THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name);
108 return Nothing<bool>();
109 }
110
111 return Just(true);
112 }
113
DeriveBits(Environment * env,const PBKDF2Config & params,ByteSource * out)114 bool PBKDF2Traits::DeriveBits(
115 Environment* env,
116 const PBKDF2Config& params,
117 ByteSource* out) {
118 ByteSource::Builder buf(params.length);
119
120 // Both pass and salt may be zero length here.
121 // The generated bytes are stored in buf, which is
122 // assigned to out on success.
123
124 if (PKCS5_PBKDF2_HMAC(params.pass.data<char>(),
125 params.pass.size(),
126 params.salt.data<unsigned char>(),
127 params.salt.size(),
128 params.iterations,
129 params.digest,
130 params.length,
131 buf.data<unsigned char>()) <= 0) {
132 return false;
133 }
134 *out = std::move(buf).release();
135 return true;
136 }
137
138 } // namespace crypto
139 } // namespace node
140