/src/node/src/crypto/crypto_dh.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "crypto/crypto_dh.h" |
2 | | #include "async_wrap-inl.h" |
3 | | #include "base_object-inl.h" |
4 | | #include "crypto/crypto_keys.h" |
5 | | #include "crypto/crypto_util.h" |
6 | | #include "env-inl.h" |
7 | | #include "memory_tracker-inl.h" |
8 | | #include "threadpoolwork-inl.h" |
9 | | #include "v8.h" |
10 | | |
11 | | #include <variant> |
12 | | |
13 | | namespace node { |
14 | | |
15 | | using v8::ArrayBuffer; |
16 | | using v8::BackingStore; |
17 | | using v8::ConstructorBehavior; |
18 | | using v8::Context; |
19 | | using v8::DontDelete; |
20 | | using v8::FunctionCallback; |
21 | | using v8::FunctionCallbackInfo; |
22 | | using v8::FunctionTemplate; |
23 | | using v8::HandleScope; |
24 | | using v8::Int32; |
25 | | using v8::Isolate; |
26 | | using v8::Just; |
27 | | using v8::Local; |
28 | | using v8::Maybe; |
29 | | using v8::Nothing; |
30 | | using v8::Object; |
31 | | using v8::PropertyAttribute; |
32 | | using v8::ReadOnly; |
33 | | using v8::SideEffectType; |
34 | | using v8::Signature; |
35 | | using v8::String; |
36 | | using v8::Value; |
37 | | |
38 | | namespace crypto { |
39 | | namespace { |
40 | | void ZeroPadDiffieHellmanSecret(size_t remainder_size, |
41 | | char* data, |
42 | 2 | size_t length) { |
43 | | // DH_size returns number of bytes in a prime number. |
44 | | // DH_compute_key returns number of bytes in a remainder of exponent, which |
45 | | // may have less bytes than a prime number. Therefore add 0-padding to the |
46 | | // allocated buffer. |
47 | 2 | const size_t prime_size = length; |
48 | 2 | if (remainder_size != prime_size) { |
49 | 0 | CHECK_LT(remainder_size, prime_size); |
50 | 0 | const size_t padding = prime_size - remainder_size; |
51 | 0 | memmove(data + padding, data, remainder_size); |
52 | 0 | memset(data, 0, padding); |
53 | 0 | } |
54 | 2 | } |
55 | | } // namespace |
56 | | |
57 | | DiffieHellman::DiffieHellman(Environment* env, Local<Object> wrap) |
58 | 2 | : BaseObject(env, wrap), verifyError_(0) { |
59 | 2 | MakeWeak(); |
60 | 2 | } |
61 | | |
62 | 9.32k | void DiffieHellman::Initialize(Environment* env, Local<Object> target) { |
63 | 9.32k | Isolate* isolate = env->isolate(); |
64 | 9.32k | Local<Context> context = env->context(); |
65 | 18.6k | auto make = [&](Local<String> name, FunctionCallback callback) { |
66 | 18.6k | Local<FunctionTemplate> t = NewFunctionTemplate(isolate, callback); |
67 | | |
68 | 18.6k | const PropertyAttribute attributes = |
69 | 18.6k | static_cast<PropertyAttribute>(ReadOnly | DontDelete); |
70 | | |
71 | 18.6k | t->InstanceTemplate()->SetInternalFieldCount( |
72 | 18.6k | DiffieHellman::kInternalFieldCount); |
73 | | |
74 | 18.6k | SetProtoMethod(isolate, t, "generateKeys", GenerateKeys); |
75 | 18.6k | SetProtoMethod(isolate, t, "computeSecret", ComputeSecret); |
76 | 18.6k | SetProtoMethodNoSideEffect(isolate, t, "getPrime", GetPrime); |
77 | 18.6k | SetProtoMethodNoSideEffect(isolate, t, "getGenerator", GetGenerator); |
78 | 18.6k | SetProtoMethodNoSideEffect(isolate, t, "getPublicKey", GetPublicKey); |
79 | 18.6k | SetProtoMethodNoSideEffect(isolate, t, "getPrivateKey", GetPrivateKey); |
80 | 18.6k | SetProtoMethod(isolate, t, "setPublicKey", SetPublicKey); |
81 | 18.6k | SetProtoMethod(isolate, t, "setPrivateKey", SetPrivateKey); |
82 | | |
83 | 18.6k | Local<FunctionTemplate> verify_error_getter_templ = |
84 | 18.6k | FunctionTemplate::New(isolate, |
85 | 18.6k | DiffieHellman::VerifyErrorGetter, |
86 | 18.6k | Local<Value>(), |
87 | 18.6k | Signature::New(env->isolate(), t), |
88 | 18.6k | /* length */ 0, |
89 | 18.6k | ConstructorBehavior::kThrow, |
90 | 18.6k | SideEffectType::kHasNoSideEffect); |
91 | | |
92 | 18.6k | t->InstanceTemplate()->SetAccessorProperty( |
93 | 18.6k | env->verify_error_string(), |
94 | 18.6k | verify_error_getter_templ, |
95 | 18.6k | Local<FunctionTemplate>(), |
96 | 18.6k | attributes); |
97 | | |
98 | 18.6k | SetConstructorFunction(context, target, name, t); |
99 | 18.6k | }; |
100 | | |
101 | 9.32k | make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellman"), New); |
102 | 9.32k | make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"), |
103 | 9.32k | DiffieHellmanGroup); |
104 | | |
105 | 9.32k | SetMethodNoSideEffect( |
106 | 9.32k | context, target, "statelessDH", DiffieHellman::Stateless); |
107 | 9.32k | DHKeyPairGenJob::Initialize(env, target); |
108 | 9.32k | DHKeyExportJob::Initialize(env, target); |
109 | 9.32k | DHBitsJob::Initialize(env, target); |
110 | 9.32k | } |
111 | | |
112 | | void DiffieHellman::RegisterExternalReferences( |
113 | 0 | ExternalReferenceRegistry* registry) { |
114 | 0 | registry->Register(New); |
115 | 0 | registry->Register(DiffieHellmanGroup); |
116 | |
|
117 | 0 | registry->Register(GenerateKeys); |
118 | 0 | registry->Register(ComputeSecret); |
119 | 0 | registry->Register(GetPrime); |
120 | 0 | registry->Register(GetGenerator); |
121 | 0 | registry->Register(GetPublicKey); |
122 | 0 | registry->Register(GetPrivateKey); |
123 | 0 | registry->Register(SetPublicKey); |
124 | 0 | registry->Register(SetPrivateKey); |
125 | |
|
126 | 0 | registry->Register(DiffieHellman::VerifyErrorGetter); |
127 | 0 | registry->Register(DiffieHellman::Stateless); |
128 | |
|
129 | 0 | DHKeyPairGenJob::RegisterExternalReferences(registry); |
130 | 0 | DHKeyExportJob::RegisterExternalReferences(registry); |
131 | 0 | DHBitsJob::RegisterExternalReferences(registry); |
132 | 0 | } |
133 | | |
134 | 0 | bool DiffieHellman::Init(int primeLength, int g) { |
135 | 0 | dh_.reset(DH_new()); |
136 | 0 | if (!DH_generate_parameters_ex(dh_.get(), primeLength, g, nullptr)) |
137 | 0 | return false; |
138 | 0 | return VerifyContext(); |
139 | 0 | } |
140 | | |
141 | 0 | void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const { |
142 | 0 | tracker->TrackFieldWithSize("dh", dh_ ? kSizeOf_DH : 0); |
143 | 0 | } |
144 | | |
145 | 2 | bool DiffieHellman::Init(BignumPointer&& bn_p, int g) { |
146 | 2 | dh_.reset(DH_new()); |
147 | 2 | CHECK_GE(g, 2); |
148 | 2 | BignumPointer bn_g(BN_new()); |
149 | 2 | return bn_g && BN_set_word(bn_g.get(), g) && |
150 | 2 | DH_set0_pqg(dh_.get(), bn_p.release(), nullptr, bn_g.release()) && |
151 | 2 | VerifyContext(); |
152 | 2 | } |
153 | | |
154 | 0 | bool DiffieHellman::Init(const char* p, int p_len, int g) { |
155 | 0 | dh_.reset(DH_new()); |
156 | 0 | if (p_len <= 0) { |
157 | 0 | ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX, |
158 | 0 | BN_R_BITS_TOO_SMALL, __FILE__, __LINE__); |
159 | 0 | return false; |
160 | 0 | } |
161 | 0 | if (g <= 1) { |
162 | 0 | ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS, |
163 | 0 | DH_R_BAD_GENERATOR, __FILE__, __LINE__); |
164 | 0 | return false; |
165 | 0 | } |
166 | 0 | BignumPointer bn_p( |
167 | 0 | BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr)); |
168 | 0 | BignumPointer bn_g(BN_new()); |
169 | 0 | if (bn_p == nullptr || bn_g == nullptr || !BN_set_word(bn_g.get(), g) || |
170 | 0 | !DH_set0_pqg(dh_.get(), bn_p.release(), nullptr, bn_g.release())) { |
171 | 0 | return false; |
172 | 0 | } |
173 | 0 | return VerifyContext(); |
174 | 0 | } |
175 | | |
176 | 0 | bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) { |
177 | 0 | dh_.reset(DH_new()); |
178 | 0 | if (p_len <= 0) { |
179 | 0 | ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX, |
180 | 0 | BN_R_BITS_TOO_SMALL, __FILE__, __LINE__); |
181 | 0 | return false; |
182 | 0 | } |
183 | 0 | if (g_len <= 0) { |
184 | 0 | ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS, |
185 | 0 | DH_R_BAD_GENERATOR, __FILE__, __LINE__); |
186 | 0 | return false; |
187 | 0 | } |
188 | 0 | BignumPointer bn_g( |
189 | 0 | BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr)); |
190 | 0 | if (BN_is_zero(bn_g.get()) || BN_is_one(bn_g.get())) { |
191 | 0 | ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS, |
192 | 0 | DH_R_BAD_GENERATOR, __FILE__, __LINE__); |
193 | 0 | return false; |
194 | 0 | } |
195 | 0 | BignumPointer bn_p( |
196 | 0 | BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr)); |
197 | 0 | if (!DH_set0_pqg(dh_.get(), bn_p.get(), nullptr, bn_g.get())) { |
198 | 0 | return false; |
199 | 0 | } |
200 | | // The DH_set0_pqg call above takes ownership of the bignums on success, |
201 | | // so we should release them here so we don't end with a possible |
202 | | // use-after-free or double free. |
203 | 0 | bn_p.release(); |
204 | 0 | bn_g.release(); |
205 | 0 | return VerifyContext(); |
206 | 0 | } |
207 | | |
208 | | constexpr int kStandardizedGenerator = 2; |
209 | | |
210 | | template <BIGNUM* (*p)(BIGNUM*)> |
211 | 2 | BignumPointer InstantiateStandardizedGroup() { |
212 | 2 | return BignumPointer(p(nullptr)); |
213 | 2 | } Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc2409_prime_768>() Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc2409_prime_1024>() Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_1536>() std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_2048>() Line | Count | Source | 211 | 2 | BignumPointer InstantiateStandardizedGroup() { | 212 | 2 | return BignumPointer(p(nullptr)); | 213 | 2 | } |
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_3072>() Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_4096>() Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_6144>() Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_8192>() |
214 | | |
215 | | typedef BignumPointer (*StandardizedGroupInstantiator)(); |
216 | | |
217 | | // Returns a function that can be used to create an instance of a standardized |
218 | | // Diffie-Hellman group. The generator is always kStandardizedGenerator. |
219 | 2 | inline StandardizedGroupInstantiator FindDiffieHellmanGroup(const char* name) { |
220 | 2 | #define V(n, p) \ |
221 | 8 | if (StringEqualNoCase(name, n)) return InstantiateStandardizedGroup<p> |
222 | 2 | V("modp1", BN_get_rfc2409_prime_768); |
223 | 2 | V("modp2", BN_get_rfc2409_prime_1024); |
224 | 2 | V("modp5", BN_get_rfc3526_prime_1536); |
225 | 2 | V("modp14", BN_get_rfc3526_prime_2048); |
226 | 0 | V("modp15", BN_get_rfc3526_prime_3072); |
227 | 0 | V("modp16", BN_get_rfc3526_prime_4096); |
228 | 0 | V("modp17", BN_get_rfc3526_prime_6144); |
229 | 0 | V("modp18", BN_get_rfc3526_prime_8192); |
230 | 0 | #undef V |
231 | 0 | return nullptr; |
232 | 0 | } |
233 | | |
234 | | void DiffieHellman::DiffieHellmanGroup( |
235 | 2 | const FunctionCallbackInfo<Value>& args) { |
236 | 2 | Environment* env = Environment::GetCurrent(args); |
237 | 2 | DiffieHellman* diffieHellman = new DiffieHellman(env, args.This()); |
238 | | |
239 | 2 | CHECK_EQ(args.Length(), 1); |
240 | 2 | THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Group name"); |
241 | | |
242 | 2 | bool initialized = false; |
243 | | |
244 | 2 | const node::Utf8Value group_name(env->isolate(), args[0]); |
245 | 2 | auto group = FindDiffieHellmanGroup(*group_name); |
246 | 2 | if (group == nullptr) |
247 | 0 | return THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env); |
248 | | |
249 | 2 | initialized = diffieHellman->Init(group(), kStandardizedGenerator); |
250 | 2 | if (!initialized) |
251 | 0 | THROW_ERR_CRYPTO_INITIALIZATION_FAILED(env); |
252 | 2 | } |
253 | | |
254 | | |
255 | 0 | void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) { |
256 | 0 | Environment* env = Environment::GetCurrent(args); |
257 | 0 | DiffieHellman* diffieHellman = |
258 | 0 | new DiffieHellman(env, args.This()); |
259 | 0 | bool initialized = false; |
260 | |
|
261 | 0 | if (args.Length() == 2) { |
262 | 0 | if (args[0]->IsInt32()) { |
263 | 0 | if (args[1]->IsInt32()) { |
264 | 0 | initialized = diffieHellman->Init(args[0].As<Int32>()->Value(), |
265 | 0 | args[1].As<Int32>()->Value()); |
266 | 0 | } |
267 | 0 | } else { |
268 | 0 | ArrayBufferOrViewContents<char> arg0(args[0]); |
269 | 0 | if (UNLIKELY(!arg0.CheckSizeInt32())) |
270 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "prime is too big"); |
271 | 0 | if (args[1]->IsInt32()) { |
272 | 0 | initialized = diffieHellman->Init(arg0.data(), |
273 | 0 | arg0.size(), |
274 | 0 | args[1].As<Int32>()->Value()); |
275 | 0 | } else { |
276 | 0 | ArrayBufferOrViewContents<char> arg1(args[1]); |
277 | 0 | if (UNLIKELY(!arg1.CheckSizeInt32())) |
278 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "generator is too big"); |
279 | 0 | initialized = diffieHellman->Init(arg0.data(), arg0.size(), |
280 | 0 | arg1.data(), arg1.size()); |
281 | 0 | } |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | 0 | if (!initialized) { |
286 | 0 | return ThrowCryptoError(env, ERR_get_error(), "Initialization failed"); |
287 | 0 | } |
288 | 0 | } |
289 | | |
290 | | |
291 | 2 | void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) { |
292 | 2 | Environment* env = Environment::GetCurrent(args); |
293 | | |
294 | 2 | DiffieHellman* diffieHellman; |
295 | 2 | ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder()); |
296 | | |
297 | 2 | if (!DH_generate_key(diffieHellman->dh_.get())) { |
298 | 0 | return ThrowCryptoError(env, ERR_get_error(), "Key generation failed"); |
299 | 0 | } |
300 | | |
301 | 2 | const BIGNUM* pub_key; |
302 | 2 | DH_get0_key(diffieHellman->dh_.get(), &pub_key, nullptr); |
303 | | |
304 | 2 | std::unique_ptr<BackingStore> bs; |
305 | 2 | { |
306 | 2 | const int size = BN_num_bytes(pub_key); |
307 | 2 | CHECK_GE(size, 0); |
308 | 2 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
309 | 2 | bs = ArrayBuffer::NewBackingStore(env->isolate(), size); |
310 | 2 | } |
311 | | |
312 | 2 | CHECK_EQ(static_cast<int>(bs->ByteLength()), |
313 | 2 | BN_bn2binpad(pub_key, |
314 | 2 | static_cast<unsigned char*>(bs->Data()), |
315 | 2 | bs->ByteLength())); |
316 | | |
317 | 2 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
318 | 2 | Local<Value> buffer; |
319 | 2 | if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; |
320 | 2 | args.GetReturnValue().Set(buffer); |
321 | 2 | } |
322 | | |
323 | | |
324 | | void DiffieHellman::GetField(const FunctionCallbackInfo<Value>& args, |
325 | | const BIGNUM* (*get_field)(const DH*), |
326 | 2 | const char* err_if_null) { |
327 | 2 | Environment* env = Environment::GetCurrent(args); |
328 | | |
329 | 2 | DiffieHellman* dh; |
330 | 2 | ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder()); |
331 | | |
332 | 2 | const BIGNUM* num = get_field(dh->dh_.get()); |
333 | 2 | if (num == nullptr) |
334 | 0 | return THROW_ERR_CRYPTO_INVALID_STATE(env, err_if_null); |
335 | | |
336 | 2 | std::unique_ptr<BackingStore> bs; |
337 | 2 | { |
338 | 2 | const int size = BN_num_bytes(num); |
339 | 2 | CHECK_GE(size, 0); |
340 | 2 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
341 | 2 | bs = ArrayBuffer::NewBackingStore(env->isolate(), size); |
342 | 2 | } |
343 | | |
344 | 2 | CHECK_EQ(static_cast<int>(bs->ByteLength()), |
345 | 2 | BN_bn2binpad(num, |
346 | 2 | static_cast<unsigned char*>(bs->Data()), |
347 | 2 | bs->ByteLength())); |
348 | | |
349 | 2 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
350 | 2 | Local<Value> buffer; |
351 | 2 | if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; |
352 | 2 | args.GetReturnValue().Set(buffer); |
353 | 2 | } |
354 | | |
355 | 0 | void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) { |
356 | 0 | GetField(args, [](const DH* dh) -> const BIGNUM* { |
357 | 0 | const BIGNUM* p; |
358 | 0 | DH_get0_pqg(dh, &p, nullptr, nullptr); |
359 | 0 | return p; |
360 | 0 | }, "p is null"); |
361 | 0 | } |
362 | | |
363 | 0 | void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) { |
364 | 0 | GetField(args, [](const DH* dh) -> const BIGNUM* { |
365 | 0 | const BIGNUM* g; |
366 | 0 | DH_get0_pqg(dh, nullptr, nullptr, &g); |
367 | 0 | return g; |
368 | 0 | }, "g is null"); |
369 | 0 | } |
370 | | |
371 | 2 | void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) { |
372 | 2 | GetField(args, [](const DH* dh) -> const BIGNUM* { |
373 | 2 | const BIGNUM* pub_key; |
374 | 2 | DH_get0_key(dh, &pub_key, nullptr); |
375 | 2 | return pub_key; |
376 | 2 | }, "No public key - did you forget to generate one?"); |
377 | 2 | } |
378 | | |
379 | 0 | void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) { |
380 | 0 | GetField(args, [](const DH* dh) -> const BIGNUM* { |
381 | 0 | const BIGNUM* priv_key; |
382 | 0 | DH_get0_key(dh, nullptr, &priv_key); |
383 | 0 | return priv_key; |
384 | 0 | }, "No private key - did you forget to generate one?"); |
385 | 0 | } |
386 | | |
387 | 2 | void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) { |
388 | 2 | Environment* env = Environment::GetCurrent(args); |
389 | | |
390 | 2 | DiffieHellman* diffieHellman; |
391 | 2 | ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder()); |
392 | | |
393 | 2 | ClearErrorOnReturn clear_error_on_return; |
394 | | |
395 | 2 | CHECK_EQ(args.Length(), 1); |
396 | 2 | ArrayBufferOrViewContents<unsigned char> key_buf(args[0]); |
397 | 2 | if (UNLIKELY(!key_buf.CheckSizeInt32())) |
398 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "secret is too big"); |
399 | 2 | BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.size(), nullptr)); |
400 | | |
401 | 2 | std::unique_ptr<BackingStore> bs; |
402 | 2 | { |
403 | 2 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
404 | 2 | bs = ArrayBuffer::NewBackingStore(env->isolate(), |
405 | 2 | DH_size(diffieHellman->dh_.get())); |
406 | 2 | } |
407 | | |
408 | 2 | int size = DH_compute_key(static_cast<unsigned char*>(bs->Data()), |
409 | 2 | key.get(), |
410 | 2 | diffieHellman->dh_.get()); |
411 | | |
412 | 2 | if (size == -1) { |
413 | 0 | int checkResult; |
414 | 0 | int checked; |
415 | |
|
416 | 0 | checked = DH_check_pub_key(diffieHellman->dh_.get(), |
417 | 0 | key.get(), |
418 | 0 | &checkResult); |
419 | |
|
420 | 0 | if (!checked) { |
421 | 0 | return ThrowCryptoError(env, ERR_get_error(), "Invalid Key"); |
422 | 0 | } else if (checkResult) { |
423 | 0 | if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) { |
424 | 0 | return THROW_ERR_CRYPTO_INVALID_KEYLEN(env, |
425 | 0 | "Supplied key is too small"); |
426 | 0 | } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) { |
427 | 0 | return THROW_ERR_CRYPTO_INVALID_KEYLEN(env, |
428 | 0 | "Supplied key is too large"); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | 0 | return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env); |
433 | 0 | } |
434 | | |
435 | 2 | CHECK_GE(size, 0); |
436 | 2 | ZeroPadDiffieHellmanSecret(size, |
437 | 2 | static_cast<char*>(bs->Data()), |
438 | 2 | bs->ByteLength()); |
439 | | |
440 | 2 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); |
441 | 2 | Local<Value> buffer; |
442 | 2 | if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; |
443 | 2 | args.GetReturnValue().Set(buffer); |
444 | 2 | } |
445 | | |
446 | | void DiffieHellman::SetKey(const FunctionCallbackInfo<Value>& args, |
447 | 0 | int (*set_field)(DH*, BIGNUM*), const char* what) { |
448 | 0 | Environment* env = Environment::GetCurrent(args); |
449 | 0 | DiffieHellman* dh; |
450 | 0 | ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder()); |
451 | 0 | CHECK_EQ(args.Length(), 1); |
452 | 0 | ArrayBufferOrViewContents<unsigned char> buf(args[0]); |
453 | 0 | if (UNLIKELY(!buf.CheckSizeInt32())) |
454 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "buf is too big"); |
455 | 0 | BIGNUM* num = BN_bin2bn(buf.data(), buf.size(), nullptr); |
456 | 0 | CHECK_NOT_NULL(num); |
457 | 0 | CHECK_EQ(1, set_field(dh->dh_.get(), num)); |
458 | 0 | } |
459 | | |
460 | 0 | void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) { |
461 | 0 | SetKey(args, |
462 | 0 | [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, num, nullptr); }, |
463 | 0 | "Public key"); |
464 | 0 | } |
465 | | |
466 | 0 | void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) { |
467 | 0 | SetKey(args, |
468 | 0 | [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, nullptr, num); }, |
469 | 0 | "Private key"); |
470 | 0 | } |
471 | | |
472 | 2 | void DiffieHellman::VerifyErrorGetter(const FunctionCallbackInfo<Value>& args) { |
473 | 2 | HandleScope scope(args.GetIsolate()); |
474 | | |
475 | 2 | DiffieHellman* diffieHellman; |
476 | 2 | ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder()); |
477 | | |
478 | 2 | args.GetReturnValue().Set(diffieHellman->verifyError_); |
479 | 2 | } |
480 | | |
481 | 2 | bool DiffieHellman::VerifyContext() { |
482 | 2 | int codes; |
483 | 2 | if (!DH_check(dh_.get(), &codes)) |
484 | 0 | return false; |
485 | 2 | verifyError_ = codes; |
486 | 2 | return true; |
487 | 2 | } |
488 | | |
489 | | // The input arguments to DhKeyPairGenJob can vary |
490 | | // 1. CryptoJobMode |
491 | | // and either |
492 | | // 2. Group name (as a string) |
493 | | // or |
494 | | // 2. Prime or Prime Length |
495 | | // 3. Generator |
496 | | // Followed by the public and private key encoding parameters: |
497 | | // * Public format |
498 | | // * Public type |
499 | | // * Private format |
500 | | // * Private type |
501 | | // * Cipher |
502 | | // * Passphrase |
503 | | Maybe<bool> DhKeyGenTraits::AdditionalConfig( |
504 | | CryptoJobMode mode, |
505 | | const FunctionCallbackInfo<Value>& args, |
506 | | unsigned int* offset, |
507 | 0 | DhKeyPairGenConfig* params) { |
508 | 0 | Environment* env = Environment::GetCurrent(args); |
509 | |
|
510 | 0 | if (args[*offset]->IsString()) { |
511 | 0 | Utf8Value group_name(env->isolate(), args[*offset]); |
512 | 0 | auto group = FindDiffieHellmanGroup(*group_name); |
513 | 0 | if (group == nullptr) { |
514 | 0 | THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env); |
515 | 0 | return Nothing<bool>(); |
516 | 0 | } |
517 | | |
518 | 0 | params->params.prime = group(); |
519 | 0 | params->params.generator = kStandardizedGenerator; |
520 | 0 | *offset += 1; |
521 | 0 | } else { |
522 | 0 | if (args[*offset]->IsInt32()) { |
523 | 0 | int size = args[*offset].As<Int32>()->Value(); |
524 | 0 | if (size < 0) { |
525 | 0 | THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size"); |
526 | 0 | return Nothing<bool>(); |
527 | 0 | } |
528 | 0 | params->params.prime = size; |
529 | 0 | } else { |
530 | 0 | ArrayBufferOrViewContents<unsigned char> input(args[*offset]); |
531 | 0 | if (UNLIKELY(!input.CheckSizeInt32())) { |
532 | 0 | THROW_ERR_OUT_OF_RANGE(env, "prime is too big"); |
533 | 0 | return Nothing<bool>(); |
534 | 0 | } |
535 | 0 | params->params.prime = BignumPointer( |
536 | 0 | BN_bin2bn(input.data(), input.size(), nullptr)); |
537 | 0 | } |
538 | | |
539 | 0 | CHECK(args[*offset + 1]->IsInt32()); |
540 | 0 | params->params.generator = args[*offset + 1].As<Int32>()->Value(); |
541 | 0 | *offset += 2; |
542 | 0 | } |
543 | | |
544 | 0 | return Just(true); |
545 | 0 | } |
546 | | |
547 | 0 | EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) { |
548 | 0 | EVPKeyPointer key_params; |
549 | 0 | if (BignumPointer* prime_fixed_value = |
550 | 0 | std::get_if<BignumPointer>(¶ms->params.prime)) { |
551 | 0 | DHPointer dh(DH_new()); |
552 | 0 | if (!dh) |
553 | 0 | return EVPKeyCtxPointer(); |
554 | | |
555 | 0 | BIGNUM* prime = prime_fixed_value->get(); |
556 | 0 | BignumPointer bn_g(BN_new()); |
557 | 0 | if (!BN_set_word(bn_g.get(), params->params.generator) || |
558 | 0 | !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get())) { |
559 | 0 | return EVPKeyCtxPointer(); |
560 | 0 | } |
561 | | |
562 | 0 | prime_fixed_value->release(); |
563 | 0 | bn_g.release(); |
564 | |
|
565 | 0 | key_params = EVPKeyPointer(EVP_PKEY_new()); |
566 | 0 | CHECK(key_params); |
567 | 0 | CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1); |
568 | 0 | } else if (int* prime_size = std::get_if<int>(¶ms->params.prime)) { |
569 | 0 | EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); |
570 | 0 | EVP_PKEY* raw_params = nullptr; |
571 | 0 | if (!param_ctx || |
572 | 0 | EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || |
573 | 0 | EVP_PKEY_CTX_set_dh_paramgen_prime_len( |
574 | 0 | param_ctx.get(), |
575 | 0 | *prime_size) <= 0 || |
576 | 0 | EVP_PKEY_CTX_set_dh_paramgen_generator( |
577 | 0 | param_ctx.get(), |
578 | 0 | params->params.generator) <= 0 || |
579 | 0 | EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { |
580 | 0 | return EVPKeyCtxPointer(); |
581 | 0 | } |
582 | | |
583 | 0 | key_params = EVPKeyPointer(raw_params); |
584 | 0 | } else { |
585 | 0 | UNREACHABLE(); |
586 | 0 | } |
587 | | |
588 | 0 | EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr)); |
589 | 0 | if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) |
590 | 0 | return EVPKeyCtxPointer(); |
591 | | |
592 | 0 | return ctx; |
593 | 0 | } |
594 | | |
595 | | Maybe<bool> DHKeyExportTraits::AdditionalConfig( |
596 | | const FunctionCallbackInfo<Value>& args, |
597 | | unsigned int offset, |
598 | 0 | DHKeyExportConfig* params) { |
599 | 0 | return Just(true); |
600 | 0 | } |
601 | | |
602 | | WebCryptoKeyExportStatus DHKeyExportTraits::DoExport( |
603 | | std::shared_ptr<KeyObjectData> key_data, |
604 | | WebCryptoKeyFormat format, |
605 | | const DHKeyExportConfig& params, |
606 | 0 | ByteSource* out) { |
607 | 0 | CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret); |
608 | | |
609 | 0 | switch (format) { |
610 | 0 | case kWebCryptoKeyFormatPKCS8: |
611 | 0 | if (key_data->GetKeyType() != kKeyTypePrivate) |
612 | 0 | return WebCryptoKeyExportStatus::INVALID_KEY_TYPE; |
613 | 0 | return PKEY_PKCS8_Export(key_data.get(), out); |
614 | 0 | case kWebCryptoKeyFormatSPKI: |
615 | 0 | if (key_data->GetKeyType() != kKeyTypePublic) |
616 | 0 | return WebCryptoKeyExportStatus::INVALID_KEY_TYPE; |
617 | 0 | return PKEY_SPKI_Export(key_data.get(), out); |
618 | 0 | default: |
619 | 0 | UNREACHABLE(); |
620 | 0 | } |
621 | 0 | } |
622 | | |
623 | | namespace { |
624 | | ByteSource StatelessDiffieHellmanThreadsafe( |
625 | | const ManagedEVPPKey& our_key, |
626 | 0 | const ManagedEVPPKey& their_key) { |
627 | 0 | size_t out_size; |
628 | |
|
629 | 0 | EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr)); |
630 | 0 | if (!ctx || |
631 | 0 | EVP_PKEY_derive_init(ctx.get()) <= 0 || |
632 | 0 | EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 || |
633 | 0 | EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) |
634 | 0 | return ByteSource(); |
635 | | |
636 | 0 | ByteSource::Builder out(out_size); |
637 | 0 | if (EVP_PKEY_derive(ctx.get(), out.data<unsigned char>(), &out_size) <= 0) { |
638 | 0 | return ByteSource(); |
639 | 0 | } |
640 | | |
641 | 0 | ZeroPadDiffieHellmanSecret(out_size, out.data<char>(), out.size()); |
642 | 0 | return std::move(out).release(); |
643 | 0 | } |
644 | | } // namespace |
645 | | |
646 | 0 | void DiffieHellman::Stateless(const FunctionCallbackInfo<Value>& args) { |
647 | 0 | Environment* env = Environment::GetCurrent(args); |
648 | |
|
649 | 0 | CHECK(args[0]->IsObject() && args[1]->IsObject()); |
650 | 0 | KeyObjectHandle* our_key_object; |
651 | 0 | ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>()); |
652 | 0 | CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate); |
653 | 0 | KeyObjectHandle* their_key_object; |
654 | 0 | ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>()); |
655 | 0 | CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret); |
656 | | |
657 | 0 | ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey(); |
658 | 0 | ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey(); |
659 | |
|
660 | 0 | Local<Value> out; |
661 | 0 | if (!StatelessDiffieHellmanThreadsafe(our_key, their_key) |
662 | 0 | .ToBuffer(env) |
663 | 0 | .ToLocal(&out)) return; |
664 | | |
665 | 0 | if (Buffer::Length(out) == 0) |
666 | 0 | return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed"); |
667 | | |
668 | 0 | args.GetReturnValue().Set(out); |
669 | 0 | } |
670 | | |
671 | | Maybe<bool> DHBitsTraits::AdditionalConfig( |
672 | | CryptoJobMode mode, |
673 | | const FunctionCallbackInfo<Value>& args, |
674 | | unsigned int offset, |
675 | 0 | DHBitsConfig* params) { |
676 | 0 | Environment* env = Environment::GetCurrent(args); |
677 | |
|
678 | 0 | CHECK(args[offset]->IsObject()); // public key |
679 | 0 | CHECK(args[offset + 1]->IsObject()); // private key |
680 | | |
681 | 0 | KeyObjectHandle* private_key; |
682 | 0 | KeyObjectHandle* public_key; |
683 | |
|
684 | 0 | ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<bool>()); |
685 | 0 | ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<bool>()); |
686 | | |
687 | 0 | if (private_key->Data()->GetKeyType() != kKeyTypePrivate || |
688 | 0 | public_key->Data()->GetKeyType() != kKeyTypePublic) { |
689 | 0 | THROW_ERR_CRYPTO_INVALID_KEYTYPE(env); |
690 | 0 | return Nothing<bool>(); |
691 | 0 | } |
692 | | |
693 | 0 | params->public_key = public_key->Data(); |
694 | 0 | params->private_key = private_key->Data(); |
695 | |
|
696 | 0 | return Just(true); |
697 | 0 | } |
698 | | |
699 | | Maybe<bool> DHBitsTraits::EncodeOutput( |
700 | | Environment* env, |
701 | | const DHBitsConfig& params, |
702 | | ByteSource* out, |
703 | 0 | v8::Local<v8::Value>* result) { |
704 | 0 | *result = out->ToArrayBuffer(env); |
705 | 0 | return Just(!result->IsEmpty()); |
706 | 0 | } |
707 | | |
708 | | bool DHBitsTraits::DeriveBits( |
709 | | Environment* env, |
710 | | const DHBitsConfig& params, |
711 | 0 | ByteSource* out) { |
712 | 0 | *out = StatelessDiffieHellmanThreadsafe( |
713 | 0 | params.private_key->GetAsymmetricKey(), |
714 | 0 | params.public_key->GetAsymmetricKey()); |
715 | 0 | return true; |
716 | 0 | } |
717 | | |
718 | | Maybe<bool> GetDhKeyDetail( |
719 | | Environment* env, |
720 | | std::shared_ptr<KeyObjectData> key, |
721 | 0 | Local<Object> target) { |
722 | 0 | ManagedEVPPKey pkey = key->GetAsymmetricKey(); |
723 | 0 | CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_DH); |
724 | 0 | return Just(true); |
725 | 0 | } |
726 | | |
727 | | } // namespace crypto |
728 | | } // namespace node |