1
2 /*
3 * Copyright (C) Dmitry Volyntsev
4 * Copyright (C) NGINX, Inc.
5 */
6
7
8 #include <njs_main.h>
9 #include "njs_openssl.h"
10
11 typedef enum {
12 NJS_KEY_FORMAT_RAW = 1 << 1,
13 NJS_KEY_FORMAT_PKCS8 = 1 << 2,
14 NJS_KEY_FORMAT_SPKI = 1 << 3,
15 NJS_KEY_FORMAT_JWK = 1 << 4,
16 NJS_KEY_FORMAT_UNKNOWN = 1 << 5,
17 } njs_webcrypto_key_format_t;
18
19
20 typedef enum {
21 NJS_KEY_USAGE_DECRYPT = 1 << 1,
22 NJS_KEY_USAGE_DERIVE_BITS = 1 << 2,
23 NJS_KEY_USAGE_DERIVE_KEY = 1 << 3,
24 NJS_KEY_USAGE_ENCRYPT = 1 << 4,
25 NJS_KEY_USAGE_GENERATE_KEY = 1 << 5,
26 NJS_KEY_USAGE_SIGN = 1 << 6,
27 NJS_KEY_USAGE_VERIFY = 1 << 7,
28 NJS_KEY_USAGE_WRAP_KEY = 1 << 8,
29 NJS_KEY_USAGE_UNSUPPORTED = 1 << 9,
30 NJS_KEY_USAGE_UNWRAP_KEY = 1 << 10,
31 } njs_webcrypto_key_usage_t;
32
33
34 typedef enum {
35 NJS_ALGORITHM_RSA_OAEP,
36 NJS_ALGORITHM_AES_GCM,
37 NJS_ALGORITHM_AES_CTR,
38 NJS_ALGORITHM_AES_CBC,
39 NJS_ALGORITHM_RSASSA_PKCS1_v1_5,
40 NJS_ALGORITHM_RSA_PSS,
41 NJS_ALGORITHM_ECDSA,
42 NJS_ALGORITHM_ECDH,
43 NJS_ALGORITHM_PBKDF2,
44 NJS_ALGORITHM_HKDF,
45 NJS_ALGORITHM_HMAC,
46 } njs_webcrypto_alg_t;
47
48
49 typedef enum {
50 NJS_HASH_SHA1,
51 NJS_HASH_SHA256,
52 NJS_HASH_SHA384,
53 NJS_HASH_SHA512,
54 } njs_webcrypto_hash_t;
55
56
57 typedef enum {
58 NJS_CURVE_P256,
59 NJS_CURVE_P384,
60 NJS_CURVE_P521,
61 } njs_webcrypto_curve_t;
62
63
64 typedef struct {
65 njs_str_t name;
66 uintptr_t value;
67 } njs_webcrypto_entry_t;
68
69
70 typedef struct {
71 njs_webcrypto_alg_t type;
72 unsigned usage;
73 unsigned fmt;
74 } njs_webcrypto_algorithm_t;
75
76
77 typedef struct {
78 njs_webcrypto_algorithm_t *alg;
79 unsigned usage;
80 njs_webcrypto_hash_t hash;
81 njs_webcrypto_curve_t curve;
82
83 EVP_PKEY *pkey;
84 njs_str_t raw;
85 } njs_webcrypto_key_t;
86
87
88 typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX *ctx);
89 typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX *ctx, unsigned char *out,
90 size_t *outlen, const unsigned char *in, size_t inlen);
91
92
93 static njs_int_t njs_ext_cipher(njs_vm_t *vm, njs_value_t *args,
94 njs_uint_t nargs, njs_index_t unused);
95 static njs_int_t njs_cipher_pkey(njs_vm_t *vm, njs_str_t *data,
96 njs_webcrypto_key_t *key, njs_index_t encrypt);
97 static njs_int_t njs_cipher_aes_gcm(njs_vm_t *vm, njs_str_t *data,
98 njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt);
99 static njs_int_t njs_cipher_aes_ctr(njs_vm_t *vm, njs_str_t *data,
100 njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt);
101 static njs_int_t njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data,
102 njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt);
103 static njs_int_t njs_ext_derive(njs_vm_t *vm, njs_value_t *args,
104 njs_uint_t nargs, njs_index_t derive_key);
105 static njs_int_t njs_ext_digest(njs_vm_t *vm, njs_value_t *args,
106 njs_uint_t nargs, njs_index_t unused);
107 static njs_int_t njs_ext_export_key(njs_vm_t *vm, njs_value_t *args,
108 njs_uint_t nargs, njs_index_t unused);
109 static njs_int_t njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args,
110 njs_uint_t nargs, njs_index_t unused);
111 static njs_int_t njs_ext_import_key(njs_vm_t *vm, njs_value_t *args,
112 njs_uint_t nargs, njs_index_t unused);
113 static njs_int_t njs_ext_sign(njs_vm_t *vm, njs_value_t *args,
114 njs_uint_t nargs, njs_index_t verify);
115 static njs_int_t njs_ext_unwrap_key(njs_vm_t *vm, njs_value_t *args,
116 njs_uint_t nargs, njs_index_t unused);
117 static njs_int_t njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args,
118 njs_uint_t nargs, njs_index_t unused);
119 static njs_int_t njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args,
120 njs_uint_t nargs, njs_index_t unused);
121
122 static void njs_webcrypto_cleanup_pkey(void *data);
123 static njs_webcrypto_key_format_t njs_key_format(njs_vm_t *vm,
124 njs_value_t *value, njs_str_t *format);
125 static njs_int_t njs_key_usage(njs_vm_t *vm, njs_value_t *value,
126 unsigned *mask);
127 static njs_webcrypto_algorithm_t *njs_key_algorithm(njs_vm_t *vm,
128 njs_value_t *value);
129 static njs_str_t *njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm);
130 static njs_int_t njs_algorithm_hash(njs_vm_t *vm, njs_value_t *value,
131 njs_webcrypto_hash_t *hash);
132 static const EVP_MD *njs_algorithm_hash_digest(njs_webcrypto_hash_t hash);
133 static njs_int_t njs_algorithm_curve(njs_vm_t *vm, njs_value_t *value,
134 njs_webcrypto_curve_t *curve);
135
136 static njs_int_t njs_webcrypto_result(njs_vm_t *vm, njs_value_t *result,
137 njs_int_t rc);
138 static void njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...);
139
140 static njs_int_t njs_webcrypto_init(njs_vm_t *vm);
141
142 static njs_webcrypto_entry_t njs_webcrypto_alg[] = {
143
144 #define njs_webcrypto_algorithm(type, usage_mask, fmt_mask) \
145 (uintptr_t) & (njs_webcrypto_algorithm_t) { type, usage_mask, fmt_mask }
146
147 {
148 njs_str("RSA-OAEP"),
149 njs_webcrypto_algorithm(NJS_ALGORITHM_RSA_OAEP,
150 NJS_KEY_USAGE_ENCRYPT |
151 NJS_KEY_USAGE_DECRYPT |
152 NJS_KEY_USAGE_WRAP_KEY |
153 NJS_KEY_USAGE_UNWRAP_KEY |
154 NJS_KEY_USAGE_GENERATE_KEY,
155 NJS_KEY_FORMAT_PKCS8 |
156 NJS_KEY_FORMAT_SPKI)
157 },
158
159 {
160 njs_str("AES-GCM"),
161 njs_webcrypto_algorithm(NJS_ALGORITHM_AES_GCM,
162 NJS_KEY_USAGE_ENCRYPT |
163 NJS_KEY_USAGE_DECRYPT |
164 NJS_KEY_USAGE_WRAP_KEY |
165 NJS_KEY_USAGE_UNWRAP_KEY |
166 NJS_KEY_USAGE_GENERATE_KEY,
167 NJS_KEY_FORMAT_RAW)
168 },
169
170 {
171 njs_str("AES-CTR"),
172 njs_webcrypto_algorithm(NJS_ALGORITHM_AES_CTR,
173 NJS_KEY_USAGE_ENCRYPT |
174 NJS_KEY_USAGE_DECRYPT |
175 NJS_KEY_USAGE_WRAP_KEY |
176 NJS_KEY_USAGE_UNWRAP_KEY |
177 NJS_KEY_USAGE_GENERATE_KEY,
178 NJS_KEY_FORMAT_RAW)
179 },
180
181 {
182 njs_str("AES-CBC"),
183 njs_webcrypto_algorithm(NJS_ALGORITHM_AES_CBC,
184 NJS_KEY_USAGE_ENCRYPT |
185 NJS_KEY_USAGE_DECRYPT |
186 NJS_KEY_USAGE_WRAP_KEY |
187 NJS_KEY_USAGE_UNWRAP_KEY |
188 NJS_KEY_USAGE_GENERATE_KEY,
189 NJS_KEY_FORMAT_RAW)
190 },
191
192 {
193 njs_str("RSASSA-PKCS1-v1_5"),
194 njs_webcrypto_algorithm(NJS_ALGORITHM_RSASSA_PKCS1_v1_5,
195 NJS_KEY_USAGE_SIGN |
196 NJS_KEY_USAGE_VERIFY |
197 NJS_KEY_USAGE_GENERATE_KEY,
198 NJS_KEY_FORMAT_PKCS8 |
199 NJS_KEY_FORMAT_SPKI)
200 },
201
202 {
203 njs_str("RSA-PSS"),
204 njs_webcrypto_algorithm(NJS_ALGORITHM_RSA_PSS,
205 NJS_KEY_USAGE_SIGN |
206 NJS_KEY_USAGE_VERIFY |
207 NJS_KEY_USAGE_GENERATE_KEY,
208 NJS_KEY_FORMAT_PKCS8 |
209 NJS_KEY_FORMAT_SPKI)
210 },
211
212 {
213 njs_str("ECDSA"),
214 njs_webcrypto_algorithm(NJS_ALGORITHM_ECDSA,
215 NJS_KEY_USAGE_SIGN |
216 NJS_KEY_USAGE_VERIFY |
217 NJS_KEY_USAGE_GENERATE_KEY,
218 NJS_KEY_FORMAT_PKCS8 |
219 NJS_KEY_FORMAT_SPKI)
220 },
221
222 {
223 njs_str("ECDH"),
224 njs_webcrypto_algorithm(NJS_ALGORITHM_ECDH,
225 NJS_KEY_USAGE_DERIVE_KEY |
226 NJS_KEY_USAGE_DERIVE_BITS |
227 NJS_KEY_USAGE_GENERATE_KEY |
228 NJS_KEY_USAGE_UNSUPPORTED,
229 NJS_KEY_FORMAT_UNKNOWN)
230 },
231
232 {
233 njs_str("PBKDF2"),
234 njs_webcrypto_algorithm(NJS_ALGORITHM_PBKDF2,
235 NJS_KEY_USAGE_DERIVE_KEY |
236 NJS_KEY_USAGE_DERIVE_BITS,
237 NJS_KEY_FORMAT_RAW)
238 },
239
240 {
241 njs_str("HKDF"),
242 njs_webcrypto_algorithm(NJS_ALGORITHM_HKDF,
243 NJS_KEY_USAGE_DERIVE_KEY |
244 NJS_KEY_USAGE_DERIVE_BITS,
245 NJS_KEY_FORMAT_RAW)
246 },
247
248 {
249 njs_str("HMAC"),
250 njs_webcrypto_algorithm(NJS_ALGORITHM_HMAC,
251 NJS_KEY_USAGE_GENERATE_KEY |
252 NJS_KEY_USAGE_SIGN |
253 NJS_KEY_USAGE_VERIFY,
254 NJS_KEY_FORMAT_RAW)
255 },
256
257 {
258 njs_null_str,
259 0
260 }
261 };
262
263
264 static njs_webcrypto_entry_t njs_webcrypto_hash[] = {
265 { njs_str("SHA-256"), NJS_HASH_SHA256 },
266 { njs_str("SHA-384"), NJS_HASH_SHA384 },
267 { njs_str("SHA-512"), NJS_HASH_SHA512 },
268 { njs_str("SHA-1"), NJS_HASH_SHA1 },
269 { njs_null_str, 0 }
270 };
271
272
273 static njs_webcrypto_entry_t njs_webcrypto_curve[] = {
274 { njs_str("P-256"), NJS_CURVE_P256 },
275 { njs_str("P-384"), NJS_CURVE_P384 },
276 { njs_str("P-521"), NJS_CURVE_P521 },
277 { njs_null_str, 0 }
278 };
279
280
281 static njs_webcrypto_entry_t njs_webcrypto_usage[] = {
282 { njs_str("decrypt"), NJS_KEY_USAGE_DECRYPT },
283 { njs_str("deriveBits"), NJS_KEY_USAGE_DERIVE_BITS },
284 { njs_str("deriveKey"), NJS_KEY_USAGE_DERIVE_KEY },
285 { njs_str("encrypt"), NJS_KEY_USAGE_ENCRYPT },
286 { njs_str("sign"), NJS_KEY_USAGE_SIGN },
287 { njs_str("unwrapKey"), NJS_KEY_USAGE_UNWRAP_KEY },
288 { njs_str("verify"), NJS_KEY_USAGE_VERIFY },
289 { njs_str("wrapKey"), NJS_KEY_USAGE_WRAP_KEY },
290 { njs_null_str, 0 }
291 };
292
293
294 static njs_external_t njs_ext_webcrypto_crypto_key[] = {
295
296 {
297 .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
298 .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
299 .u.property = {
300 .value = "CryptoKey",
301 }
302 },
303 };
304
305
306 static njs_external_t njs_ext_subtle_webcrypto[] = {
307
308 {
309 .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
310 .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
311 .u.property = {
312 .value = "SubtleCrypto",
313 }
314 },
315
316 {
317 .flags = NJS_EXTERN_METHOD,
318 .name.string = njs_str("decrypt"),
319 .writable = 1,
320 .configurable = 1,
321 .enumerable = 1,
322 .u.method = {
323 .native = njs_ext_cipher,
324 .magic8 = 0,
325 }
326 },
327
328 {
329 .flags = NJS_EXTERN_METHOD,
330 .name.string = njs_str("deriveBits"),
331 .writable = 1,
332 .configurable = 1,
333 .enumerable = 1,
334 .u.method = {
335 .native = njs_ext_derive,
336 .magic8 = 0,
337 }
338 },
339
340 {
341 .flags = NJS_EXTERN_METHOD,
342 .name.string = njs_str("deriveKey"),
343 .writable = 1,
344 .configurable = 1,
345 .enumerable = 1,
346 .u.method = {
347 .native = njs_ext_derive,
348 .magic8 = 1,
349 }
350 },
351
352 {
353 .flags = NJS_EXTERN_METHOD,
354 .name.string = njs_str("digest"),
355 .writable = 1,
356 .configurable = 1,
357 .enumerable = 1,
358 .u.method = {
359 .native = njs_ext_digest,
360 }
361 },
362
363 {
364 .flags = NJS_EXTERN_METHOD,
365 .name.string = njs_str("encrypt"),
366 .writable = 1,
367 .configurable = 1,
368 .enumerable = 1,
369 .u.method = {
370 .native = njs_ext_cipher,
371 .magic8 = 1,
372 }
373 },
374
375 {
376 .flags = NJS_EXTERN_METHOD,
377 .name.string = njs_str("exportKey"),
378 .writable = 1,
379 .configurable = 1,
380 .enumerable = 1,
381 .u.method = {
382 .native = njs_ext_export_key,
383 }
384 },
385
386 {
387 .flags = NJS_EXTERN_METHOD,
388 .name.string = njs_str("generateKey"),
389 .writable = 1,
390 .configurable = 1,
391 .enumerable = 1,
392 .u.method = {
393 .native = njs_ext_generate_key,
394 }
395 },
396
397 {
398 .flags = NJS_EXTERN_METHOD,
399 .name.string = njs_str("importKey"),
400 .writable = 1,
401 .configurable = 1,
402 .enumerable = 1,
403 .u.method = {
404 .native = njs_ext_import_key,
405 }
406 },
407
408 {
409 .flags = NJS_EXTERN_METHOD,
410 .name.string = njs_str("sign"),
411 .writable = 1,
412 .configurable = 1,
413 .enumerable = 1,
414 .u.method = {
415 .native = njs_ext_sign,
416 }
417 },
418
419 {
420 .flags = NJS_EXTERN_METHOD,
421 .name.string = njs_str("unwrapKey"),
422 .writable = 1,
423 .configurable = 1,
424 .enumerable = 1,
425 .u.method = {
426 .native = njs_ext_unwrap_key,
427 }
428 },
429
430 {
431 .flags = NJS_EXTERN_METHOD,
432 .name.string = njs_str("verify"),
433 .writable = 1,
434 .configurable = 1,
435 .enumerable = 1,
436 .u.method = {
437 .native = njs_ext_sign,
438 .magic8 = 1,
439 }
440 },
441
442 {
443 .flags = NJS_EXTERN_METHOD,
444 .name.string = njs_str("wrapKey"),
445 .writable = 1,
446 .configurable = 1,
447 .enumerable = 1,
448 .u.method = {
449 .native = njs_ext_wrap_key,
450 }
451 },
452
453 };
454
455 static njs_external_t njs_ext_webcrypto[] = {
456
457 {
458 .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
459 .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
460 .u.property = {
461 .value = "Crypto",
462 }
463 },
464
465 {
466 .flags = NJS_EXTERN_METHOD,
467 .name.string = njs_str("getRandomValues"),
468 .writable = 1,
469 .configurable = 1,
470 .enumerable = 1,
471 .u.method = {
472 .native = njs_ext_get_random_values,
473 }
474 },
475
476 {
477 .flags = NJS_EXTERN_OBJECT,
478 .name.string = njs_str("subtle"),
479 .enumerable = 1,
480 .writable = 1,
481 .u.object = {
482 .enumerable = 1,
483 .properties = njs_ext_subtle_webcrypto,
484 .nproperties = njs_nitems(njs_ext_subtle_webcrypto),
485 }
486 },
487
488 };
489
490
491 njs_module_t njs_webcrypto_module = {
492 .name = njs_str("webcrypto"),
493 .init = njs_webcrypto_init,
494 };
495
496
497 static njs_int_t njs_webcrypto_crypto_key_proto_id;
498
499
500 static njs_int_t
njs_ext_cipher(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t encrypt)501 njs_ext_cipher(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
502 njs_index_t encrypt)
503 {
504 unsigned mask;
505 njs_int_t ret;
506 njs_str_t data;
507 njs_value_t *options;
508 njs_webcrypto_key_t *key;
509 njs_webcrypto_algorithm_t *alg;
510
511 options = njs_arg(args, nargs, 1);
512 alg = njs_key_algorithm(vm, options);
513 if (njs_slow_path(alg == NULL)) {
514 goto fail;
515 }
516
517 key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
518 njs_arg(args, nargs, 2));
519 if (njs_slow_path(key == NULL)) {
520 njs_type_error(vm, "\"key\" is not a CryptoKey object");
521 goto fail;
522 }
523
524 mask = encrypt ? NJS_KEY_USAGE_ENCRYPT : NJS_KEY_USAGE_DECRYPT;
525 if (njs_slow_path(!(key->usage & mask))) {
526 njs_type_error(vm, "provide key does not support %s operation",
527 encrypt ? "encrypt" : "decrypt");
528 goto fail;
529 }
530
531 if (njs_slow_path(key->alg != alg)) {
532 njs_type_error(vm, "cannot %s using \"%V\" with \"%V\" key",
533 encrypt ? "encrypt" : "decrypt",
534 njs_algorithm_string(key->alg),
535 njs_algorithm_string(alg));
536 goto fail;
537 }
538
539 ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 3));
540 if (njs_slow_path(ret != NJS_OK)) {
541 goto fail;
542 }
543
544 switch (alg->type) {
545 case NJS_ALGORITHM_RSA_OAEP:
546 ret = njs_cipher_pkey(vm, &data, key, encrypt);
547 break;
548
549 case NJS_ALGORITHM_AES_GCM:
550 ret = njs_cipher_aes_gcm(vm, &data, key, options, encrypt);
551 break;
552
553 case NJS_ALGORITHM_AES_CTR:
554 ret = njs_cipher_aes_ctr(vm, &data, key, options, encrypt);
555 break;
556
557 case NJS_ALGORITHM_AES_CBC:
558 default:
559 ret = njs_cipher_aes_cbc(vm, &data, key, options, encrypt);
560 }
561
562 return njs_webcrypto_result(vm, njs_vm_retval(vm), ret);
563
564 fail:
565
566 return njs_webcrypto_result(vm, njs_vm_retval(vm), NJS_ERROR);
567 }
568
569
570 static njs_int_t
njs_cipher_pkey(njs_vm_t * vm,njs_str_t * data,njs_webcrypto_key_t * key,njs_index_t encrypt)571 njs_cipher_pkey(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
572 njs_index_t encrypt)
573 {
574 u_char *dst;
575 size_t outlen;
576 njs_int_t ret;
577 const EVP_MD *md;
578 EVP_PKEY_CTX *ctx;
579 EVP_PKEY_cipher_t cipher;
580 EVP_PKEY_cipher_init_t init;
581
582 ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
583 if (njs_slow_path(ctx == NULL)) {
584 njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed");
585 return NJS_ERROR;
586 }
587
588 if (encrypt) {
589 init = EVP_PKEY_encrypt_init;
590 cipher = EVP_PKEY_encrypt;
591
592 } else {
593 init = EVP_PKEY_decrypt_init;
594 cipher = EVP_PKEY_decrypt;
595 }
596
597 ret = init(ctx);
598 if (njs_slow_path(ret <= 0)) {
599 njs_webcrypto_error(vm, "EVP_PKEY_%scrypt_init() failed",
600 encrypt ? "en" : "de");
601 ret = NJS_ERROR;
602 goto fail;
603 }
604
605 md = njs_algorithm_hash_digest(key->hash);
606
607 EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
608 EVP_PKEY_CTX_set_signature_md(ctx, md);
609 EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md);
610
611 ret = cipher(ctx, NULL, &outlen, data->start, data->length);
612 if (njs_slow_path(ret <= 0)) {
613 njs_webcrypto_error(vm, "EVP_PKEY_%scrypt() failed",
614 encrypt ? "en" : "de");
615 ret = NJS_ERROR;
616 goto fail;
617 }
618
619 dst = njs_mp_alloc(njs_vm_memory_pool(vm), outlen);
620 if (njs_slow_path(dst == NULL)) {
621 njs_memory_error(vm);
622 ret = NJS_ERROR;
623 goto fail;
624 }
625
626 ret = cipher(ctx, dst, &outlen, data->start, data->length);
627 if (njs_slow_path(ret <= 0)) {
628 njs_webcrypto_error(vm, "EVP_PKEY_%scrypt() failed",
629 encrypt ? "en" : "de");
630 ret = NJS_ERROR;
631 goto fail;
632 }
633
634 ret = njs_vm_value_array_buffer_set(vm, njs_vm_retval(vm), dst, outlen);
635
636 fail:
637
638 EVP_PKEY_CTX_free(ctx);
639
640 return ret;
641 }
642
643
644 static njs_int_t
njs_cipher_aes_gcm(njs_vm_t * vm,njs_str_t * data,njs_webcrypto_key_t * key,njs_value_t * options,njs_bool_t encrypt)645 njs_cipher_aes_gcm(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
646 njs_value_t *options, njs_bool_t encrypt)
647 {
648 int len, outlen, dstlen;
649 u_char *dst, *p;
650 int64_t taglen;
651 njs_str_t iv, aad;
652 njs_int_t ret;
653 njs_value_t value;
654 EVP_CIPHER_CTX *ctx;
655 const EVP_CIPHER *cipher;
656
657 static const njs_value_t string_iv = njs_string("iv");
658 static const njs_value_t string_ad = njs_string("additionalData");
659 static const njs_value_t string_tl = njs_string("tagLength");
660
661 switch (key->raw.length) {
662 case 16:
663 cipher = EVP_aes_128_gcm();
664 break;
665
666 case 32:
667 cipher = EVP_aes_256_gcm();
668 break;
669
670 default:
671 njs_type_error(vm, "AES-GCM Invalid key length");
672 return NJS_ERROR;
673 }
674
675 ret = njs_value_property(vm, options, njs_value_arg(&string_iv), &value);
676 if (njs_slow_path(ret != NJS_OK)) {
677 if (ret == NJS_DECLINED) {
678 njs_type_error(vm, "AES-GCM algorithm.iv is not provided");
679 }
680
681 return NJS_ERROR;
682 }
683
684 ret = njs_vm_value_to_bytes(vm, &iv, &value);
685 if (njs_slow_path(ret != NJS_OK)) {
686 return NJS_ERROR;
687 }
688
689 taglen = 128;
690
691 ret = njs_value_property(vm, options, njs_value_arg(&string_tl), &value);
692 if (njs_slow_path(ret == NJS_ERROR)) {
693 return NJS_ERROR;
694 }
695
696 if (njs_is_defined(&value)) {
697 ret = njs_value_to_integer(vm, &value, &taglen);
698 if (njs_slow_path(ret != NJS_OK)) {
699 return NJS_ERROR;
700 }
701 }
702
703 if (njs_slow_path(taglen != 32
704 && taglen != 64
705 && taglen != 96
706 && taglen != 104
707 && taglen != 112
708 && taglen != 120
709 && taglen != 128))
710 {
711 njs_type_error(vm, "AES-GCM Invalid tagLength");
712 return NJS_ERROR;
713 }
714
715 taglen /= 8;
716
717 if (njs_slow_path(!encrypt && (data->length < (size_t) taglen))) {
718 njs_type_error(vm, "AES-GCM data is too short");
719 return NJS_ERROR;
720 }
721
722 ctx = EVP_CIPHER_CTX_new();
723 if (njs_slow_path(ctx == NULL)) {
724 njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
725 return NJS_ERROR;
726 }
727
728 ret = EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt);
729 if (njs_slow_path(ret <= 0)) {
730 njs_webcrypto_error(vm, "EVP_%sInit_ex() failed",
731 encrypt ? "Encrypt" : "Decrypt");
732 ret = NJS_ERROR;
733 goto fail;
734 }
735
736 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.length, NULL);
737 if (njs_slow_path(ret <= 0)) {
738 njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed");
739 ret = NJS_ERROR;
740 goto fail;
741 }
742
743 ret = EVP_CipherInit_ex(ctx, NULL, NULL, key->raw.start, iv.start,
744 encrypt);
745 if (njs_slow_path(ret <= 0)) {
746 njs_webcrypto_error(vm, "EVP_%sInit_ex() failed",
747 encrypt ? "Encrypt" : "Decrypt");
748 ret = NJS_ERROR;
749 goto fail;
750 }
751
752 if (!encrypt) {
753 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, taglen,
754 &data->start[data->length - taglen]);
755 if (njs_slow_path(ret <= 0)) {
756 njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed");
757 ret = NJS_ERROR;
758 goto fail;
759 }
760 }
761
762 ret = njs_value_property(vm, options, njs_value_arg(&string_ad), &value);
763 if (njs_slow_path(ret == NJS_ERROR)) {
764 return NJS_ERROR;
765 }
766
767 aad.length = 0;
768
769 if (njs_is_defined(&value)) {
770 ret = njs_vm_value_to_bytes(vm, &aad, &value);
771 if (njs_slow_path(ret != NJS_OK)) {
772 return NJS_ERROR;
773 }
774 }
775
776 if (aad.length != 0) {
777 ret = EVP_CipherUpdate(ctx, NULL, &outlen, aad.start, aad.length);
778 if (njs_slow_path(ret <= 0)) {
779 njs_webcrypto_error(vm, "EVP_%sUpdate() failed",
780 encrypt ? "Encrypt" : "Decrypt");
781 ret = NJS_ERROR;
782 goto fail;
783 }
784 }
785
786 dstlen = data->length + EVP_CIPHER_CTX_block_size(ctx) + taglen;
787 dst = njs_mp_alloc(njs_vm_memory_pool(vm), dstlen);
788 if (njs_slow_path(dst == NULL)) {
789 njs_memory_error(vm);
790 return NJS_ERROR;
791 }
792
793 ret = EVP_CipherUpdate(ctx, dst, &outlen, data->start,
794 data->length - (encrypt ? 0 : taglen));
795 if (njs_slow_path(ret <= 0)) {
796 njs_webcrypto_error(vm, "EVP_%sUpdate() failed",
797 encrypt ? "Encrypt" : "Decrypt");
798 ret = NJS_ERROR;
799 goto fail;
800 }
801
802 p = &dst[outlen];
803 len = EVP_CIPHER_CTX_block_size(ctx);
804
805 ret = EVP_CipherFinal_ex(ctx, p, &len);
806 if (njs_slow_path(ret <= 0)) {
807 njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed",
808 encrypt ? "Encrypt" : "Decrypt");
809 ret = NJS_ERROR;
810 goto fail;
811 }
812
813 outlen += len;
814 p += len;
815
816 if (encrypt) {
817 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, taglen, p);
818 if (njs_slow_path(ret <= 0)) {
819 njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed");
820 ret = NJS_ERROR;
821 goto fail;
822 }
823
824 outlen += taglen;
825 }
826
827 ret = njs_vm_value_array_buffer_set(vm, njs_vm_retval(vm), dst, outlen);
828
829 fail:
830
831 EVP_CIPHER_CTX_free(ctx);
832
833 return ret;
834 }
835
836
837 static njs_int_t
njs_cipher_aes_ctr128(njs_vm_t * vm,const EVP_CIPHER * cipher,u_char * key,u_char * data,size_t dlen,u_char * counter,u_char * dst,int * olen,njs_bool_t encrypt)838 njs_cipher_aes_ctr128(njs_vm_t *vm, const EVP_CIPHER *cipher, u_char *key,
839 u_char *data, size_t dlen, u_char *counter, u_char *dst, int *olen,
840 njs_bool_t encrypt)
841 {
842 int len, outlen;
843 njs_int_t ret;
844 EVP_CIPHER_CTX *ctx;
845
846 ctx = EVP_CIPHER_CTX_new();
847 if (njs_slow_path(ctx == NULL)) {
848 njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
849 return NJS_ERROR;
850 }
851
852 ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, counter, encrypt);
853 if (njs_slow_path(ret <= 0)) {
854 njs_webcrypto_error(vm, "EVP_%sInit_ex() failed",
855 encrypt ? "Encrypt" : "Decrypt");
856 ret = NJS_ERROR;
857 goto fail;
858 }
859
860 ret = EVP_CipherUpdate(ctx, dst, &outlen, data, dlen);
861 if (njs_slow_path(ret <= 0)) {
862 njs_webcrypto_error(vm, "EVP_%sUpdate() failed",
863 encrypt ? "Encrypt" : "Decrypt");
864 ret = NJS_ERROR;
865 goto fail;
866 }
867
868 ret = EVP_CipherFinal_ex(ctx, &dst[outlen], &len);
869 if (njs_slow_path(ret <= 0)) {
870 njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed",
871 encrypt ? "Encrypt" : "Decrypt");
872 ret = NJS_ERROR;
873 goto fail;
874 }
875
876 outlen += len;
877 *olen = outlen;
878
879 ret = NJS_OK;
880
881 fail:
882
883 EVP_CIPHER_CTX_free(ctx);
884
885 return ret;
886 }
887
888
889 njs_inline njs_uint_t
njs_ceil_div(njs_uint_t dend,njs_uint_t dsor)890 njs_ceil_div(njs_uint_t dend, njs_uint_t dsor)
891 {
892 return (dsor == 0) ? 0 : 1 + (dend - 1) / dsor;
893 }
894
895
896 njs_inline BIGNUM *
njs_bn_counter128(njs_str_t * ctr,njs_uint_t bits)897 njs_bn_counter128(njs_str_t *ctr, njs_uint_t bits)
898 {
899 njs_uint_t remainder, bytes;
900 uint8_t buf[16];
901
902 remainder = bits % 8;
903
904 if (remainder == 0) {
905 bytes = bits / 8;
906
907 return BN_bin2bn(&ctr->start[ctr->length - bytes], bytes, NULL);
908 }
909
910 bytes = njs_ceil_div(bits, 8);
911
912 memcpy(buf, &ctr->start[ctr->length - bytes], bytes);
913
914 buf[0] &= ~(0xFF << remainder);
915
916 return BN_bin2bn(buf, bytes, NULL);
917 }
918
919
920 njs_inline void
njs_counter128_reset(u_char * src,u_char * dst,njs_uint_t bits)921 njs_counter128_reset(u_char *src, u_char *dst, njs_uint_t bits)
922 {
923 size_t index;
924 njs_uint_t remainder, bytes;
925
926 bytes = bits / 8;
927 remainder = bits % 8;
928
929 memcpy(dst, src, 16);
930
931 index = 16 - bytes;
932
933 memset(&dst[index], 0, bytes);
934
935 if (remainder) {
936 dst[index - 1] &= 0xff << remainder;
937 }
938 }
939
940
941 static njs_int_t
njs_cipher_aes_ctr(njs_vm_t * vm,njs_str_t * data,njs_webcrypto_key_t * key,njs_value_t * options,njs_bool_t encrypt)942 njs_cipher_aes_ctr(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
943 njs_value_t *options, njs_bool_t encrypt)
944 {
945 int len, len2;
946 u_char *dst;
947 int64_t length;
948 BIGNUM *total, *blocks, *left, *ctr;
949 njs_int_t ret;
950 njs_str_t iv;
951 njs_uint_t size1;
952 njs_value_t value;
953 const EVP_CIPHER *cipher;
954 u_char iv2[16];
955
956 static const njs_value_t string_counter = njs_string("counter");
957 static const njs_value_t string_length = njs_string("length");
958
959 switch (key->raw.length) {
960 case 16:
961 cipher = EVP_aes_128_ctr();
962 break;
963
964 case 32:
965 cipher = EVP_aes_256_ctr();
966 break;
967
968 default:
969 njs_type_error(vm, "AES-CTR Invalid key length");
970 return NJS_ERROR;
971 }
972
973 ret = njs_value_property(vm, options, njs_value_arg(&string_counter),
974 &value);
975 if (njs_slow_path(ret != NJS_OK)) {
976 if (ret == NJS_DECLINED) {
977 njs_type_error(vm, "AES-CTR algorithm.counter is not provided");
978 }
979
980 return NJS_ERROR;
981 }
982
983 ret = njs_vm_value_to_bytes(vm, &iv, &value);
984 if (njs_slow_path(ret != NJS_OK)) {
985 return NJS_ERROR;
986 }
987
988 if (njs_slow_path(iv.length != 16)) {
989 njs_type_error(vm, "AES-CTR algorithm.counter must be 16 bytes long");
990 return NJS_ERROR;
991 }
992
993 ret = njs_value_property(vm, options, njs_value_arg(&string_length),
994 &value);
995 if (njs_slow_path(ret != NJS_OK)) {
996 if (ret == NJS_DECLINED) {
997 njs_type_error(vm, "AES-CTR algorithm.length is not provided");
998 }
999
1000 return NJS_ERROR;
1001 }
1002
1003 ret = njs_value_to_integer(vm, &value, &length);
1004 if (njs_slow_path(ret != NJS_OK)) {
1005 return NJS_ERROR;
1006 }
1007
1008 if (njs_slow_path(length == 0 || length > 128)) {
1009 njs_type_error(vm, "AES-CTR algorithm.length "
1010 "must be between 1 and 128");
1011 return NJS_ERROR;
1012 }
1013
1014 ctr = NULL;
1015 blocks = NULL;
1016 left = NULL;
1017
1018 total = BN_new();
1019 if (njs_slow_path(total == NULL)) {
1020 njs_webcrypto_error(vm, "BN_new() failed");
1021 return NJS_ERROR;
1022 }
1023
1024 ret = BN_lshift(total, BN_value_one(), length);
1025 if (njs_slow_path(ret != 1)) {
1026 njs_webcrypto_error(vm, "BN_lshift() failed");
1027 ret = NJS_ERROR;
1028 goto fail;
1029 }
1030
1031 ctr = njs_bn_counter128(&iv, length);
1032 if (njs_slow_path(ctr == NULL)) {
1033 njs_webcrypto_error(vm, "BN_bin2bn() failed");
1034 ret = NJS_ERROR;
1035 goto fail;
1036 }
1037
1038 blocks = BN_new();
1039 if (njs_slow_path(blocks == NULL)) {
1040 njs_webcrypto_error(vm, "BN_new() failed");
1041 return NJS_ERROR;
1042 }
1043
1044 ret = BN_set_word(blocks, njs_ceil_div(data->length, AES_BLOCK_SIZE));
1045 if (njs_slow_path(ret != 1)) {
1046 njs_webcrypto_error(vm, "BN_set_word() failed");
1047 ret = NJS_ERROR;
1048 goto fail;
1049 }
1050
1051 ret = BN_cmp(blocks, total);
1052 if (njs_slow_path(ret > 0)) {
1053 njs_type_error(vm, "AES-CTR repeated counter");
1054 ret = NJS_ERROR;
1055 goto fail;
1056 }
1057
1058 left = BN_new();
1059 if (njs_slow_path(left == NULL)) {
1060 njs_webcrypto_error(vm, "BN_new() failed");
1061 return NJS_ERROR;
1062 }
1063
1064 ret = BN_sub(left, total, ctr);
1065 if (njs_slow_path(ret != 1)) {
1066 njs_webcrypto_error(vm, "BN_sub() failed");
1067 ret = NJS_ERROR;
1068 goto fail;
1069 }
1070
1071 dst = njs_mp_alloc(njs_vm_memory_pool(vm),
1072 data->length + EVP_MAX_BLOCK_LENGTH);
1073 if (njs_slow_path(dst == NULL)) {
1074 njs_memory_error(vm);
1075 return NJS_ERROR;
1076 }
1077
1078 ret = BN_cmp(left, blocks);
1079 if (ret >= 0) {
1080
1081 /*
1082 * Doing a single run if a counter is not wrapped-around
1083 * during the ciphering.
1084 * */
1085
1086 ret = njs_cipher_aes_ctr128(vm, cipher, key->raw.start,
1087 data->start, data->length, iv.start, dst,
1088 &len, encrypt);
1089 if (njs_slow_path(ret != NJS_OK)) {
1090 goto fail;
1091 }
1092
1093 goto done;
1094 }
1095
1096 /*
1097 * Otherwise splitting ciphering into two parts:
1098 * Until the wrapping moment
1099 * After the resetting counter to zero.
1100 */
1101
1102 size1 = BN_get_word(left) * AES_BLOCK_SIZE;
1103
1104 ret = njs_cipher_aes_ctr128(vm, cipher, key->raw.start, data->start, size1,
1105 iv.start, dst, &len, encrypt);
1106 if (njs_slow_path(ret != NJS_OK)) {
1107 goto fail;
1108 }
1109
1110 njs_counter128_reset(iv.start, (u_char *) iv2, length);
1111
1112 ret = njs_cipher_aes_ctr128(vm, cipher, key->raw.start, &data->start[size1],
1113 data->length - size1, iv2, &dst[size1], &len2,
1114 encrypt);
1115 if (njs_slow_path(ret != NJS_OK)) {
1116 goto fail;
1117 }
1118
1119 len += len2;
1120
1121 done:
1122
1123 ret = njs_vm_value_array_buffer_set(vm, njs_vm_retval(vm), dst, len);
1124
1125 fail:
1126
1127 BN_free(total);
1128
1129 if (ctr != NULL) {
1130 BN_free(ctr);
1131 }
1132
1133 if (blocks != NULL) {
1134 BN_free(blocks);
1135 }
1136
1137 if (left != NULL) {
1138 BN_free(left);
1139 }
1140
1141 return ret;
1142 }
1143
1144
1145 static njs_int_t
njs_cipher_aes_cbc(njs_vm_t * vm,njs_str_t * data,njs_webcrypto_key_t * key,njs_value_t * options,njs_bool_t encrypt)1146 njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
1147 njs_value_t *options, njs_bool_t encrypt)
1148 {
1149 int olen_max, olen, olen2;
1150 u_char *dst;
1151 unsigned remainder;
1152 njs_str_t iv;
1153 njs_int_t ret;
1154 njs_value_t value;
1155 EVP_CIPHER_CTX *ctx;
1156 const EVP_CIPHER *cipher;
1157
1158 static const njs_value_t string_iv = njs_string("iv");
1159
1160 switch (key->raw.length) {
1161 case 16:
1162 cipher = EVP_aes_128_cbc();
1163 break;
1164
1165 case 32:
1166 cipher = EVP_aes_256_cbc();
1167 break;
1168
1169 default:
1170 njs_type_error(vm, "AES-CBC Invalid key length");
1171 return NJS_ERROR;
1172 }
1173
1174 ret = njs_value_property(vm, options, njs_value_arg(&string_iv), &value);
1175 if (njs_slow_path(ret != NJS_OK)) {
1176 if (ret == NJS_DECLINED) {
1177 njs_type_error(vm, "AES-CBC algorithm.iv is not provided");
1178 }
1179
1180 return NJS_ERROR;
1181 }
1182
1183 ret = njs_vm_value_to_bytes(vm, &iv, &value);
1184 if (njs_slow_path(ret != NJS_OK)) {
1185 return NJS_ERROR;
1186 }
1187
1188 if (njs_slow_path(iv.length != 16)) {
1189 njs_type_error(vm, "AES-CBC algorithm.iv must be 16 bytes long");
1190 return NJS_ERROR;
1191 }
1192
1193 olen_max = data->length + AES_BLOCK_SIZE - 1;
1194 remainder = olen_max % AES_BLOCK_SIZE;
1195
1196 if (remainder != 0) {
1197 olen_max += AES_BLOCK_SIZE - remainder;
1198 }
1199
1200 ctx = EVP_CIPHER_CTX_new();
1201 if (njs_slow_path(ctx == NULL)) {
1202 njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
1203 return NJS_ERROR;
1204 }
1205
1206 ret = EVP_CipherInit_ex(ctx, cipher, NULL, key->raw.start, iv.start,
1207 encrypt);
1208 if (njs_slow_path(ret <= 0)) {
1209 njs_webcrypto_error(vm, "EVP_%SInit_ex() failed",
1210 encrypt ? "Encrypt" : "Decrypt");
1211 ret = NJS_ERROR;
1212 goto fail;
1213 }
1214
1215 dst = njs_mp_alloc(njs_vm_memory_pool(vm), olen_max);
1216 if (njs_slow_path(dst == NULL)) {
1217 njs_memory_error(vm);
1218 ret = NJS_ERROR;
1219 goto fail;
1220 }
1221
1222 ret = EVP_CipherUpdate(ctx, dst, &olen, data->start, data->length);
1223 if (njs_slow_path(ret <= 0)) {
1224 njs_webcrypto_error(vm, "EVP_%SUpdate() failed",
1225 encrypt ? "Encrypt" : "Decrypt");
1226 ret = NJS_ERROR;
1227 goto fail;
1228 }
1229
1230 ret = EVP_CipherFinal_ex(ctx, &dst[olen], &olen2);
1231 if (njs_slow_path(ret <= 0)) {
1232 njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed",
1233 encrypt ? "Encrypt" : "Decrypt");
1234 ret = NJS_ERROR;
1235 goto fail;
1236 }
1237
1238 olen += olen2;
1239
1240 ret = njs_vm_value_array_buffer_set(vm, njs_vm_retval(vm), dst, olen);
1241
1242 fail:
1243
1244 EVP_CIPHER_CTX_free(ctx);
1245
1246 return ret;
1247 }
1248
1249
1250 static njs_int_t
njs_ext_derive(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t derive_key)1251 njs_ext_derive(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1252 njs_index_t derive_key)
1253 {
1254 u_char *k;
1255 size_t olen;
1256 int64_t iterations, length;
1257 EVP_PKEY *pkey;
1258 unsigned usage, mask;
1259 njs_int_t ret;
1260 njs_str_t salt, info;
1261 njs_value_t value, *aobject, *dobject;
1262 const EVP_MD *md;
1263 EVP_PKEY_CTX *pctx;
1264 njs_mp_cleanup_t *cln;
1265 njs_webcrypto_key_t *key, *dkey;
1266 njs_webcrypto_hash_t hash;
1267 njs_webcrypto_algorithm_t *alg, *dalg;
1268
1269 static const njs_value_t string_info = njs_string("info");
1270 static const njs_value_t string_salt = njs_string("salt");
1271 static const njs_value_t string_length = njs_string("length");
1272 static const njs_value_t string_iterations = njs_string("iterations");
1273
1274 aobject = njs_arg(args, nargs, 1);
1275 alg = njs_key_algorithm(vm, aobject);
1276 if (njs_slow_path(alg == NULL)) {
1277 goto fail;
1278 }
1279
1280 key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
1281 njs_arg(args, nargs, 2));
1282 if (njs_slow_path(key == NULL)) {
1283 njs_type_error(vm, "\"baseKey\" is not a CryptoKey object");
1284 goto fail;
1285 }
1286
1287 mask = derive_key ? NJS_KEY_USAGE_DERIVE_KEY : NJS_KEY_USAGE_DERIVE_BITS;
1288 if (njs_slow_path(!(key->usage & mask))) {
1289 njs_type_error(vm, "provide key does not support \"%s\" operation",
1290 derive_key ? "deriveKey" : "deriveBits");
1291 goto fail;
1292 }
1293
1294 if (njs_slow_path(key->alg != alg)) {
1295 njs_type_error(vm, "cannot derive %s using \"%V\" with \"%V\" key",
1296 derive_key ? "key" : "bits",
1297 njs_algorithm_string(key->alg),
1298 njs_algorithm_string(alg));
1299 goto fail;
1300 }
1301
1302 dobject = njs_arg(args, nargs, 3);
1303
1304 if (derive_key) {
1305 dalg = njs_key_algorithm(vm, dobject);
1306 if (njs_slow_path(dalg == NULL)) {
1307 goto fail;
1308 }
1309
1310 ret = njs_value_property(vm, dobject, njs_value_arg(&string_length),
1311 &value);
1312 if (njs_slow_path(ret != NJS_OK)) {
1313 if (ret == NJS_DECLINED) {
1314 njs_type_error(vm, "derivedKeyAlgorithm.length "
1315 "is not provided");
1316 goto fail;
1317 }
1318 }
1319
1320 } else {
1321 dalg = NULL;
1322 njs_value_assign(&value, dobject);
1323 }
1324
1325 ret = njs_value_to_integer(vm, &value, &length);
1326 if (njs_slow_path(ret != NJS_OK)) {
1327 goto fail;
1328 }
1329
1330 dkey = NULL;
1331 length /= 8;
1332
1333 if (derive_key) {
1334 switch (dalg->type) {
1335 case NJS_ALGORITHM_AES_GCM:
1336 case NJS_ALGORITHM_AES_CTR:
1337 case NJS_ALGORITHM_AES_CBC:
1338
1339 if (length != 16 && length != 32) {
1340 njs_type_error(vm, "deriveKey \"%V\" length must be 128 or 256",
1341 njs_algorithm_string(dalg));
1342 goto fail;
1343 }
1344
1345 break;
1346
1347 default:
1348 njs_internal_error(vm, "not implemented deriveKey: \"%V\"",
1349 njs_algorithm_string(dalg));
1350 goto fail;
1351 }
1352
1353 ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage);
1354 if (njs_slow_path(ret != NJS_OK)) {
1355 goto fail;
1356 }
1357
1358 if (njs_slow_path(usage & ~dalg->usage)) {
1359 njs_type_error(vm, "unsupported key usage for \"%V\" key",
1360 njs_algorithm_string(alg));
1361 goto fail;
1362 }
1363
1364 dkey = njs_mp_zalloc(njs_vm_memory_pool(vm),
1365 sizeof(njs_webcrypto_key_t));
1366 if (njs_slow_path(dkey == NULL)) {
1367 njs_memory_error(vm);
1368 goto fail;
1369 }
1370
1371 dkey->alg = dalg;
1372 dkey->usage = usage;
1373 }
1374
1375 k = njs_mp_zalloc(njs_vm_memory_pool(vm), length);
1376 if (njs_slow_path(k == NULL)) {
1377 njs_memory_error(vm);
1378 goto fail;
1379 }
1380
1381 switch (alg->type) {
1382 case NJS_ALGORITHM_PBKDF2:
1383 ret = njs_algorithm_hash(vm, aobject, &hash);
1384 if (njs_slow_path(ret == NJS_ERROR)) {
1385 goto fail;
1386 }
1387
1388 ret = njs_value_property(vm, aobject, njs_value_arg(&string_salt),
1389 &value);
1390 if (njs_slow_path(ret != NJS_OK)) {
1391 if (ret == NJS_DECLINED) {
1392 njs_type_error(vm, "PBKDF2 algorithm.salt is not provided");
1393 }
1394
1395 goto fail;
1396 }
1397
1398 ret = njs_vm_value_to_bytes(vm, &salt, &value);
1399 if (njs_slow_path(ret != NJS_OK)) {
1400 goto fail;
1401 }
1402
1403 if (njs_slow_path(salt.length < 16)) {
1404 njs_type_error(vm, "PBKDF2 algorithm.salt must be "
1405 "at least 16 bytes long");
1406 goto fail;
1407 }
1408
1409 ret = njs_value_property(vm, aobject, njs_value_arg(&string_iterations),
1410 &value);
1411 if (njs_slow_path(ret != NJS_OK)) {
1412 if (ret == NJS_DECLINED) {
1413 njs_type_error(vm, "PBKDF2 algorithm.iterations "
1414 "is not provided");
1415 }
1416
1417 goto fail;
1418 }
1419
1420 ret = njs_value_to_integer(vm, &value, &iterations);
1421 if (njs_slow_path(ret != NJS_OK)) {
1422 goto fail;
1423 }
1424
1425 md = njs_algorithm_hash_digest(hash);
1426
1427 ret = PKCS5_PBKDF2_HMAC((char *) key->raw.start, key->raw.length,
1428 salt.start, salt.length, iterations, md,
1429 length, k);
1430 if (njs_slow_path(ret <= 0)) {
1431 njs_webcrypto_error(vm, "PKCS5_PBKDF2_HMAC() failed");
1432 goto fail;
1433 }
1434 break;
1435
1436 case NJS_ALGORITHM_HKDF:
1437 #ifdef EVP_PKEY_HKDF
1438 ret = njs_algorithm_hash(vm, aobject, &hash);
1439 if (njs_slow_path(ret == NJS_ERROR)) {
1440 goto fail;
1441 }
1442
1443 ret = njs_value_property(vm, aobject, njs_value_arg(&string_salt),
1444 &value);
1445 if (njs_slow_path(ret != NJS_OK)) {
1446 if (ret == NJS_DECLINED) {
1447 njs_type_error(vm, "HKDF algorithm.salt is not provided");
1448 }
1449
1450 goto fail;
1451 }
1452
1453 ret = njs_vm_value_to_bytes(vm, &salt, &value);
1454 if (njs_slow_path(ret != NJS_OK)) {
1455 goto fail;
1456 }
1457
1458 ret = njs_value_property(vm, aobject, njs_value_arg(&string_info),
1459 &value);
1460 if (njs_slow_path(ret != NJS_OK)) {
1461 if (ret == NJS_DECLINED) {
1462 njs_type_error(vm, "HKDF algorithm.info is not provided");
1463 }
1464
1465 goto fail;
1466 }
1467
1468 ret = njs_vm_value_to_bytes(vm, &info, &value);
1469 if (njs_slow_path(ret != NJS_OK)) {
1470 goto fail;
1471 }
1472
1473 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
1474 if (njs_slow_path(pctx == NULL)) {
1475 njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed");
1476 goto fail;
1477 }
1478
1479 ret = EVP_PKEY_derive_init(pctx);
1480 if (njs_slow_path(ret <= 0)) {
1481 njs_webcrypto_error(vm, "EVP_PKEY_derive_init() failed");
1482 goto free;
1483 }
1484
1485 md = njs_algorithm_hash_digest(hash);
1486
1487 ret = EVP_PKEY_CTX_set_hkdf_md(pctx, md);
1488 if (njs_slow_path(ret <= 0)) {
1489 njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_hkdf_md() failed");
1490 goto free;
1491 }
1492
1493 ret = EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.start, salt.length);
1494 if (njs_slow_path(ret <= 0)) {
1495 njs_webcrypto_error(vm, "EVP_PKEY_CTX_set1_hkdf_salt() failed");
1496 goto free;
1497 }
1498
1499 ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, key->raw.start, key->raw.length);
1500 if (njs_slow_path(ret <= 0)) {
1501 njs_webcrypto_error(vm, "EVP_PKEY_CTX_set1_hkdf_key() failed");
1502 goto free;
1503 }
1504
1505 ret = EVP_PKEY_CTX_add1_hkdf_info(pctx, info.start, info.length);
1506 if (njs_slow_path(ret <= 0)) {
1507 njs_webcrypto_error(vm, "EVP_PKEY_CTX_add1_hkdf_info() failed");
1508 goto free;
1509 }
1510
1511 olen = (size_t) length;
1512 ret = EVP_PKEY_derive(pctx, k, &olen);
1513 if (njs_slow_path(ret <= 0 || olen != (size_t) length)) {
1514 njs_webcrypto_error(vm, "EVP_PKEY_derive() failed");
1515 goto free;
1516 }
1517
1518 free:
1519
1520 EVP_PKEY_CTX_free(pctx);
1521
1522 if (njs_slow_path(ret <= 0)) {
1523 goto fail;
1524 }
1525
1526 break;
1527 #else
1528 (void) pctx;
1529 (void) olen;
1530 (void) &string_info;
1531 (void) &info;
1532 #endif
1533
1534 case NJS_ALGORITHM_ECDH:
1535 default:
1536 njs_internal_error(vm, "not implemented deriveKey "
1537 "algorithm: \"%V\"", njs_algorithm_string(alg));
1538 goto fail;
1539 }
1540
1541 if (derive_key) {
1542 if (dalg->type == NJS_ALGORITHM_HMAC) {
1543 ret = njs_algorithm_hash(vm, dobject, &dkey->hash);
1544 if (njs_slow_path(ret == NJS_ERROR)) {
1545 goto fail;
1546 }
1547
1548 pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, k, length);
1549 if (njs_slow_path(pkey == NULL)) {
1550 njs_webcrypto_error(vm, "EVP_PKEY_new_mac_key() failed");
1551 goto fail;
1552 }
1553
1554 cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0);
1555 if (cln == NULL) {
1556 njs_memory_error(vm);
1557 goto fail;
1558 }
1559
1560 cln->handler = njs_webcrypto_cleanup_pkey;
1561 cln->data = key;
1562
1563 dkey->pkey = pkey;
1564
1565 } else {
1566 dkey->raw.start = k;
1567 dkey->raw.length = length;
1568 }
1569
1570 ret = njs_vm_external_create(vm, &value,
1571 njs_webcrypto_crypto_key_proto_id,
1572 dkey, 0);
1573 } else {
1574 ret = njs_vm_value_array_buffer_set(vm, &value, k, length);
1575 }
1576
1577 if (njs_slow_path(ret != NJS_OK)) {
1578 goto fail;
1579 }
1580
1581 return njs_webcrypto_result(vm, &value, NJS_OK);
1582
1583 fail:
1584
1585 return njs_webcrypto_result(vm, njs_vm_retval(vm), NJS_ERROR);
1586 }
1587
1588
1589 static njs_int_t
njs_ext_digest(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1590 njs_ext_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1591 njs_index_t unused)
1592 {
1593 unsigned olen;
1594 u_char *dst;
1595 njs_str_t data;
1596 njs_int_t ret;
1597 njs_value_t value;
1598 const EVP_MD *md;
1599 njs_webcrypto_hash_t hash;
1600
1601 ret = njs_algorithm_hash(vm, njs_arg(args, nargs, 1), &hash);
1602 if (njs_slow_path(ret == NJS_ERROR)) {
1603 goto fail;
1604 }
1605
1606 ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 2));
1607 if (njs_slow_path(ret != NJS_OK)) {
1608 goto fail;
1609 }
1610
1611 md = njs_algorithm_hash_digest(hash);
1612 olen = EVP_MD_size(md);
1613
1614 dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen);
1615 if (njs_slow_path(dst == NULL)) {
1616 njs_memory_error(vm);
1617 goto fail;
1618 }
1619
1620 ret = EVP_Digest(data.start, data.length, dst, &olen, md, NULL);
1621 if (njs_slow_path(ret <= 0)) {
1622 njs_webcrypto_error(vm, "EVP_Digest() failed");
1623 goto fail;
1624 }
1625
1626 ret = njs_vm_value_array_buffer_set(vm, &value, dst, olen);
1627 if (njs_slow_path(ret != NJS_OK)) {
1628 goto fail;
1629 }
1630
1631 return njs_webcrypto_result(vm, &value, NJS_OK);
1632
1633 fail:
1634
1635 return njs_webcrypto_result(vm, njs_vm_retval(vm), NJS_ERROR);
1636 }
1637
1638
1639 static njs_int_t
njs_ext_export_key(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1640 njs_ext_export_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1641 njs_index_t unused)
1642 {
1643 njs_internal_error(vm, "\"exportKey\" not implemented");
1644 return NJS_ERROR;
1645 }
1646
1647
1648 static njs_int_t
njs_ext_generate_key(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1649 njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1650 njs_index_t unused)
1651 {
1652 njs_internal_error(vm, "\"generateKey\" not implemented");
1653 return NJS_ERROR;
1654 }
1655
1656
1657 static njs_int_t
njs_ext_import_key(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1658 njs_ext_import_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1659 njs_index_t unused)
1660 {
1661 int nid;
1662 BIO *bio;
1663 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
1664 RSA *rsa;
1665 EC_KEY *ec;
1666 #else
1667 char gname[80];
1668 #endif
1669 unsigned usage;
1670 EVP_PKEY *pkey;
1671 njs_int_t ret;
1672 njs_str_t key_data, format;
1673 njs_value_t value, *options;
1674 const u_char *start;
1675 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
1676 const EC_GROUP *group;
1677 #endif
1678 njs_mp_cleanup_t *cln;
1679 njs_webcrypto_key_t *key;
1680 PKCS8_PRIV_KEY_INFO *pkcs8;
1681 njs_webcrypto_algorithm_t *alg;
1682 njs_webcrypto_key_format_t fmt;
1683
1684 static const int curves[] = {
1685 NID_X9_62_prime256v1,
1686 NID_secp384r1,
1687 NID_secp521r1,
1688 };
1689
1690 pkey = NULL;
1691
1692 fmt = njs_key_format(vm, njs_arg(args, nargs, 1), &format);
1693 if (njs_slow_path(fmt == NJS_KEY_FORMAT_UNKNOWN)) {
1694 njs_type_error(vm, "unknown key format: \"%V\"", &format);
1695 goto fail;
1696 }
1697
1698 options = njs_arg(args, nargs, 3);
1699 alg = njs_key_algorithm(vm, options);
1700 if (njs_slow_path(alg == NULL)) {
1701 goto fail;
1702 }
1703
1704 if (njs_slow_path(!(fmt & alg->fmt))) {
1705 njs_type_error(vm, "unsupported key fmt for \"%V\" key",
1706 njs_algorithm_string(alg));
1707 goto fail;
1708 }
1709
1710 ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage);
1711 if (njs_slow_path(ret != NJS_OK)) {
1712 goto fail;
1713 }
1714
1715 if (njs_slow_path(usage & ~alg->usage)) {
1716 njs_type_error(vm, "unsupported key usage for \"%V\" key",
1717 njs_algorithm_string(alg));
1718 goto fail;
1719 }
1720
1721 ret = njs_vm_value_to_bytes(vm, &key_data, njs_arg(args, nargs, 2));
1722 if (njs_slow_path(ret != NJS_OK)) {
1723 goto fail;
1724 }
1725
1726 start = key_data.start;
1727
1728 switch (fmt) {
1729 case NJS_KEY_FORMAT_PKCS8:
1730 bio = njs_bio_new_mem_buf(start, key_data.length);
1731 if (njs_slow_path(bio == NULL)) {
1732 njs_webcrypto_error(vm, "BIO_new_mem_buf() failed");
1733 goto fail;
1734 }
1735
1736 pkcs8 = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
1737 if (njs_slow_path(pkcs8 == NULL)) {
1738 BIO_free(bio);
1739 njs_webcrypto_error(vm, "d2i_PKCS8_PRIV_KEY_INFO_bio() failed");
1740 goto fail;
1741 }
1742
1743 pkey = EVP_PKCS82PKEY(pkcs8);
1744 if (njs_slow_path(pkey == NULL)) {
1745 PKCS8_PRIV_KEY_INFO_free(pkcs8);
1746 BIO_free(bio);
1747 njs_webcrypto_error(vm, "EVP_PKCS82PKEY() failed");
1748 goto fail;
1749 }
1750
1751 PKCS8_PRIV_KEY_INFO_free(pkcs8);
1752 BIO_free(bio);
1753
1754 break;
1755
1756 case NJS_KEY_FORMAT_SPKI:
1757 pkey = d2i_PUBKEY(NULL, &start, key_data.length);
1758 if (njs_slow_path(pkey == NULL)) {
1759 njs_webcrypto_error(vm, "d2i_PUBKEY() failed");
1760 goto fail;
1761 }
1762
1763 break;
1764
1765 case NJS_KEY_FORMAT_RAW:
1766 break;
1767
1768 default:
1769 njs_internal_error(vm, "not implemented key format: \"%V\"", &format);
1770 goto fail;
1771 }
1772
1773 key = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_webcrypto_key_t));
1774 if (njs_slow_path(key == NULL)) {
1775 njs_memory_error(vm);
1776 goto fail;
1777 }
1778
1779 key->alg = alg;
1780 key->usage = usage;
1781
1782 switch (alg->type) {
1783 case NJS_ALGORITHM_RSA_OAEP:
1784 case NJS_ALGORITHM_RSA_PSS:
1785 case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
1786
1787 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
1788
1789 rsa = EVP_PKEY_get1_RSA(pkey);
1790 if (njs_slow_path(rsa == NULL)) {
1791 njs_webcrypto_error(vm, "RSA key is not found");
1792 goto fail;
1793 }
1794
1795 RSA_free(rsa);
1796
1797 #else
1798 if (!EVP_PKEY_is_a(pkey, "RSA")) {
1799 njs_webcrypto_error(vm, "RSA key is not found");
1800 goto fail;
1801 }
1802 #endif
1803
1804 ret = njs_algorithm_hash(vm, options, &key->hash);
1805 if (njs_slow_path(ret == NJS_ERROR)) {
1806 goto fail;
1807 }
1808
1809 key->pkey = pkey;
1810
1811 break;
1812
1813 case NJS_ALGORITHM_ECDSA:
1814 case NJS_ALGORITHM_ECDH:
1815
1816 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
1817
1818 ec = EVP_PKEY_get1_EC_KEY(pkey);
1819 if (njs_slow_path(ec == NULL)) {
1820 njs_webcrypto_error(vm, "EC key is not found");
1821 goto fail;
1822 }
1823
1824 group = EC_KEY_get0_group(ec);
1825 nid = EC_GROUP_get_curve_name(group);
1826 EC_KEY_free(ec);
1827
1828 #else
1829
1830 if (!EVP_PKEY_is_a(pkey, "EC")) {
1831 njs_webcrypto_error(vm, "EC key is not found");
1832 goto fail;
1833 }
1834
1835 if (EVP_PKEY_get_group_name(pkey, gname, sizeof(gname), NULL) != 1) {
1836 njs_webcrypto_error(vm, "EVP_PKEY_get_group_name() failed");
1837 goto fail;
1838 }
1839
1840 nid = OBJ_txt2nid(gname);
1841
1842 #endif
1843
1844 ret = njs_algorithm_curve(vm, options, &key->curve);
1845 if (njs_slow_path(ret == NJS_ERROR)) {
1846 goto fail;
1847 }
1848
1849 if (njs_slow_path(curves[key->curve] != nid)) {
1850 njs_webcrypto_error(vm, "name curve mismatch");
1851 goto fail;
1852 }
1853
1854 key->pkey = pkey;
1855
1856 break;
1857
1858 case NJS_ALGORITHM_HMAC:
1859 pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key_data.start,
1860 key_data.length);
1861 if (njs_slow_path(pkey == NULL)) {
1862 njs_webcrypto_error(vm, "EVP_PKEY_new_mac_key() failed");
1863 goto fail;
1864 }
1865
1866 ret = njs_algorithm_hash(vm, options, &key->hash);
1867 if (njs_slow_path(ret == NJS_ERROR)) {
1868 goto fail;
1869 }
1870
1871 key->pkey = pkey;
1872
1873 break;
1874
1875 case NJS_ALGORITHM_AES_GCM:
1876 case NJS_ALGORITHM_AES_CTR:
1877 case NJS_ALGORITHM_AES_CBC:
1878 case NJS_ALGORITHM_PBKDF2:
1879 case NJS_ALGORITHM_HKDF:
1880 key->raw = key_data;
1881 default:
1882 break;
1883 }
1884
1885 if (pkey != NULL) {
1886 cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0);
1887 if (cln == NULL) {
1888 njs_memory_error(vm);
1889 goto fail;
1890 }
1891
1892 cln->handler = njs_webcrypto_cleanup_pkey;
1893 cln->data = key;
1894 pkey = NULL;
1895 }
1896
1897 ret = njs_vm_external_create(vm, &value, njs_webcrypto_crypto_key_proto_id,
1898 key, 0);
1899 if (njs_slow_path(ret != NJS_OK)) {
1900 goto fail;
1901 }
1902
1903 return njs_webcrypto_result(vm, &value, NJS_OK);
1904
1905 fail:
1906
1907 if (pkey != NULL) {
1908 EVP_PKEY_free(pkey);
1909 }
1910
1911 return njs_webcrypto_result(vm, njs_vm_retval(vm), NJS_ERROR);
1912 }
1913
1914
1915 static njs_int_t
njs_set_rsa_padding(njs_vm_t * vm,njs_value_t * options,EVP_PKEY * pkey,EVP_PKEY_CTX * ctx,njs_webcrypto_alg_t type)1916 njs_set_rsa_padding(njs_vm_t *vm, njs_value_t *options, EVP_PKEY *pkey,
1917 EVP_PKEY_CTX *ctx, njs_webcrypto_alg_t type)
1918 {
1919 int padding;
1920 int64_t salt_length;
1921 njs_int_t ret;
1922 njs_value_t value;
1923
1924 static const njs_value_t string_saltl = njs_string("saltLength");
1925
1926 if (type == NJS_ALGORITHM_ECDSA) {
1927 return NJS_OK;
1928 }
1929
1930 padding = (type == NJS_ALGORITHM_RSA_PSS) ? RSA_PKCS1_PSS_PADDING
1931 : RSA_PKCS1_PADDING;
1932 ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
1933 if (njs_slow_path(ret <= 0)) {
1934 njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_rsa_padding() failed");
1935 return NJS_ERROR;
1936 }
1937
1938 if (padding == RSA_PKCS1_PSS_PADDING) {
1939 ret = njs_value_property(vm, options, njs_value_arg(&string_saltl),
1940 &value);
1941 if (njs_slow_path(ret != NJS_OK)) {
1942 if (ret == NJS_DECLINED) {
1943 njs_type_error(vm, "RSA-PSS algorithm.saltLength "
1944 "is not provided");
1945 }
1946
1947 return NJS_ERROR;
1948 }
1949
1950 ret = njs_value_to_integer(vm, &value, &salt_length);
1951 if (njs_slow_path(ret != NJS_OK)) {
1952 return NJS_ERROR;
1953 }
1954
1955 ret = EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_length);
1956 if (njs_slow_path(ret <= 0)) {
1957 njs_webcrypto_error(vm,
1958 "EVP_PKEY_CTX_set_rsa_pss_saltlen() failed");
1959 return NJS_ERROR;
1960 }
1961 }
1962
1963 return NJS_OK;
1964 }
1965
1966
1967 static njs_int_t
njs_ext_sign(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t verify)1968 njs_ext_sign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1969 njs_index_t verify)
1970 {
1971 u_char *dst;
1972 size_t olen, outlen;
1973 unsigned mask, m_len;
1974 njs_int_t ret;
1975 njs_str_t data, sig;
1976 EVP_MD_CTX *mctx;
1977 njs_value_t value, *options;
1978 EVP_PKEY_CTX *pctx;
1979 const EVP_MD *md;
1980 njs_webcrypto_key_t *key;
1981 njs_webcrypto_hash_t hash;
1982 njs_webcrypto_algorithm_t *alg;
1983 unsigned char m[EVP_MAX_MD_SIZE];
1984
1985 mctx = NULL;
1986 pctx = NULL;
1987
1988 options = njs_arg(args, nargs, 1);
1989 alg = njs_key_algorithm(vm, options);
1990 if (njs_slow_path(alg == NULL)) {
1991 goto fail;
1992 }
1993
1994 key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
1995 njs_arg(args, nargs, 2));
1996 if (njs_slow_path(key == NULL)) {
1997 njs_type_error(vm, "\"key\" is not a CryptoKey object");
1998 goto fail;
1999 }
2000
2001 mask = verify ? NJS_KEY_USAGE_VERIFY : NJS_KEY_USAGE_SIGN;
2002 if (njs_slow_path(!(key->usage & mask))) {
2003 njs_type_error(vm, "provide key does not support \"sign\" operation");
2004 goto fail;
2005 }
2006
2007 if (njs_slow_path(key->alg != alg)) {
2008 njs_type_error(vm, "cannot %s using \"%V\" with \"%V\" key",
2009 verify ? "verify" : "sign",
2010 njs_algorithm_string(key->alg),
2011 njs_algorithm_string(alg));
2012 goto fail;
2013 }
2014
2015 if (verify) {
2016 ret = njs_vm_value_to_bytes(vm, &sig, njs_arg(args, nargs, 3));
2017 if (njs_slow_path(ret != NJS_OK)) {
2018 goto fail;
2019 }
2020
2021 ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 4));
2022 if (njs_slow_path(ret != NJS_OK)) {
2023 goto fail;
2024 }
2025
2026 } else {
2027 ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 3));
2028 if (njs_slow_path(ret != NJS_OK)) {
2029 goto fail;
2030 }
2031 }
2032
2033 mctx = njs_evp_md_ctx_new();
2034 if (njs_slow_path(mctx == NULL)) {
2035 njs_webcrypto_error(vm, "njs_evp_md_ctx_new() failed");
2036 goto fail;
2037 }
2038
2039 if (alg->type == NJS_ALGORITHM_ECDSA) {
2040 ret = njs_algorithm_hash(vm, options, &hash);
2041 if (njs_slow_path(ret == NJS_ERROR)) {
2042 goto fail;
2043 }
2044
2045 } else {
2046 hash = key->hash;
2047 }
2048
2049 md = njs_algorithm_hash_digest(hash);
2050
2051 outlen = 0;
2052
2053 switch (alg->type) {
2054 case NJS_ALGORITHM_HMAC:
2055 ret = EVP_DigestSignInit(mctx, NULL, md, NULL, key->pkey);
2056 if (njs_slow_path(ret <= 0)) {
2057 njs_webcrypto_error(vm, "EVP_DigestSignInit() failed");
2058 goto fail;
2059 }
2060
2061 ret = EVP_DigestSignUpdate(mctx, data.start, data.length);
2062 if (njs_slow_path(ret <= 0)) {
2063 njs_webcrypto_error(vm, "EVP_DigestSignUpdate() failed");
2064 goto fail;
2065 }
2066
2067 olen = EVP_MD_size(md);
2068
2069 if (!verify) {
2070 dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen);
2071 if (njs_slow_path(dst == NULL)) {
2072 njs_memory_error(vm);
2073 goto fail;
2074 }
2075
2076 } else {
2077 dst = (u_char *) &m[0];
2078 }
2079
2080 outlen = olen;
2081
2082 ret = EVP_DigestSignFinal(mctx, dst, &outlen);
2083 if (njs_slow_path(ret <= 0 || olen != outlen)) {
2084 njs_webcrypto_error(vm, "EVP_DigestSignFinal() failed");
2085 goto fail;
2086 }
2087
2088 if (verify) {
2089 ret = (sig.length == outlen && memcmp(sig.start, dst, outlen) == 0);
2090 }
2091
2092 break;
2093
2094 case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
2095 case NJS_ALGORITHM_RSA_PSS:
2096 case NJS_ALGORITHM_ECDSA:
2097 default:
2098 ret = EVP_DigestInit_ex(mctx, md, NULL);
2099 if (njs_slow_path(ret <= 0)) {
2100 njs_webcrypto_error(vm, "EVP_DigestInit_ex() failed");
2101 goto fail;
2102 }
2103
2104 ret = EVP_DigestUpdate(mctx, data.start, data.length);
2105 if (njs_slow_path(ret <= 0)) {
2106 njs_webcrypto_error(vm, "EVP_DigestUpdate() failed");
2107 goto fail;
2108 }
2109
2110 ret = EVP_DigestFinal_ex(mctx, m, &m_len);
2111 if (njs_slow_path(ret <= 0)) {
2112 njs_webcrypto_error(vm, "EVP_DigestFinal_ex() failed");
2113 goto fail;
2114 }
2115
2116 olen = EVP_PKEY_size(key->pkey);
2117 dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen);
2118 if (njs_slow_path(dst == NULL)) {
2119 njs_memory_error(vm);
2120 goto fail;
2121 }
2122
2123 pctx = EVP_PKEY_CTX_new(key->pkey, NULL);
2124 if (njs_slow_path(pctx == NULL)) {
2125 njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed");
2126 goto fail;
2127 }
2128
2129 if (!verify) {
2130 ret = EVP_PKEY_sign_init(pctx);
2131 if (njs_slow_path(ret <= 0)) {
2132 njs_webcrypto_error(vm, "EVP_PKEY_sign_init() failed");
2133 goto fail;
2134 }
2135
2136 } else {
2137 ret = EVP_PKEY_verify_init(pctx);
2138 if (njs_slow_path(ret <= 0)) {
2139 njs_webcrypto_error(vm, "EVP_PKEY_verify_init() failed");
2140 goto fail;
2141 }
2142 }
2143
2144 ret = njs_set_rsa_padding(vm, options, key->pkey, pctx, alg->type);
2145 if (njs_slow_path(ret != NJS_OK)) {
2146 goto fail;
2147 }
2148
2149 ret = EVP_PKEY_CTX_set_signature_md(pctx, md);
2150 if (njs_slow_path(ret <= 0)) {
2151 njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_signature_md() failed");
2152 goto fail;
2153 }
2154
2155 if (!verify) {
2156 outlen = olen;
2157 ret = EVP_PKEY_sign(pctx, dst, &outlen, m, m_len);
2158 if (njs_slow_path(ret <= 0)) {
2159 njs_webcrypto_error(vm, "EVP_PKEY_sign() failed");
2160 goto fail;
2161 }
2162
2163 } else {
2164 ret = EVP_PKEY_verify(pctx, sig.start, sig.length, m, m_len);
2165 if (njs_slow_path(ret < 0)) {
2166 njs_webcrypto_error(vm, "EVP_PKEY_verify() failed");
2167 goto fail;
2168 }
2169 }
2170
2171 EVP_PKEY_CTX_free(pctx);
2172
2173 break;
2174 }
2175
2176 if (!verify) {
2177 ret = njs_vm_value_array_buffer_set(vm, &value, dst, outlen);
2178 if (njs_slow_path(ret != NJS_OK)) {
2179 goto fail;
2180 }
2181
2182 } else {
2183 njs_set_boolean(&value, ret != 0);
2184 }
2185
2186 njs_evp_md_ctx_free(mctx);
2187
2188 return njs_webcrypto_result(vm, &value, NJS_OK);
2189
2190 fail:
2191
2192 if (mctx != NULL) {
2193 njs_evp_md_ctx_free(mctx);
2194 }
2195
2196 if (pctx != NULL) {
2197 EVP_PKEY_CTX_free(pctx);
2198 }
2199
2200 return njs_webcrypto_result(vm, njs_vm_retval(vm), NJS_ERROR);
2201 }
2202
2203
2204 static njs_int_t
njs_ext_unwrap_key(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2205 njs_ext_unwrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2206 njs_index_t unused)
2207 {
2208 njs_internal_error(vm, "\"unwrapKey\" not implemented");
2209 return NJS_ERROR;
2210 }
2211
2212
2213 static njs_int_t
njs_ext_wrap_key(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2214 njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2215 njs_index_t unused)
2216 {
2217 njs_internal_error(vm, "\"wrapKey\" not implemented");
2218 return NJS_ERROR;
2219 }
2220
2221
2222 static njs_int_t
njs_ext_get_random_values(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2223 njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2224 njs_index_t unused)
2225 {
2226 njs_int_t ret;
2227 njs_str_t fill;
2228
2229 ret = njs_vm_value_to_bytes(vm, &fill, njs_arg(args, nargs, 1));
2230 if (njs_slow_path(ret != NJS_OK)) {
2231 return NJS_ERROR;
2232 }
2233
2234 if (njs_slow_path(fill.length > 65536)) {
2235 njs_type_error(vm, "requested length exceeds 65536 bytes");
2236 return NJS_ERROR;
2237 }
2238
2239 if (RAND_bytes(fill.start, fill.length) != 1) {
2240 njs_webcrypto_error(vm, "RAND_bytes() failed");
2241 return NJS_ERROR;
2242 }
2243
2244 return NJS_OK;
2245 }
2246
2247
2248 static void
njs_webcrypto_cleanup_pkey(void * data)2249 njs_webcrypto_cleanup_pkey(void *data)
2250 {
2251 njs_webcrypto_key_t *key = data;
2252
2253 if (key->pkey != NULL) {
2254 EVP_PKEY_free(key->pkey);
2255 }
2256 }
2257
2258
2259 static njs_webcrypto_key_format_t
njs_key_format(njs_vm_t * vm,njs_value_t * value,njs_str_t * format)2260 njs_key_format(njs_vm_t *vm, njs_value_t *value, njs_str_t *format)
2261 {
2262 njs_int_t ret;
2263 njs_uint_t fmt;
2264
2265 static const struct {
2266 njs_str_t name;
2267 njs_uint_t value;
2268 } formats[] = {
2269 { njs_str("raw"), NJS_KEY_FORMAT_RAW },
2270 { njs_str("pkcs8"), NJS_KEY_FORMAT_PKCS8 },
2271 { njs_str("spki"), NJS_KEY_FORMAT_SPKI },
2272 { njs_str("jwk"), NJS_KEY_FORMAT_JWK },
2273 };
2274
2275 ret = njs_value_to_string(vm, value, value);
2276 if (njs_slow_path(ret != NJS_OK)) {
2277 return NJS_ERROR;
2278 }
2279
2280 njs_string_get(value, format);
2281
2282 fmt = 0;
2283
2284 while (fmt < sizeof(formats) / sizeof(formats[0])) {
2285 if (njs_strstr_eq(format, &formats[fmt].name)) {
2286 return formats[fmt].value;
2287 }
2288
2289 fmt++;
2290 }
2291
2292 return NJS_KEY_FORMAT_UNKNOWN;
2293 }
2294
2295
2296 static njs_int_t
njs_key_usage_array_handler(njs_vm_t * vm,njs_iterator_args_t * args,njs_value_t * value,int64_t index)2297 njs_key_usage_array_handler(njs_vm_t *vm, njs_iterator_args_t *args,
2298 njs_value_t *value, int64_t index)
2299 {
2300 unsigned *mask;
2301 njs_str_t u;
2302 njs_int_t ret;
2303 njs_value_t usage;
2304 njs_webcrypto_entry_t *e;
2305
2306 njs_value_assign(&usage, value);
2307
2308 ret = njs_value_to_string(vm, &usage, &usage);
2309 if (njs_slow_path(ret != NJS_OK)) {
2310 return NJS_ERROR;
2311 }
2312
2313 njs_string_get(&usage, &u);
2314
2315 for (e = &njs_webcrypto_usage[0]; e->name.length != 0; e++) {
2316 if (njs_strstr_eq(&u, &e->name)) {
2317 mask = args->data;
2318 *mask |= e->value;
2319 return NJS_OK;
2320 }
2321 }
2322
2323 njs_type_error(vm, "unknown key usage: \"%V\"", &u);
2324
2325 return NJS_ERROR;
2326 }
2327
2328
2329 static njs_int_t
njs_key_usage(njs_vm_t * vm,njs_value_t * value,unsigned * mask)2330 njs_key_usage(njs_vm_t *vm, njs_value_t *value, unsigned *mask)
2331 {
2332 int64_t length;
2333 njs_int_t ret;
2334 njs_iterator_args_t args;
2335
2336 ret = njs_object_length(vm, value, &length);
2337 if (njs_slow_path(ret != NJS_OK)) {
2338 return NJS_ERROR;
2339 }
2340
2341 *mask = 0;
2342
2343 args.value = value;
2344 args.from = 0;
2345 args.to = length;
2346 args.data = mask;
2347
2348 return njs_object_iterate(vm, &args, njs_key_usage_array_handler);
2349 }
2350
2351
2352 static njs_webcrypto_algorithm_t *
njs_key_algorithm(njs_vm_t * vm,njs_value_t * options)2353 njs_key_algorithm(njs_vm_t *vm, njs_value_t *options)
2354 {
2355 njs_int_t ret;
2356 njs_str_t a;
2357 njs_value_t name;
2358 njs_webcrypto_entry_t *e;
2359 njs_webcrypto_algorithm_t *alg;
2360
2361 static const njs_value_t string_name = njs_string("name");
2362
2363 if (njs_is_object(options)) {
2364 ret = njs_value_property(vm, options, njs_value_arg(&string_name),
2365 &name);
2366 if (njs_slow_path(ret != NJS_OK)) {
2367 if (ret == NJS_DECLINED) {
2368 njs_type_error(vm, "algorithm name is not provided");
2369 }
2370
2371 return NULL;
2372 }
2373
2374 } else {
2375 njs_value_assign(&name, options);
2376 }
2377
2378 ret = njs_value_to_string(vm, &name, &name);
2379 if (njs_slow_path(ret != NJS_OK)) {
2380 return NULL;
2381 }
2382
2383 njs_string_get(&name, &a);
2384
2385 for (e = &njs_webcrypto_alg[0]; e->name.length != 0; e++) {
2386 if (njs_strstr_case_eq(&a, &e->name)) {
2387 alg = (njs_webcrypto_algorithm_t *) e->value;
2388 if (alg->usage & NJS_KEY_USAGE_UNSUPPORTED) {
2389 njs_type_error(vm, "unsupported algorithm: \"%V\"", &a);
2390 return NULL;
2391 }
2392
2393 return alg;
2394 }
2395 }
2396
2397 njs_type_error(vm, "unknown algorithm name: \"%V\"", &a);
2398
2399 return NULL;
2400 }
2401
2402
2403 static njs_str_t *
njs_algorithm_string(njs_webcrypto_algorithm_t * algorithm)2404 njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm)
2405 {
2406 njs_webcrypto_entry_t *e;
2407 njs_webcrypto_algorithm_t *alg;
2408
2409 for (e = &njs_webcrypto_alg[0]; e->name.length != 0; e++) {
2410 alg = (njs_webcrypto_algorithm_t *) e->value;
2411 if (alg->type == algorithm->type) {
2412 break;
2413 }
2414 }
2415
2416 return &e->name;
2417 }
2418
2419
2420 static njs_int_t
njs_algorithm_hash(njs_vm_t * vm,njs_value_t * options,njs_webcrypto_hash_t * hash)2421 njs_algorithm_hash(njs_vm_t *vm, njs_value_t *options,
2422 njs_webcrypto_hash_t *hash)
2423 {
2424 njs_int_t ret;
2425 njs_str_t name;
2426 njs_value_t value;
2427 njs_webcrypto_entry_t *e;
2428
2429 static const njs_value_t string_hash = njs_string("hash");
2430
2431 if (njs_is_object(options)) {
2432 ret = njs_value_property(vm, options, njs_value_arg(&string_hash),
2433 &value);
2434 if (njs_slow_path(ret == NJS_ERROR)) {
2435 return NJS_ERROR;
2436 }
2437
2438 } else {
2439 njs_value_assign(&value, options);
2440 }
2441
2442 ret = njs_value_to_string(vm, &value, &value);
2443 if (njs_slow_path(ret != NJS_OK)) {
2444 return NJS_ERROR;
2445 }
2446
2447 njs_string_get(&value, &name);
2448
2449 for (e = &njs_webcrypto_hash[0]; e->name.length != 0; e++) {
2450 if (njs_strstr_eq(&name, &e->name)) {
2451 *hash = e->value;
2452 return NJS_OK;
2453 }
2454 }
2455
2456 njs_type_error(vm, "unknown hash name: \"%V\"", &name);
2457
2458 return NJS_ERROR;
2459 }
2460
2461
2462 static const EVP_MD *
njs_algorithm_hash_digest(njs_webcrypto_hash_t hash)2463 njs_algorithm_hash_digest(njs_webcrypto_hash_t hash)
2464 {
2465 switch (hash) {
2466 case NJS_HASH_SHA256:
2467 return EVP_sha256();
2468
2469 case NJS_HASH_SHA384:
2470 return EVP_sha384();
2471
2472 case NJS_HASH_SHA512:
2473 return EVP_sha512();
2474
2475 case NJS_HASH_SHA1:
2476 default:
2477 break;
2478 }
2479
2480 return EVP_sha1();
2481 }
2482
2483
2484 static njs_int_t
njs_algorithm_curve(njs_vm_t * vm,njs_value_t * options,njs_webcrypto_curve_t * curve)2485 njs_algorithm_curve(njs_vm_t *vm, njs_value_t *options,
2486 njs_webcrypto_curve_t *curve)
2487 {
2488 njs_int_t ret;
2489 njs_str_t name;
2490 njs_value_t value;
2491 njs_webcrypto_entry_t *e;
2492
2493 static const njs_value_t string_curve = njs_string("namedCurve");
2494
2495 ret = njs_value_property(vm, options, njs_value_arg(&string_curve),
2496 &value);
2497 if (njs_slow_path(ret != NJS_OK)) {
2498 return ret;
2499 }
2500
2501 ret = njs_value_to_string(vm, &value, &value);
2502 if (njs_slow_path(ret != NJS_OK)) {
2503 return NJS_ERROR;
2504 }
2505
2506 njs_string_get(&value, &name);
2507
2508 for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) {
2509 if (njs_strstr_eq(&name, &e->name)) {
2510 *curve = e->value;
2511 return NJS_OK;
2512 }
2513 }
2514
2515 njs_type_error(vm, "unknown namedCurve: \"%V\"", &name);
2516
2517 return NJS_ERROR;
2518 }
2519
2520
2521 static njs_int_t
njs_promise_trampoline(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2522 njs_promise_trampoline(njs_vm_t *vm, njs_value_t *args,
2523 njs_uint_t nargs, njs_index_t unused)
2524 {
2525 njs_function_t *callback;
2526
2527 callback = njs_value_function(njs_argument(args, 1));
2528
2529 if (callback != NULL) {
2530 return njs_vm_call(vm, callback, njs_argument(args, 2), 1);
2531 }
2532
2533 return NJS_OK;
2534 }
2535
2536
2537 static njs_int_t
njs_webcrypto_result(njs_vm_t * vm,njs_value_t * result,njs_int_t rc)2538 njs_webcrypto_result(njs_vm_t *vm, njs_value_t *result, njs_int_t rc)
2539 {
2540 njs_int_t ret;
2541 njs_value_t retval, arguments[2];
2542 njs_function_t *callback;
2543 njs_vm_event_t vm_event;
2544
2545 ret = njs_vm_promise_create(vm, &retval, njs_value_arg(&arguments));
2546 if (ret != NJS_OK) {
2547 goto error;
2548 }
2549
2550 callback = njs_vm_function_alloc(vm, njs_promise_trampoline);
2551 if (callback == NULL) {
2552 goto error;
2553 }
2554
2555 vm_event = njs_vm_add_event(vm, callback, 1, NULL, NULL);
2556 if (vm_event == NULL) {
2557 goto error;
2558 }
2559
2560 njs_value_assign(&arguments[0], &arguments[(rc != NJS_OK)]);
2561 njs_value_assign(&arguments[1], result);
2562
2563 ret = njs_vm_post_event(vm, vm_event, njs_value_arg(&arguments), 2);
2564 if (ret == NJS_ERROR) {
2565 goto error;
2566 }
2567
2568 njs_vm_retval_set(vm, njs_value_arg(&retval));
2569
2570 return NJS_OK;
2571
2572 error:
2573
2574 njs_vm_error(vm, "internal error");
2575
2576 return NJS_ERROR;
2577 }
2578
2579
2580 static u_char *
njs_cpystrn(u_char * dst,u_char * src,size_t n)2581 njs_cpystrn(u_char *dst, u_char *src, size_t n)
2582 {
2583 if (n == 0) {
2584 return dst;
2585 }
2586
2587 while (--n) {
2588 *dst = *src;
2589
2590 if (*dst == '\0') {
2591 return dst;
2592 }
2593
2594 dst++;
2595 src++;
2596 }
2597
2598 *dst = '\0';
2599
2600 return dst;
2601 }
2602
2603
2604 static void
njs_webcrypto_error(njs_vm_t * vm,const char * fmt,...)2605 njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...)
2606 {
2607 int flags;
2608 u_char *p, *last;
2609 va_list args;
2610 const char *data;
2611 unsigned long n;
2612 u_char errstr[NJS_MAX_ERROR_STR];
2613
2614 last = &errstr[NJS_MAX_ERROR_STR];
2615
2616 va_start(args, fmt);
2617 p = njs_vsprintf(errstr, last - 1, fmt, args);
2618 va_end(args);
2619
2620 if (ERR_peek_error()) {
2621 p = njs_cpystrn(p, (u_char *) " (SSL:", last - p);
2622
2623 for ( ;; ) {
2624
2625 n = ERR_peek_error_data(&data, &flags);
2626
2627 if (n == 0) {
2628 break;
2629 }
2630
2631 /* ERR_error_string_n() requires at least one byte */
2632
2633 if (p >= last - 1) {
2634 goto next;
2635 }
2636
2637 *p++ = ' ';
2638
2639 ERR_error_string_n(n, (char *) p, last - p);
2640
2641 while (p < last && *p) {
2642 p++;
2643 }
2644
2645 if (p < last && *data && (flags & ERR_TXT_STRING)) {
2646 *p++ = ':';
2647 p = njs_cpystrn(p, (u_char *) data, last - p);
2648 }
2649
2650 next:
2651
2652 (void) ERR_get_error();
2653 }
2654
2655 if (p < last) {
2656 *p++ = ')';
2657 }
2658 }
2659
2660 njs_vm_value_error_set(vm, njs_vm_retval(vm), "%*s", p - errstr, errstr);
2661 }
2662
2663
2664 static njs_int_t
njs_webcrypto_init(njs_vm_t * vm)2665 njs_webcrypto_init(njs_vm_t *vm)
2666 {
2667 njs_int_t ret, proto_id;
2668 njs_str_t name;
2669 njs_opaque_value_t value;
2670
2671 #if (OPENSSL_VERSION_NUMBER < 0x10100003L)
2672 OpenSSL_add_all_algorithms();
2673 #endif
2674
2675 njs_webcrypto_crypto_key_proto_id =
2676 njs_vm_external_prototype(vm, njs_ext_webcrypto_crypto_key,
2677 njs_nitems(njs_ext_webcrypto_crypto_key));
2678 if (njs_slow_path(njs_webcrypto_crypto_key_proto_id < 0)) {
2679 return NJS_ERROR;
2680 }
2681
2682 proto_id = njs_vm_external_prototype(vm, njs_ext_webcrypto,
2683 njs_nitems(njs_ext_webcrypto));
2684 if (njs_slow_path(proto_id < 0)) {
2685 return NJS_ERROR;
2686 }
2687
2688 ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
2689 if (njs_slow_path(ret != NJS_OK)) {
2690 return NJS_ERROR;
2691 }
2692
2693 name.length = njs_length("crypto");
2694 name.start = (u_char *) "crypto";
2695
2696 ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1);
2697 if (njs_slow_path(ret != NJS_OK)) {
2698 return NJS_ERROR;
2699 }
2700
2701 return NJS_OK;
2702 }
2703