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