/src/node/src/crypto/crypto_cipher.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "crypto/crypto_cipher.h" |
2 | | #include "base_object-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 "node_internals.h" |
8 | | #include "node_process-inl.h" |
9 | | #include "v8.h" |
10 | | |
11 | | namespace node { |
12 | | |
13 | | using v8::Array; |
14 | | using v8::ArrayBuffer; |
15 | | using v8::BackingStore; |
16 | | using v8::Context; |
17 | | using v8::FunctionCallbackInfo; |
18 | | using v8::FunctionTemplate; |
19 | | using v8::HandleScope; |
20 | | using v8::Int32; |
21 | | using v8::Isolate; |
22 | | using v8::Local; |
23 | | using v8::Object; |
24 | | using v8::Uint32; |
25 | | using v8::Value; |
26 | | |
27 | | namespace crypto { |
28 | | namespace { |
29 | 49 | bool IsSupportedAuthenticatedMode(const EVP_CIPHER* cipher) { |
30 | 49 | switch (EVP_CIPHER_mode(cipher)) { |
31 | 30 | case EVP_CIPH_CCM_MODE: |
32 | 30 | case EVP_CIPH_GCM_MODE: |
33 | 30 | #ifndef OPENSSL_NO_OCB |
34 | 30 | case EVP_CIPH_OCB_MODE: |
35 | 30 | #endif |
36 | 30 | return true; |
37 | 0 | case EVP_CIPH_STREAM_CIPHER: |
38 | 0 | return EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305; |
39 | 19 | default: |
40 | 19 | return false; |
41 | 49 | } |
42 | 49 | } |
43 | | |
44 | 31 | bool IsSupportedAuthenticatedMode(const EVP_CIPHER_CTX* ctx) { |
45 | 31 | const EVP_CIPHER* cipher = EVP_CIPHER_CTX_cipher(ctx); |
46 | 31 | return IsSupportedAuthenticatedMode(cipher); |
47 | 31 | } |
48 | | |
49 | 0 | bool IsValidGCMTagLength(unsigned int tag_len) { |
50 | 0 | return tag_len == 4 || tag_len == 8 || (tag_len >= 12 && tag_len <= 16); |
51 | 0 | } |
52 | | |
53 | | // Collects and returns information on the given cipher |
54 | 0 | void GetCipherInfo(const FunctionCallbackInfo<Value>& args) { |
55 | 0 | Environment* env = Environment::GetCurrent(args); |
56 | 0 | CHECK(args[0]->IsObject()); |
57 | 0 | Local<Object> info = args[0].As<Object>(); |
58 | |
|
59 | 0 | CHECK(args[1]->IsString() || args[1]->IsInt32()); |
60 | | |
61 | 0 | const EVP_CIPHER* cipher; |
62 | 0 | if (args[1]->IsString()) { |
63 | 0 | Utf8Value name(env->isolate(), args[1]); |
64 | 0 | cipher = EVP_get_cipherbyname(*name); |
65 | 0 | } else { |
66 | 0 | int nid = args[1].As<Int32>()->Value(); |
67 | 0 | cipher = EVP_get_cipherbynid(nid); |
68 | 0 | } |
69 | |
|
70 | 0 | if (cipher == nullptr) |
71 | 0 | return; |
72 | | |
73 | 0 | int mode = EVP_CIPHER_mode(cipher); |
74 | 0 | int iv_length = EVP_CIPHER_iv_length(cipher); |
75 | 0 | int key_length = EVP_CIPHER_key_length(cipher); |
76 | 0 | int block_length = EVP_CIPHER_block_size(cipher); |
77 | 0 | const char* mode_label = nullptr; |
78 | 0 | switch (mode) { |
79 | 0 | case EVP_CIPH_CBC_MODE: mode_label = "cbc"; break; |
80 | 0 | case EVP_CIPH_CCM_MODE: mode_label = "ccm"; break; |
81 | 0 | case EVP_CIPH_CFB_MODE: mode_label = "cfb"; break; |
82 | 0 | case EVP_CIPH_CTR_MODE: mode_label = "ctr"; break; |
83 | 0 | case EVP_CIPH_ECB_MODE: mode_label = "ecb"; break; |
84 | 0 | case EVP_CIPH_GCM_MODE: mode_label = "gcm"; break; |
85 | 0 | case EVP_CIPH_OCB_MODE: mode_label = "ocb"; break; |
86 | 0 | case EVP_CIPH_OFB_MODE: mode_label = "ofb"; break; |
87 | 0 | case EVP_CIPH_WRAP_MODE: mode_label = "wrap"; break; |
88 | 0 | case EVP_CIPH_XTS_MODE: mode_label = "xts"; break; |
89 | 0 | case EVP_CIPH_STREAM_CIPHER: mode_label = "stream"; break; |
90 | 0 | } |
91 | | |
92 | | // If the testKeyLen and testIvLen arguments are specified, |
93 | | // then we will make an attempt to see if they are usable for |
94 | | // the cipher in question, returning undefined if they are not. |
95 | | // If they are, the info object will be returned with the values |
96 | | // given. |
97 | 0 | if (args[2]->IsInt32() || args[3]->IsInt32()) { |
98 | | // Test and input IV or key length to determine if it's acceptable. |
99 | | // If it is, then the getCipherInfo will succeed with the given |
100 | | // values. |
101 | 0 | CipherCtxPointer ctx(EVP_CIPHER_CTX_new()); |
102 | 0 | if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr, 1)) |
103 | 0 | return; |
104 | | |
105 | 0 | if (args[2]->IsInt32()) { |
106 | 0 | int check_len = args[2].As<Int32>()->Value(); |
107 | 0 | if (!EVP_CIPHER_CTX_set_key_length(ctx.get(), check_len)) |
108 | 0 | return; |
109 | 0 | key_length = check_len; |
110 | 0 | } |
111 | | |
112 | 0 | if (args[3]->IsInt32()) { |
113 | 0 | int check_len = args[3].As<Int32>()->Value(); |
114 | | // For CCM modes, the IV may be between 7 and 13 bytes. |
115 | | // For GCM and OCB modes, we'll check by attempting to |
116 | | // set the value. For everything else, just check that |
117 | | // check_len == iv_length. |
118 | 0 | switch (mode) { |
119 | 0 | case EVP_CIPH_CCM_MODE: |
120 | 0 | if (check_len < 7 || check_len > 13) |
121 | 0 | return; |
122 | 0 | break; |
123 | 0 | case EVP_CIPH_GCM_MODE: |
124 | | // Fall through |
125 | 0 | case EVP_CIPH_OCB_MODE: |
126 | 0 | if (!EVP_CIPHER_CTX_ctrl( |
127 | 0 | ctx.get(), |
128 | 0 | EVP_CTRL_AEAD_SET_IVLEN, |
129 | 0 | check_len, |
130 | 0 | nullptr)) { |
131 | 0 | return; |
132 | 0 | } |
133 | 0 | break; |
134 | 0 | default: |
135 | 0 | if (check_len != iv_length) |
136 | 0 | return; |
137 | 0 | } |
138 | 0 | iv_length = check_len; |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 0 | if (mode_label != nullptr && |
143 | 0 | info->Set( |
144 | 0 | env->context(), |
145 | 0 | FIXED_ONE_BYTE_STRING(env->isolate(), "mode"), |
146 | 0 | OneByteString(env->isolate(), mode_label)).IsNothing()) { |
147 | 0 | return; |
148 | 0 | } |
149 | | |
150 | | // OBJ_nid2sn(EVP_CIPHER_nid(cipher)) is used here instead of |
151 | | // EVP_CIPHER_name(cipher) for compatibility with BoringSSL. |
152 | 0 | if (info->Set( |
153 | 0 | env->context(), |
154 | 0 | env->name_string(), |
155 | 0 | OneByteString( |
156 | 0 | env->isolate(), |
157 | 0 | OBJ_nid2sn(EVP_CIPHER_nid(cipher)))).IsNothing()) { |
158 | 0 | return; |
159 | 0 | } |
160 | | |
161 | 0 | if (info->Set( |
162 | 0 | env->context(), |
163 | 0 | FIXED_ONE_BYTE_STRING(env->isolate(), "nid"), |
164 | 0 | Int32::New(env->isolate(), EVP_CIPHER_nid(cipher))).IsNothing()) { |
165 | 0 | return; |
166 | 0 | } |
167 | | |
168 | | // Stream ciphers do not have a meaningful block size |
169 | 0 | if (mode != EVP_CIPH_STREAM_CIPHER && |
170 | 0 | info->Set( |
171 | 0 | env->context(), |
172 | 0 | FIXED_ONE_BYTE_STRING(env->isolate(), "blockSize"), |
173 | 0 | Int32::New(env->isolate(), block_length)).IsNothing()) { |
174 | 0 | return; |
175 | 0 | } |
176 | | |
177 | | // Ciphers that do not use an IV shouldn't report a length |
178 | 0 | if (iv_length != 0 && |
179 | 0 | info->Set( |
180 | 0 | env->context(), |
181 | 0 | FIXED_ONE_BYTE_STRING(env->isolate(), "ivLength"), |
182 | 0 | Int32::New(env->isolate(), iv_length)).IsNothing()) { |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | 0 | if (info->Set( |
187 | 0 | env->context(), |
188 | 0 | FIXED_ONE_BYTE_STRING(env->isolate(), "keyLength"), |
189 | 0 | Int32::New(env->isolate(), key_length)).IsNothing()) { |
190 | 0 | return; |
191 | 0 | } |
192 | | |
193 | 0 | args.GetReturnValue().Set(info); |
194 | 0 | } |
195 | | } // namespace |
196 | | |
197 | 0 | void CipherBase::GetSSLCiphers(const FunctionCallbackInfo<Value>& args) { |
198 | 0 | Environment* env = Environment::GetCurrent(args); |
199 | |
|
200 | 0 | SSLCtxPointer ctx(SSL_CTX_new(TLS_method())); |
201 | 0 | if (!ctx) { |
202 | 0 | return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_new"); |
203 | 0 | } |
204 | | |
205 | 0 | SSLPointer ssl(SSL_new(ctx.get())); |
206 | 0 | if (!ssl) { |
207 | 0 | return ThrowCryptoError(env, ERR_get_error(), "SSL_new"); |
208 | 0 | } |
209 | | |
210 | 0 | STACK_OF(SSL_CIPHER)* ciphers = SSL_get_ciphers(ssl.get()); |
211 | | |
212 | | // TLSv1.3 ciphers aren't listed by EVP. There are only 5, we could just |
213 | | // document them, but since there are only 5, easier to just add them manually |
214 | | // and not have to explain their absence in the API docs. They are lower-cased |
215 | | // because the docs say they will be. |
216 | 0 | static const char* TLS13_CIPHERS[] = { |
217 | 0 | "tls_aes_256_gcm_sha384", |
218 | 0 | "tls_chacha20_poly1305_sha256", |
219 | 0 | "tls_aes_128_gcm_sha256", |
220 | 0 | "tls_aes_128_ccm_8_sha256", |
221 | 0 | "tls_aes_128_ccm_sha256" |
222 | 0 | }; |
223 | |
|
224 | 0 | const int n = sk_SSL_CIPHER_num(ciphers); |
225 | 0 | std::vector<Local<Value>> arr(n + arraysize(TLS13_CIPHERS)); |
226 | |
|
227 | 0 | for (int i = 0; i < n; ++i) { |
228 | 0 | const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i); |
229 | 0 | arr[i] = OneByteString(env->isolate(), SSL_CIPHER_get_name(cipher)); |
230 | 0 | } |
231 | |
|
232 | 0 | for (unsigned i = 0; i < arraysize(TLS13_CIPHERS); ++i) { |
233 | 0 | const char* name = TLS13_CIPHERS[i]; |
234 | 0 | arr[n + i] = OneByteString(env->isolate(), name); |
235 | 0 | } |
236 | |
|
237 | 0 | args.GetReturnValue().Set(Array::New(env->isolate(), arr.data(), arr.size())); |
238 | 0 | } |
239 | | |
240 | 1 | void CipherBase::GetCiphers(const FunctionCallbackInfo<Value>& args) { |
241 | 1 | Environment* env = Environment::GetCurrent(args); |
242 | 1 | MarkPopErrorOnReturn mark_pop_error_on_return; |
243 | 1 | CipherPushContext ctx(env); |
244 | 1 | EVP_CIPHER_do_all_sorted( |
245 | 1 | #if OPENSSL_VERSION_MAJOR >= 3 |
246 | 1 | array_push_back<EVP_CIPHER, |
247 | 1 | EVP_CIPHER_fetch, |
248 | 1 | EVP_CIPHER_free, |
249 | 1 | EVP_get_cipherbyname, |
250 | 1 | EVP_CIPHER_get0_name>, |
251 | | #else |
252 | | array_push_back<EVP_CIPHER>, |
253 | | #endif |
254 | 1 | &ctx); |
255 | 1 | args.GetReturnValue().Set(ctx.ToJSArray()); |
256 | 1 | } |
257 | | |
258 | | CipherBase::CipherBase(Environment* env, |
259 | | Local<Object> wrap, |
260 | | CipherKind kind) |
261 | 9 | : BaseObject(env, wrap), |
262 | 9 | ctx_(nullptr), |
263 | 9 | kind_(kind), |
264 | 9 | auth_tag_state_(kAuthTagUnknown), |
265 | 9 | auth_tag_len_(kNoAuthTagLength), |
266 | 9 | pending_auth_failed_(false) { |
267 | 9 | MakeWeak(); |
268 | 9 | } |
269 | | |
270 | 0 | void CipherBase::MemoryInfo(MemoryTracker* tracker) const { |
271 | 0 | tracker->TrackFieldWithSize("context", ctx_ ? kSizeOf_EVP_CIPHER_CTX : 0); |
272 | 0 | } |
273 | | |
274 | 9.32k | void CipherBase::Initialize(Environment* env, Local<Object> target) { |
275 | 9.32k | Isolate* isolate = env->isolate(); |
276 | 9.32k | Local<Context> context = env->context(); |
277 | | |
278 | 9.32k | Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New); |
279 | | |
280 | 9.32k | t->InstanceTemplate()->SetInternalFieldCount(CipherBase::kInternalFieldCount); |
281 | | |
282 | 9.32k | SetProtoMethod(isolate, t, "init", Init); |
283 | 9.32k | SetProtoMethod(isolate, t, "initiv", InitIv); |
284 | 9.32k | SetProtoMethod(isolate, t, "update", Update); |
285 | 9.32k | SetProtoMethod(isolate, t, "final", Final); |
286 | 9.32k | SetProtoMethod(isolate, t, "setAutoPadding", SetAutoPadding); |
287 | 9.32k | SetProtoMethodNoSideEffect(isolate, t, "getAuthTag", GetAuthTag); |
288 | 9.32k | SetProtoMethod(isolate, t, "setAuthTag", SetAuthTag); |
289 | 9.32k | SetProtoMethod(isolate, t, "setAAD", SetAAD); |
290 | 9.32k | SetConstructorFunction(context, target, "CipherBase", t); |
291 | | |
292 | 9.32k | SetMethodNoSideEffect(context, target, "getSSLCiphers", GetSSLCiphers); |
293 | 9.32k | SetMethodNoSideEffect(context, target, "getCiphers", GetCiphers); |
294 | | |
295 | 9.32k | SetMethod(context, |
296 | 9.32k | target, |
297 | 9.32k | "publicEncrypt", |
298 | 9.32k | PublicKeyCipher::Cipher<PublicKeyCipher::kPublic, |
299 | 9.32k | EVP_PKEY_encrypt_init, |
300 | 9.32k | EVP_PKEY_encrypt>); |
301 | 9.32k | SetMethod(context, |
302 | 9.32k | target, |
303 | 9.32k | "privateDecrypt", |
304 | 9.32k | PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate, |
305 | 9.32k | EVP_PKEY_decrypt_init, |
306 | 9.32k | EVP_PKEY_decrypt>); |
307 | 9.32k | SetMethod(context, |
308 | 9.32k | target, |
309 | 9.32k | "privateEncrypt", |
310 | 9.32k | PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate, |
311 | 9.32k | EVP_PKEY_sign_init, |
312 | 9.32k | EVP_PKEY_sign>); |
313 | 9.32k | SetMethod(context, |
314 | 9.32k | target, |
315 | 9.32k | "publicDecrypt", |
316 | 9.32k | PublicKeyCipher::Cipher<PublicKeyCipher::kPublic, |
317 | 9.32k | EVP_PKEY_verify_recover_init, |
318 | 9.32k | EVP_PKEY_verify_recover>); |
319 | | |
320 | 9.32k | SetMethodNoSideEffect(context, target, "getCipherInfo", GetCipherInfo); |
321 | | |
322 | 9.32k | NODE_DEFINE_CONSTANT(target, kWebCryptoCipherEncrypt); |
323 | 9.32k | NODE_DEFINE_CONSTANT(target, kWebCryptoCipherDecrypt); |
324 | 9.32k | } |
325 | | |
326 | | void CipherBase::RegisterExternalReferences( |
327 | 0 | ExternalReferenceRegistry* registry) { |
328 | 0 | registry->Register(New); |
329 | |
|
330 | 0 | registry->Register(Init); |
331 | 0 | registry->Register(InitIv); |
332 | 0 | registry->Register(Update); |
333 | 0 | registry->Register(Final); |
334 | 0 | registry->Register(SetAutoPadding); |
335 | 0 | registry->Register(GetAuthTag); |
336 | 0 | registry->Register(SetAuthTag); |
337 | 0 | registry->Register(SetAAD); |
338 | |
|
339 | 0 | registry->Register(GetSSLCiphers); |
340 | 0 | registry->Register(GetCiphers); |
341 | |
|
342 | 0 | registry->Register(PublicKeyCipher::Cipher<PublicKeyCipher::kPublic, |
343 | 0 | EVP_PKEY_encrypt_init, |
344 | 0 | EVP_PKEY_encrypt>); |
345 | 0 | registry->Register(PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate, |
346 | 0 | EVP_PKEY_decrypt_init, |
347 | 0 | EVP_PKEY_decrypt>); |
348 | 0 | registry->Register(PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate, |
349 | 0 | EVP_PKEY_sign_init, |
350 | 0 | EVP_PKEY_sign>); |
351 | 0 | registry->Register(PublicKeyCipher::Cipher<PublicKeyCipher::kPublic, |
352 | 0 | EVP_PKEY_verify_recover_init, |
353 | 0 | EVP_PKEY_verify_recover>); |
354 | |
|
355 | 0 | registry->Register(GetCipherInfo); |
356 | 0 | } |
357 | | |
358 | 9 | void CipherBase::New(const FunctionCallbackInfo<Value>& args) { |
359 | 9 | CHECK(args.IsConstructCall()); |
360 | 9 | Environment* env = Environment::GetCurrent(args); |
361 | 9 | new CipherBase(env, args.This(), args[0]->IsTrue() ? kCipher : kDecipher); |
362 | 9 | } |
363 | | |
364 | | void CipherBase::CommonInit(const char* cipher_type, |
365 | | const EVP_CIPHER* cipher, |
366 | | const unsigned char* key, |
367 | | int key_len, |
368 | | const unsigned char* iv, |
369 | | int iv_len, |
370 | 9 | unsigned int auth_tag_len) { |
371 | 9 | CHECK(!ctx_); |
372 | 9 | ctx_.reset(EVP_CIPHER_CTX_new()); |
373 | | |
374 | 9 | const int mode = EVP_CIPHER_mode(cipher); |
375 | 9 | if (mode == EVP_CIPH_WRAP_MODE) |
376 | 0 | EVP_CIPHER_CTX_set_flags(ctx_.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); |
377 | | |
378 | 9 | const bool encrypt = (kind_ == kCipher); |
379 | 9 | if (1 != EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, |
380 | 9 | nullptr, nullptr, encrypt)) { |
381 | 0 | return ThrowCryptoError(env(), ERR_get_error(), |
382 | 0 | "Failed to initialize cipher"); |
383 | 0 | } |
384 | | |
385 | 9 | if (IsSupportedAuthenticatedMode(cipher)) { |
386 | 4 | CHECK_GE(iv_len, 0); |
387 | 4 | if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len)) |
388 | 0 | return; |
389 | 4 | } |
390 | | |
391 | 9 | if (!EVP_CIPHER_CTX_set_key_length(ctx_.get(), key_len)) { |
392 | 0 | ctx_.reset(); |
393 | 0 | return THROW_ERR_CRYPTO_INVALID_KEYLEN(env()); |
394 | 0 | } |
395 | | |
396 | 9 | if (1 != EVP_CipherInit_ex(ctx_.get(), nullptr, nullptr, key, iv, encrypt)) { |
397 | 0 | return ThrowCryptoError(env(), ERR_get_error(), |
398 | 0 | "Failed to initialize cipher"); |
399 | 0 | } |
400 | 9 | } |
401 | | |
402 | | void CipherBase::Init(const char* cipher_type, |
403 | | const ArrayBufferOrViewContents<unsigned char>& key_buf, |
404 | 0 | unsigned int auth_tag_len) { |
405 | 0 | HandleScope scope(env()->isolate()); |
406 | 0 | MarkPopErrorOnReturn mark_pop_error_on_return; |
407 | 0 | const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); |
408 | 0 | if (cipher == nullptr) |
409 | 0 | return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env()); |
410 | | |
411 | 0 | unsigned char key[EVP_MAX_KEY_LENGTH]; |
412 | 0 | unsigned char iv[EVP_MAX_IV_LENGTH]; |
413 | |
|
414 | 0 | int key_len = EVP_BytesToKey(cipher, |
415 | 0 | EVP_md5(), |
416 | 0 | nullptr, |
417 | 0 | key_buf.data(), |
418 | 0 | key_buf.size(), |
419 | 0 | 1, |
420 | 0 | key, |
421 | 0 | iv); |
422 | 0 | CHECK_NE(key_len, 0); |
423 | | |
424 | 0 | const int mode = EVP_CIPHER_mode(cipher); |
425 | 0 | if (kind_ == kCipher && (mode == EVP_CIPH_CTR_MODE || |
426 | 0 | mode == EVP_CIPH_GCM_MODE || |
427 | 0 | mode == EVP_CIPH_CCM_MODE)) { |
428 | | // Ignore the return value (i.e. possible exception) because we are |
429 | | // not calling back into JS anyway. |
430 | 0 | ProcessEmitWarning(env(), |
431 | 0 | "Use Cipheriv for counter mode of %s", |
432 | 0 | cipher_type); |
433 | 0 | } |
434 | |
|
435 | 0 | CommonInit(cipher_type, cipher, key, key_len, iv, |
436 | 0 | EVP_CIPHER_iv_length(cipher), auth_tag_len); |
437 | 0 | } |
438 | | |
439 | 0 | void CipherBase::Init(const FunctionCallbackInfo<Value>& args) { |
440 | 0 | CipherBase* cipher; |
441 | 0 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
442 | 0 | Environment* env = Environment::GetCurrent(args); |
443 | |
|
444 | 0 | CHECK_GE(args.Length(), 3); |
445 | | |
446 | 0 | const Utf8Value cipher_type(args.GetIsolate(), args[0]); |
447 | 0 | ArrayBufferOrViewContents<unsigned char> key_buf(args[1]); |
448 | 0 | if (!key_buf.CheckSizeInt32()) |
449 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "password is too large"); |
450 | | |
451 | | // Don't assign to cipher->auth_tag_len_ directly; the value might not |
452 | | // represent a valid length at this point. |
453 | 0 | unsigned int auth_tag_len; |
454 | 0 | if (args[2]->IsUint32()) { |
455 | 0 | auth_tag_len = args[2].As<Uint32>()->Value(); |
456 | 0 | } else { |
457 | 0 | CHECK(args[2]->IsInt32() && args[2].As<Int32>()->Value() == -1); |
458 | 0 | auth_tag_len = kNoAuthTagLength; |
459 | 0 | } |
460 | | |
461 | 0 | cipher->Init(*cipher_type, key_buf, auth_tag_len); |
462 | 0 | } |
463 | | |
464 | | void CipherBase::InitIv(const char* cipher_type, |
465 | | const ByteSource& key_buf, |
466 | | const ArrayBufferOrViewContents<unsigned char>& iv_buf, |
467 | 9 | unsigned int auth_tag_len) { |
468 | 9 | HandleScope scope(env()->isolate()); |
469 | 9 | MarkPopErrorOnReturn mark_pop_error_on_return; |
470 | | |
471 | 9 | const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); |
472 | 9 | if (cipher == nullptr) |
473 | 0 | return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env()); |
474 | | |
475 | 9 | const int expected_iv_len = EVP_CIPHER_iv_length(cipher); |
476 | 9 | const bool is_authenticated_mode = IsSupportedAuthenticatedMode(cipher); |
477 | 9 | const bool has_iv = iv_buf.size() > 0; |
478 | | |
479 | | // Throw if no IV was passed and the cipher requires an IV |
480 | 9 | if (!has_iv && expected_iv_len != 0) |
481 | 0 | return THROW_ERR_CRYPTO_INVALID_IV(env()); |
482 | | |
483 | | // Throw if an IV was passed which does not match the cipher's fixed IV length |
484 | | // static_cast<int> for the iv_buf.size() is safe because we've verified |
485 | | // prior that the value is not larger than INT_MAX. |
486 | 9 | if (!is_authenticated_mode && |
487 | 9 | has_iv && |
488 | 9 | static_cast<int>(iv_buf.size()) != expected_iv_len) { |
489 | 0 | return THROW_ERR_CRYPTO_INVALID_IV(env()); |
490 | 0 | } |
491 | | |
492 | 9 | if (EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305) { |
493 | 0 | CHECK(has_iv); |
494 | | // Check for invalid IV lengths, since OpenSSL does not under some |
495 | | // conditions: |
496 | | // https://www.openssl.org/news/secadv/20190306.txt. |
497 | 0 | if (iv_buf.size() > 12) |
498 | 0 | return THROW_ERR_CRYPTO_INVALID_IV(env()); |
499 | 0 | } |
500 | | |
501 | 9 | CommonInit( |
502 | 9 | cipher_type, |
503 | 9 | cipher, |
504 | 9 | key_buf.data<unsigned char>(), |
505 | 9 | key_buf.size(), |
506 | 9 | iv_buf.data(), |
507 | 9 | iv_buf.size(), |
508 | 9 | auth_tag_len); |
509 | 9 | } |
510 | | |
511 | 9 | void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) { |
512 | 9 | CipherBase* cipher; |
513 | 9 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
514 | 9 | Environment* env = cipher->env(); |
515 | | |
516 | 9 | CHECK_GE(args.Length(), 4); |
517 | | |
518 | 9 | const Utf8Value cipher_type(env->isolate(), args[0]); |
519 | | |
520 | | // The argument can either be a KeyObjectHandle or a byte source |
521 | | // (e.g. ArrayBuffer, TypedArray, etc). Whichever it is, grab the |
522 | | // raw bytes and proceed... |
523 | 9 | const ByteSource key_buf = ByteSource::FromSecretKeyBytes(env, args[1]); |
524 | | |
525 | 9 | if (UNLIKELY(key_buf.size() > INT_MAX)) |
526 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "key is too big"); |
527 | | |
528 | 9 | ArrayBufferOrViewContents<unsigned char> iv_buf( |
529 | 9 | !args[2]->IsNull() ? args[2] : Local<Value>()); |
530 | | |
531 | 9 | if (UNLIKELY(!iv_buf.CheckSizeInt32())) |
532 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "iv is too big"); |
533 | | |
534 | | // Don't assign to cipher->auth_tag_len_ directly; the value might not |
535 | | // represent a valid length at this point. |
536 | 9 | unsigned int auth_tag_len; |
537 | 9 | if (args[3]->IsUint32()) { |
538 | 4 | auth_tag_len = args[3].As<Uint32>()->Value(); |
539 | 5 | } else { |
540 | 5 | CHECK(args[3]->IsInt32() && args[3].As<Int32>()->Value() == -1); |
541 | 5 | auth_tag_len = kNoAuthTagLength; |
542 | 5 | } |
543 | | |
544 | 9 | cipher->InitIv(*cipher_type, key_buf, iv_buf, auth_tag_len); |
545 | 9 | } |
546 | | |
547 | | bool CipherBase::InitAuthenticated( |
548 | | const char* cipher_type, |
549 | | int iv_len, |
550 | 4 | unsigned int auth_tag_len) { |
551 | 4 | CHECK(IsAuthenticatedMode()); |
552 | 4 | MarkPopErrorOnReturn mark_pop_error_on_return; |
553 | | |
554 | 4 | if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), |
555 | 4 | EVP_CTRL_AEAD_SET_IVLEN, |
556 | 4 | iv_len, |
557 | 4 | nullptr)) { |
558 | 0 | THROW_ERR_CRYPTO_INVALID_IV(env()); |
559 | 0 | return false; |
560 | 0 | } |
561 | | |
562 | 4 | const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); |
563 | 4 | if (mode == EVP_CIPH_GCM_MODE) { |
564 | 0 | if (auth_tag_len != kNoAuthTagLength) { |
565 | 0 | if (!IsValidGCMTagLength(auth_tag_len)) { |
566 | 0 | THROW_ERR_CRYPTO_INVALID_AUTH_TAG( |
567 | 0 | env(), |
568 | 0 | "Invalid authentication tag length: %u", |
569 | 0 | auth_tag_len); |
570 | 0 | return false; |
571 | 0 | } |
572 | | |
573 | | // Remember the given authentication tag length for later. |
574 | 0 | auth_tag_len_ = auth_tag_len; |
575 | 0 | } |
576 | 4 | } else { |
577 | 4 | if (auth_tag_len == kNoAuthTagLength) { |
578 | | // We treat ChaCha20-Poly1305 specially. Like GCM, the authentication tag |
579 | | // length defaults to 16 bytes when encrypting. Unlike GCM, the |
580 | | // authentication tag length also defaults to 16 bytes when decrypting, |
581 | | // whereas GCM would accept any valid authentication tag length. |
582 | 0 | if (EVP_CIPHER_CTX_nid(ctx_.get()) == NID_chacha20_poly1305) { |
583 | 0 | auth_tag_len = 16; |
584 | 0 | } else { |
585 | 0 | THROW_ERR_CRYPTO_INVALID_AUTH_TAG( |
586 | 0 | env(), "authTagLength required for %s", cipher_type); |
587 | 0 | return false; |
588 | 0 | } |
589 | 0 | } |
590 | | |
591 | | // TODO(tniessen) Support CCM decryption in FIPS mode |
592 | | |
593 | 4 | #if OPENSSL_VERSION_MAJOR >= 3 |
594 | 4 | if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && |
595 | 4 | EVP_default_properties_is_fips_enabled(nullptr)) { |
596 | | #else |
597 | | if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && FIPS_mode()) { |
598 | | #endif |
599 | 0 | THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env(), |
600 | 0 | "CCM encryption not supported in FIPS mode"); |
601 | 0 | return false; |
602 | 0 | } |
603 | | |
604 | | // Tell OpenSSL about the desired length. |
605 | 4 | if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_TAG, auth_tag_len, |
606 | 4 | nullptr)) { |
607 | 0 | THROW_ERR_CRYPTO_INVALID_AUTH_TAG( |
608 | 0 | env(), "Invalid authentication tag length: %u", auth_tag_len); |
609 | 0 | return false; |
610 | 0 | } |
611 | | |
612 | | // Remember the given authentication tag length for later. |
613 | 4 | auth_tag_len_ = auth_tag_len; |
614 | | |
615 | 4 | if (mode == EVP_CIPH_CCM_MODE) { |
616 | | // Restrict the message length to min(INT_MAX, 2^(8*(15-iv_len))-1) bytes. |
617 | 4 | CHECK(iv_len >= 7 && iv_len <= 13); |
618 | 4 | max_message_size_ = INT_MAX; |
619 | 4 | if (iv_len == 12) max_message_size_ = 16777215; |
620 | 4 | if (iv_len == 13) max_message_size_ = 65535; |
621 | 4 | } |
622 | 4 | } |
623 | | |
624 | 4 | return true; |
625 | 4 | } |
626 | | |
627 | 8 | bool CipherBase::CheckCCMMessageLength(int message_len) { |
628 | 8 | CHECK(ctx_); |
629 | 8 | CHECK(EVP_CIPHER_CTX_mode(ctx_.get()) == EVP_CIPH_CCM_MODE); |
630 | | |
631 | 8 | if (message_len > max_message_size_) { |
632 | 0 | THROW_ERR_CRYPTO_INVALID_MESSAGELEN(env()); |
633 | 0 | return false; |
634 | 0 | } |
635 | | |
636 | 8 | return true; |
637 | 8 | } |
638 | | |
639 | 24 | bool CipherBase::IsAuthenticatedMode() const { |
640 | | // Check if this cipher operates in an AEAD mode that we support. |
641 | 24 | CHECK(ctx_); |
642 | 24 | return IsSupportedAuthenticatedMode(ctx_.get()); |
643 | 24 | } |
644 | | |
645 | 2 | void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) { |
646 | 2 | Environment* env = Environment::GetCurrent(args); |
647 | 2 | CipherBase* cipher; |
648 | 2 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
649 | | |
650 | | // Only callable after Final and if encrypting. |
651 | 2 | if (cipher->ctx_ || |
652 | 2 | cipher->kind_ != kCipher || |
653 | 2 | cipher->auth_tag_len_ == kNoAuthTagLength) { |
654 | 0 | return; |
655 | 0 | } |
656 | | |
657 | 2 | args.GetReturnValue().Set( |
658 | 2 | Buffer::Copy(env, cipher->auth_tag_, cipher->auth_tag_len_) |
659 | 2 | .FromMaybe(Local<Value>())); |
660 | 2 | } |
661 | | |
662 | 2 | void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) { |
663 | 2 | CipherBase* cipher; |
664 | 2 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
665 | 2 | Environment* env = Environment::GetCurrent(args); |
666 | | |
667 | 2 | if (!cipher->ctx_ || |
668 | 2 | !cipher->IsAuthenticatedMode() || |
669 | 2 | cipher->kind_ != kDecipher || |
670 | 2 | cipher->auth_tag_state_ != kAuthTagUnknown) { |
671 | 0 | return args.GetReturnValue().Set(false); |
672 | 0 | } |
673 | | |
674 | 2 | ArrayBufferOrViewContents<char> auth_tag(args[0]); |
675 | 2 | if (UNLIKELY(!auth_tag.CheckSizeInt32())) |
676 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "buffer is too big"); |
677 | | |
678 | 2 | unsigned int tag_len = auth_tag.size(); |
679 | | |
680 | 2 | const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get()); |
681 | 2 | bool is_valid; |
682 | 2 | if (mode == EVP_CIPH_GCM_MODE) { |
683 | | // Restrict GCM tag lengths according to NIST 800-38d, page 9. |
684 | 0 | is_valid = (cipher->auth_tag_len_ == kNoAuthTagLength || |
685 | 0 | cipher->auth_tag_len_ == tag_len) && |
686 | 0 | IsValidGCMTagLength(tag_len); |
687 | 2 | } else { |
688 | | // At this point, the tag length is already known and must match the |
689 | | // length of the given authentication tag. |
690 | 2 | CHECK(IsSupportedAuthenticatedMode(cipher->ctx_.get())); |
691 | 2 | CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength); |
692 | 2 | is_valid = cipher->auth_tag_len_ == tag_len; |
693 | 2 | } |
694 | | |
695 | 2 | if (!is_valid) { |
696 | 0 | return THROW_ERR_CRYPTO_INVALID_AUTH_TAG( |
697 | 0 | env, "Invalid authentication tag length: %u", tag_len); |
698 | 0 | } |
699 | | |
700 | 2 | cipher->auth_tag_len_ = tag_len; |
701 | 2 | cipher->auth_tag_state_ = kAuthTagKnown; |
702 | 2 | CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_)); |
703 | | |
704 | 2 | memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_)); |
705 | 2 | auth_tag.CopyTo(cipher->auth_tag_, cipher->auth_tag_len_); |
706 | | |
707 | 2 | args.GetReturnValue().Set(true); |
708 | 2 | } |
709 | | |
710 | 6 | bool CipherBase::MaybePassAuthTagToOpenSSL() { |
711 | 6 | if (auth_tag_state_ == kAuthTagKnown) { |
712 | 2 | if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), |
713 | 2 | EVP_CTRL_AEAD_SET_TAG, |
714 | 2 | auth_tag_len_, |
715 | 2 | reinterpret_cast<unsigned char*>(auth_tag_))) { |
716 | 0 | return false; |
717 | 0 | } |
718 | 2 | auth_tag_state_ = kAuthTagPassedToOpenSSL; |
719 | 2 | } |
720 | 6 | return true; |
721 | 6 | } |
722 | | |
723 | | bool CipherBase::SetAAD( |
724 | | const ArrayBufferOrViewContents<unsigned char>& data, |
725 | 4 | int plaintext_len) { |
726 | 4 | if (!ctx_ || !IsAuthenticatedMode()) |
727 | 0 | return false; |
728 | 4 | MarkPopErrorOnReturn mark_pop_error_on_return; |
729 | | |
730 | 4 | int outlen; |
731 | 4 | const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); |
732 | | |
733 | | // When in CCM mode, we need to set the authentication tag and the plaintext |
734 | | // length in advance. |
735 | 4 | if (mode == EVP_CIPH_CCM_MODE) { |
736 | 4 | if (plaintext_len < 0) { |
737 | 0 | THROW_ERR_MISSING_ARGS(env(), |
738 | 0 | "options.plaintextLength required for CCM mode with AAD"); |
739 | 0 | return false; |
740 | 0 | } |
741 | | |
742 | 4 | if (!CheckCCMMessageLength(plaintext_len)) |
743 | 0 | return false; |
744 | | |
745 | 4 | if (kind_ == kDecipher) { |
746 | 2 | if (!MaybePassAuthTagToOpenSSL()) |
747 | 0 | return false; |
748 | 2 | } |
749 | | |
750 | | // Specify the plaintext length. |
751 | 4 | if (!EVP_CipherUpdate(ctx_.get(), nullptr, &outlen, nullptr, plaintext_len)) |
752 | 0 | return false; |
753 | 4 | } |
754 | | |
755 | 4 | return 1 == EVP_CipherUpdate(ctx_.get(), |
756 | 4 | nullptr, |
757 | 4 | &outlen, |
758 | 4 | data.data(), |
759 | 4 | data.size()); |
760 | 4 | } |
761 | | |
762 | 4 | void CipherBase::SetAAD(const FunctionCallbackInfo<Value>& args) { |
763 | 4 | CipherBase* cipher; |
764 | 4 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
765 | 4 | Environment* env = Environment::GetCurrent(args); |
766 | | |
767 | 4 | CHECK_EQ(args.Length(), 2); |
768 | 4 | CHECK(args[1]->IsInt32()); |
769 | 4 | int plaintext_len = args[1].As<Int32>()->Value(); |
770 | 4 | ArrayBufferOrViewContents<unsigned char> buf(args[0]); |
771 | | |
772 | 4 | if (UNLIKELY(!buf.CheckSizeInt32())) |
773 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "buffer is too big"); |
774 | 4 | args.GetReturnValue().Set(cipher->SetAAD(buf, plaintext_len)); |
775 | 4 | } |
776 | | |
777 | | CipherBase::UpdateResult CipherBase::Update( |
778 | | const char* data, |
779 | | size_t len, |
780 | 7 | std::unique_ptr<BackingStore>* out) { |
781 | 7 | if (!ctx_ || len > INT_MAX) |
782 | 0 | return kErrorState; |
783 | 7 | MarkPopErrorOnReturn mark_pop_error_on_return; |
784 | | |
785 | 7 | const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); |
786 | | |
787 | 7 | if (mode == EVP_CIPH_CCM_MODE && !CheckCCMMessageLength(len)) |
788 | 0 | return kErrorMessageSize; |
789 | | |
790 | | // Pass the authentication tag to OpenSSL if possible. This will only happen |
791 | | // once, usually on the first update. |
792 | 7 | if (kind_ == kDecipher && IsAuthenticatedMode()) |
793 | 2 | CHECK(MaybePassAuthTagToOpenSSL()); |
794 | | |
795 | 7 | const int block_size = EVP_CIPHER_CTX_block_size(ctx_.get()); |
796 | 7 | CHECK_GT(block_size, 0); |
797 | 7 | if (len + block_size > INT_MAX) return kErrorState; |
798 | 7 | int buf_len = len + block_size; |
799 | | |
800 | | // For key wrapping algorithms, get output size by calling |
801 | | // EVP_CipherUpdate() with null output. |
802 | 7 | if (kind_ == kCipher && mode == EVP_CIPH_WRAP_MODE && |
803 | 7 | EVP_CipherUpdate(ctx_.get(), |
804 | 0 | nullptr, |
805 | 0 | &buf_len, |
806 | 0 | reinterpret_cast<const unsigned char*>(data), |
807 | 0 | len) != 1) { |
808 | 0 | return kErrorState; |
809 | 0 | } |
810 | | |
811 | 7 | { |
812 | 7 | NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); |
813 | 7 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); |
814 | 7 | } |
815 | | |
816 | 7 | int r = EVP_CipherUpdate(ctx_.get(), |
817 | 7 | static_cast<unsigned char*>((*out)->Data()), |
818 | 7 | &buf_len, |
819 | 7 | reinterpret_cast<const unsigned char*>(data), |
820 | 7 | len); |
821 | | |
822 | 7 | CHECK_LE(static_cast<size_t>(buf_len), (*out)->ByteLength()); |
823 | 7 | if (buf_len == 0) { |
824 | 0 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), 0); |
825 | 7 | } else if (static_cast<size_t>(buf_len) != (*out)->ByteLength()) { |
826 | 7 | std::unique_ptr<BackingStore> old_out = std::move(*out); |
827 | 7 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); |
828 | 7 | memcpy(static_cast<char*>((*out)->Data()), |
829 | 7 | static_cast<char*>(old_out->Data()), |
830 | 7 | buf_len); |
831 | 7 | } |
832 | | |
833 | | // When in CCM mode, EVP_CipherUpdate will fail if the authentication tag is |
834 | | // invalid. In that case, remember the error and throw in final(). |
835 | 7 | if (!r && kind_ == kDecipher && mode == EVP_CIPH_CCM_MODE) { |
836 | 0 | pending_auth_failed_ = true; |
837 | 0 | return kSuccess; |
838 | 0 | } |
839 | 7 | return r == 1 ? kSuccess : kErrorState; |
840 | 7 | } |
841 | | |
842 | 7 | void CipherBase::Update(const FunctionCallbackInfo<Value>& args) { |
843 | 7 | Decode<CipherBase>(args, [](CipherBase* cipher, |
844 | 7 | const FunctionCallbackInfo<Value>& args, |
845 | 7 | const char* data, size_t size) { |
846 | 7 | std::unique_ptr<BackingStore> out; |
847 | 7 | Environment* env = Environment::GetCurrent(args); |
848 | | |
849 | 7 | if (UNLIKELY(size > INT_MAX)) |
850 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); |
851 | | |
852 | 7 | UpdateResult r = cipher->Update(data, size, &out); |
853 | | |
854 | 7 | if (r != kSuccess) { |
855 | 0 | if (r == kErrorState) { |
856 | 0 | ThrowCryptoError(env, ERR_get_error(), |
857 | 0 | "Trying to add data in unsupported state"); |
858 | 0 | } |
859 | 0 | return; |
860 | 0 | } |
861 | | |
862 | 7 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(out)); |
863 | 7 | args.GetReturnValue().Set( |
864 | 7 | Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>())); |
865 | 7 | }); |
866 | 7 | } |
867 | | |
868 | 0 | bool CipherBase::SetAutoPadding(bool auto_padding) { |
869 | 0 | if (!ctx_) |
870 | 0 | return false; |
871 | 0 | MarkPopErrorOnReturn mark_pop_error_on_return; |
872 | 0 | return EVP_CIPHER_CTX_set_padding(ctx_.get(), auto_padding); |
873 | 0 | } |
874 | | |
875 | 0 | void CipherBase::SetAutoPadding(const FunctionCallbackInfo<Value>& args) { |
876 | 0 | CipherBase* cipher; |
877 | 0 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
878 | | |
879 | 0 | bool b = cipher->SetAutoPadding(args.Length() < 1 || args[0]->IsTrue()); |
880 | 0 | args.GetReturnValue().Set(b); // Possibly report invalid state failure |
881 | 0 | } |
882 | | |
883 | 7 | bool CipherBase::Final(std::unique_ptr<BackingStore>* out) { |
884 | 7 | if (!ctx_) |
885 | 0 | return false; |
886 | | |
887 | 7 | const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); |
888 | | |
889 | 7 | { |
890 | 7 | NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); |
891 | 7 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), |
892 | 7 | static_cast<size_t>(EVP_CIPHER_CTX_block_size(ctx_.get()))); |
893 | 7 | } |
894 | | |
895 | 7 | if (kind_ == kDecipher && IsSupportedAuthenticatedMode(ctx_.get())) |
896 | 2 | MaybePassAuthTagToOpenSSL(); |
897 | | |
898 | | // OpenSSL v1.x doesn't verify the presence of the auth tag so do |
899 | | // it ourselves, see https://github.com/nodejs/node/issues/45874. |
900 | 7 | if (OPENSSL_VERSION_NUMBER < 0x30000000L && kind_ == kDecipher && |
901 | 7 | NID_chacha20_poly1305 == EVP_CIPHER_CTX_nid(ctx_.get()) && |
902 | 7 | auth_tag_state_ != kAuthTagPassedToOpenSSL) { |
903 | 0 | return false; |
904 | 0 | } |
905 | | |
906 | | // In CCM mode, final() only checks whether authentication failed in update(). |
907 | | // EVP_CipherFinal_ex must not be called and will fail. |
908 | 7 | bool ok; |
909 | 7 | if (kind_ == kDecipher && mode == EVP_CIPH_CCM_MODE) { |
910 | 2 | ok = !pending_auth_failed_; |
911 | 2 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), 0); |
912 | 5 | } else { |
913 | 5 | int out_len = (*out)->ByteLength(); |
914 | 5 | ok = EVP_CipherFinal_ex(ctx_.get(), |
915 | 5 | static_cast<unsigned char*>((*out)->Data()), |
916 | 5 | &out_len) == 1; |
917 | | |
918 | 5 | CHECK_LE(static_cast<size_t>(out_len), (*out)->ByteLength()); |
919 | 5 | if (out_len == 0) { |
920 | 3 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), 0); |
921 | 3 | } else if (static_cast<size_t>(out_len) != (*out)->ByteLength()) { |
922 | 2 | std::unique_ptr<BackingStore> old_out = std::move(*out); |
923 | 2 | *out = ArrayBuffer::NewBackingStore(env()->isolate(), out_len); |
924 | 2 | memcpy(static_cast<char*>((*out)->Data()), |
925 | 2 | static_cast<char*>(old_out->Data()), |
926 | 2 | out_len); |
927 | 2 | } |
928 | | |
929 | 5 | if (ok && kind_ == kCipher && IsAuthenticatedMode()) { |
930 | | // In GCM mode, the authentication tag length can be specified in advance, |
931 | | // but defaults to 16 bytes when encrypting. In CCM and OCB mode, it must |
932 | | // always be given by the user. |
933 | 2 | if (auth_tag_len_ == kNoAuthTagLength) { |
934 | 0 | CHECK(mode == EVP_CIPH_GCM_MODE); |
935 | 0 | auth_tag_len_ = sizeof(auth_tag_); |
936 | 0 | } |
937 | 2 | ok = (1 == EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_GET_TAG, |
938 | 2 | auth_tag_len_, |
939 | 2 | reinterpret_cast<unsigned char*>(auth_tag_))); |
940 | 2 | } |
941 | 5 | } |
942 | | |
943 | 7 | ctx_.reset(); |
944 | | |
945 | 7 | return ok; |
946 | 7 | } |
947 | | |
948 | 7 | void CipherBase::Final(const FunctionCallbackInfo<Value>& args) { |
949 | 7 | Environment* env = Environment::GetCurrent(args); |
950 | | |
951 | 7 | CipherBase* cipher; |
952 | 7 | ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); |
953 | 7 | if (cipher->ctx_ == nullptr) |
954 | 0 | return THROW_ERR_CRYPTO_INVALID_STATE(env); |
955 | | |
956 | 7 | std::unique_ptr<BackingStore> out; |
957 | | |
958 | | // Check IsAuthenticatedMode() first, Final() destroys the EVP_CIPHER_CTX. |
959 | 7 | const bool is_auth_mode = cipher->IsAuthenticatedMode(); |
960 | 7 | bool r = cipher->Final(&out); |
961 | | |
962 | 7 | if (!r) { |
963 | 1 | const char* msg = is_auth_mode |
964 | 1 | ? "Unsupported state or unable to authenticate data" |
965 | 1 | : "Unsupported state"; |
966 | | |
967 | 1 | return ThrowCryptoError(env, ERR_get_error(), msg); |
968 | 1 | } |
969 | | |
970 | 6 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(out)); |
971 | 6 | args.GetReturnValue().Set( |
972 | 6 | Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>())); |
973 | 6 | } |
974 | | |
975 | | template <PublicKeyCipher::Operation operation, |
976 | | PublicKeyCipher::EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init, |
977 | | PublicKeyCipher::EVP_PKEY_cipher_t EVP_PKEY_cipher> |
978 | | bool PublicKeyCipher::Cipher( |
979 | | Environment* env, |
980 | | const ManagedEVPPKey& pkey, |
981 | | int padding, |
982 | | const EVP_MD* digest, |
983 | | const ArrayBufferOrViewContents<unsigned char>& oaep_label, |
984 | | const ArrayBufferOrViewContents<unsigned char>& data, |
985 | 0 | std::unique_ptr<BackingStore>* out) { |
986 | 0 | EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr)); |
987 | 0 | if (!ctx) |
988 | 0 | return false; |
989 | 0 | if (EVP_PKEY_cipher_init(ctx.get()) <= 0) |
990 | 0 | return false; |
991 | 0 | if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) |
992 | 0 | return false; |
993 | | |
994 | 0 | if (digest != nullptr) { |
995 | 0 | if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), digest) <= 0) |
996 | 0 | return false; |
997 | 0 | } |
998 | | |
999 | 0 | if (!SetRsaOaepLabel(ctx, oaep_label.ToByteSource())) return false; |
1000 | | |
1001 | 0 | size_t out_len = 0; |
1002 | 0 | if (EVP_PKEY_cipher( |
1003 | 0 | ctx.get(), |
1004 | 0 | nullptr, |
1005 | 0 | &out_len, |
1006 | 0 | data.data(), |
1007 | 0 | data.size()) <= 0) { |
1008 | 0 | return false; |
1009 | 0 | } |
1010 | | |
1011 | 0 | { |
1012 | 0 | NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); |
1013 | 0 | *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); |
1014 | 0 | } |
1015 | |
|
1016 | 0 | if (EVP_PKEY_cipher( |
1017 | 0 | ctx.get(), |
1018 | 0 | static_cast<unsigned char*>((*out)->Data()), |
1019 | 0 | &out_len, |
1020 | 0 | data.data(), |
1021 | 0 | data.size()) <= 0) { |
1022 | 0 | return false; |
1023 | 0 | } |
1024 | | |
1025 | 0 | CHECK_LE(out_len, (*out)->ByteLength()); |
1026 | 0 | if (out_len == 0) { |
1027 | 0 | *out = ArrayBuffer::NewBackingStore(env->isolate(), 0); |
1028 | 0 | } else if (out_len != (*out)->ByteLength()) { |
1029 | 0 | std::unique_ptr<BackingStore> old_out = std::move(*out); |
1030 | 0 | *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); |
1031 | 0 | memcpy(static_cast<char*>((*out)->Data()), |
1032 | 0 | static_cast<char*>(old_out->Data()), |
1033 | 0 | out_len); |
1034 | 0 | } |
1035 | |
|
1036 | 0 | return true; |
1037 | 0 | } Unexecuted instantiation: bool node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)0, &EVP_PKEY_encrypt_init, &EVP_PKEY_encrypt>(node::Environment*, node::crypto::ManagedEVPPKey const&, int, evp_md_st const*, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, std::__1::unique_ptr<v8::BackingStore, std::__1::default_delete<v8::BackingStore> >*) Unexecuted instantiation: bool node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)1, &EVP_PKEY_decrypt_init, &EVP_PKEY_decrypt>(node::Environment*, node::crypto::ManagedEVPPKey const&, int, evp_md_st const*, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, std::__1::unique_ptr<v8::BackingStore, std::__1::default_delete<v8::BackingStore> >*) Unexecuted instantiation: bool node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)1, &EVP_PKEY_sign_init, &EVP_PKEY_sign>(node::Environment*, node::crypto::ManagedEVPPKey const&, int, evp_md_st const*, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, std::__1::unique_ptr<v8::BackingStore, std::__1::default_delete<v8::BackingStore> >*) Unexecuted instantiation: bool node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)0, &EVP_PKEY_verify_recover_init, &EVP_PKEY_verify_recover>(node::Environment*, node::crypto::ManagedEVPPKey const&, int, evp_md_st const*, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, node::crypto::ArrayBufferOrViewContents<unsigned char> const&, std::__1::unique_ptr<v8::BackingStore, std::__1::default_delete<v8::BackingStore> >*) |
1038 | | |
1039 | | template <PublicKeyCipher::Operation operation, |
1040 | | PublicKeyCipher::EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init, |
1041 | | PublicKeyCipher::EVP_PKEY_cipher_t EVP_PKEY_cipher> |
1042 | 0 | void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) { |
1043 | 0 | MarkPopErrorOnReturn mark_pop_error_on_return; |
1044 | 0 | Environment* env = Environment::GetCurrent(args); |
1045 | |
|
1046 | 0 | unsigned int offset = 0; |
1047 | 0 | ManagedEVPPKey pkey = |
1048 | 0 | ManagedEVPPKey::GetPublicOrPrivateKeyFromJs(args, &offset); |
1049 | 0 | if (!pkey) |
1050 | 0 | return; |
1051 | | |
1052 | 0 | ArrayBufferOrViewContents<unsigned char> buf(args[offset]); |
1053 | 0 | if (UNLIKELY(!buf.CheckSizeInt32())) |
1054 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "buffer is too long"); |
1055 | | |
1056 | 0 | uint32_t padding; |
1057 | 0 | if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return; |
1058 | | |
1059 | 0 | if (EVP_PKEY_cipher == EVP_PKEY_decrypt && |
1060 | 0 | operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING) { |
1061 | 0 | EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr)); |
1062 | 0 | CHECK(ctx); |
1063 | | |
1064 | 0 | if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) { |
1065 | 0 | return ThrowCryptoError(env, ERR_get_error()); |
1066 | 0 | } |
1067 | | |
1068 | 0 | int rsa_pkcs1_implicit_rejection = |
1069 | 0 | EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1"); |
1070 | | // From the doc -2 means that the option is not supported. |
1071 | | // The default for the option is enabled and if it has been |
1072 | | // specifically disabled we want to respect that so we will |
1073 | | // not throw an error if the option is supported regardless |
1074 | | // of how it is set. The call to set the value |
1075 | | // will not affect what is used since a different context is |
1076 | | // used in the call if the option is supported |
1077 | 0 | if (rsa_pkcs1_implicit_rejection <= 0) { |
1078 | 0 | return THROW_ERR_INVALID_ARG_VALUE( |
1079 | 0 | env, |
1080 | 0 | "RSA_PKCS1_PADDING is no longer supported for private decryption," |
1081 | 0 | " this can be reverted with --security-revert=CVE-2024-PEND"); |
1082 | 0 | } |
1083 | 0 | } |
1084 | | |
1085 | 0 | const EVP_MD* digest = nullptr; |
1086 | 0 | if (args[offset + 2]->IsString()) { |
1087 | 0 | const Utf8Value oaep_str(env->isolate(), args[offset + 2]); |
1088 | 0 | digest = EVP_get_digestbyname(*oaep_str); |
1089 | 0 | if (digest == nullptr) |
1090 | 0 | return THROW_ERR_OSSL_EVP_INVALID_DIGEST(env); |
1091 | 0 | } |
1092 | | |
1093 | 0 | ArrayBufferOrViewContents<unsigned char> oaep_label( |
1094 | 0 | !args[offset + 3]->IsUndefined() ? args[offset + 3] : Local<Value>()); |
1095 | 0 | if (UNLIKELY(!oaep_label.CheckSizeInt32())) |
1096 | 0 | return THROW_ERR_OUT_OF_RANGE(env, "oaepLabel is too big"); |
1097 | | |
1098 | 0 | std::unique_ptr<BackingStore> out; |
1099 | 0 | if (!Cipher<operation, EVP_PKEY_cipher_init, EVP_PKEY_cipher>( |
1100 | 0 | env, pkey, padding, digest, oaep_label, buf, &out)) { |
1101 | 0 | return ThrowCryptoError(env, ERR_get_error()); |
1102 | 0 | } |
1103 | | |
1104 | 0 | Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(out)); |
1105 | 0 | args.GetReturnValue().Set( |
1106 | 0 | Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>())); |
1107 | 0 | } Unexecuted instantiation: void node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)0, &EVP_PKEY_encrypt_init, &EVP_PKEY_encrypt>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: void node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)1, &EVP_PKEY_decrypt_init, &EVP_PKEY_decrypt>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: void node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)1, &EVP_PKEY_sign_init, &EVP_PKEY_sign>(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: void node::crypto::PublicKeyCipher::Cipher<(node::crypto::PublicKeyCipher::Operation)0, &EVP_PKEY_verify_recover_init, &EVP_PKEY_verify_recover>(v8::FunctionCallbackInfo<v8::Value> const&) |
1108 | | |
1109 | | } // namespace crypto |
1110 | | } // namespace node |