Coverage Report

Created: 2025-07-11 06:16

/src/njs/external/njs_webcrypto_module.c
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 * Copyright (C) Dmitry Volyntsev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
8
#include <njs.h>
9
#include <njs_assert.h>
10
#include <njs_string.h>
11
#include "njs_openssl.h"
12
13
typedef enum {
14
    NJS_KEY_FORMAT_RAW          = 1 << 1,
15
    NJS_KEY_FORMAT_PKCS8        = 1 << 2,
16
    NJS_KEY_FORMAT_SPKI         = 1 << 3,
17
    NJS_KEY_FORMAT_JWK          = 1 << 4,
18
    NJS_KEY_FORMAT_UNKNOWN      = 1 << 5,
19
} njs_webcrypto_key_format_t;
20
21
22
typedef enum {
23
    NJS_KEY_USAGE_DECRYPT       = 1 << 1,
24
    NJS_KEY_USAGE_DERIVE_BITS   = 1 << 2,
25
    NJS_KEY_USAGE_DERIVE_KEY    = 1 << 3,
26
    NJS_KEY_USAGE_ENCRYPT       = 1 << 4,
27
    NJS_KEY_USAGE_GENERATE_KEY  = 1 << 5,
28
    NJS_KEY_USAGE_SIGN          = 1 << 6,
29
    NJS_KEY_USAGE_VERIFY        = 1 << 7,
30
    NJS_KEY_USAGE_WRAP_KEY      = 1 << 8,
31
    NJS_KEY_USAGE_UNWRAP_KEY    = 1 << 10,
32
} njs_webcrypto_key_usage_t;
33
34
35
typedef enum {
36
    NJS_ALGORITHM_RSASSA_PKCS1_v1_5 = 0,
37
    NJS_ALGORITHM_RSA_PSS,
38
    NJS_ALGORITHM_RSA_OAEP,
39
    NJS_ALGORITHM_HMAC,
40
    NJS_ALGORITHM_AES_GCM,
41
    NJS_ALGORITHM_AES_CTR,
42
    NJS_ALGORITHM_AES_CBC,
43
    NJS_ALGORITHM_ECDSA,
44
    NJS_ALGORITHM_ECDH,
45
    NJS_ALGORITHM_PBKDF2,
46
    NJS_ALGORITHM_HKDF,
47
    NJS_ALGORITHM_MAX,
48
} njs_webcrypto_alg_t;
49
50
51
typedef enum {
52
    NJS_HASH_UNSET = 0,
53
    NJS_HASH_SHA1,
54
    NJS_HASH_SHA256,
55
    NJS_HASH_SHA384,
56
    NJS_HASH_SHA512,
57
    NJS_HASH_MAX,
58
} njs_webcrypto_hash_t;
59
60
61
typedef struct {
62
    njs_str_t                  name;
63
    uintptr_t                  value;
64
} njs_webcrypto_entry_t;
65
66
67
typedef struct {
68
    njs_webcrypto_alg_t        type;
69
    unsigned                   usage;
70
    unsigned                   fmt;
71
    unsigned                   raw;
72
} njs_webcrypto_algorithm_t;
73
74
75
typedef struct {
76
    njs_webcrypto_algorithm_t  *alg;
77
    unsigned                   usage;
78
    njs_bool_t                 extractable;
79
80
    njs_webcrypto_hash_t       hash;
81
82
    union {
83
        struct {
84
            EVP_PKEY          *pkey;
85
            njs_bool_t        privat;
86
            int               curve;
87
        } a;
88
        struct {
89
            njs_str_t         raw;
90
        } s;
91
    } u;
92
93
} njs_webcrypto_key_t;
94
95
96
typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX *ctx);
97
typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX *ctx, unsigned char *out,
98
    size_t *outlen, const unsigned char *in, size_t inlen);
99
100
101
static njs_int_t njs_ext_cipher(njs_vm_t *vm, njs_value_t *args,
102
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
103
static njs_int_t njs_cipher_pkey(njs_vm_t *vm, njs_str_t *data,
104
    njs_webcrypto_key_t *key, njs_index_t encrypt, njs_value_t *retval);
105
static njs_int_t njs_cipher_aes_gcm(njs_vm_t *vm, njs_str_t *data,
106
    njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt,
107
    njs_value_t *retval);
108
static njs_int_t njs_cipher_aes_ctr(njs_vm_t *vm, njs_str_t *data,
109
    njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt,
110
    njs_value_t *retval);
111
static njs_int_t njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data,
112
    njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt,
113
    njs_value_t *retval);
114
static njs_int_t njs_ext_derive(njs_vm_t *vm, njs_value_t *args,
115
    njs_uint_t nargs, njs_index_t derive_key, njs_value_t *retval);
116
static njs_int_t njs_ext_digest(njs_vm_t *vm, njs_value_t *args,
117
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
118
static njs_int_t njs_ext_export_key(njs_vm_t *vm, njs_value_t *args,
119
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
120
static njs_int_t njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args,
121
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
122
static njs_int_t njs_ext_import_key(njs_vm_t *vm, njs_value_t *args,
123
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
124
static njs_int_t njs_ext_sign(njs_vm_t *vm, njs_value_t *args,
125
    njs_uint_t nargs, njs_index_t verify, njs_value_t *retval);
126
static njs_int_t njs_ext_unwrap_key(njs_vm_t *vm, njs_value_t *args,
127
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
128
static njs_int_t njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args,
129
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
130
static njs_int_t njs_key_ext_algorithm(njs_vm_t *vm, njs_object_prop_t *prop,
131
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
132
    njs_value_t *retval);
133
static njs_int_t njs_key_ext_extractable(njs_vm_t *vm, njs_object_prop_t *prop,
134
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
135
    njs_value_t *retval);
136
static njs_int_t njs_key_ext_type(njs_vm_t *vm, njs_object_prop_t *prop,
137
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
138
    njs_value_t *retval);
139
static njs_int_t njs_key_ext_usages(njs_vm_t *vm, njs_object_prop_t *prop,
140
    uint32_t unused, njs_value_t *value, njs_value_t *setval,
141
    njs_value_t *retval);
142
static njs_int_t njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args,
143
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
144
145
static njs_webcrypto_key_t *njs_webcrypto_key_alloc(njs_vm_t *vm,
146
    njs_webcrypto_algorithm_t *alg, unsigned usage, njs_bool_t extractable);
147
static njs_webcrypto_key_format_t njs_key_format(njs_vm_t *vm,
148
    njs_value_t *value);
149
static njs_str_t *njs_format_string(njs_webcrypto_key_format_t fmt);
150
static njs_int_t njs_key_usage(njs_vm_t *vm, njs_value_t *value,
151
    unsigned *mask);
152
static njs_int_t njs_key_ops(njs_vm_t *vm, njs_value_t *retval, unsigned mask);
153
static njs_webcrypto_algorithm_t *njs_key_algorithm(njs_vm_t *vm,
154
    njs_value_t *value);
155
static njs_str_t *njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm);
156
static njs_int_t njs_algorithm_hash(njs_vm_t *vm, njs_value_t *value,
157
    njs_webcrypto_hash_t *hash);
158
static njs_str_t *njs_algorithm_hash_name(njs_webcrypto_hash_t hash);
159
static const EVP_MD *njs_algorithm_hash_digest(njs_webcrypto_hash_t hash);
160
static njs_int_t njs_algorithm_curve(njs_vm_t *vm, njs_value_t *value,
161
    int *curve);
162
static njs_str_t *njs_algorithm_curve_name(int curve);
163
164
static njs_int_t njs_webcrypto_result(njs_vm_t *vm, njs_opaque_value_t *result,
165
    njs_int_t rc, njs_value_t *retval);
166
static njs_int_t njs_webcrypto_array_buffer(njs_vm_t *vm, njs_value_t *retval,
167
    u_char *start, size_t length);
168
static void njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...);
169
170
static njs_int_t njs_webcrypto_init(njs_vm_t *vm);
171
172
static njs_webcrypto_entry_t njs_webcrypto_alg[] = {
173
174
#define njs_webcrypto_algorithm(type, usage, fmt, raw)                       \
175
    (uintptr_t) & (njs_webcrypto_algorithm_t) { type, usage, fmt, raw }
176
177
    {
178
      njs_str("RSASSA-PKCS1-v1_5"),
179
      njs_webcrypto_algorithm(NJS_ALGORITHM_RSASSA_PKCS1_v1_5,
180
                              NJS_KEY_USAGE_SIGN |
181
                              NJS_KEY_USAGE_VERIFY |
182
                              NJS_KEY_USAGE_GENERATE_KEY,
183
                              NJS_KEY_FORMAT_PKCS8 |
184
                              NJS_KEY_FORMAT_SPKI |
185
                              NJS_KEY_FORMAT_JWK,
186
                              0)
187
    },
188
189
    {
190
      njs_str("RSA-PSS"),
191
      njs_webcrypto_algorithm(NJS_ALGORITHM_RSA_PSS,
192
                              NJS_KEY_USAGE_SIGN |
193
                              NJS_KEY_USAGE_VERIFY |
194
                              NJS_KEY_USAGE_GENERATE_KEY,
195
                              NJS_KEY_FORMAT_PKCS8 |
196
                              NJS_KEY_FORMAT_SPKI |
197
                              NJS_KEY_FORMAT_JWK,
198
                              0)
199
    },
200
201
    {
202
      njs_str("RSA-OAEP"),
203
      njs_webcrypto_algorithm(NJS_ALGORITHM_RSA_OAEP,
204
                              NJS_KEY_USAGE_ENCRYPT |
205
                              NJS_KEY_USAGE_DECRYPT |
206
                              NJS_KEY_USAGE_WRAP_KEY |
207
                              NJS_KEY_USAGE_UNWRAP_KEY |
208
                              NJS_KEY_USAGE_GENERATE_KEY,
209
                              NJS_KEY_FORMAT_PKCS8 |
210
                              NJS_KEY_FORMAT_SPKI |
211
                              NJS_KEY_FORMAT_JWK,
212
                              0)
213
    },
214
215
    {
216
      njs_str("HMAC"),
217
      njs_webcrypto_algorithm(NJS_ALGORITHM_HMAC,
218
                              NJS_KEY_USAGE_GENERATE_KEY |
219
                              NJS_KEY_USAGE_SIGN |
220
                              NJS_KEY_USAGE_VERIFY,
221
                              NJS_KEY_FORMAT_RAW |
222
                              NJS_KEY_FORMAT_JWK,
223
                              1)
224
    },
225
226
    {
227
      njs_str("AES-GCM"),
228
      njs_webcrypto_algorithm(NJS_ALGORITHM_AES_GCM,
229
                              NJS_KEY_USAGE_ENCRYPT |
230
                              NJS_KEY_USAGE_DECRYPT |
231
                              NJS_KEY_USAGE_WRAP_KEY |
232
                              NJS_KEY_USAGE_UNWRAP_KEY |
233
                              NJS_KEY_USAGE_GENERATE_KEY,
234
                              NJS_KEY_FORMAT_RAW |
235
                              NJS_KEY_FORMAT_JWK,
236
                              1)
237
    },
238
239
    {
240
      njs_str("AES-CTR"),
241
      njs_webcrypto_algorithm(NJS_ALGORITHM_AES_CTR,
242
                              NJS_KEY_USAGE_ENCRYPT |
243
                              NJS_KEY_USAGE_DECRYPT |
244
                              NJS_KEY_USAGE_WRAP_KEY |
245
                              NJS_KEY_USAGE_UNWRAP_KEY |
246
                              NJS_KEY_USAGE_GENERATE_KEY,
247
                              NJS_KEY_FORMAT_RAW |
248
                              NJS_KEY_FORMAT_JWK,
249
                              1)
250
    },
251
252
    {
253
      njs_str("AES-CBC"),
254
      njs_webcrypto_algorithm(NJS_ALGORITHM_AES_CBC,
255
                              NJS_KEY_USAGE_ENCRYPT |
256
                              NJS_KEY_USAGE_DECRYPT |
257
                              NJS_KEY_USAGE_WRAP_KEY |
258
                              NJS_KEY_USAGE_UNWRAP_KEY |
259
                              NJS_KEY_USAGE_GENERATE_KEY,
260
                              NJS_KEY_FORMAT_RAW |
261
                              NJS_KEY_FORMAT_JWK,
262
                              1)
263
    },
264
265
    {
266
      njs_str("ECDSA"),
267
      njs_webcrypto_algorithm(NJS_ALGORITHM_ECDSA,
268
                              NJS_KEY_USAGE_SIGN |
269
                              NJS_KEY_USAGE_VERIFY |
270
                              NJS_KEY_USAGE_GENERATE_KEY,
271
                              NJS_KEY_FORMAT_PKCS8 |
272
                              NJS_KEY_FORMAT_SPKI |
273
                              NJS_KEY_FORMAT_RAW |
274
                              NJS_KEY_FORMAT_JWK,
275
                              0)
276
    },
277
278
    {
279
      njs_str("ECDH"),
280
      njs_webcrypto_algorithm(NJS_ALGORITHM_ECDH,
281
                              NJS_KEY_USAGE_DERIVE_KEY |
282
                              NJS_KEY_USAGE_DERIVE_BITS |
283
                              NJS_KEY_USAGE_GENERATE_KEY,
284
                              NJS_KEY_FORMAT_PKCS8 |
285
                              NJS_KEY_FORMAT_SPKI |
286
                              NJS_KEY_FORMAT_RAW |
287
                              NJS_KEY_FORMAT_JWK,
288
                              0)
289
    },
290
291
    {
292
      njs_str("PBKDF2"),
293
      njs_webcrypto_algorithm(NJS_ALGORITHM_PBKDF2,
294
                              NJS_KEY_USAGE_DERIVE_KEY |
295
                              NJS_KEY_USAGE_DERIVE_BITS,
296
                              NJS_KEY_FORMAT_RAW,
297
                              1)
298
    },
299
300
    {
301
      njs_str("HKDF"),
302
      njs_webcrypto_algorithm(NJS_ALGORITHM_HKDF,
303
                              NJS_KEY_USAGE_DERIVE_KEY |
304
                              NJS_KEY_USAGE_DERIVE_BITS,
305
                              NJS_KEY_FORMAT_RAW,
306
                              1)
307
    },
308
309
    {
310
        njs_null_str,
311
        0
312
    }
313
};
314
315
316
static njs_webcrypto_entry_t njs_webcrypto_hash[] = {
317
    { njs_str("SHA-256"), NJS_HASH_SHA256 },
318
    { njs_str("SHA-384"), NJS_HASH_SHA384 },
319
    { njs_str("SHA-512"), NJS_HASH_SHA512 },
320
    { njs_str("SHA-1"), NJS_HASH_SHA1 },
321
    { njs_null_str, 0 }
322
};
323
324
325
static njs_webcrypto_entry_t njs_webcrypto_curve[] = {
326
    { njs_str("P-256"), NID_X9_62_prime256v1 },
327
    { njs_str("P-384"), NID_secp384r1 },
328
    { njs_str("P-521"), NID_secp521r1 },
329
    { njs_null_str, 0 }
330
};
331
332
333
static njs_webcrypto_entry_t njs_webcrypto_format[] = {
334
    { njs_str("raw"), NJS_KEY_FORMAT_RAW },
335
    { njs_str("pkcs8"), NJS_KEY_FORMAT_PKCS8 },
336
    { njs_str("spki"), NJS_KEY_FORMAT_SPKI },
337
    { njs_str("jwk"), NJS_KEY_FORMAT_JWK },
338
    { njs_null_str, NJS_KEY_FORMAT_UNKNOWN }
339
};
340
341
342
static njs_webcrypto_entry_t njs_webcrypto_usage[] = {
343
    { njs_str("decrypt"), NJS_KEY_USAGE_DECRYPT },
344
    { njs_str("deriveBits"), NJS_KEY_USAGE_DERIVE_BITS },
345
    { njs_str("deriveKey"), NJS_KEY_USAGE_DERIVE_KEY },
346
    { njs_str("encrypt"), NJS_KEY_USAGE_ENCRYPT },
347
    { njs_str("sign"), NJS_KEY_USAGE_SIGN },
348
    { njs_str("unwrapKey"), NJS_KEY_USAGE_UNWRAP_KEY },
349
    { njs_str("verify"), NJS_KEY_USAGE_VERIFY },
350
    { njs_str("wrapKey"), NJS_KEY_USAGE_WRAP_KEY },
351
    { njs_null_str, 0 }
352
};
353
354
355
static njs_webcrypto_entry_t njs_webcrypto_alg_hash[] = {
356
    { njs_str("RS1"), NJS_HASH_SHA1 },
357
    { njs_str("RS256"), NJS_HASH_SHA256 },
358
    { njs_str("RS384"), NJS_HASH_SHA384 },
359
    { njs_str("RS512"), NJS_HASH_SHA512 },
360
    { njs_str("PS1"), NJS_HASH_SHA1 },
361
    { njs_str("PS256"), NJS_HASH_SHA256 },
362
    { njs_str("PS384"), NJS_HASH_SHA384 },
363
    { njs_str("PS512"), NJS_HASH_SHA512 },
364
    { njs_str("RSA-OAEP"), NJS_HASH_SHA1 },
365
    { njs_str("RSA-OAEP-256"), NJS_HASH_SHA256 },
366
    { njs_str("RSA-OAEP-384"), NJS_HASH_SHA384 },
367
    { njs_str("RSA-OAEP-512"), NJS_HASH_SHA512 },
368
    { njs_null_str, 0 }
369
};
370
371
372
static njs_str_t
373
    njs_webcrypto_alg_name[NJS_ALGORITHM_HMAC + 1][NJS_HASH_MAX] = {
374
    {
375
        njs_null_str,
376
        njs_str("RS1"),
377
        njs_str("RS256"),
378
        njs_str("RS384"),
379
        njs_str("RS512"),
380
    },
381
382
    {
383
        njs_null_str,
384
        njs_str("PS1"),
385
        njs_str("PS256"),
386
        njs_str("PS384"),
387
        njs_str("PS512"),
388
    },
389
390
    {
391
        njs_null_str,
392
        njs_str("RSA-OAEP"),
393
        njs_str("RSA-OAEP-256"),
394
        njs_str("RSA-OAEP-384"),
395
        njs_str("RSA-OAEP-512"),
396
    },
397
398
    {
399
        njs_null_str,
400
        njs_str("HS1"),
401
        njs_str("HS256"),
402
        njs_str("HS384"),
403
        njs_str("HS512"),
404
    },
405
};
406
407
static njs_str_t njs_webcrypto_alg_aes_name[3][3 + 1] = {
408
    {
409
        njs_str("A128GCM"),
410
        njs_str("A192GCM"),
411
        njs_str("A256GCM"),
412
        njs_null_str,
413
    },
414
415
    {
416
        njs_str("A128CTR"),
417
        njs_str("A192CTR"),
418
        njs_str("A256CTR"),
419
        njs_null_str,
420
    },
421
422
    {
423
        njs_str("A128CBC"),
424
        njs_str("A192CBC"),
425
        njs_str("A256CBC"),
426
        njs_null_str,
427
    },
428
};
429
430
431
static njs_external_t  njs_ext_subtle_webcrypto[] = {
432
433
    {
434
        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
435
        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
436
        .u.property = {
437
            .value = "SubtleCrypto",
438
        }
439
    },
440
441
    {
442
        .flags = NJS_EXTERN_METHOD,
443
        .name.string = njs_str("decrypt"),
444
        .writable = 1,
445
        .configurable = 1,
446
        .enumerable = 1,
447
        .u.method = {
448
            .native = njs_ext_cipher,
449
            .magic8 = 0,
450
        }
451
    },
452
453
    {
454
        .flags = NJS_EXTERN_METHOD,
455
        .name.string = njs_str("deriveBits"),
456
        .writable = 1,
457
        .configurable = 1,
458
        .enumerable = 1,
459
        .u.method = {
460
            .native = njs_ext_derive,
461
            .magic8 = 0,
462
        }
463
    },
464
465
    {
466
        .flags = NJS_EXTERN_METHOD,
467
        .name.string = njs_str("deriveKey"),
468
        .writable = 1,
469
        .configurable = 1,
470
        .enumerable = 1,
471
        .u.method = {
472
            .native = njs_ext_derive,
473
            .magic8 = 1,
474
        }
475
    },
476
477
    {
478
        .flags = NJS_EXTERN_METHOD,
479
        .name.string = njs_str("digest"),
480
        .writable = 1,
481
        .configurable = 1,
482
        .enumerable = 1,
483
        .u.method = {
484
            .native = njs_ext_digest,
485
        }
486
    },
487
488
    {
489
        .flags = NJS_EXTERN_METHOD,
490
        .name.string = njs_str("encrypt"),
491
        .writable = 1,
492
        .configurable = 1,
493
        .enumerable = 1,
494
        .u.method = {
495
            .native = njs_ext_cipher,
496
            .magic8 = 1,
497
        }
498
    },
499
500
    {
501
        .flags = NJS_EXTERN_METHOD,
502
        .name.string = njs_str("exportKey"),
503
        .writable = 1,
504
        .configurable = 1,
505
        .enumerable = 1,
506
        .u.method = {
507
            .native = njs_ext_export_key,
508
        }
509
    },
510
511
    {
512
        .flags = NJS_EXTERN_METHOD,
513
        .name.string = njs_str("generateKey"),
514
        .writable = 1,
515
        .configurable = 1,
516
        .enumerable = 1,
517
        .u.method = {
518
            .native = njs_ext_generate_key,
519
        }
520
    },
521
522
    {
523
        .flags = NJS_EXTERN_METHOD,
524
        .name.string = njs_str("importKey"),
525
        .writable = 1,
526
        .configurable = 1,
527
        .enumerable = 1,
528
        .u.method = {
529
            .native = njs_ext_import_key,
530
        }
531
    },
532
533
    {
534
        .flags = NJS_EXTERN_METHOD,
535
        .name.string = njs_str("sign"),
536
        .writable = 1,
537
        .configurable = 1,
538
        .enumerable = 1,
539
        .u.method = {
540
            .native = njs_ext_sign,
541
        }
542
    },
543
544
    {
545
        .flags = NJS_EXTERN_METHOD,
546
        .name.string = njs_str("unwrapKey"),
547
        .writable = 1,
548
        .configurable = 1,
549
        .enumerable = 1,
550
        .u.method = {
551
            .native = njs_ext_unwrap_key,
552
        }
553
    },
554
555
    {
556
        .flags = NJS_EXTERN_METHOD,
557
        .name.string = njs_str("verify"),
558
        .writable = 1,
559
        .configurable = 1,
560
        .enumerable = 1,
561
        .u.method = {
562
            .native = njs_ext_sign,
563
            .magic8 = 1,
564
        }
565
    },
566
567
    {
568
        .flags = NJS_EXTERN_METHOD,
569
        .name.string = njs_str("wrapKey"),
570
        .writable = 1,
571
        .configurable = 1,
572
        .enumerable = 1,
573
        .u.method = {
574
            .native = njs_ext_wrap_key,
575
        }
576
    },
577
578
};
579
580
581
static njs_external_t  njs_ext_webcrypto_crypto_key[] = {
582
583
    {
584
        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
585
        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
586
        .u.property = {
587
            .value = "CryptoKey",
588
        }
589
    },
590
591
    {
592
        .flags = NJS_EXTERN_PROPERTY,
593
        .name.string = njs_str("algorithm"),
594
        .enumerable = 1,
595
        .u.property = {
596
            .handler = njs_key_ext_algorithm,
597
        }
598
    },
599
600
    {
601
        .flags = NJS_EXTERN_PROPERTY,
602
        .name.string = njs_str("extractable"),
603
        .enumerable = 1,
604
        .u.property = {
605
            .handler = njs_key_ext_extractable,
606
        }
607
    },
608
609
    {
610
        .flags = NJS_EXTERN_PROPERTY,
611
        .name.string = njs_str("type"),
612
        .enumerable = 1,
613
        .u.property = {
614
            .handler = njs_key_ext_type,
615
        }
616
    },
617
618
    {
619
        .flags = NJS_EXTERN_PROPERTY,
620
        .name.string = njs_str("usages"),
621
        .enumerable = 1,
622
        .u.property = {
623
            .handler = njs_key_ext_usages,
624
        }
625
    },
626
};
627
628
629
static njs_external_t  njs_ext_webcrypto[] = {
630
631
    {
632
        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
633
        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
634
        .u.property = {
635
            .value = "Crypto",
636
        }
637
    },
638
639
    {
640
        .flags = NJS_EXTERN_METHOD,
641
        .name.string = njs_str("getRandomValues"),
642
        .writable = 1,
643
        .configurable = 1,
644
        .enumerable = 1,
645
        .u.method = {
646
            .native = njs_ext_get_random_values,
647
        }
648
    },
649
650
    {
651
        .flags = NJS_EXTERN_OBJECT,
652
        .name.string = njs_str("subtle"),
653
        .enumerable = 1,
654
        .writable = 1,
655
        .u.object = {
656
            .enumerable = 1,
657
            .properties = njs_ext_subtle_webcrypto,
658
            .nproperties = njs_nitems(njs_ext_subtle_webcrypto),
659
        }
660
    },
661
662
};
663
664
665
njs_module_t  njs_webcrypto_module = {
666
    .name = njs_str("webcrypto"),
667
    .preinit = NULL,
668
    .init = njs_webcrypto_init,
669
};
670
671
672
static const njs_str_t  string_alg = njs_str("alg");
673
static const njs_str_t  string_d = njs_str("d");
674
static const njs_str_t  string_dp = njs_str("dp");
675
static const njs_str_t  string_dq = njs_str("dq");
676
static const njs_str_t  string_e = njs_str("e");
677
static const njs_str_t  string_k = njs_str("k");
678
static const njs_str_t  string_n = njs_str("n");
679
static const njs_str_t  string_p = njs_str("p");
680
static const njs_str_t  string_q = njs_str("q");
681
static const njs_str_t  string_qi = njs_str("qi");
682
static const njs_str_t  string_x = njs_str("x");
683
static const njs_str_t  string_y = njs_str("y");
684
static const njs_str_t  string_ext = njs_str("ext");
685
static const njs_str_t  string_crv = njs_str("crv");
686
static const njs_str_t  string_kty = njs_str("kty");
687
static const njs_str_t  key_ops = njs_str("key_ops");
688
static const njs_str_t  string_hash = njs_str("hash");
689
static const njs_str_t  string_name = njs_str("name");
690
static const njs_str_t  string_length = njs_str("length");
691
static const njs_str_t  string_ml = njs_str("modulusLength");
692
static const njs_str_t  string_curve = njs_str("namedCurve");
693
694
695
static njs_int_t    njs_webcrypto_crypto_key_proto_id;
696
697
698
static njs_int_t
699
njs_ext_cipher(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
700
    njs_index_t encrypt, njs_value_t *retval)
701
0
{
702
0
    unsigned                   mask;
703
0
    njs_int_t                  ret;
704
0
    njs_str_t                  data;
705
0
    njs_value_t                *options;
706
0
    njs_opaque_value_t         result;
707
0
    njs_webcrypto_key_t        *key;
708
0
    njs_webcrypto_algorithm_t  *alg;
709
710
0
    options = njs_arg(args, nargs, 1);
711
0
    alg = njs_key_algorithm(vm, options);
712
0
    if (njs_slow_path(alg == NULL)) {
713
0
        goto fail;
714
0
    }
715
716
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
717
0
                          njs_arg(args, nargs, 2));
718
0
    if (njs_slow_path(key == NULL)) {
719
0
        njs_vm_type_error(vm, "\"key\" is not a CryptoKey object");
720
0
        goto fail;
721
0
    }
722
723
0
    mask = encrypt ? NJS_KEY_USAGE_ENCRYPT : NJS_KEY_USAGE_DECRYPT;
724
0
    if (njs_slow_path(!(key->usage & mask))) {
725
0
        njs_vm_type_error(vm, "provide key does not support %s operation",
726
0
                          encrypt ? "encrypt" : "decrypt");
727
0
        goto fail;
728
0
    }
729
730
0
    if (njs_slow_path(key->alg != alg)) {
731
0
        njs_vm_type_error(vm, "cannot %s using \"%V\" with \"%V\" key",
732
0
                          encrypt ? "encrypt" : "decrypt",
733
0
                          njs_algorithm_string(key->alg),
734
0
                          njs_algorithm_string(alg));
735
0
        goto fail;
736
0
    }
737
738
0
    ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 3));
739
0
    if (njs_slow_path(ret != NJS_OK)) {
740
0
        goto fail;
741
0
    }
742
743
0
    switch (alg->type) {
744
0
    case NJS_ALGORITHM_RSA_OAEP:
745
0
        ret = njs_cipher_pkey(vm, &data, key, encrypt, njs_value_arg(&result));
746
0
        break;
747
748
0
    case NJS_ALGORITHM_AES_GCM:
749
0
        ret = njs_cipher_aes_gcm(vm, &data, key, options, encrypt,
750
0
                                 njs_value_arg(&result));
751
0
        break;
752
753
0
    case NJS_ALGORITHM_AES_CTR:
754
0
        ret = njs_cipher_aes_ctr(vm, &data, key, options, encrypt,
755
0
                                 njs_value_arg(&result));
756
0
        break;
757
758
0
    case NJS_ALGORITHM_AES_CBC:
759
0
    default:
760
0
        ret = njs_cipher_aes_cbc(vm, &data, key, options, encrypt,
761
0
                                 njs_value_arg(&result));
762
0
    }
763
764
0
    return njs_webcrypto_result(vm, &result, ret, retval);
765
766
0
fail:
767
768
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
769
0
}
770
771
772
static njs_int_t
773
njs_cipher_pkey(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
774
    njs_index_t encrypt, njs_value_t *retval)
775
0
{
776
0
    u_char                  *dst;
777
0
    size_t                  outlen;
778
0
    njs_int_t               ret;
779
0
    const EVP_MD            *md;
780
0
    EVP_PKEY_CTX            *ctx;
781
0
    EVP_PKEY_cipher_t       cipher;
782
0
    EVP_PKEY_cipher_init_t  init;
783
784
0
    ctx = EVP_PKEY_CTX_new(key->u.a.pkey, NULL);
785
0
    if (njs_slow_path(ctx == NULL)) {
786
0
        njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed");
787
0
        return NJS_ERROR;
788
0
    }
789
790
0
    if (encrypt) {
791
0
        init = EVP_PKEY_encrypt_init;
792
0
        cipher = EVP_PKEY_encrypt;
793
794
0
    } else {
795
0
        init = EVP_PKEY_decrypt_init;
796
0
        cipher = EVP_PKEY_decrypt;
797
0
    }
798
799
0
    ret = init(ctx);
800
0
    if (njs_slow_path(ret <= 0)) {
801
0
        njs_webcrypto_error(vm, "EVP_PKEY_%scrypt_init() failed",
802
0
                            encrypt ? "en" : "de");
803
0
        ret = NJS_ERROR;
804
0
        goto fail;
805
0
    }
806
807
0
    md = njs_algorithm_hash_digest(key->hash);
808
809
0
    EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
810
0
    EVP_PKEY_CTX_set_signature_md(ctx, md);
811
0
    EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md);
812
813
0
    ret = cipher(ctx, NULL, &outlen, data->start, data->length);
814
0
    if (njs_slow_path(ret <= 0)) {
815
0
        njs_webcrypto_error(vm, "EVP_PKEY_%scrypt() failed",
816
0
                            encrypt ? "en" : "de");
817
0
        ret = NJS_ERROR;
818
0
        goto fail;
819
0
    }
820
821
0
    dst = njs_mp_alloc(njs_vm_memory_pool(vm), outlen);
822
0
    if (njs_slow_path(dst == NULL)) {
823
0
        njs_vm_memory_error(vm);
824
0
        ret = NJS_ERROR;
825
0
        goto fail;
826
0
    }
827
828
0
    ret = cipher(ctx, dst, &outlen, data->start, data->length);
829
0
    if (njs_slow_path(ret <= 0)) {
830
0
        njs_webcrypto_error(vm, "EVP_PKEY_%scrypt() failed",
831
0
                            encrypt ? "en" : "de");
832
0
        ret = NJS_ERROR;
833
0
        goto fail;
834
0
    }
835
836
0
    ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen);
837
838
0
fail:
839
840
0
    EVP_PKEY_CTX_free(ctx);
841
842
0
    return ret;
843
0
}
844
845
846
static njs_int_t
847
njs_cipher_aes_gcm(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
848
    njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval)
849
0
{
850
0
    int                 len, outlen, dstlen;
851
0
    u_char              *dst, *p;
852
0
    int64_t             taglen;
853
0
    njs_str_t           iv, aad;
854
0
    njs_int_t           ret;
855
0
    njs_value_t         *value;
856
0
    EVP_CIPHER_CTX      *ctx;
857
0
    const EVP_CIPHER    *cipher;
858
0
    njs_opaque_value_t  lvalue;
859
860
0
    static const njs_str_t  string_iv = njs_str("iv");
861
0
    static const njs_str_t  string_ad = njs_str("additionalData");
862
0
    static const njs_str_t  string_tl = njs_str("tagLength");
863
864
0
    switch (key->u.s.raw.length) {
865
0
    case 16:
866
0
        cipher = EVP_aes_128_gcm();
867
0
        break;
868
869
0
    case 24:
870
0
        cipher = EVP_aes_192_gcm();
871
0
        break;
872
873
0
    case 32:
874
0
        cipher = EVP_aes_256_gcm();
875
0
        break;
876
877
0
    default:
878
0
        njs_vm_type_error(vm, "AES-GCM Invalid key length");
879
0
        return NJS_ERROR;
880
0
    }
881
882
0
    value = njs_vm_object_prop(vm, options, &string_iv, &lvalue);
883
0
    if (value == NULL) {
884
0
        njs_vm_type_error(vm, "AES-GCM algorithm.iv is not provided");
885
0
        return NJS_ERROR;
886
0
    }
887
888
0
    ret = njs_vm_value_to_bytes(vm, &iv, njs_value_arg(&lvalue));
889
0
    if (njs_slow_path(ret != NJS_OK)) {
890
0
        return NJS_ERROR;
891
0
    }
892
893
0
    taglen = 128;
894
895
0
    value = njs_vm_object_prop(vm, options, &string_tl, &lvalue);
896
0
    if (value != NULL && !njs_value_is_undefined(value)) {
897
0
        ret = njs_value_to_integer(vm, value, &taglen);
898
0
        if (njs_slow_path(ret != NJS_OK)) {
899
0
            return NJS_ERROR;
900
0
        }
901
0
    }
902
903
0
    if (njs_slow_path(taglen != 32
904
0
                      && taglen != 64
905
0
                      && taglen != 96
906
0
                      && taglen != 104
907
0
                      && taglen != 112
908
0
                      && taglen != 120
909
0
                      && taglen != 128))
910
0
    {
911
0
        njs_vm_type_error(vm, "AES-GCM Invalid tagLength");
912
0
        return NJS_ERROR;
913
0
    }
914
915
0
    taglen /= 8;
916
917
0
    if (njs_slow_path(!encrypt && (data->length < (size_t) taglen))) {
918
0
        njs_vm_type_error(vm, "AES-GCM data is too short");
919
0
        return NJS_ERROR;
920
0
    }
921
922
0
    ctx = EVP_CIPHER_CTX_new();
923
0
    if (njs_slow_path(ctx == NULL)) {
924
0
        njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
925
0
        return NJS_ERROR;
926
0
    }
927
928
0
    ret = EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt);
929
0
    if (njs_slow_path(ret <= 0)) {
930
0
        njs_webcrypto_error(vm, "EVP_%sInit_ex() failed",
931
0
                            encrypt ? "Encrypt" : "Decrypt");
932
0
        ret = NJS_ERROR;
933
0
        goto fail;
934
0
    }
935
936
0
    ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.length, NULL);
937
0
    if (njs_slow_path(ret <= 0)) {
938
0
        njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed");
939
0
        ret = NJS_ERROR;
940
0
        goto fail;
941
0
    }
942
943
0
    ret = EVP_CipherInit_ex(ctx, NULL, NULL, key->u.s.raw.start, iv.start,
944
0
                            encrypt);
945
0
    if (njs_slow_path(ret <= 0)) {
946
0
        njs_webcrypto_error(vm, "EVP_%sInit_ex() failed",
947
0
                            encrypt ? "Encrypt" : "Decrypt");
948
0
        ret = NJS_ERROR;
949
0
        goto fail;
950
0
    }
951
952
0
    if (!encrypt) {
953
0
        ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, taglen,
954
0
                                  &data->start[data->length - taglen]);
955
0
        if (njs_slow_path(ret <= 0)) {
956
0
            njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed");
957
0
            ret = NJS_ERROR;
958
0
            goto fail;
959
0
        }
960
0
    }
961
962
0
    aad.length = 0;
963
964
0
    value = njs_vm_object_prop(vm, options, &string_ad, &lvalue);
965
0
    if (value != NULL && !njs_value_is_undefined(value)) {
966
0
        ret = njs_vm_value_to_bytes(vm, &aad, value);
967
0
        if (njs_slow_path(ret != NJS_OK)) {
968
0
            return NJS_ERROR;
969
0
        }
970
0
    }
971
972
0
    if (aad.length != 0) {
973
0
        ret = EVP_CipherUpdate(ctx, NULL, &outlen, aad.start, aad.length);
974
0
        if (njs_slow_path(ret <= 0)) {
975
0
            njs_webcrypto_error(vm, "EVP_%sUpdate() failed",
976
0
                                encrypt ? "Encrypt" : "Decrypt");
977
0
            ret = NJS_ERROR;
978
0
            goto fail;
979
0
        }
980
0
    }
981
982
0
    dstlen = data->length + EVP_CIPHER_CTX_block_size(ctx) + taglen;
983
0
    dst = njs_mp_alloc(njs_vm_memory_pool(vm), dstlen);
984
0
    if (njs_slow_path(dst == NULL)) {
985
0
        njs_vm_memory_error(vm);
986
0
        return NJS_ERROR;
987
0
    }
988
989
0
    ret = EVP_CipherUpdate(ctx, dst, &outlen, data->start,
990
0
                           data->length - (encrypt ? 0 : taglen));
991
0
    if (njs_slow_path(ret <= 0)) {
992
0
        njs_webcrypto_error(vm, "EVP_%sUpdate() failed",
993
0
                            encrypt ? "Encrypt" : "Decrypt");
994
0
        ret = NJS_ERROR;
995
0
        goto fail;
996
0
    }
997
998
0
    p = &dst[outlen];
999
0
    len = EVP_CIPHER_CTX_block_size(ctx);
1000
1001
0
    ret = EVP_CipherFinal_ex(ctx, p, &len);
1002
0
    if (njs_slow_path(ret <= 0)) {
1003
0
        njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed",
1004
0
                            encrypt ? "Encrypt" : "Decrypt");
1005
0
        ret = NJS_ERROR;
1006
0
        goto fail;
1007
0
    }
1008
1009
0
    outlen += len;
1010
0
    p += len;
1011
1012
0
    if (encrypt) {
1013
0
        ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, taglen, p);
1014
0
        if (njs_slow_path(ret <= 0)) {
1015
0
            njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed");
1016
0
            ret = NJS_ERROR;
1017
0
            goto fail;
1018
0
        }
1019
1020
0
        outlen += taglen;
1021
0
    }
1022
1023
0
    ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen);
1024
1025
0
fail:
1026
1027
0
    EVP_CIPHER_CTX_free(ctx);
1028
1029
0
    return ret;
1030
0
}
1031
1032
1033
static njs_int_t
1034
njs_cipher_aes_ctr128(njs_vm_t *vm, const EVP_CIPHER *cipher, u_char *key,
1035
    u_char *data, size_t dlen, u_char *counter, u_char *dst, int *olen,
1036
    njs_bool_t encrypt)
1037
0
{
1038
0
    int             len, outlen;
1039
0
    njs_int_t       ret;
1040
0
    EVP_CIPHER_CTX  *ctx;
1041
1042
0
    ctx = EVP_CIPHER_CTX_new();
1043
0
    if (njs_slow_path(ctx == NULL)) {
1044
0
        njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
1045
0
        return NJS_ERROR;
1046
0
    }
1047
1048
0
    ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, counter, encrypt);
1049
0
    if (njs_slow_path(ret <= 0)) {
1050
0
        njs_webcrypto_error(vm, "EVP_%sInit_ex() failed",
1051
0
                            encrypt ? "Encrypt" : "Decrypt");
1052
0
        ret = NJS_ERROR;
1053
0
        goto fail;
1054
0
    }
1055
1056
0
    ret = EVP_CipherUpdate(ctx, dst, &outlen, data, dlen);
1057
0
    if (njs_slow_path(ret <= 0)) {
1058
0
        njs_webcrypto_error(vm, "EVP_%sUpdate() failed",
1059
0
                            encrypt ? "Encrypt" : "Decrypt");
1060
0
        ret = NJS_ERROR;
1061
0
        goto fail;
1062
0
    }
1063
1064
0
    ret = EVP_CipherFinal_ex(ctx, &dst[outlen], &len);
1065
0
    if (njs_slow_path(ret <= 0)) {
1066
0
        njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed",
1067
0
                            encrypt ? "Encrypt" : "Decrypt");
1068
0
        ret = NJS_ERROR;
1069
0
        goto fail;
1070
0
    }
1071
1072
0
    outlen += len;
1073
0
    *olen = outlen;
1074
1075
0
    ret = NJS_OK;
1076
1077
0
fail:
1078
1079
0
    EVP_CIPHER_CTX_free(ctx);
1080
1081
0
    return ret;
1082
0
}
1083
1084
1085
njs_inline njs_uint_t
1086
njs_ceil_div(njs_uint_t dend, njs_uint_t dsor)
1087
0
{
1088
0
    return (dsor == 0) ? 0 : 1 + (dend - 1) / dsor;
1089
0
}
1090
1091
1092
njs_inline BIGNUM *
1093
njs_bn_counter128(njs_str_t *ctr, njs_uint_t bits)
1094
0
{
1095
0
    njs_uint_t  remainder, bytes;
1096
0
    uint8_t     buf[16];
1097
1098
0
    remainder = bits % 8;
1099
1100
0
    if (remainder == 0) {
1101
0
        bytes = bits / 8;
1102
1103
0
        return BN_bin2bn(&ctr->start[ctr->length - bytes], bytes, NULL);
1104
0
    }
1105
1106
0
    bytes = njs_ceil_div(bits, 8);
1107
1108
0
    memcpy(buf, &ctr->start[ctr->length - bytes], bytes);
1109
1110
0
    buf[0] &= ~(0xFF << remainder);
1111
1112
0
    return BN_bin2bn(buf, bytes, NULL);
1113
0
}
1114
1115
1116
njs_inline void
1117
njs_counter128_reset(u_char *src, u_char *dst, njs_uint_t bits)
1118
0
{
1119
0
    size_t      index;
1120
0
    njs_uint_t  remainder, bytes;
1121
1122
0
    bytes = bits / 8;
1123
0
    remainder = bits % 8;
1124
1125
0
    memcpy(dst, src, 16);
1126
1127
0
    index = 16 - bytes;
1128
1129
0
    memset(&dst[index], 0, bytes);
1130
1131
0
    if (remainder) {
1132
0
        dst[index - 1] &= 0xff << remainder;
1133
0
    }
1134
0
}
1135
1136
1137
static njs_int_t
1138
njs_cipher_aes_ctr(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
1139
    njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval)
1140
0
{
1141
0
    int                 len, len2;
1142
0
    u_char              *dst;
1143
0
    int64_t             length;
1144
0
    BIGNUM              *total, *blocks, *left, *ctr;
1145
0
    njs_int_t           ret;
1146
0
    njs_str_t           iv;
1147
0
    njs_uint_t          size1;
1148
0
    njs_value_t         *value;
1149
0
    const EVP_CIPHER    *cipher;
1150
0
    njs_opaque_value_t  lvalue;
1151
0
    u_char              iv2[16];
1152
1153
0
    static const njs_str_t  string_counter = njs_str("counter");
1154
1155
0
    switch (key->u.s.raw.length) {
1156
0
    case 16:
1157
0
        cipher = EVP_aes_128_ctr();
1158
0
        break;
1159
1160
0
    case 24:
1161
0
        cipher = EVP_aes_192_ctr();
1162
0
        break;
1163
1164
0
    case 32:
1165
0
        cipher = EVP_aes_256_ctr();
1166
0
        break;
1167
1168
0
    default:
1169
0
        njs_vm_type_error(vm, "AES-CTR Invalid key length");
1170
0
        return NJS_ERROR;
1171
0
    }
1172
1173
0
    value = njs_vm_object_prop(vm, options, &string_counter, &lvalue);
1174
0
    if (value == NULL) {
1175
0
        njs_vm_type_error(vm, "AES-CTR algorithm.counter is not provided");
1176
0
        return NJS_ERROR;
1177
0
    }
1178
1179
0
    ret = njs_vm_value_to_bytes(vm, &iv, value);
1180
0
    if (njs_slow_path(ret != NJS_OK)) {
1181
0
        return NJS_ERROR;
1182
0
    }
1183
1184
0
    if (njs_slow_path(iv.length != 16)) {
1185
0
        njs_vm_type_error(vm, "AES-CTR algorithm.counter must be 16 bytes "
1186
0
                          "long");
1187
0
        return NJS_ERROR;
1188
0
    }
1189
1190
0
    value = njs_vm_object_prop(vm, options, &string_length, &lvalue);
1191
0
    if (value == NULL) {
1192
0
        njs_vm_type_error(vm, "AES-CTR algorithm.length is not provided");
1193
0
        return NJS_ERROR;
1194
0
    }
1195
1196
0
    ret = njs_value_to_integer(vm, value, &length);
1197
0
    if (njs_slow_path(ret != NJS_OK)) {
1198
0
        return NJS_ERROR;
1199
0
    }
1200
1201
0
    if (njs_slow_path(length == 0 || length > 128)) {
1202
0
        njs_vm_type_error(vm, "AES-CTR algorithm.length must be between "
1203
0
                          "1 and 128");
1204
0
        return NJS_ERROR;
1205
0
    }
1206
1207
0
    ctr = NULL;
1208
0
    blocks = NULL;
1209
0
    left = NULL;
1210
1211
0
    total = BN_new();
1212
0
    if (njs_slow_path(total == NULL)) {
1213
0
        njs_webcrypto_error(vm, "BN_new() failed");
1214
0
        return NJS_ERROR;
1215
0
    }
1216
1217
0
    ret = BN_lshift(total, BN_value_one(), length);
1218
0
    if (njs_slow_path(ret != 1)) {
1219
0
        njs_webcrypto_error(vm, "BN_lshift() failed");
1220
0
        ret = NJS_ERROR;
1221
0
        goto fail;
1222
0
    }
1223
1224
0
    ctr = njs_bn_counter128(&iv, length);
1225
0
    if (njs_slow_path(ctr == NULL)) {
1226
0
        njs_webcrypto_error(vm, "BN_bin2bn() failed");
1227
0
        ret = NJS_ERROR;
1228
0
        goto fail;
1229
0
    }
1230
1231
0
    blocks = BN_new();
1232
0
    if (njs_slow_path(blocks == NULL)) {
1233
0
        njs_webcrypto_error(vm, "BN_new() failed");
1234
0
        return NJS_ERROR;
1235
0
    }
1236
1237
0
    ret = BN_set_word(blocks, njs_ceil_div(data->length, AES_BLOCK_SIZE));
1238
0
    if (njs_slow_path(ret != 1)) {
1239
0
        njs_webcrypto_error(vm, "BN_set_word() failed");
1240
0
        ret = NJS_ERROR;
1241
0
        goto fail;
1242
0
    }
1243
1244
0
    ret = BN_cmp(blocks, total);
1245
0
    if (njs_slow_path(ret > 0)) {
1246
0
        njs_vm_type_error(vm, "AES-CTR repeated counter");
1247
0
        ret = NJS_ERROR;
1248
0
        goto fail;
1249
0
    }
1250
1251
0
    left = BN_new();
1252
0
    if (njs_slow_path(left == NULL)) {
1253
0
        njs_webcrypto_error(vm, "BN_new() failed");
1254
0
        return NJS_ERROR;
1255
0
    }
1256
1257
0
    ret = BN_sub(left, total, ctr);
1258
0
    if (njs_slow_path(ret != 1)) {
1259
0
        njs_webcrypto_error(vm, "BN_sub() failed");
1260
0
        ret = NJS_ERROR;
1261
0
        goto fail;
1262
0
    }
1263
1264
0
    dst = njs_mp_alloc(njs_vm_memory_pool(vm),
1265
0
                       data->length + EVP_MAX_BLOCK_LENGTH);
1266
0
    if (njs_slow_path(dst == NULL)) {
1267
0
        njs_vm_memory_error(vm);
1268
0
        return NJS_ERROR;
1269
0
    }
1270
1271
0
    ret = BN_cmp(left, blocks);
1272
0
    if (ret >= 0) {
1273
1274
        /*
1275
         * Doing a single run if a counter is not wrapped-around
1276
         * during the ciphering.
1277
         * */
1278
1279
0
        ret = njs_cipher_aes_ctr128(vm, cipher, key->u.s.raw.start,
1280
0
                                    data->start, data->length, iv.start, dst,
1281
0
                                    &len, encrypt);
1282
0
        if (njs_slow_path(ret != NJS_OK)) {
1283
0
            goto fail;
1284
0
        }
1285
1286
0
        goto done;
1287
0
    }
1288
1289
    /*
1290
     * Otherwise splitting ciphering into two parts:
1291
     *  Until the wrapping moment
1292
     *  After the resetting counter to zero.
1293
     */
1294
1295
0
    size1 = BN_get_word(left) * AES_BLOCK_SIZE;
1296
1297
0
    ret = njs_cipher_aes_ctr128(vm, cipher, key->u.s.raw.start, data->start,
1298
0
                                size1, iv.start, dst, &len, encrypt);
1299
0
    if (njs_slow_path(ret != NJS_OK)) {
1300
0
        goto fail;
1301
0
    }
1302
1303
0
    njs_counter128_reset(iv.start, (u_char *) iv2, length);
1304
1305
0
    ret = njs_cipher_aes_ctr128(vm, cipher, key->u.s.raw.start,
1306
0
                                &data->start[size1], data->length - size1,
1307
0
                                iv2, &dst[size1], &len2, encrypt);
1308
0
    if (njs_slow_path(ret != NJS_OK)) {
1309
0
        goto fail;
1310
0
    }
1311
1312
0
    len += len2;
1313
1314
0
done:
1315
1316
0
    ret = njs_vm_value_array_buffer_set(vm, retval, dst, len);
1317
1318
0
fail:
1319
1320
0
    BN_free(total);
1321
1322
0
    if (ctr != NULL) {
1323
0
        BN_free(ctr);
1324
0
    }
1325
1326
0
    if (blocks != NULL) {
1327
0
        BN_free(blocks);
1328
0
    }
1329
1330
0
    if (left != NULL) {
1331
0
        BN_free(left);
1332
0
    }
1333
1334
0
    return ret;
1335
0
}
1336
1337
1338
static njs_int_t
1339
njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
1340
    njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval)
1341
0
{
1342
0
    int                 olen_max, olen, olen2;
1343
0
    u_char              *dst;
1344
0
    unsigned            remainder;
1345
0
    njs_str_t           iv;
1346
0
    njs_int_t           ret;
1347
0
    njs_value_t         *value;
1348
0
    EVP_CIPHER_CTX      *ctx;
1349
0
    const EVP_CIPHER    *cipher;
1350
0
    njs_opaque_value_t  lvalue;
1351
1352
0
    static const njs_str_t  string_iv = njs_str("iv");
1353
1354
0
    switch (key->u.s.raw.length) {
1355
0
    case 16:
1356
0
        cipher = EVP_aes_128_cbc();
1357
0
        break;
1358
1359
0
    case 24:
1360
0
        cipher = EVP_aes_192_cbc();
1361
0
        break;
1362
1363
0
    case 32:
1364
0
        cipher = EVP_aes_256_cbc();
1365
0
        break;
1366
1367
0
    default:
1368
0
        njs_vm_type_error(vm, "AES-CBC Invalid key length");
1369
0
        return NJS_ERROR;
1370
0
    }
1371
1372
0
    value = njs_vm_object_prop(vm, options, &string_iv, &lvalue);
1373
0
    if (value == NULL) {
1374
0
        njs_vm_type_error(vm, "AES-CBC algorithm.iv is not provided");
1375
0
        return NJS_ERROR;
1376
0
    }
1377
1378
0
    ret = njs_vm_value_to_bytes(vm, &iv, value);
1379
0
    if (njs_slow_path(ret != NJS_OK)) {
1380
0
        return NJS_ERROR;
1381
0
    }
1382
1383
0
    if (njs_slow_path(iv.length != 16)) {
1384
0
        njs_vm_type_error(vm, "AES-CBC algorithm.iv must be 16 bytes long");
1385
0
        return NJS_ERROR;
1386
0
    }
1387
1388
0
    olen_max = data->length + AES_BLOCK_SIZE - 1;
1389
0
    remainder = olen_max % AES_BLOCK_SIZE;
1390
1391
0
    if (remainder != 0) {
1392
0
        olen_max += AES_BLOCK_SIZE - remainder;
1393
0
    }
1394
1395
0
    ctx = EVP_CIPHER_CTX_new();
1396
0
    if (njs_slow_path(ctx == NULL)) {
1397
0
        njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
1398
0
        return NJS_ERROR;
1399
0
    }
1400
1401
0
    ret = EVP_CipherInit_ex(ctx, cipher, NULL, key->u.s.raw.start, iv.start,
1402
0
                            encrypt);
1403
0
    if (njs_slow_path(ret <= 0)) {
1404
0
        njs_webcrypto_error(vm, "EVP_%SInit_ex() failed",
1405
0
                            encrypt ? "Encrypt" : "Decrypt");
1406
0
        ret = NJS_ERROR;
1407
0
        goto fail;
1408
0
    }
1409
1410
0
    dst = njs_mp_alloc(njs_vm_memory_pool(vm), olen_max);
1411
0
    if (njs_slow_path(dst == NULL)) {
1412
0
        njs_vm_memory_error(vm);
1413
0
        ret = NJS_ERROR;
1414
0
        goto fail;
1415
0
    }
1416
1417
0
    ret = EVP_CipherUpdate(ctx, dst, &olen, data->start, data->length);
1418
0
    if (njs_slow_path(ret <= 0)) {
1419
0
        njs_webcrypto_error(vm, "EVP_%SUpdate() failed",
1420
0
                            encrypt ? "Encrypt" : "Decrypt");
1421
0
        ret = NJS_ERROR;
1422
0
        goto fail;
1423
0
    }
1424
1425
0
    ret = EVP_CipherFinal_ex(ctx, &dst[olen], &olen2);
1426
0
    if (njs_slow_path(ret <= 0)) {
1427
0
        njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed",
1428
0
                            encrypt ? "Encrypt" : "Decrypt");
1429
0
        ret = NJS_ERROR;
1430
0
        goto fail;
1431
0
    }
1432
1433
0
    olen += olen2;
1434
1435
0
    ret = njs_vm_value_array_buffer_set(vm, retval, dst, olen);
1436
1437
0
fail:
1438
1439
0
    EVP_CIPHER_CTX_free(ctx);
1440
1441
0
    return ret;
1442
0
}
1443
1444
1445
static njs_int_t
1446
njs_ext_derive_ecdh(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1447
    njs_index_t derive_key, njs_webcrypto_key_t *key, njs_value_t *retval)
1448
0
{
1449
0
    u_char                     *k;
1450
0
    size_t                     olen;
1451
0
    int64_t                    length;
1452
0
    unsigned                   usage;
1453
0
    EVP_PKEY                   *priv_pkey, *pub_pkey;
1454
0
    njs_int_t                  ret;
1455
0
    njs_value_t                *value, *dobject;
1456
0
    EVP_PKEY_CTX               *pctx;
1457
0
    njs_opaque_value_t         lvalue;
1458
0
    njs_webcrypto_key_t        *dkey, *pkey;
1459
0
    njs_webcrypto_algorithm_t  *dalg;
1460
1461
0
    static const njs_str_t  string_public = njs_str("public");
1462
1463
0
    dobject = njs_arg(args, nargs, 3);
1464
1465
0
    if (derive_key) {
1466
0
        dalg = njs_key_algorithm(vm, dobject);
1467
0
        if (njs_slow_path(dalg == NULL)) {
1468
0
            goto fail;
1469
0
        }
1470
1471
0
        value = njs_vm_object_prop(vm, dobject, &string_length, &lvalue);
1472
0
        if (value == NULL) {
1473
0
            njs_vm_type_error(vm, "derivedKeyAlgorithm.length is not provided");
1474
0
            goto fail;
1475
0
        }
1476
1477
0
    } else {
1478
0
        dalg = NULL;
1479
0
        value = dobject;
1480
0
    }
1481
1482
0
    ret = njs_value_to_integer(vm, value, &length);
1483
0
    if (njs_slow_path(ret != NJS_OK)) {
1484
0
        goto fail;
1485
0
    }
1486
1487
0
    dkey = NULL;
1488
0
    length /= 8;
1489
1490
0
    if (derive_key) {
1491
0
        ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage);
1492
0
        if (njs_slow_path(ret != NJS_OK)) {
1493
0
            goto fail;
1494
0
        }
1495
1496
0
        if (njs_slow_path(usage & ~dalg->usage)) {
1497
0
            njs_vm_type_error(vm, "unsupported key usage for \"ECDH\" key");
1498
0
            goto fail;
1499
0
        }
1500
1501
0
        dkey = njs_mp_zalloc(njs_vm_memory_pool(vm),
1502
0
                             sizeof(njs_webcrypto_key_t));
1503
0
        if (njs_slow_path(dkey == NULL)) {
1504
0
            njs_vm_memory_error(vm);
1505
0
            goto fail;
1506
0
        }
1507
1508
0
        dkey->alg = dalg;
1509
0
        dkey->usage = usage;
1510
0
    }
1511
1512
0
    value = njs_vm_object_prop(vm, njs_arg(args, nargs, 1), &string_public,
1513
0
                               &lvalue);
1514
0
    if (value == NULL) {
1515
0
        njs_vm_type_error(vm, "ECDH algorithm.public is not provided");
1516
0
        goto fail;
1517
0
    }
1518
1519
0
    pkey = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
1520
0
    if (njs_slow_path(pkey == NULL)) {
1521
0
        njs_vm_type_error(vm, "algorithm.public is not a CryptoKey object");
1522
0
        goto fail;
1523
0
    }
1524
1525
0
    if (njs_slow_path(pkey->alg->type != NJS_ALGORITHM_ECDH)) {
1526
0
        njs_vm_type_error(vm, "algorithm.public is not an ECDH key");
1527
0
        goto fail;
1528
0
    }
1529
1530
0
    if (njs_slow_path(key->u.a.curve != pkey->u.a.curve)) {
1531
0
        njs_vm_type_error(vm, "ECDH keys must use the same curve");
1532
0
        goto fail;
1533
0
    }
1534
1535
0
    if (!key->u.a.privat) {
1536
0
        njs_vm_type_error(vm, "baseKey must be a private key for ECDH");
1537
0
        goto fail;
1538
0
    }
1539
1540
0
    if (pkey->u.a.privat) {
1541
0
        njs_vm_type_error(vm, "algorithm.public must be a public key");
1542
0
        goto fail;
1543
0
    }
1544
1545
0
    priv_pkey = key->u.a.pkey;
1546
0
    pub_pkey = pkey->u.a.pkey;
1547
1548
0
    pctx = EVP_PKEY_CTX_new(priv_pkey, NULL);
1549
0
    if (njs_slow_path(pctx == NULL)) {
1550
0
        njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed");
1551
0
        goto fail;
1552
0
    }
1553
1554
0
    if (EVP_PKEY_derive_init(pctx) != 1) {
1555
0
        njs_webcrypto_error(vm, "EVP_PKEY_derive_init() failed");
1556
0
        EVP_PKEY_CTX_free(pctx);
1557
0
        goto fail;
1558
0
    }
1559
1560
0
    if (EVP_PKEY_derive_set_peer(pctx, pub_pkey) != 1) {
1561
0
        njs_webcrypto_error(vm, "EVP_PKEY_derive_set_peer() failed");
1562
0
        EVP_PKEY_CTX_free(pctx);
1563
0
        goto fail;
1564
0
    }
1565
1566
0
    olen = (size_t) length;
1567
0
    if (EVP_PKEY_derive(pctx, NULL, &olen) != 1) {
1568
0
        njs_webcrypto_error(vm, "EVP_PKEY_derive() failed (size query)");
1569
0
        EVP_PKEY_CTX_free(pctx);
1570
0
        goto fail;
1571
0
    }
1572
1573
0
    if (njs_slow_path(olen < (size_t) length)) {
1574
0
        njs_vm_type_error(vm, "derived bit length is too small");
1575
0
        EVP_PKEY_CTX_free(pctx);
1576
0
        goto fail;
1577
0
    }
1578
1579
0
    k = njs_mp_alloc(njs_vm_memory_pool(vm), olen);
1580
0
    if (njs_slow_path(k == NULL)) {
1581
0
        njs_vm_memory_error(vm);
1582
0
        EVP_PKEY_CTX_free(pctx);
1583
0
        goto fail;
1584
0
    }
1585
1586
0
    if (EVP_PKEY_derive(pctx, k, &olen) != 1) {
1587
0
        njs_webcrypto_error(vm, "EVP_PKEY_derive() failed");
1588
0
        EVP_PKEY_CTX_free(pctx);
1589
0
        goto fail;
1590
0
    }
1591
1592
0
    EVP_PKEY_CTX_free(pctx);
1593
1594
0
    if (derive_key) {
1595
0
        if (dalg->type == NJS_ALGORITHM_HMAC) {
1596
0
            ret = njs_algorithm_hash(vm, dobject, &dkey->hash);
1597
0
            if (njs_slow_path(ret == NJS_ERROR)) {
1598
0
                goto fail;
1599
0
            }
1600
0
        }
1601
1602
0
        dkey->extractable = njs_value_bool(njs_arg(args, nargs, 4));
1603
1604
0
        dkey->u.s.raw.start = k;
1605
0
        dkey->u.s.raw.length = length;
1606
1607
0
        ret = njs_vm_external_create(vm, njs_value_arg(&lvalue),
1608
0
                                     njs_webcrypto_crypto_key_proto_id,
1609
0
                                     dkey, 0);
1610
0
    } else {
1611
0
        ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&lvalue), k,
1612
0
                                            length);
1613
0
    }
1614
1615
0
    if (njs_slow_path(ret != NJS_OK)) {
1616
0
        goto fail;
1617
0
    }
1618
1619
0
    return njs_webcrypto_result(vm, &lvalue, NJS_OK, retval);
1620
1621
0
fail:
1622
1623
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
1624
0
}
1625
1626
1627
static njs_int_t
1628
njs_ext_derive(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1629
    njs_index_t derive_key, njs_value_t *retval)
1630
0
{
1631
0
    u_char                     *k;
1632
0
    size_t                     olen;
1633
0
    int64_t                    iterations, length;
1634
0
    unsigned                   usage, mask;
1635
0
    njs_int_t                  ret;
1636
0
    njs_str_t                  salt, info;
1637
0
    njs_value_t                *value, *aobject, *dobject;
1638
0
    const EVP_MD               *md;
1639
0
    EVP_PKEY_CTX               *pctx;
1640
0
    njs_opaque_value_t         lvalue;
1641
0
    njs_webcrypto_key_t        *key, *dkey;
1642
0
    njs_webcrypto_hash_t       hash;
1643
0
    njs_webcrypto_algorithm_t  *alg, *dalg;
1644
1645
0
    static const njs_str_t  string_info = njs_str("info");
1646
0
    static const njs_str_t  string_salt = njs_str("salt");
1647
0
    static const njs_str_t  string_iterations = njs_str("iterations");
1648
1649
0
    aobject = njs_arg(args, nargs, 1);
1650
0
    alg = njs_key_algorithm(vm, aobject);
1651
0
    if (njs_slow_path(alg == NULL)) {
1652
0
        goto fail;
1653
0
    }
1654
1655
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
1656
0
                          njs_arg(args, nargs, 2));
1657
0
    if (njs_slow_path(key == NULL)) {
1658
0
        njs_vm_type_error(vm, "\"baseKey\" is not a CryptoKey object");
1659
0
        goto fail;
1660
0
    }
1661
1662
0
    mask = derive_key ? NJS_KEY_USAGE_DERIVE_KEY : NJS_KEY_USAGE_DERIVE_BITS;
1663
0
    if (njs_slow_path(!(key->usage & mask))) {
1664
0
        njs_vm_type_error(vm, "provide key does not support \"%s\" operation",
1665
0
                          derive_key ? "deriveKey" : "deriveBits");
1666
0
        goto fail;
1667
0
    }
1668
1669
0
    if (njs_slow_path(key->alg != alg)) {
1670
0
        njs_vm_type_error(vm, "cannot derive %s using \"%V\" with \"%V\" key",
1671
0
                          derive_key ? "key" : "bits",
1672
0
                          njs_algorithm_string(key->alg),
1673
0
                          njs_algorithm_string(alg));
1674
0
        goto fail;
1675
0
    }
1676
1677
0
    if (alg->type == NJS_ALGORITHM_ECDH) {
1678
0
        return njs_ext_derive_ecdh(vm, args, nargs, derive_key, key, retval);
1679
0
    }
1680
1681
0
    dobject = njs_arg(args, nargs, 3);
1682
1683
0
    if (derive_key) {
1684
0
        dalg = njs_key_algorithm(vm, dobject);
1685
0
        if (njs_slow_path(dalg == NULL)) {
1686
0
            goto fail;
1687
0
        }
1688
1689
0
        value = njs_vm_object_prop(vm, dobject, &string_length, &lvalue);
1690
0
        if (value == NULL) {
1691
0
            njs_vm_type_error(vm, "derivedKeyAlgorithm.length is not provided");
1692
0
            goto fail;
1693
0
        }
1694
1695
0
    } else {
1696
0
        dalg = NULL;
1697
0
        value = dobject;
1698
0
    }
1699
1700
0
    ret = njs_value_to_integer(vm, value, &length);
1701
0
    if (njs_slow_path(ret != NJS_OK)) {
1702
0
        goto fail;
1703
0
    }
1704
1705
0
    dkey = NULL;
1706
0
    length /= 8;
1707
1708
0
    if (derive_key) {
1709
0
        switch (dalg->type) {
1710
0
        case NJS_ALGORITHM_AES_GCM:
1711
0
        case NJS_ALGORITHM_AES_CTR:
1712
0
        case NJS_ALGORITHM_AES_CBC:
1713
1714
0
            if (length != 16 && length != 32) {
1715
0
                njs_vm_type_error(vm, "deriveKey \"%V\" length must be "
1716
0
                                  "128 or 256", njs_algorithm_string(dalg));
1717
0
                goto fail;
1718
0
            }
1719
1720
0
            break;
1721
1722
0
        case NJS_ALGORITHM_HMAC:
1723
0
            break;
1724
1725
0
        default:
1726
0
            njs_vm_internal_error(vm, "not implemented deriveKey: \"%V\"",
1727
0
                                  njs_algorithm_string(dalg));
1728
0
            goto fail;
1729
0
        }
1730
1731
0
        ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage);
1732
0
        if (njs_slow_path(ret != NJS_OK)) {
1733
0
            goto fail;
1734
0
        }
1735
1736
0
        if (njs_slow_path(usage & ~dalg->usage)) {
1737
0
            njs_vm_type_error(vm, "unsupported key usage for \"%V\" key",
1738
0
                              njs_algorithm_string(alg));
1739
0
            goto fail;
1740
0
        }
1741
1742
0
        dkey = njs_mp_zalloc(njs_vm_memory_pool(vm),
1743
0
                             sizeof(njs_webcrypto_key_t));
1744
0
        if (njs_slow_path(dkey == NULL)) {
1745
0
            njs_vm_memory_error(vm);
1746
0
            goto fail;
1747
0
        }
1748
1749
0
        dkey->alg = dalg;
1750
0
        dkey->usage = usage;
1751
0
    }
1752
1753
0
    k = njs_mp_zalloc(njs_vm_memory_pool(vm), length);
1754
0
    if (njs_slow_path(k == NULL)) {
1755
0
        njs_vm_memory_error(vm);
1756
0
        goto fail;
1757
0
    }
1758
1759
0
    switch (alg->type) {
1760
0
    case NJS_ALGORITHM_PBKDF2:
1761
0
        ret = njs_algorithm_hash(vm, aobject, &hash);
1762
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1763
0
            goto fail;
1764
0
        }
1765
1766
0
        value = njs_vm_object_prop(vm, aobject, &string_salt, &lvalue);
1767
0
        if (value == NULL) {
1768
0
            njs_vm_type_error(vm, "PBKDF2 algorithm.salt is not provided");
1769
0
            goto fail;
1770
0
        }
1771
1772
0
        ret = njs_vm_value_to_bytes(vm, &salt, value);
1773
0
        if (njs_slow_path(ret != NJS_OK)) {
1774
0
            goto fail;
1775
0
        }
1776
1777
0
        if (njs_slow_path(salt.length < 16)) {
1778
0
            njs_vm_type_error(vm, "PBKDF2 algorithm.salt must be "
1779
0
                              "at least 16 bytes long");
1780
0
            goto fail;
1781
0
        }
1782
1783
0
        value = njs_vm_object_prop(vm, aobject, &string_iterations, &lvalue);
1784
0
        if (value == NULL) {
1785
0
            njs_vm_type_error(vm, "PBKDF2 algorithm.iterations is not "
1786
0
                              "provided");
1787
0
            goto fail;
1788
0
        }
1789
1790
0
        ret = njs_value_to_integer(vm, value, &iterations);
1791
0
        if (njs_slow_path(ret != NJS_OK)) {
1792
0
            goto fail;
1793
0
        }
1794
1795
0
        md = njs_algorithm_hash_digest(hash);
1796
1797
0
        ret = PKCS5_PBKDF2_HMAC((char *) key->u.s.raw.start, key->u.s.raw.length,
1798
0
                                salt.start, salt.length, iterations, md,
1799
0
                                length, k);
1800
0
        if (njs_slow_path(ret <= 0)) {
1801
0
            njs_webcrypto_error(vm, "PKCS5_PBKDF2_HMAC() failed");
1802
0
            goto fail;
1803
0
        }
1804
0
        break;
1805
1806
0
    case NJS_ALGORITHM_HKDF:
1807
0
#ifdef EVP_PKEY_HKDF
1808
0
        ret = njs_algorithm_hash(vm, aobject, &hash);
1809
0
        if (njs_slow_path(ret == NJS_ERROR)) {
1810
0
            goto fail;
1811
0
        }
1812
1813
0
        value = njs_vm_object_prop(vm, aobject, &string_salt, &lvalue);
1814
0
        if (value == NULL) {
1815
0
            njs_vm_type_error(vm, "HKDF algorithm.salt is not provided");
1816
0
            goto fail;
1817
0
        }
1818
1819
0
        ret = njs_vm_value_to_bytes(vm, &salt, value);
1820
0
        if (njs_slow_path(ret != NJS_OK)) {
1821
0
            goto fail;
1822
0
        }
1823
1824
0
        value = njs_vm_object_prop(vm, aobject, &string_info, &lvalue);
1825
0
        if (value == NULL) {
1826
0
            njs_vm_type_error(vm, "HKDF algorithm.info is not provided");
1827
0
            goto fail;
1828
0
        }
1829
1830
0
        ret = njs_vm_value_to_bytes(vm, &info, value);
1831
0
        if (njs_slow_path(ret != NJS_OK)) {
1832
0
            goto fail;
1833
0
        }
1834
1835
0
        pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
1836
0
        if (njs_slow_path(pctx == NULL)) {
1837
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed");
1838
0
            goto fail;
1839
0
        }
1840
1841
0
        ret = EVP_PKEY_derive_init(pctx);
1842
0
        if (njs_slow_path(ret <= 0)) {
1843
0
            njs_webcrypto_error(vm, "EVP_PKEY_derive_init() failed");
1844
0
            goto free;
1845
0
        }
1846
1847
0
        md = njs_algorithm_hash_digest(hash);
1848
1849
0
        ret = EVP_PKEY_CTX_set_hkdf_md(pctx, md);
1850
0
        if (njs_slow_path(ret <= 0)) {
1851
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_hkdf_md() failed");
1852
0
            goto free;
1853
0
        }
1854
1855
0
        ret = EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.start, salt.length);
1856
0
        if (njs_slow_path(ret <= 0)) {
1857
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_set1_hkdf_salt() failed");
1858
0
            goto free;
1859
0
        }
1860
1861
0
        ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, key->u.s.raw.start,
1862
0
                                         key->u.s.raw.length);
1863
0
        if (njs_slow_path(ret <= 0)) {
1864
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_set1_hkdf_key() failed");
1865
0
            goto free;
1866
0
        }
1867
1868
0
        ret = EVP_PKEY_CTX_add1_hkdf_info(pctx, info.start, info.length);
1869
0
        if (njs_slow_path(ret <= 0)) {
1870
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_add1_hkdf_info() failed");
1871
0
            goto free;
1872
0
        }
1873
1874
0
        olen = (size_t) length;
1875
0
        ret = EVP_PKEY_derive(pctx, k, &olen);
1876
0
        if (njs_slow_path(ret <= 0 || olen != (size_t) length)) {
1877
0
            njs_webcrypto_error(vm, "EVP_PKEY_derive() failed");
1878
0
            goto free;
1879
0
        }
1880
1881
0
free:
1882
1883
0
        EVP_PKEY_CTX_free(pctx);
1884
1885
0
        if (njs_slow_path(ret <= 0)) {
1886
0
            goto fail;
1887
0
        }
1888
1889
0
        break;
1890
#else
1891
        (void) pctx;
1892
        (void) olen;
1893
        (void) &string_info;
1894
        (void) &info;
1895
#endif
1896
1897
0
    default:
1898
0
        njs_vm_internal_error(vm, "not implemented deriveKey "
1899
0
                              "algorithm: \"%V\"", njs_algorithm_string(alg));
1900
0
        goto fail;
1901
0
    }
1902
1903
0
    if (derive_key) {
1904
0
        if (dalg->type == NJS_ALGORITHM_HMAC) {
1905
0
            ret = njs_algorithm_hash(vm, dobject, &dkey->hash);
1906
0
            if (njs_slow_path(ret == NJS_ERROR)) {
1907
0
                goto fail;
1908
0
            }
1909
0
        }
1910
1911
0
        dkey->extractable = njs_value_bool(njs_arg(args, nargs, 4));
1912
0
        dkey->u.s.raw.start = k;
1913
0
        dkey->u.s.raw.length = length;
1914
1915
0
        ret = njs_vm_external_create(vm, njs_value_arg(&lvalue),
1916
0
                                     njs_webcrypto_crypto_key_proto_id,
1917
0
                                     dkey, 0);
1918
0
    } else {
1919
0
        ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&lvalue), k,
1920
0
                                            length);
1921
0
    }
1922
1923
0
    if (njs_slow_path(ret != NJS_OK)) {
1924
0
        goto fail;
1925
0
    }
1926
1927
0
    return njs_webcrypto_result(vm, &lvalue, NJS_OK, retval);
1928
1929
0
fail:
1930
1931
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
1932
0
}
1933
1934
1935
static njs_int_t
1936
njs_ext_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1937
    njs_index_t unused, njs_value_t *retval)
1938
0
{
1939
0
    unsigned              olen;
1940
0
    u_char                *dst;
1941
0
    njs_str_t             data;
1942
0
    njs_int_t             ret;
1943
0
    const EVP_MD          *md;
1944
0
    njs_opaque_value_t    result;
1945
0
    njs_webcrypto_hash_t  hash;
1946
1947
0
    ret = njs_algorithm_hash(vm, njs_arg(args, nargs, 1), &hash);
1948
0
    if (njs_slow_path(ret == NJS_ERROR)) {
1949
0
        goto fail;
1950
0
    }
1951
1952
0
    ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 2));
1953
0
    if (njs_slow_path(ret != NJS_OK)) {
1954
0
        goto fail;
1955
0
    }
1956
1957
0
    md = njs_algorithm_hash_digest(hash);
1958
0
    olen = EVP_MD_size(md);
1959
1960
0
    dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen);
1961
0
    if (njs_slow_path(dst == NULL)) {
1962
0
        njs_vm_memory_error(vm);
1963
0
        goto fail;
1964
0
    }
1965
1966
0
    ret = EVP_Digest(data.start, data.length, dst, &olen, md, NULL);
1967
0
    if (njs_slow_path(ret <= 0)) {
1968
0
        njs_webcrypto_error(vm, "EVP_Digest() failed");
1969
0
        goto fail;
1970
0
    }
1971
1972
0
    ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst, olen);
1973
0
    if (njs_slow_path(ret != NJS_OK)) {
1974
0
        goto fail;
1975
0
    }
1976
1977
0
    return njs_webcrypto_result(vm, &result, NJS_OK, retval);
1978
1979
0
fail:
1980
1981
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
1982
0
}
1983
1984
1985
static njs_int_t
1986
njs_export_base64url_bignum(njs_vm_t *vm, njs_opaque_value_t *retval,
1987
    const BIGNUM *v, size_t size)
1988
0
{
1989
0
    njs_str_t  src;
1990
0
    u_char     buf[512];
1991
1992
0
    if (size == 0) {
1993
0
        size = BN_num_bytes(v);
1994
0
    }
1995
1996
0
    if (njs_bn_bn2binpad(v, &buf[0], size) <= 0) {
1997
0
        return NJS_ERROR;
1998
0
    }
1999
2000
0
    src.start = buf;
2001
0
    src.length = size;
2002
2003
0
    return njs_string_base64url(vm, njs_value_arg(retval), &src);
2004
0
}
2005
2006
2007
static njs_int_t
2008
njs_base64url_bignum_set(njs_vm_t *vm, njs_value_t *jwk, const njs_str_t *key,
2009
    const BIGNUM *v, size_t size)
2010
0
{
2011
0
    njs_int_t           ret;
2012
0
    njs_opaque_value_t  value;
2013
2014
0
    ret = njs_export_base64url_bignum(vm, &value, v, size);
2015
0
    if (ret != NJS_OK) {
2016
0
        return NJS_ERROR;
2017
0
    }
2018
2019
0
    return njs_vm_object_prop_set(vm, jwk, key, &value);
2020
0
}
2021
2022
2023
static njs_int_t
2024
njs_export_jwk_rsa(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval)
2025
0
{
2026
0
    njs_int_t           ret;
2027
0
    const RSA           *rsa;
2028
0
    njs_str_t           *nm;
2029
0
    const BIGNUM        *n_bn, *e_bn, *d_bn, *p_bn, *q_bn, *dp_bn, *dq_bn,
2030
0
                        *qi_bn;
2031
0
    njs_opaque_value_t  nvalue, evalue, alg, rsa_s;
2032
2033
0
    rsa = njs_pkey_get_rsa_key(key->u.a.pkey);
2034
2035
0
    njs_rsa_get0_key(rsa, &n_bn, &e_bn, &d_bn);
2036
2037
0
    ret = njs_export_base64url_bignum(vm, &nvalue, n_bn, 0);
2038
0
    if (ret != NJS_OK) {
2039
0
        return NJS_ERROR;
2040
0
    }
2041
2042
0
    ret = njs_export_base64url_bignum(vm, &evalue, e_bn, 0);
2043
0
    if (ret != NJS_OK) {
2044
0
        return NJS_ERROR;
2045
0
    }
2046
2047
0
    ret = njs_vm_object_alloc(vm, retval, NULL);
2048
0
    if (ret != NJS_OK) {
2049
0
        return NJS_ERROR;
2050
0
    }
2051
2052
0
    njs_vm_value_string_create(vm, njs_value_arg(&rsa_s), (u_char *) "RSA", 3);
2053
2054
0
    ret = njs_vm_object_prop_set(vm, retval, &string_kty, &rsa_s);
2055
0
    if (ret != NJS_OK) {
2056
0
        return NJS_ERROR;
2057
0
    }
2058
2059
0
    ret = njs_vm_object_prop_set(vm, retval, &string_n, &nvalue);
2060
0
    if (ret != NJS_OK) {
2061
0
        return NJS_ERROR;
2062
0
    }
2063
2064
0
    ret = njs_vm_object_prop_set(vm, retval, &string_e, &evalue);
2065
0
    if (ret != NJS_OK) {
2066
0
        return NJS_ERROR;
2067
0
    }
2068
2069
0
    if (key->u.a.privat) {
2070
0
        njs_rsa_get0_factors(rsa, &p_bn, &q_bn);
2071
0
        njs_rsa_get0_ctr_params(rsa, &dp_bn, &dq_bn, &qi_bn);
2072
2073
0
        ret = njs_base64url_bignum_set(vm, retval, &string_d, d_bn, 0);
2074
0
        if (ret != NJS_OK) {
2075
0
            return NJS_ERROR;
2076
0
        }
2077
2078
0
        ret = njs_base64url_bignum_set(vm, retval, &string_p, p_bn, 0);
2079
0
        if (ret != NJS_OK) {
2080
0
            return NJS_ERROR;
2081
0
        }
2082
2083
0
        ret = njs_base64url_bignum_set(vm, retval, &string_q, q_bn, 0);
2084
0
        if (ret != NJS_OK) {
2085
0
            return NJS_ERROR;
2086
0
        }
2087
2088
0
        ret = njs_base64url_bignum_set(vm, retval, &string_dp, dp_bn, 0);
2089
0
        if (ret != NJS_OK) {
2090
0
            return NJS_ERROR;
2091
0
        }
2092
2093
0
        ret = njs_base64url_bignum_set(vm, retval, &string_dq, dq_bn, 0);
2094
0
        if (ret != NJS_OK) {
2095
0
            return NJS_ERROR;
2096
0
        }
2097
2098
0
        ret = njs_base64url_bignum_set(vm, retval, &string_qi, qi_bn, 0);
2099
0
        if (ret != NJS_OK) {
2100
0
            return NJS_ERROR;
2101
0
        }
2102
0
    }
2103
2104
0
    nm = &njs_webcrypto_alg_name[key->alg->type][key->hash];
2105
2106
0
    (void) njs_vm_value_string_create(vm, njs_value_arg(&alg), nm->start,
2107
0
                                      nm->length);
2108
2109
0
    return njs_vm_object_prop_set(vm, retval, &string_alg, &alg);
2110
0
}
2111
2112
2113
static njs_int_t
2114
njs_export_jwk_ec(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval)
2115
0
{
2116
0
    int                 nid, group_bits, group_bytes;
2117
0
    BIGNUM              *x_bn, *y_bn;
2118
0
    njs_int_t           ret;
2119
0
    njs_str_t           *cname;
2120
0
    const EC_KEY        *ec;
2121
0
    const BIGNUM        *d_bn;
2122
0
    const EC_POINT      *pub;
2123
0
    const EC_GROUP      *group;
2124
0
    njs_opaque_value_t  xvalue, yvalue, dvalue, name, ec_s;
2125
2126
0
    x_bn = NULL;
2127
0
    y_bn = NULL;
2128
0
    d_bn = NULL;
2129
2130
0
    ec = njs_pkey_get_ec_key(key->u.a.pkey);
2131
2132
0
    pub = EC_KEY_get0_public_key(ec);
2133
0
    group = EC_KEY_get0_group(ec);
2134
2135
0
    group_bits = EC_GROUP_get_degree(group);
2136
0
    group_bytes = (group_bits / 8) + (7 + (group_bits % 8)) / 8;
2137
2138
0
    x_bn = BN_new();
2139
0
    if (x_bn == NULL) {
2140
0
        goto fail;
2141
0
    }
2142
2143
0
    y_bn = BN_new();
2144
0
    if (y_bn == NULL) {
2145
0
        goto fail;
2146
0
    }
2147
2148
0
    if (!njs_ec_point_get_affine_coordinates(group, pub, x_bn, y_bn)) {
2149
0
        njs_webcrypto_error(vm, "EC_POINT_get_affine_coordinates() failed");
2150
0
        goto fail;
2151
0
    }
2152
2153
0
    ret = njs_export_base64url_bignum(vm, &xvalue, x_bn, group_bytes);
2154
0
    if (ret != NJS_OK) {
2155
0
        goto fail;
2156
0
    }
2157
2158
0
    BN_free(x_bn);
2159
0
    x_bn = NULL;
2160
2161
0
    ret = njs_export_base64url_bignum(vm, &yvalue, y_bn, group_bytes);
2162
0
    if (ret != NJS_OK) {
2163
0
        goto fail;
2164
0
    }
2165
2166
0
    BN_free(y_bn);
2167
0
    y_bn = NULL;
2168
2169
0
    nid = EC_GROUP_get_curve_name(group);
2170
2171
0
    cname = njs_algorithm_curve_name(nid);
2172
0
    (void) njs_vm_value_string_create(vm, njs_value_arg(&name),
2173
0
                                      cname->start, cname->length);
2174
2175
0
    if (cname->length == 0) {
2176
0
        njs_vm_type_error(vm, "Unsupported JWK EC curve: %s", OBJ_nid2sn(nid));
2177
0
        goto fail;
2178
0
    }
2179
2180
0
    ret = njs_vm_object_alloc(vm, retval, NULL);
2181
0
    if (ret != NJS_OK) {
2182
0
        goto fail;
2183
0
    }
2184
2185
0
    njs_vm_value_string_create(vm, njs_value_arg(&ec_s), (u_char *) "EC", 2);
2186
2187
0
    ret = njs_vm_object_prop_set(vm, retval, &string_kty, &ec_s);
2188
0
    if (ret != NJS_OK) {
2189
0
        return NJS_ERROR;
2190
0
    }
2191
2192
0
    ret = njs_vm_object_prop_set(vm, retval, &string_x, &xvalue);
2193
0
    if (ret != NJS_OK) {
2194
0
        return NJS_ERROR;
2195
0
    }
2196
2197
0
    ret = njs_vm_object_prop_set(vm, retval, &string_y, &yvalue);
2198
0
    if (ret != NJS_OK) {
2199
0
        return NJS_ERROR;
2200
0
    }
2201
2202
0
    ret = njs_vm_object_prop_set(vm, retval, &string_crv, &name);
2203
0
    if (ret != NJS_OK) {
2204
0
        return NJS_ERROR;
2205
0
    }
2206
2207
0
    if (key->u.a.privat) {
2208
0
        d_bn = EC_KEY_get0_private_key(ec);
2209
2210
0
        ret = njs_export_base64url_bignum(vm, &dvalue, d_bn, group_bytes);
2211
0
        if (ret != NJS_OK) {
2212
0
            goto fail;
2213
0
        }
2214
2215
0
        ret = njs_vm_object_prop_set(vm, retval, &string_d, &dvalue);
2216
0
        if (ret != NJS_OK) {
2217
0
            goto fail;
2218
0
        }
2219
0
    }
2220
2221
0
    return NJS_OK;
2222
2223
0
fail:
2224
2225
0
    if (x_bn != NULL) {
2226
0
        BN_free(x_bn);
2227
0
    }
2228
2229
0
    if (y_bn != NULL) {
2230
0
        BN_free(y_bn);
2231
0
    }
2232
2233
0
    return NJS_ERROR;
2234
0
}
2235
2236
2237
static njs_int_t
2238
njs_export_raw_ec(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval)
2239
0
{
2240
0
    size_t                   size;
2241
0
    u_char                   *dst;
2242
0
    const EC_KEY             *ec;
2243
0
    const EC_GROUP           *group;
2244
0
    const EC_POINT           *point;
2245
0
    point_conversion_form_t  form;
2246
2247
0
    njs_assert(key->u.a.pkey != NULL);
2248
2249
0
    if (key->u.a.privat) {
2250
0
        njs_vm_type_error(vm, "private key of \"%V\" cannot be exported "
2251
0
                          "in \"raw\" format", njs_algorithm_string(key->alg));
2252
0
        return NJS_ERROR;
2253
0
    }
2254
2255
0
    ec = njs_pkey_get_ec_key(key->u.a.pkey);
2256
2257
0
    group = EC_KEY_get0_group(ec);
2258
0
    point = EC_KEY_get0_public_key(ec);
2259
0
    form = POINT_CONVERSION_UNCOMPRESSED;
2260
2261
0
    size = EC_POINT_point2oct(group, point, form, NULL, 0, NULL);
2262
0
    if (njs_slow_path(size == 0)) {
2263
0
        njs_webcrypto_error(vm, "EC_POINT_point2oct() failed");
2264
0
        return NJS_ERROR;
2265
0
    }
2266
2267
0
    dst = njs_mp_alloc(njs_vm_memory_pool(vm), size);
2268
0
    if (njs_slow_path(dst == NULL)) {
2269
0
        return NJS_ERROR;
2270
0
    }
2271
2272
0
    size = EC_POINT_point2oct(group, point, form, dst, size, NULL);
2273
0
    if (njs_slow_path(size == 0)) {
2274
0
        njs_webcrypto_error(vm, "EC_POINT_point2oct() failed");
2275
0
        return NJS_ERROR;
2276
0
    }
2277
2278
0
    return njs_vm_value_array_buffer_set(vm, retval, dst, size);
2279
0
}
2280
2281
2282
static njs_int_t
2283
njs_export_jwk_asymmetric(njs_vm_t *vm, njs_webcrypto_key_t *key,
2284
    njs_value_t *retval)
2285
0
{
2286
0
    njs_int_t           ret;
2287
0
    njs_opaque_value_t  ops, extractable;
2288
2289
0
    njs_assert(key->u.a.pkey != NULL);
2290
2291
0
    switch (EVP_PKEY_id(key->u.a.pkey)) {
2292
0
    case EVP_PKEY_RSA:
2293
0
#if (OPENSSL_VERSION_NUMBER >= 0x10101001L)
2294
0
    case EVP_PKEY_RSA_PSS:
2295
0
#endif
2296
0
        ret = njs_export_jwk_rsa(vm, key, retval);
2297
0
        if (njs_slow_path(ret != NJS_OK)) {
2298
0
            return NJS_ERROR;
2299
0
        }
2300
2301
0
        break;
2302
2303
0
    case EVP_PKEY_EC:
2304
0
        ret = njs_export_jwk_ec(vm, key, retval);
2305
0
        if (njs_slow_path(ret != NJS_OK)) {
2306
0
            return NJS_ERROR;
2307
0
        }
2308
2309
0
        break;
2310
2311
0
    default:
2312
0
        njs_vm_type_error(vm, "provided key cannot be exported as JWK");
2313
0
        return NJS_ERROR;
2314
0
    }
2315
2316
0
    ret = njs_key_ops(vm, njs_value_arg(&ops), key->usage);
2317
0
    if (njs_slow_path(ret != NJS_OK)) {
2318
0
        return NJS_ERROR;
2319
0
    }
2320
2321
0
    ret = njs_vm_object_prop_set(vm, retval, &key_ops, &ops);
2322
0
    if (njs_slow_path(ret != NJS_OK)) {
2323
0
        return NJS_ERROR;
2324
0
    }
2325
2326
0
    njs_value_boolean_set(njs_value_arg(&extractable), key->extractable);
2327
2328
0
    return njs_vm_object_prop_set(vm, retval, &string_ext, &extractable);
2329
0
}
2330
2331
2332
static njs_int_t
2333
njs_export_jwk_oct(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval)
2334
0
{
2335
0
    njs_int_t            ret;
2336
0
    njs_str_t            *nm;
2337
0
    njs_opaque_value_t   k, alg, ops, extractable, oct_s;
2338
0
    njs_webcrypto_alg_t  type;
2339
2340
0
    njs_assert(key->u.s.raw.start != NULL);
2341
2342
0
    ret = njs_string_base64url(vm, njs_value_arg(&k), &key->u.s.raw);
2343
0
    if (njs_slow_path(ret != NJS_OK)) {
2344
0
        return NJS_ERROR;
2345
0
    }
2346
2347
0
    type = key->alg->type;
2348
2349
0
    if (key->alg->type == NJS_ALGORITHM_HMAC) {
2350
0
        nm = &njs_webcrypto_alg_name[type][key->hash];
2351
0
        (void) njs_vm_value_string_create(vm, njs_value_arg(&alg), nm->start,
2352
0
                                          nm->length);
2353
2354
0
    } else {
2355
0
        switch (key->u.s.raw.length) {
2356
0
        case 16:
2357
0
        case 24:
2358
0
        case 32:
2359
0
            nm = &njs_webcrypto_alg_aes_name
2360
0
                 [type - NJS_ALGORITHM_AES_GCM][(key->u.s.raw.length - 16) / 8];
2361
0
            (void) njs_vm_value_string_create(vm, njs_value_arg(&alg),
2362
0
                                              nm->start, nm->length);
2363
0
            break;
2364
2365
0
        default:
2366
0
            njs_value_undefined_set(njs_value_arg(&alg));
2367
0
            break;
2368
0
        }
2369
0
    }
2370
2371
0
    ret = njs_key_ops(vm, njs_value_arg(&ops), key->usage);
2372
0
    if (njs_slow_path(ret != NJS_OK)) {
2373
0
        return NJS_ERROR;
2374
0
    }
2375
2376
0
    njs_value_boolean_set(njs_value_arg(&extractable), key->extractable);
2377
2378
0
    ret = njs_vm_object_alloc(vm, retval, NULL);
2379
0
    if (njs_slow_path(ret != NJS_OK)) {
2380
0
        return NJS_ERROR;
2381
0
    }
2382
2383
0
    njs_vm_value_string_create(vm, njs_value_arg(&oct_s), (u_char *) "oct", 3);
2384
2385
0
    ret = njs_vm_object_prop_set(vm, retval, &string_kty, &oct_s);
2386
0
    if (ret != NJS_OK) {
2387
0
        return NJS_ERROR;
2388
0
    }
2389
2390
0
    ret = njs_vm_object_prop_set(vm, retval, &string_k, &k);
2391
0
    if (ret != NJS_OK) {
2392
0
        return NJS_ERROR;
2393
0
    }
2394
2395
0
    ret = njs_vm_object_prop_set(vm, retval, &key_ops, &ops);
2396
0
    if (ret != NJS_OK) {
2397
0
        return NJS_ERROR;
2398
0
    }
2399
2400
0
    ret = njs_vm_object_prop_set(vm, retval, &string_ext, &extractable);
2401
0
    if (ret != NJS_OK) {
2402
0
        return NJS_ERROR;
2403
0
    }
2404
2405
0
    if (!njs_value_is_undefined(njs_value_arg(&alg))) {
2406
0
        ret = njs_vm_object_prop_set(vm, retval, &string_alg, &alg);
2407
0
        if (njs_slow_path(ret != NJS_OK)) {
2408
0
            return NJS_ERROR;
2409
0
        }
2410
0
    }
2411
2412
0
    return NJS_OK;
2413
0
}
2414
2415
2416
static njs_int_t
2417
njs_ext_export_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2418
    njs_index_t unused, njs_value_t *retval)
2419
0
{
2420
0
    BIO                         *bio;
2421
0
    BUF_MEM                     *mem;
2422
0
    njs_int_t                   ret;
2423
0
    njs_webcrypto_key_t         *key;
2424
0
    PKCS8_PRIV_KEY_INFO         *pkcs8;
2425
0
    njs_opaque_value_t          value;
2426
0
    njs_webcrypto_key_format_t  fmt;
2427
2428
0
    fmt = njs_key_format(vm, njs_arg(args, nargs, 1));
2429
0
    if (njs_slow_path(fmt == NJS_KEY_FORMAT_UNKNOWN)) {
2430
0
        goto fail;
2431
0
    }
2432
2433
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
2434
0
                          njs_arg(args, nargs, 2));
2435
0
    if (njs_slow_path(key == NULL)) {
2436
0
        njs_vm_type_error(vm, "\"key\" is not a CryptoKey object");
2437
0
        goto fail;
2438
0
    }
2439
2440
0
    if (njs_slow_path(!(fmt & key->alg->fmt))) {
2441
0
        njs_vm_type_error(vm, "unsupported key fmt \"%V\" for \"%V\" key",
2442
0
                          njs_format_string(fmt),
2443
0
                          njs_algorithm_string(key->alg));
2444
0
        goto fail;
2445
0
    }
2446
2447
0
    if (njs_slow_path(!key->extractable)) {
2448
0
        njs_vm_type_error(vm, "provided key cannot be extracted");
2449
0
        goto fail;
2450
0
    }
2451
2452
0
    switch (fmt) {
2453
0
    case NJS_KEY_FORMAT_JWK:
2454
0
        switch (key->alg->type) {
2455
0
        case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
2456
0
        case NJS_ALGORITHM_RSA_PSS:
2457
0
        case NJS_ALGORITHM_RSA_OAEP:
2458
0
        case NJS_ALGORITHM_ECDSA:
2459
0
        case NJS_ALGORITHM_ECDH:
2460
0
            ret = njs_export_jwk_asymmetric(vm, key, njs_value_arg(&value));
2461
0
            if (njs_slow_path(ret != NJS_OK)) {
2462
0
                goto fail;
2463
0
            }
2464
2465
0
            break;
2466
2467
0
        case NJS_ALGORITHM_AES_GCM:
2468
0
        case NJS_ALGORITHM_AES_CTR:
2469
0
        case NJS_ALGORITHM_AES_CBC:
2470
0
        case NJS_ALGORITHM_HMAC:
2471
0
            ret = njs_export_jwk_oct(vm, key, njs_value_arg(&value));
2472
0
            if (njs_slow_path(ret != NJS_OK)) {
2473
0
                goto fail;
2474
0
            }
2475
2476
0
            break;
2477
2478
0
        default:
2479
0
            break;
2480
0
        }
2481
2482
0
        break;
2483
2484
0
    case NJS_KEY_FORMAT_PKCS8:
2485
0
        if (!key->u.a.privat) {
2486
0
            njs_vm_type_error(vm, "public key of \"%V\" cannot be exported "
2487
0
                              "as PKCS8", njs_algorithm_string(key->alg));
2488
0
            goto fail;
2489
0
        }
2490
2491
0
        bio = BIO_new(BIO_s_mem());
2492
0
        if (njs_slow_path(bio == NULL)) {
2493
0
            njs_webcrypto_error(vm, "BIO_new(BIO_s_mem()) failed");
2494
0
            goto fail;
2495
0
        }
2496
2497
0
        njs_assert(key->u.a.pkey != NULL);
2498
2499
0
        pkcs8 = EVP_PKEY2PKCS8(key->u.a.pkey);
2500
0
        if (njs_slow_path(pkcs8 == NULL)) {
2501
0
            BIO_free(bio);
2502
0
            njs_webcrypto_error(vm, "EVP_PKEY2PKCS8() failed");
2503
0
            goto fail;
2504
0
        }
2505
2506
0
        if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8)) {
2507
0
            BIO_free(bio);
2508
0
            PKCS8_PRIV_KEY_INFO_free(pkcs8);
2509
0
            njs_webcrypto_error(vm, "i2d_PKCS8_PRIV_KEY_INFO_bio() failed");
2510
0
            goto fail;
2511
0
        }
2512
2513
0
        BIO_get_mem_ptr(bio, &mem);
2514
2515
0
        ret = njs_webcrypto_array_buffer(vm, njs_value_arg(&value),
2516
0
                                         (u_char *) mem->data, mem->length);
2517
2518
0
        BIO_free(bio);
2519
0
        PKCS8_PRIV_KEY_INFO_free(pkcs8);
2520
2521
0
        if (njs_slow_path(ret != NJS_OK)) {
2522
0
            goto fail;
2523
0
        }
2524
2525
0
        break;
2526
2527
0
    case NJS_KEY_FORMAT_SPKI:
2528
0
        if (key->u.a.privat) {
2529
0
            njs_vm_type_error(vm, "private key of \"%V\" cannot be exported "
2530
0
                              "as SPKI", njs_algorithm_string(key->alg));
2531
0
            goto fail;
2532
0
        }
2533
2534
0
        bio = BIO_new(BIO_s_mem());
2535
0
        if (njs_slow_path(bio == NULL)) {
2536
0
            njs_webcrypto_error(vm, "BIO_new(BIO_s_mem()) failed");
2537
0
            goto fail;
2538
0
        }
2539
2540
0
        njs_assert(key->u.a.pkey != NULL);
2541
2542
0
        if (!i2d_PUBKEY_bio(bio, key->u.a.pkey)) {
2543
0
            BIO_free(bio);
2544
0
            njs_webcrypto_error(vm, "i2d_PUBKEY_bio() failed");
2545
0
            goto fail;
2546
0
        }
2547
2548
0
        BIO_get_mem_ptr(bio, &mem);
2549
2550
0
        ret = njs_webcrypto_array_buffer(vm, njs_value_arg(&value),
2551
0
                                         (u_char *) mem->data, mem->length);
2552
2553
0
        BIO_free(bio);
2554
2555
0
        if (njs_slow_path(ret != NJS_OK)) {
2556
0
            goto fail;
2557
0
        }
2558
2559
0
        break;
2560
2561
0
    case NJS_KEY_FORMAT_RAW:
2562
0
    default:
2563
0
        if (key->alg->type == NJS_ALGORITHM_ECDSA
2564
0
            || key->alg->type == NJS_ALGORITHM_ECDH)
2565
0
        {
2566
0
            ret = njs_export_raw_ec(vm, key, njs_value_arg(&value));
2567
0
            if (njs_slow_path(ret != NJS_OK)) {
2568
0
                goto fail;
2569
0
            }
2570
2571
0
            break;
2572
0
        }
2573
2574
0
        ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&value),
2575
0
                                            key->u.s.raw.start,
2576
0
                                            key->u.s.raw.length);
2577
0
        if (njs_slow_path(ret != NJS_OK)) {
2578
0
            goto fail;
2579
0
        }
2580
2581
0
        break;
2582
0
    }
2583
2584
0
    return njs_webcrypto_result(vm, &value, NJS_OK, retval);
2585
2586
0
fail:
2587
2588
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
2589
0
}
2590
2591
2592
static njs_int_t
2593
njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2594
    njs_index_t unused, njs_value_t *retval)
2595
0
{
2596
0
    unsigned                   usage;
2597
0
    njs_int_t                  ret;
2598
0
    njs_bool_t                 extractable;
2599
0
    njs_value_t                *aobject, *val;
2600
0
    EVP_PKEY_CTX               *ctx;
2601
0
    njs_webcrypto_key_t        *key, *keypub;
2602
0
    njs_opaque_value_t         value, pub, priv;
2603
0
    njs_webcrypto_algorithm_t  *alg;
2604
2605
0
    static const njs_str_t  string_priv = njs_str("privateKey");
2606
0
    static const njs_str_t  string_pub = njs_str("publicKey");
2607
2608
0
    ctx = NULL;
2609
2610
0
    aobject = njs_arg(args, nargs, 1);
2611
0
    extractable = njs_value_bool(njs_arg(args, nargs, 2));
2612
2613
0
    alg = njs_key_algorithm(vm, aobject);
2614
0
    if (njs_slow_path(alg == NULL)) {
2615
0
        goto fail;
2616
0
    }
2617
2618
0
    ret = njs_key_usage(vm, njs_arg(args, nargs, 3), &usage);
2619
0
    if (njs_slow_path(ret != NJS_OK)) {
2620
0
        goto fail;
2621
0
    }
2622
2623
0
    key = njs_webcrypto_key_alloc(vm, alg, usage, extractable);
2624
0
    if (njs_slow_path(key == NULL)) {
2625
0
        goto fail;
2626
0
    }
2627
2628
0
    if (njs_slow_path(usage & ~alg->usage)) {
2629
0
        njs_vm_type_error(vm, "unsupported key usage for \"%V\" key",
2630
0
                          njs_algorithm_string(alg));
2631
0
        goto fail;
2632
0
    }
2633
2634
0
    switch (alg->type) {
2635
0
    case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
2636
0
    case NJS_ALGORITHM_RSA_PSS:
2637
0
    case NJS_ALGORITHM_RSA_OAEP:
2638
0
        ret = njs_algorithm_hash(vm, aobject, &key->hash);
2639
0
        if (njs_slow_path(ret == NJS_ERROR)) {
2640
0
            goto fail;
2641
0
        }
2642
2643
0
        val = njs_vm_object_prop(vm, aobject, &string_ml, &value);
2644
0
        if (njs_slow_path(val == NULL)) {
2645
0
            goto fail;
2646
0
        }
2647
2648
0
        if (!njs_value_is_number(val)) {
2649
0
            njs_vm_type_error(vm, "\"modulusLength\" is not a number");
2650
0
            goto fail;
2651
0
        }
2652
2653
0
        ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
2654
0
        if (njs_slow_path(ctx == NULL)) {
2655
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed");
2656
0
            goto fail;
2657
0
        }
2658
2659
0
        if (EVP_PKEY_keygen_init(ctx) <= 0) {
2660
0
            njs_webcrypto_error(vm, "EVP_PKEY_keygen_init() failed");
2661
0
            goto fail;
2662
0
        }
2663
2664
0
        if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, njs_value_number(val)) <= 0) {
2665
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_rsa_keygen_bits() "
2666
0
                                "failed");
2667
0
            goto fail;
2668
0
        }
2669
2670
0
        if (EVP_PKEY_keygen(ctx, &key->u.a.pkey) <= 0) {
2671
0
            njs_webcrypto_error(vm, "EVP_PKEY_keygen() failed");
2672
0
            goto fail;
2673
0
        }
2674
2675
0
        EVP_PKEY_CTX_free(ctx);
2676
0
        ctx = NULL;
2677
2678
0
        key->u.a.privat = 1;
2679
0
        key->usage = (alg->type == NJS_ALGORITHM_RSA_OAEP)
2680
0
                        ? NJS_KEY_USAGE_DECRYPT
2681
0
                        : NJS_KEY_USAGE_SIGN;
2682
2683
0
        keypub = njs_webcrypto_key_alloc(vm, alg, usage, extractable);
2684
0
        if (njs_slow_path(keypub == NULL)) {
2685
0
            goto fail;
2686
0
        }
2687
2688
0
        if (njs_pkey_up_ref(key->u.a.pkey) <= 0) {
2689
0
            njs_webcrypto_error(vm, "njs_pkey_up_ref() failed");
2690
0
            goto fail;
2691
0
        }
2692
2693
0
        keypub->u.a.pkey = key->u.a.pkey;
2694
0
        keypub->hash = key->hash;
2695
0
        keypub->usage = (alg->type == NJS_ALGORITHM_RSA_OAEP)
2696
0
                          ? NJS_KEY_USAGE_ENCRYPT
2697
0
                          : NJS_KEY_USAGE_VERIFY;
2698
2699
0
        ret = njs_vm_external_create(vm, njs_value_arg(&priv),
2700
0
                                     njs_webcrypto_crypto_key_proto_id, key, 0);
2701
0
        if (njs_slow_path(ret != NJS_OK)) {
2702
0
            goto fail;
2703
0
        }
2704
2705
0
        ret = njs_vm_external_create(vm, njs_value_arg(&pub),
2706
0
                                  njs_webcrypto_crypto_key_proto_id, keypub, 0);
2707
0
        if (njs_slow_path(ret != NJS_OK)) {
2708
0
            goto fail;
2709
0
        }
2710
2711
0
        ret = njs_vm_object_alloc(vm, njs_value_arg(&value), NULL);
2712
0
        if (ret != NJS_OK) {
2713
0
            goto fail;
2714
0
        }
2715
2716
0
        ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_priv,
2717
0
                                     &priv);
2718
0
        if (ret != NJS_OK) {
2719
0
            goto fail;
2720
0
        }
2721
2722
0
        ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_pub,
2723
0
                                     &pub);
2724
0
        if (ret != NJS_OK) {
2725
0
            goto fail;
2726
0
        }
2727
2728
0
        break;
2729
2730
0
    case NJS_ALGORITHM_ECDSA:
2731
0
    case NJS_ALGORITHM_ECDH:
2732
0
        ret = njs_algorithm_curve(vm, aobject, &key->u.a.curve);
2733
0
        if (njs_slow_path(ret == NJS_ERROR)) {
2734
0
            goto fail;
2735
0
        }
2736
2737
0
        ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
2738
0
        if (njs_slow_path(ctx == NULL)) {
2739
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed");
2740
0
            goto fail;
2741
0
        }
2742
2743
0
        if (EVP_PKEY_keygen_init(ctx) <= 0) {
2744
0
            njs_webcrypto_error(vm, "EVP_PKEY_keygen_init() failed");
2745
0
            goto fail;
2746
0
        }
2747
2748
0
        if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, key->u.a.curve) <= 0) {
2749
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_ec_paramgen_curve_nid() "
2750
0
                                "failed");
2751
0
            goto fail;
2752
0
        }
2753
2754
0
        if (EVP_PKEY_keygen(ctx, &key->u.a.pkey) <= 0) {
2755
0
            njs_webcrypto_error(vm, "EVP_PKEY_keygen() failed");
2756
0
            goto fail;
2757
0
        }
2758
2759
0
        EVP_PKEY_CTX_free(ctx);
2760
0
        ctx = NULL;
2761
2762
0
        key->u.a.privat = 1;
2763
2764
0
        if (alg->type == NJS_ALGORITHM_ECDSA) {
2765
0
            key->usage = NJS_KEY_USAGE_SIGN;
2766
2767
0
        } else {
2768
            /* ECDH */
2769
0
            key->usage = NJS_KEY_USAGE_DERIVE_KEY | NJS_KEY_USAGE_DERIVE_BITS;
2770
0
        }
2771
2772
0
        keypub = njs_webcrypto_key_alloc(vm, alg, usage, extractable);
2773
0
        if (njs_slow_path(keypub == NULL)) {
2774
0
            goto fail;
2775
0
        }
2776
2777
0
        if (njs_pkey_up_ref(key->u.a.pkey) <= 0) {
2778
0
            njs_webcrypto_error(vm, "njs_pkey_up_ref() failed");
2779
0
            goto fail;
2780
0
        }
2781
2782
0
        keypub->u.a.pkey = key->u.a.pkey;
2783
0
        keypub->u.a.curve = key->u.a.curve;
2784
2785
0
        if (alg->type == NJS_ALGORITHM_ECDSA) {
2786
0
            keypub->usage = NJS_KEY_USAGE_VERIFY;
2787
2788
0
        } else {
2789
            /* ECDH */
2790
0
            keypub->usage = NJS_KEY_USAGE_DERIVE_KEY
2791
0
                            | NJS_KEY_USAGE_DERIVE_BITS;
2792
0
        }
2793
2794
0
        ret = njs_vm_external_create(vm, njs_value_arg(&priv),
2795
0
                                     njs_webcrypto_crypto_key_proto_id, key, 0);
2796
0
        if (njs_slow_path(ret != NJS_OK)) {
2797
0
            goto fail;
2798
0
        }
2799
2800
0
        ret = njs_vm_external_create(vm, njs_value_arg(&pub),
2801
0
                                  njs_webcrypto_crypto_key_proto_id, keypub, 0);
2802
0
        if (njs_slow_path(ret != NJS_OK)) {
2803
0
            goto fail;
2804
0
        }
2805
2806
0
        ret = njs_vm_object_alloc(vm, njs_value_arg(&value), NULL);
2807
0
        if (ret != NJS_OK) {
2808
0
            goto fail;
2809
0
        }
2810
2811
0
        ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_priv,
2812
0
                                     &priv);
2813
0
        if (ret != NJS_OK) {
2814
0
            goto fail;
2815
0
        }
2816
2817
0
        ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_pub,
2818
0
                                     &pub);
2819
0
        if (ret != NJS_OK) {
2820
0
            goto fail;
2821
0
        }
2822
2823
0
        break;
2824
2825
0
    case NJS_ALGORITHM_AES_GCM:
2826
0
    case NJS_ALGORITHM_AES_CTR:
2827
0
    case NJS_ALGORITHM_AES_CBC:
2828
0
    case NJS_ALGORITHM_HMAC:
2829
2830
0
        if (alg->type == NJS_ALGORITHM_HMAC) {
2831
0
            ret = njs_algorithm_hash(vm, aobject, &key->hash);
2832
0
            if (njs_slow_path(ret == NJS_ERROR)) {
2833
0
                goto fail;
2834
0
            }
2835
2836
0
            key->u.s.raw.length =
2837
0
                              EVP_MD_size(njs_algorithm_hash_digest(key->hash));
2838
2839
0
        } else {
2840
0
            val = njs_vm_object_prop(vm, aobject, &string_length, &value);
2841
0
            if (val != NULL) {
2842
0
                key->u.s.raw.length = njs_value_number(val) / 8;
2843
2844
0
                if (key->u.s.raw.length != 16
2845
0
                    && key->u.s.raw.length != 24
2846
0
                    && key->u.s.raw.length != 32)
2847
0
                {
2848
0
                    njs_vm_type_error(vm, "length for \"%V\" key should be "
2849
0
                                      "one of 128, 192, 256",
2850
0
                                      njs_algorithm_string(alg));
2851
0
                    goto fail;
2852
0
                }
2853
0
            }
2854
0
        }
2855
2856
0
        key->u.s.raw.start = njs_mp_alloc(njs_vm_memory_pool(vm),
2857
0
                                          key->u.s.raw.length);
2858
0
        if (njs_slow_path(key->u.s.raw.start == NULL)) {
2859
0
            njs_vm_memory_error(vm);
2860
0
            goto fail;
2861
0
        }
2862
2863
0
        if (RAND_bytes(key->u.s.raw.start, key->u.s.raw.length) <= 0) {
2864
0
            njs_webcrypto_error(vm, "RAND_bytes() failed");
2865
0
            goto fail;
2866
0
        }
2867
2868
0
        ret = njs_vm_external_create(vm, njs_value_arg(&value),
2869
0
                                     njs_webcrypto_crypto_key_proto_id, key, 0);
2870
0
        if (njs_slow_path(ret != NJS_OK)) {
2871
0
            goto fail;
2872
0
        }
2873
2874
0
        break;
2875
2876
0
    default:
2877
0
        njs_vm_internal_error(vm, "not implemented generateKey"
2878
0
                              "algorithm: \"%V\"", njs_algorithm_string(alg));
2879
0
        return NJS_ERROR;
2880
0
    }
2881
2882
0
    return njs_webcrypto_result(vm, &value, NJS_OK, retval);
2883
2884
0
fail:
2885
2886
0
    if (ctx != NULL) {
2887
0
        EVP_PKEY_CTX_free(ctx);
2888
0
    }
2889
2890
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
2891
0
}
2892
2893
2894
static BIGNUM *
2895
njs_import_base64url_bignum(njs_vm_t *vm, njs_opaque_value_t *value)
2896
0
{
2897
0
    njs_int_t  ret;
2898
0
    njs_str_t  data, decoded;
2899
0
    u_char     buf[512];
2900
2901
0
    ret = njs_vm_value_to_bytes(vm, &data, njs_value_arg(value));
2902
0
    if (njs_slow_path(ret != NJS_OK)) {
2903
0
        return NULL;
2904
0
    }
2905
2906
0
    (void) njs_decode_base64url_length(&data, &decoded.length);
2907
2908
0
    if (njs_slow_path(decoded.length > sizeof(buf))) {
2909
0
        return NULL;
2910
0
    }
2911
2912
0
    decoded.start = buf;
2913
2914
0
    njs_decode_base64url(&decoded, &data);
2915
2916
0
    return BN_bin2bn(decoded.start, decoded.length, NULL);
2917
0
}
2918
2919
2920
static EVP_PKEY *
2921
njs_import_jwk_rsa(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key)
2922
0
{
2923
0
    RSA                    *rsa;
2924
0
    BIGNUM                 *n_bn, *e_bn, *d_bn, *p_bn, *q_bn, *dp_bn, *dq_bn,
2925
0
                           *qi_bn;
2926
0
    njs_str_t              alg;
2927
0
    unsigned               usage;
2928
0
    EVP_PKEY               *pkey;
2929
0
    njs_int_t              ret;
2930
0
    njs_value_t            *val;
2931
0
    njs_opaque_value_t     n, e, d, p, q, dp, dq, qi, value;
2932
0
    njs_webcrypto_entry_t  *w;
2933
2934
0
    val = njs_vm_object_prop(vm, jwk, &string_n, &n);
2935
0
    if (njs_slow_path(val == NULL)) {
2936
0
        goto fail0;
2937
0
    }
2938
2939
0
    val = njs_vm_object_prop(vm, jwk, &string_e, &e);
2940
0
    if (njs_slow_path(val == NULL)) {
2941
0
        goto fail0;
2942
0
    }
2943
2944
0
    val = njs_vm_object_prop(vm, jwk, &string_d, &d);
2945
0
    if (njs_slow_path(val == NULL)) {
2946
0
        njs_value_undefined_set(njs_value_arg(&d));
2947
0
    }
2948
2949
0
    if (!njs_value_is_string(njs_value_arg(&n))
2950
0
        || !njs_value_is_string(njs_value_arg(&e))
2951
0
        || (!njs_value_is_undefined(njs_value_arg(&d))
2952
0
            && !njs_value_is_string(njs_value_arg(&d))))
2953
0
    {
2954
0
fail0:
2955
0
        njs_vm_type_error(vm, "Invalid JWK RSA key");
2956
0
        return NULL;
2957
0
    }
2958
2959
0
    key->u.a.privat = njs_value_is_string(njs_value_arg(&d));
2960
2961
0
    val = njs_vm_object_prop(vm, jwk, &key_ops, &value);
2962
0
    if (val != NULL && !njs_value_is_undefined(val)){
2963
0
        ret = njs_key_usage(vm, val, &usage);
2964
0
        if (njs_slow_path(ret != NJS_OK)) {
2965
0
            return NULL;
2966
0
        }
2967
2968
0
        if ((key->usage & usage) != key->usage) {
2969
0
            njs_vm_type_error(vm, "Key operations and usage mismatch");
2970
0
            return NULL;
2971
0
        }
2972
0
    }
2973
2974
0
    val = njs_vm_object_prop(vm, jwk, &string_alg, &value);
2975
0
    if (val != NULL && !njs_value_is_undefined(val)){
2976
0
        ret = njs_value_to_string(vm, val, val);
2977
0
        if (njs_slow_path(ret != NJS_OK)) {
2978
0
            return NULL;
2979
0
        }
2980
2981
0
        njs_value_string_get(vm, val, &alg);
2982
2983
0
        for (w = &njs_webcrypto_alg_hash[0]; w->name.length != 0; w++) {
2984
0
            if (njs_strstr_eq(&alg, &w->name)) {
2985
0
                key->hash = w->value;
2986
0
                break;
2987
0
            }
2988
0
        }
2989
0
    }
2990
2991
0
    if (key->extractable) {
2992
0
        val = njs_vm_object_prop(vm, jwk, &string_ext, &value);
2993
0
        if (val != NULL
2994
0
            && !njs_value_is_undefined(val)
2995
0
            && !njs_value_bool(val))
2996
0
        {
2997
0
            njs_vm_type_error(vm, "JWK RSA is not extractable");
2998
0
            return NULL;
2999
0
        }
3000
0
    }
3001
3002
0
    rsa = RSA_new();
3003
0
    if (rsa == NULL) {
3004
0
        njs_webcrypto_error(vm, "RSA_new() failed");
3005
0
        return NULL;
3006
0
    }
3007
3008
0
    n_bn = njs_import_base64url_bignum(vm, &n);
3009
0
    if (njs_slow_path(n_bn == NULL)) {
3010
0
        goto fail;
3011
0
    }
3012
3013
0
    e_bn = njs_import_base64url_bignum(vm, &e);
3014
0
    if (njs_slow_path(e_bn == NULL)) {
3015
0
        goto fail;
3016
0
    }
3017
3018
0
    if (!njs_rsa_set0_key(rsa, n_bn, e_bn, NULL)) {
3019
0
        BN_free(n_bn);
3020
0
        BN_free(e_bn);
3021
3022
0
        njs_webcrypto_error(vm, "RSA_set0_key() failed");
3023
0
        goto fail;
3024
0
    }
3025
3026
0
    if (!key->u.a.privat) {
3027
0
        goto done;
3028
0
    }
3029
3030
0
    val = njs_vm_object_prop(vm, jwk, &string_p, &p);
3031
0
    if (njs_slow_path(val == NULL)) {
3032
0
        goto fail1;
3033
0
    }
3034
3035
0
    val = njs_vm_object_prop(vm, jwk, &string_q, &q);
3036
0
    if (njs_slow_path(val == NULL)) {
3037
0
        goto fail1;
3038
0
    }
3039
3040
0
    val = njs_vm_object_prop(vm, jwk, &string_dp, &dp);
3041
0
    if (njs_slow_path(val == NULL)) {
3042
0
        goto fail1;
3043
0
    }
3044
3045
0
    val = njs_vm_object_prop(vm, jwk, &string_dq, &dq);
3046
0
    if (njs_slow_path(val == NULL)) {
3047
0
        goto fail1;
3048
0
    }
3049
3050
0
    val = njs_vm_object_prop(vm, jwk, &string_qi, &qi);
3051
0
    if (njs_slow_path(val == NULL)) {
3052
0
        goto fail1;
3053
0
    }
3054
3055
0
    if (!njs_value_is_string(njs_value_arg(&d))
3056
0
        || !njs_value_is_string(njs_value_arg(&p))
3057
0
        || !njs_value_is_string(njs_value_arg(&q))
3058
0
        || !njs_value_is_string(njs_value_arg(&dp))
3059
0
        || !njs_value_is_string(njs_value_arg(&dq))
3060
0
        || !njs_value_is_string(njs_value_arg(&qi)))
3061
0
    {
3062
0
fail1:
3063
0
        njs_vm_type_error(vm, "Invalid JWK RSA key");
3064
0
        goto fail;
3065
0
    }
3066
3067
0
    d_bn = njs_import_base64url_bignum(vm, &d);
3068
0
    if (njs_slow_path(d_bn == NULL)) {
3069
0
        goto fail;
3070
0
    }
3071
3072
0
    if (!njs_rsa_set0_key(rsa, NULL, NULL, d_bn)) {
3073
0
        BN_free(d_bn);
3074
3075
0
        njs_webcrypto_error(vm, "RSA_set0_key() failed");
3076
0
        goto fail;
3077
0
    }
3078
3079
0
    p_bn = njs_import_base64url_bignum(vm, &p);
3080
0
    if (njs_slow_path(p_bn == NULL)) {
3081
0
        goto fail;
3082
0
    }
3083
3084
0
    q_bn = njs_import_base64url_bignum(vm, &q);
3085
0
    if (njs_slow_path(q_bn == NULL)) {
3086
0
        BN_free(p_bn);
3087
0
        goto fail;
3088
0
    }
3089
3090
0
    if (!njs_rsa_set0_factors(rsa, p_bn, q_bn)) {
3091
0
        BN_free(p_bn);
3092
0
        BN_free(q_bn);
3093
3094
0
        njs_webcrypto_error(vm, "RSA_set0_factors() failed");
3095
0
        goto fail;
3096
0
    }
3097
3098
0
    dp_bn = njs_import_base64url_bignum(vm, &dp);
3099
0
    if (njs_slow_path(dp_bn == NULL)) {
3100
0
        goto fail;
3101
0
    }
3102
3103
0
    dq_bn = njs_import_base64url_bignum(vm, &dq);
3104
0
    if (njs_slow_path(dq_bn == NULL)) {
3105
0
        BN_free(dp_bn);
3106
0
        goto fail;
3107
0
    }
3108
3109
0
    qi_bn = njs_import_base64url_bignum(vm, &qi);
3110
0
    if (njs_slow_path(qi_bn == NULL)) {
3111
0
        BN_free(dp_bn);
3112
0
        BN_free(dq_bn);
3113
0
        goto fail;
3114
0
    }
3115
3116
0
    if (!njs_rsa_set0_ctr_params(rsa, dp_bn, dq_bn, qi_bn)) {
3117
0
        BN_free(dp_bn);
3118
0
        BN_free(dq_bn);
3119
0
        BN_free(qi_bn);
3120
0
        njs_webcrypto_error(vm, "RSA_set0_crt_params() failed");
3121
0
        goto fail;
3122
0
    }
3123
3124
0
done:
3125
3126
0
    pkey = EVP_PKEY_new();
3127
0
    if (njs_slow_path(pkey == NULL)) {
3128
0
        goto fail;
3129
0
    }
3130
3131
0
    if (!EVP_PKEY_set1_RSA(pkey, rsa)) {
3132
0
        EVP_PKEY_free(pkey);
3133
0
        goto fail;
3134
0
    }
3135
3136
0
    RSA_free(rsa);
3137
3138
0
    return pkey;
3139
3140
0
fail:
3141
3142
0
    RSA_free(rsa);
3143
3144
0
    return NULL;
3145
0
}
3146
3147
3148
static EVP_PKEY *
3149
njs_import_raw_ec(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key)
3150
0
{
3151
0
    EC_KEY          *ec;
3152
0
    EVP_PKEY        *pkey;
3153
0
    EC_POINT        *pub;
3154
0
    const EC_GROUP  *group;
3155
3156
0
    ec = EC_KEY_new_by_curve_name(key->u.a.curve);
3157
0
    if (njs_slow_path(ec == NULL)) {
3158
0
        njs_webcrypto_error(vm, "EC_KEY_new_by_curve_name() failed");
3159
0
        return NULL;
3160
0
    }
3161
3162
0
    group = EC_KEY_get0_group(ec);
3163
3164
0
    pub = EC_POINT_new(group);
3165
0
    if (njs_slow_path(pub == NULL)) {
3166
0
        EC_KEY_free(ec);
3167
0
        njs_webcrypto_error(vm, "EC_POINT_new() failed");
3168
0
        return NULL;
3169
0
    }
3170
3171
0
    if (!EC_POINT_oct2point(group, pub, data->start, data->length, NULL)) {
3172
0
        EC_KEY_free(ec);
3173
0
        EC_POINT_free(pub);
3174
0
        njs_webcrypto_error(vm, "EC_POINT_oct2point() failed");
3175
0
        return NULL;
3176
0
    }
3177
3178
0
    if (!EC_KEY_set_public_key(ec, pub)) {
3179
0
        EC_KEY_free(ec);
3180
0
        EC_POINT_free(pub);
3181
0
        njs_webcrypto_error(vm, "EC_KEY_set_public_key() failed");
3182
0
        return NULL;
3183
0
    }
3184
3185
0
    pkey = EVP_PKEY_new();
3186
0
    if (njs_slow_path(pkey == NULL)) {
3187
0
        EC_KEY_free(ec);
3188
0
        EC_POINT_free(pub);
3189
0
        njs_webcrypto_error(vm, "EVP_PKEY_new() failed");
3190
0
        return NULL;
3191
0
    }
3192
3193
0
    if (!EVP_PKEY_set1_EC_KEY(pkey, ec)) {
3194
0
        EC_KEY_free(ec);
3195
0
        EC_POINT_free(pub);
3196
0
        EVP_PKEY_free(pkey);
3197
0
        njs_webcrypto_error(vm, "EVP_PKEY_set1_EC_KEY() failed");
3198
0
        return NULL;
3199
0
    }
3200
3201
0
    EC_KEY_free(ec);
3202
0
    EC_POINT_free(pub);
3203
3204
0
    return pkey;
3205
0
}
3206
3207
3208
static EVP_PKEY *
3209
njs_import_jwk_ec(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key)
3210
0
{
3211
0
    int                    curve;
3212
0
    EC_KEY                 *ec;
3213
0
    BIGNUM                 *x_bn, *y_bn, *d_bn;
3214
0
    unsigned               usage;
3215
0
    EVP_PKEY               *pkey;
3216
0
    njs_str_t              name;
3217
0
    njs_int_t              ret;
3218
0
    njs_value_t            *val;
3219
0
    njs_opaque_value_t     x, y, d, value;
3220
0
    njs_webcrypto_entry_t  *e;
3221
3222
0
    ec = NULL;
3223
0
    x_bn = NULL;
3224
0
    y_bn = NULL;
3225
0
    d_bn = NULL;
3226
3227
0
    val = njs_vm_object_prop(vm, jwk, &string_x, &x);
3228
0
    if (njs_slow_path(val == NULL)) {
3229
0
        goto fail0;
3230
0
    }
3231
3232
0
    val = njs_vm_object_prop(vm, jwk, &string_y, &y);
3233
0
    if (njs_slow_path(val == NULL)) {
3234
0
        goto fail0;
3235
0
    }
3236
3237
0
    val = njs_vm_object_prop(vm, jwk, &string_d, &d);
3238
0
    if (njs_slow_path(val == NULL)) {
3239
0
        njs_value_undefined_set(njs_value_arg(&d));
3240
0
    }
3241
3242
0
    if (!njs_value_is_string(njs_value_arg(&x))
3243
0
        || !njs_value_is_string(njs_value_arg(&y))
3244
0
        || (!njs_value_is_undefined(njs_value_arg(&d))
3245
0
            && !njs_value_is_string(njs_value_arg(&d))))
3246
0
    {
3247
0
fail0:
3248
0
        njs_vm_type_error(vm, "Invalid JWK EC key");
3249
0
        return NULL;
3250
0
    }
3251
3252
0
    key->u.a.privat = njs_value_is_string(njs_value_arg(&d));
3253
3254
0
    val = njs_vm_object_prop(vm, jwk, &key_ops, &value);
3255
0
    if (val != NULL && !njs_value_is_undefined(val)) {
3256
0
        ret = njs_key_usage(vm, val, &usage);
3257
0
        if (njs_slow_path(ret != NJS_OK)) {
3258
0
            return NULL;
3259
0
        }
3260
3261
0
        if ((key->usage & usage) != key->usage) {
3262
0
            njs_vm_type_error(vm, "Key operations and usage mismatch");
3263
0
            return NULL;
3264
0
        }
3265
0
    }
3266
3267
0
    if (key->extractable) {
3268
0
        val = njs_vm_object_prop(vm, jwk, &string_ext, &value);
3269
0
        if (val != NULL
3270
0
            && !njs_value_is_undefined(val)
3271
0
            && !njs_value_bool(val))
3272
0
        {
3273
0
            njs_vm_type_error(vm, "JWK EC is not extractable");
3274
0
            return NULL;
3275
0
        }
3276
0
    }
3277
3278
0
    curve = 0;
3279
3280
0
    val = njs_vm_object_prop(vm, jwk, &string_crv, &value);
3281
0
    if (val != NULL && !njs_value_is_undefined(val)) {
3282
0
        njs_value_string_get(vm, val, &name);
3283
3284
0
        for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) {
3285
0
            if (njs_strstr_eq(&name, &e->name)) {
3286
0
                curve = e->value;
3287
0
                break;
3288
0
            }
3289
0
        }
3290
0
    }
3291
3292
0
    if (curve != key->u.a.curve) {
3293
0
        njs_vm_type_error(vm, "JWK EC curve mismatch");
3294
0
        return NULL;
3295
0
    }
3296
3297
0
    ec = EC_KEY_new_by_curve_name(key->u.a.curve);
3298
0
    if (njs_slow_path(ec == NULL)) {
3299
0
        njs_webcrypto_error(vm, "EC_KEY_new_by_curve_name() failed");
3300
0
        return NULL;
3301
0
    }
3302
3303
0
    x_bn = njs_import_base64url_bignum(vm, &x);
3304
0
    if (njs_slow_path(x_bn == NULL)) {
3305
0
        goto fail;
3306
0
    }
3307
3308
0
    y_bn = njs_import_base64url_bignum(vm, &y);
3309
0
    if (njs_slow_path(y_bn == NULL)) {
3310
0
        goto fail;
3311
0
    }
3312
3313
0
    if (key->u.a.privat) {
3314
0
        d_bn = njs_import_base64url_bignum(vm, &d);
3315
0
        if (njs_slow_path(d_bn == NULL)) {
3316
0
            goto fail;
3317
0
        }
3318
0
    }
3319
3320
0
    if (!EC_KEY_set_public_key_affine_coordinates(ec, x_bn, y_bn)) {
3321
0
        njs_webcrypto_error(vm, "EC_KEY_set_public_key_affine_coordinates() "
3322
0
                            "failed");
3323
0
        goto fail;
3324
0
    }
3325
3326
0
    BN_free(x_bn);
3327
0
    x_bn = NULL;
3328
3329
0
    BN_free(y_bn);
3330
0
    y_bn = NULL;
3331
3332
0
    pkey = EVP_PKEY_new();
3333
0
    if (njs_slow_path(pkey == NULL)) {
3334
0
        goto fail;
3335
0
    }
3336
3337
0
    if (!EVP_PKEY_set1_EC_KEY(pkey, ec)) {
3338
0
        njs_webcrypto_error(vm, "EVP_PKEY_set1_EC_KEY() failed");
3339
0
        goto fail_pkey;
3340
0
    }
3341
3342
0
    if (key->u.a.privat) {
3343
0
        if (!EC_KEY_set_private_key(ec, d_bn)) {
3344
0
            njs_webcrypto_error(vm, "EC_KEY_set_private_key() failed");
3345
0
            goto fail_pkey;
3346
0
        }
3347
3348
0
        BN_free(d_bn);
3349
0
        d_bn = NULL;
3350
0
    }
3351
3352
0
    EC_KEY_free(ec);
3353
3354
0
    return pkey;
3355
3356
0
fail_pkey:
3357
3358
0
    EVP_PKEY_free(pkey);
3359
0
    EC_KEY_free(ec);
3360
0
    ec = NULL;
3361
3362
0
fail:
3363
3364
0
    EC_KEY_free(ec);
3365
3366
0
    if (x_bn != NULL) {
3367
0
        BN_free(x_bn);
3368
0
    }
3369
3370
0
    if (y_bn != NULL) {
3371
0
        BN_free(y_bn);
3372
0
    }
3373
3374
0
    if (d_bn != NULL) {
3375
0
        BN_free(d_bn);
3376
0
    }
3377
3378
0
    return NULL;
3379
0
}
3380
3381
3382
static njs_int_t
3383
njs_import_jwk_oct(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key)
3384
0
{
3385
0
    size_t                 size;
3386
0
    unsigned               usage;
3387
0
    njs_int_t              ret;
3388
0
    njs_str_t              *a, alg, b64;
3389
0
    njs_value_t            *val;
3390
0
    njs_opaque_value_t     value;
3391
0
    njs_webcrypto_alg_t    type;
3392
0
    njs_webcrypto_entry_t  *w;
3393
3394
0
    static njs_webcrypto_entry_t hashes[] = {
3395
0
        { njs_str("HS1"), NJS_HASH_SHA1 },
3396
0
        { njs_str("HS256"), NJS_HASH_SHA256 },
3397
0
        { njs_str("HS384"), NJS_HASH_SHA384 },
3398
0
        { njs_str("HS512"), NJS_HASH_SHA512 },
3399
0
        { njs_null_str, 0 }
3400
0
    };
3401
3402
0
    val = njs_vm_object_prop(vm, jwk, &string_k, &value);
3403
0
    if (njs_slow_path(val == NULL || !njs_value_is_string(val))) {
3404
0
        njs_vm_type_error(vm, "Invalid JWK oct key");
3405
0
        return NJS_ERROR;
3406
0
    }
3407
3408
0
    njs_value_string_get(vm, val, &b64);
3409
3410
0
    (void) njs_decode_base64url_length(&b64, &key->u.s.raw.length);
3411
3412
0
    key->u.s.raw.start = njs_mp_alloc(njs_vm_memory_pool(vm),
3413
0
                                      key->u.s.raw.length);
3414
0
    if (njs_slow_path(key->u.s.raw.start == NULL)) {
3415
0
        njs_vm_memory_error(vm);
3416
0
        return NJS_ERROR;
3417
0
    }
3418
3419
0
    njs_decode_base64url(&key->u.s.raw, &b64);
3420
3421
0
    val = njs_vm_object_prop(vm, jwk, &string_alg, &value);
3422
0
    if (njs_slow_path(val == NULL || !njs_value_is_string(val))) {
3423
0
        njs_vm_type_error(vm, "Invalid JWK oct alg");
3424
0
        return NJS_ERROR;
3425
0
    }
3426
3427
0
    njs_value_string_get(vm, val, &alg);
3428
3429
0
    size = 16;
3430
3431
0
    if (key->alg->type == NJS_ALGORITHM_HMAC) {
3432
0
        for (w = &hashes[0]; w->name.length != 0; w++) {
3433
0
            if (njs_strstr_eq(&alg, &w->name)) {
3434
0
                key->hash = w->value;
3435
0
                goto done;
3436
0
            }
3437
0
        }
3438
3439
0
    } else {
3440
0
        type = key->alg->type;
3441
0
        a = &njs_webcrypto_alg_aes_name[type - NJS_ALGORITHM_AES_GCM][0];
3442
0
        for (; a->length != 0; a++) {
3443
0
            if (njs_strstr_eq(&alg, a)) {
3444
0
                goto done;
3445
0
            }
3446
3447
0
            size += 8;
3448
0
        }
3449
0
    }
3450
3451
0
    njs_vm_type_error(vm, "unexpected \"alg\" value \"%V\" for JWK key",
3452
0
                      &alg);
3453
0
    return NJS_ERROR;
3454
3455
0
done:
3456
3457
0
    if (key->alg->type != NJS_ALGORITHM_HMAC) {
3458
0
        if (key->u.s.raw.length != size) {
3459
0
            njs_vm_type_error(vm, "key size and \"alg\" value \"%V\" mismatch",
3460
0
                              &alg);
3461
0
            return NJS_ERROR;
3462
0
        }
3463
0
    }
3464
3465
0
    val = njs_vm_object_prop(vm, jwk, &key_ops, &value);
3466
0
    if (val != NULL && !njs_value_is_undefined(val)) {
3467
0
        ret = njs_key_usage(vm, val, &usage);
3468
0
        if (njs_slow_path(ret != NJS_OK)) {
3469
0
            return NJS_ERROR;
3470
0
        }
3471
3472
0
        if ((key->usage & usage) != key->usage) {
3473
0
            njs_vm_type_error(vm, "Key operations and usage mismatch");
3474
0
            return NJS_ERROR;
3475
0
        }
3476
0
    }
3477
3478
0
    if (key->extractable) {
3479
0
        val = njs_vm_object_prop(vm, jwk, &string_ext, &value);
3480
0
        if (val != NULL
3481
0
            && !njs_value_is_undefined(val)
3482
0
            && !njs_value_bool(val))
3483
0
        {
3484
0
            njs_vm_type_error(vm, "JWK oct is not extractable");
3485
0
            return NJS_ERROR;
3486
0
        }
3487
0
    }
3488
3489
0
    return NJS_OK;
3490
0
}
3491
3492
3493
static njs_int_t
3494
njs_ext_import_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
3495
    njs_index_t unused, njs_value_t *retval)
3496
0
{
3497
0
    int                         nid;
3498
0
    BIO                         *bio;
3499
0
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
3500
0
    RSA                         *rsa;
3501
0
    EC_KEY                      *ec;
3502
#else
3503
    char                        gname[80];
3504
#endif
3505
0
    unsigned                    mask, usage;
3506
0
    EVP_PKEY                    *pkey;
3507
0
    njs_int_t                   ret;
3508
0
    njs_str_t                   key_data, kty;
3509
0
    njs_value_t                 *options, *jwk, *val;
3510
0
    const u_char                *start;
3511
0
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
3512
0
    const EC_GROUP              *group;
3513
0
#endif
3514
0
    njs_webcrypto_key_t         *key;
3515
0
    PKCS8_PRIV_KEY_INFO         *pkcs8;
3516
0
    njs_opaque_value_t          value;
3517
0
    njs_webcrypto_hash_t        hash;
3518
0
    njs_webcrypto_algorithm_t   *alg;
3519
0
    njs_webcrypto_key_format_t  fmt;
3520
3521
0
    pkey = NULL;
3522
0
    key_data.start = NULL;
3523
0
    key_data.length = 0;
3524
3525
0
    fmt = njs_key_format(vm, njs_arg(args, nargs, 1));
3526
0
    if (njs_slow_path(fmt == NJS_KEY_FORMAT_UNKNOWN)) {
3527
0
        goto fail;
3528
0
    }
3529
3530
0
    options = njs_arg(args, nargs, 3);
3531
0
    alg = njs_key_algorithm(vm, options);
3532
0
    if (njs_slow_path(alg == NULL)) {
3533
0
        goto fail;
3534
0
    }
3535
3536
0
    if (njs_slow_path(!(fmt & alg->fmt))) {
3537
0
        njs_vm_type_error(vm, "unsupported key fmt \"%V\" for \"%V\" key",
3538
0
                          njs_format_string(fmt),
3539
0
                          njs_algorithm_string(alg));
3540
0
        goto fail;
3541
0
    }
3542
3543
0
    ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage);
3544
0
    if (njs_slow_path(ret != NJS_OK)) {
3545
0
        goto fail;
3546
0
    }
3547
3548
0
    if (njs_slow_path(usage & ~alg->usage)) {
3549
0
        njs_vm_type_error(vm, "unsupported key usage for \"%V\" key",
3550
0
                          njs_algorithm_string(alg));
3551
0
        goto fail;
3552
0
    }
3553
3554
0
    if (fmt != NJS_KEY_FORMAT_JWK) {
3555
0
        ret = njs_vm_value_to_bytes(vm, &key_data, njs_arg(args, nargs, 2));
3556
0
        if (njs_slow_path(ret != NJS_OK)) {
3557
0
            goto fail;
3558
0
        }
3559
0
    }
3560
3561
0
    key = njs_webcrypto_key_alloc(vm, alg, usage,
3562
0
                                  njs_value_bool(njs_arg(args, nargs, 4)));
3563
0
    if (njs_slow_path(key == NULL)) {
3564
0
        goto fail;
3565
0
    }
3566
3567
    /*
3568
     * set by njs_webcrypto_key_alloc():
3569
     *
3570
     *  key->u.a.pkey = NULL;
3571
     *  key->u.s.raw.length = 0;
3572
     *  key->u.s.raw.start = NULL;
3573
     *  key->u.a.curve = 0;
3574
     *  key->u.a.privat = 0;
3575
     *  key->hash = NJS_HASH_UNSET;
3576
     */
3577
3578
0
    switch (fmt) {
3579
0
    case NJS_KEY_FORMAT_PKCS8:
3580
0
        bio = njs_bio_new_mem_buf(key_data.start, key_data.length);
3581
0
        if (njs_slow_path(bio == NULL)) {
3582
0
            njs_webcrypto_error(vm, "BIO_new_mem_buf() failed");
3583
0
            goto fail;
3584
0
        }
3585
3586
0
        pkcs8 = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
3587
0
        if (njs_slow_path(pkcs8 == NULL)) {
3588
0
            BIO_free(bio);
3589
0
            njs_webcrypto_error(vm, "d2i_PKCS8_PRIV_KEY_INFO_bio() failed");
3590
0
            goto fail;
3591
0
        }
3592
3593
0
        pkey = EVP_PKCS82PKEY(pkcs8);
3594
0
        if (njs_slow_path(pkey == NULL)) {
3595
0
            PKCS8_PRIV_KEY_INFO_free(pkcs8);
3596
0
            BIO_free(bio);
3597
0
            njs_webcrypto_error(vm, "EVP_PKCS82PKEY() failed");
3598
0
            goto fail;
3599
0
        }
3600
3601
0
        PKCS8_PRIV_KEY_INFO_free(pkcs8);
3602
0
        BIO_free(bio);
3603
3604
0
        key->u.a.privat = 1;
3605
3606
0
        break;
3607
3608
0
    case NJS_KEY_FORMAT_SPKI:
3609
0
        start = key_data.start;
3610
0
        pkey = d2i_PUBKEY(NULL, &start, key_data.length);
3611
0
        if (njs_slow_path(pkey == NULL)) {
3612
0
            njs_webcrypto_error(vm, "d2i_PUBKEY() failed");
3613
0
            goto fail;
3614
0
        }
3615
3616
0
        break;
3617
3618
0
    case NJS_KEY_FORMAT_JWK:
3619
0
        jwk = njs_arg(args, nargs, 2);
3620
0
        if (!njs_value_is_object(jwk)) {
3621
0
            njs_vm_type_error(vm, "invalid JWK key data: object value "
3622
0
                              "expected");
3623
0
            goto fail;
3624
0
        }
3625
3626
0
        val = njs_vm_object_prop(vm, jwk, &string_kty, &value);
3627
0
        if (njs_slow_path(val == NULL)) {
3628
0
            val = njs_value_arg(&njs_value_undefined);
3629
0
        }
3630
3631
0
        ret = njs_vm_value_to_bytes(vm, &kty, val);
3632
0
        if (njs_slow_path(ret != NJS_OK)) {
3633
0
            goto fail;
3634
0
        }
3635
3636
0
        if (njs_strstr_eq(&kty, &njs_str_value("RSA"))) {
3637
0
            pkey = njs_import_jwk_rsa(vm, jwk, key);
3638
0
            if (njs_slow_path(pkey == NULL)) {
3639
0
                goto fail;
3640
0
            }
3641
3642
0
        } else if (njs_strstr_eq(&kty, &njs_str_value("EC"))) {
3643
0
            ret = njs_algorithm_curve(vm, options, &key->u.a.curve);
3644
0
            if (njs_slow_path(ret == NJS_ERROR)) {
3645
0
                goto fail;
3646
0
            }
3647
3648
0
            pkey = njs_import_jwk_ec(vm, jwk, key);
3649
0
            if (njs_slow_path(pkey == NULL)) {
3650
0
                goto fail;
3651
0
            }
3652
3653
0
        } else if (njs_strstr_eq(&kty, &njs_str_value("oct"))) {
3654
0
            ret = njs_import_jwk_oct(vm, jwk, key);
3655
0
            if (njs_slow_path(ret != NJS_OK)) {
3656
0
                goto fail;
3657
0
            }
3658
3659
0
        } else {
3660
0
            njs_vm_type_error(vm, "invalid JWK key type: %V", &kty);
3661
0
            goto fail;
3662
0
        }
3663
3664
0
        break;
3665
3666
0
    case NJS_KEY_FORMAT_RAW:
3667
0
    default:
3668
0
        break;
3669
0
    }
3670
3671
0
    switch (alg->type) {
3672
0
    case NJS_ALGORITHM_RSA_OAEP:
3673
0
    case NJS_ALGORITHM_RSA_PSS:
3674
0
    case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
3675
3676
0
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
3677
3678
0
        rsa = EVP_PKEY_get1_RSA(pkey);
3679
0
        if (njs_slow_path(rsa == NULL)) {
3680
0
            njs_webcrypto_error(vm, "RSA key is not found");
3681
0
            goto fail;
3682
0
        }
3683
3684
0
        RSA_free(rsa);
3685
3686
#else
3687
        if (!EVP_PKEY_is_a(pkey, "RSA")) {
3688
            njs_webcrypto_error(vm, "RSA key is not found");
3689
            goto fail;
3690
        }
3691
#endif
3692
3693
0
        ret = njs_algorithm_hash(vm, options, &hash);
3694
0
        if (njs_slow_path(ret == NJS_ERROR)) {
3695
0
            goto fail;
3696
0
        }
3697
3698
0
        if (key->hash != NJS_HASH_UNSET && key->hash != hash) {
3699
0
            njs_vm_type_error(vm, "RSA JWK hash mismatch");
3700
0
            goto fail;
3701
0
        }
3702
3703
0
        if (key->u.a.privat) {
3704
0
            mask = (alg->type == NJS_ALGORITHM_RSA_OAEP)
3705
0
                         ? ~(NJS_KEY_USAGE_DECRYPT | NJS_KEY_USAGE_UNWRAP_KEY)
3706
0
                         : ~(NJS_KEY_USAGE_SIGN);
3707
0
        } else {
3708
0
            mask = (alg->type == NJS_ALGORITHM_RSA_OAEP)
3709
0
                         ? ~(NJS_KEY_USAGE_ENCRYPT | NJS_KEY_USAGE_WRAP_KEY)
3710
0
                         : ~(NJS_KEY_USAGE_VERIFY);
3711
0
        }
3712
3713
0
        if (key->usage & mask) {
3714
0
            njs_vm_type_error(vm, "key usage mismatch for \"%V\" key",
3715
0
                              njs_algorithm_string(alg));
3716
0
            goto fail;
3717
0
        }
3718
3719
0
        key->hash = hash;
3720
0
        key->u.a.pkey = pkey;
3721
3722
0
        break;
3723
3724
0
    case NJS_ALGORITHM_ECDSA:
3725
0
    case NJS_ALGORITHM_ECDH:
3726
0
        ret = njs_algorithm_curve(vm, options, &key->u.a.curve);
3727
0
        if (njs_slow_path(ret == NJS_ERROR)) {
3728
0
            goto fail;
3729
0
        }
3730
3731
0
        if (fmt == NJS_KEY_FORMAT_RAW) {
3732
0
            pkey = njs_import_raw_ec(vm, &key_data, key);
3733
0
            if (njs_slow_path(pkey == NULL)) {
3734
0
                goto fail;
3735
0
            }
3736
0
        }
3737
3738
0
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
3739
3740
0
        ec = EVP_PKEY_get1_EC_KEY(pkey);
3741
0
        if (njs_slow_path(ec == NULL)) {
3742
0
            njs_webcrypto_error(vm, "EC key is not found");
3743
0
            goto fail;
3744
0
        }
3745
3746
0
        group = EC_KEY_get0_group(ec);
3747
0
        nid = EC_GROUP_get_curve_name(group);
3748
0
        EC_KEY_free(ec);
3749
3750
#else
3751
3752
        if (!EVP_PKEY_is_a(pkey, "EC")) {
3753
            njs_webcrypto_error(vm, "EC key is not found");
3754
            goto fail;
3755
        }
3756
3757
        if (EVP_PKEY_get_group_name(pkey, gname, sizeof(gname), NULL) != 1) {
3758
            njs_webcrypto_error(vm, "EVP_PKEY_get_group_name() failed");
3759
            goto fail;
3760
        }
3761
3762
        nid = OBJ_txt2nid(gname);
3763
3764
#endif
3765
3766
0
        if (njs_slow_path(key->u.a.curve != nid)) {
3767
0
            njs_webcrypto_error(vm, "name curve mismatch");
3768
0
            goto fail;
3769
0
        }
3770
3771
0
        if (alg->type == NJS_ALGORITHM_ECDSA) {
3772
0
            mask = key->u.a.privat ? ~NJS_KEY_USAGE_SIGN
3773
0
                                   : ~NJS_KEY_USAGE_VERIFY;
3774
0
        } else {
3775
0
            if (key->u.a.privat) {
3776
0
                mask = ~(NJS_KEY_USAGE_DERIVE_KEY | NJS_KEY_USAGE_DERIVE_BITS);
3777
3778
0
            } else {
3779
0
                mask = 0;
3780
0
            }
3781
0
        }
3782
3783
0
        if (key->usage & mask) {
3784
0
            njs_vm_type_error(vm, "key usage mismatch for \"%V\" key",
3785
0
                              njs_algorithm_string(alg));
3786
0
            goto fail;
3787
0
        }
3788
3789
0
        key->u.a.pkey = pkey;
3790
3791
0
        break;
3792
3793
0
    case NJS_ALGORITHM_HMAC:
3794
0
        if (fmt == NJS_KEY_FORMAT_RAW) {
3795
0
            ret = njs_algorithm_hash(vm, options, &key->hash);
3796
0
            if (njs_slow_path(ret == NJS_ERROR)) {
3797
0
                goto fail;
3798
0
            }
3799
3800
0
            key->u.s.raw = key_data;
3801
3802
0
        } else {
3803
            /* NJS_KEY_FORMAT_JWK. */
3804
3805
0
            ret = njs_algorithm_hash(vm, options, &hash);
3806
0
            if (njs_slow_path(ret == NJS_ERROR)) {
3807
0
                goto fail;
3808
0
            }
3809
3810
0
            if (key->hash != NJS_HASH_UNSET && key->hash != hash) {
3811
0
                njs_vm_type_error(vm, "HMAC JWK hash mismatch");
3812
0
                goto fail;
3813
0
            }
3814
0
        }
3815
3816
0
        break;
3817
3818
0
    case NJS_ALGORITHM_AES_GCM:
3819
0
    case NJS_ALGORITHM_AES_CTR:
3820
0
    case NJS_ALGORITHM_AES_CBC:
3821
0
        if (fmt == NJS_KEY_FORMAT_RAW) {
3822
0
            switch (key_data.length) {
3823
0
            case 16:
3824
0
            case 24:
3825
0
            case 32:
3826
0
                break;
3827
3828
0
            default:
3829
0
                njs_vm_type_error(vm, "AES Invalid key length");
3830
0
                goto fail;
3831
0
            }
3832
3833
0
            key->u.s.raw = key_data;
3834
0
        }
3835
3836
0
        break;
3837
3838
0
    case NJS_ALGORITHM_PBKDF2:
3839
0
    case NJS_ALGORITHM_HKDF:
3840
0
    default:
3841
0
        key->u.s.raw = key_data;
3842
0
        break;
3843
0
    }
3844
3845
0
    ret = njs_vm_external_create(vm, njs_value_arg(&value),
3846
0
                                 njs_webcrypto_crypto_key_proto_id, key, 0);
3847
0
    if (njs_slow_path(ret != NJS_OK)) {
3848
0
        goto fail;
3849
0
    }
3850
3851
0
    return njs_webcrypto_result(vm, &value, NJS_OK, retval);
3852
3853
0
fail:
3854
3855
0
    if (pkey != NULL) {
3856
0
        EVP_PKEY_free(pkey);
3857
0
    }
3858
3859
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
3860
0
}
3861
3862
3863
static njs_int_t
3864
njs_set_rsa_padding(njs_vm_t *vm, njs_value_t *options, EVP_PKEY *pkey,
3865
    EVP_PKEY_CTX *ctx, njs_webcrypto_alg_t type)
3866
0
{
3867
0
    int                 padding;
3868
0
    int64_t             salt_length;
3869
0
    njs_int_t           ret;
3870
0
    njs_value_t         *value;
3871
0
    njs_opaque_value_t  lvalue;
3872
3873
0
    static const njs_str_t  string_saltl = njs_str("saltLength");
3874
3875
0
    if (type == NJS_ALGORITHM_ECDSA) {
3876
0
        return NJS_OK;
3877
0
    }
3878
3879
0
    padding = (type == NJS_ALGORITHM_RSA_PSS) ? RSA_PKCS1_PSS_PADDING
3880
0
                                              : RSA_PKCS1_PADDING;
3881
0
    ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
3882
0
    if (njs_slow_path(ret <= 0)) {
3883
0
        njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_rsa_padding() failed");
3884
0
        return NJS_ERROR;
3885
0
    }
3886
3887
0
    if (padding == RSA_PKCS1_PSS_PADDING) {
3888
0
        value = njs_vm_object_prop(vm, options, &string_saltl, &lvalue);
3889
0
        if (njs_slow_path(value == NULL)) {
3890
0
            njs_vm_type_error(vm, "RSA-PSS algorithm.saltLength is not "
3891
0
                              "provided");
3892
0
            return NJS_ERROR;
3893
0
        }
3894
3895
0
        ret = njs_value_to_integer(vm, value, &salt_length);
3896
0
        if (njs_slow_path(ret != NJS_OK)) {
3897
0
            return NJS_ERROR;
3898
0
        }
3899
3900
0
        ret = EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_length);
3901
0
        if (njs_slow_path(ret <= 0)) {
3902
0
            njs_webcrypto_error(vm,
3903
0
                                "EVP_PKEY_CTX_set_rsa_pss_saltlen() failed");
3904
0
            return NJS_ERROR;
3905
0
        }
3906
0
    }
3907
3908
0
    return NJS_OK;
3909
0
}
3910
3911
3912
static unsigned int
3913
njs_ec_rs_size(EVP_PKEY *pkey)
3914
0
{
3915
0
    int             bits;
3916
0
    const EC_KEY    *ec_key;
3917
0
    const EC_GROUP  *ec_group;
3918
3919
0
    ec_key = njs_pkey_get_ec_key(pkey);
3920
0
    if (ec_key == NULL) {
3921
0
        return 0;
3922
0
    }
3923
3924
0
    ec_group = EC_KEY_get0_group(ec_key);
3925
0
    if (ec_group == NULL) {
3926
0
        return 0;
3927
0
    }
3928
3929
0
    bits = njs_ec_group_order_bits(ec_group);
3930
0
    if (bits == 0) {
3931
0
        return 0;
3932
0
    }
3933
3934
0
    return (bits + 7) / 8;
3935
0
}
3936
3937
3938
static njs_int_t
3939
njs_convert_der_to_p1363(njs_vm_t *vm, EVP_PKEY *pkey, const u_char *der,
3940
    size_t der_len, u_char **pout, size_t *out_len)
3941
0
{
3942
0
    u_char        *data;
3943
0
    unsigned      n;
3944
0
    njs_int_t     ret;
3945
0
    ECDSA_SIG     *ec_sig;
3946
0
    const BIGNUM  *r, *s;
3947
3948
0
    ret = NJS_OK;
3949
0
    ec_sig = NULL;
3950
3951
0
    n = njs_ec_rs_size(pkey);
3952
0
    if (n == 0) {
3953
0
        goto fail;
3954
0
    }
3955
3956
0
    data = njs_mp_alloc(njs_vm_memory_pool(vm), 2 * n);
3957
0
    if (njs_slow_path(data == NULL)) {
3958
0
        goto memory_error;
3959
0
    }
3960
3961
0
    ec_sig = d2i_ECDSA_SIG(NULL, &der, der_len);
3962
0
    if (ec_sig == NULL) {
3963
0
        goto fail;
3964
0
    }
3965
3966
0
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
3967
0
    ECDSA_SIG_get0(ec_sig, &r, &s);
3968
#else
3969
    r = ec_sig->r;
3970
    s = ec_sig->s;
3971
#endif
3972
3973
0
    if (njs_bn_bn2binpad(r, data, n) <= 0) {
3974
0
        goto fail;
3975
0
    }
3976
3977
0
    if (njs_bn_bn2binpad(s, &data[n], n) <= 0) {
3978
0
        goto fail;
3979
0
    }
3980
3981
0
    *pout = data;
3982
0
    *out_len = 2 * n;
3983
3984
0
    goto done;
3985
3986
0
fail:
3987
3988
0
    *out_len = 0;
3989
3990
0
done:
3991
3992
0
    if (ec_sig != NULL) {
3993
0
        ECDSA_SIG_free(ec_sig);
3994
0
    }
3995
3996
0
    return ret;
3997
3998
0
memory_error:
3999
4000
0
    njs_vm_memory_error(vm);
4001
4002
0
    return NJS_ERROR;
4003
0
}
4004
4005
4006
static njs_int_t
4007
njs_convert_p1363_to_der(njs_vm_t *vm, EVP_PKEY *pkey, u_char *p1363,
4008
    size_t p1363_len, u_char **pout, size_t *out_len)
4009
0
{
4010
0
    int        len;
4011
0
    BIGNUM     *r, *s;
4012
0
    u_char     *data;
4013
0
    unsigned   n;
4014
0
    njs_int_t  ret;
4015
0
    ECDSA_SIG  *ec_sig;
4016
4017
0
    ret = NJS_OK;
4018
0
    ec_sig = NULL;
4019
4020
0
    n = njs_ec_rs_size(pkey);
4021
4022
0
    if (njs_slow_path(n == 0 || p1363_len != 2 * n)) {
4023
0
        goto fail;
4024
0
    }
4025
4026
0
    ec_sig = ECDSA_SIG_new();
4027
0
    if (njs_slow_path(ec_sig == NULL)) {
4028
0
        goto memory_error;
4029
0
    }
4030
4031
0
    r = BN_new();
4032
0
    if (njs_slow_path(r == NULL)) {
4033
0
        goto memory_error;
4034
0
    }
4035
4036
0
    s = BN_new();
4037
0
    if (njs_slow_path(s == NULL)) {
4038
0
        goto memory_error;
4039
0
    }
4040
4041
0
    if (r != BN_bin2bn(p1363, n, r)) {
4042
0
        goto fail;
4043
0
    }
4044
4045
0
    if (s != BN_bin2bn(&p1363[n], n, s)) {
4046
0
        goto fail;
4047
0
    }
4048
4049
0
    if (njs_ecdsa_sig_set0(ec_sig, r, s) != 1) {
4050
0
        njs_webcrypto_error(vm, "njs_ecdsa_sig_set0() failed");
4051
0
        ret = NJS_ERROR;
4052
0
        goto fail;
4053
0
    }
4054
4055
0
    data = njs_mp_alloc(njs_vm_memory_pool(vm), 2 * n + 16);
4056
0
    if (njs_slow_path(data == NULL)) {
4057
0
        goto memory_error;
4058
0
    }
4059
4060
0
    *pout = data;
4061
0
    len = i2d_ECDSA_SIG(ec_sig, &data);
4062
4063
0
    if (len < 0) {
4064
0
        goto fail;
4065
0
    }
4066
4067
0
    *out_len = len;
4068
4069
0
    goto done;
4070
4071
0
fail:
4072
4073
0
    *out_len = 0;
4074
4075
0
done:
4076
4077
0
    if (ec_sig != NULL) {
4078
0
        ECDSA_SIG_free(ec_sig);
4079
0
    }
4080
4081
0
    return ret;
4082
4083
0
memory_error:
4084
4085
0
    njs_vm_memory_error(vm);
4086
4087
0
    return NJS_ERROR;
4088
0
}
4089
4090
4091
static njs_int_t
4092
njs_ext_sign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
4093
    njs_index_t verify, njs_value_t *retval)
4094
0
{
4095
0
    u_char                     *dst, *p;
4096
0
    size_t                     olen, outlen;
4097
0
    unsigned                   mask, m_len;
4098
0
    njs_int_t                  ret;
4099
0
    njs_str_t                  data, sig;
4100
0
    EVP_MD_CTX                 *mctx;
4101
0
    njs_value_t                *options;
4102
0
    EVP_PKEY_CTX               *pctx;
4103
0
    const EVP_MD               *md;
4104
0
    njs_opaque_value_t         result;
4105
0
    njs_webcrypto_key_t        *key;
4106
0
    njs_webcrypto_hash_t       hash;
4107
0
    njs_webcrypto_algorithm_t  *alg;
4108
0
    unsigned char              m[EVP_MAX_MD_SIZE];
4109
4110
0
    mctx = NULL;
4111
0
    pctx = NULL;
4112
4113
0
    options = njs_arg(args, nargs, 1);
4114
0
    alg = njs_key_algorithm(vm, options);
4115
0
    if (njs_slow_path(alg == NULL)) {
4116
0
        goto fail;
4117
0
    }
4118
4119
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id,
4120
0
                          njs_arg(args, nargs, 2));
4121
0
    if (njs_slow_path(key == NULL)) {
4122
0
        njs_vm_type_error(vm, "\"key\" is not a CryptoKey object");
4123
0
        goto fail;
4124
0
    }
4125
4126
0
    mask = verify ? NJS_KEY_USAGE_VERIFY : NJS_KEY_USAGE_SIGN;
4127
0
    if (njs_slow_path(!(key->usage & mask))) {
4128
0
        njs_vm_type_error(vm, "provide key does not support \"sign\" "
4129
0
                          "operation");
4130
0
        goto fail;
4131
0
    }
4132
4133
0
    if (njs_slow_path(key->alg != alg)) {
4134
0
        njs_vm_type_error(vm, "cannot %s using \"%V\" with \"%V\" key",
4135
0
                          verify ? "verify" : "sign",
4136
0
                          njs_algorithm_string(key->alg),
4137
0
                          njs_algorithm_string(alg));
4138
0
        goto fail;
4139
0
    }
4140
4141
0
    if (verify) {
4142
0
        ret = njs_vm_value_to_bytes(vm, &sig, njs_arg(args, nargs, 3));
4143
0
        if (njs_slow_path(ret != NJS_OK)) {
4144
0
            goto fail;
4145
0
        }
4146
4147
0
        ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 4));
4148
0
        if (njs_slow_path(ret != NJS_OK)) {
4149
0
            goto fail;
4150
0
        }
4151
4152
0
    } else {
4153
0
        ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 3));
4154
0
        if (njs_slow_path(ret != NJS_OK)) {
4155
0
            goto fail;
4156
0
        }
4157
0
    }
4158
4159
0
    if (alg->type == NJS_ALGORITHM_ECDSA) {
4160
0
        ret = njs_algorithm_hash(vm, options, &hash);
4161
0
        if (njs_slow_path(ret == NJS_ERROR)) {
4162
0
            goto fail;
4163
0
        }
4164
4165
0
    } else {
4166
0
        hash = key->hash;
4167
0
    }
4168
4169
0
    md = njs_algorithm_hash_digest(hash);
4170
4171
0
    outlen = 0;
4172
4173
0
    switch (alg->type) {
4174
0
    case NJS_ALGORITHM_HMAC:
4175
0
        m_len = EVP_MD_size(md);
4176
4177
0
        if (!verify) {
4178
0
            dst = njs_mp_alloc(njs_vm_memory_pool(vm), m_len);
4179
0
            if (njs_slow_path(dst == NULL)) {
4180
0
                njs_vm_memory_error(vm);
4181
0
                goto fail;
4182
0
            }
4183
4184
0
        } else {
4185
0
            dst = (u_char *) &m[0];
4186
0
        }
4187
4188
0
        outlen = m_len;
4189
4190
0
        p = HMAC(md, key->u.s.raw.start, key->u.s.raw.length, data.start,
4191
0
                 data.length, dst, &m_len);
4192
4193
0
        if (njs_slow_path(p == NULL || m_len != outlen)) {
4194
0
            njs_webcrypto_error(vm, "HMAC() failed");
4195
0
            goto fail;
4196
0
        }
4197
4198
0
        if (verify) {
4199
0
            ret = (sig.length == outlen && memcmp(sig.start, dst, outlen) == 0);
4200
0
        }
4201
4202
0
        break;
4203
4204
0
    case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
4205
0
    case NJS_ALGORITHM_RSA_PSS:
4206
0
    case NJS_ALGORITHM_ECDSA:
4207
0
    default:
4208
0
        mctx = njs_evp_md_ctx_new();
4209
0
        if (njs_slow_path(mctx == NULL)) {
4210
0
            njs_webcrypto_error(vm, "njs_evp_md_ctx_new() failed");
4211
0
            goto fail;
4212
0
        }
4213
4214
0
        ret = EVP_DigestInit_ex(mctx, md, NULL);
4215
0
        if (njs_slow_path(ret <= 0)) {
4216
0
            njs_webcrypto_error(vm, "EVP_DigestInit_ex() failed");
4217
0
            goto fail;
4218
0
        }
4219
4220
0
        ret = EVP_DigestUpdate(mctx, data.start, data.length);
4221
0
        if (njs_slow_path(ret <= 0)) {
4222
0
            njs_webcrypto_error(vm, "EVP_DigestUpdate() failed");
4223
0
            goto fail;
4224
0
        }
4225
4226
0
        ret = EVP_DigestFinal_ex(mctx, m, &m_len);
4227
0
        if (njs_slow_path(ret <= 0)) {
4228
0
            njs_webcrypto_error(vm, "EVP_DigestFinal_ex() failed");
4229
0
            goto fail;
4230
0
        }
4231
4232
0
        olen = EVP_PKEY_size(key->u.a.pkey);
4233
0
        dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen);
4234
0
        if (njs_slow_path(dst == NULL)) {
4235
0
            njs_vm_memory_error(vm);
4236
0
            goto fail;
4237
0
        }
4238
4239
0
        pctx = EVP_PKEY_CTX_new(key->u.a.pkey, NULL);
4240
0
        if (njs_slow_path(pctx == NULL)) {
4241
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed");
4242
0
            goto fail;
4243
0
        }
4244
4245
0
        if (!verify) {
4246
0
            ret = EVP_PKEY_sign_init(pctx);
4247
0
            if (njs_slow_path(ret <= 0)) {
4248
0
                njs_webcrypto_error(vm, "EVP_PKEY_sign_init() failed");
4249
0
                goto fail;
4250
0
            }
4251
4252
0
        } else {
4253
0
            ret = EVP_PKEY_verify_init(pctx);
4254
0
            if (njs_slow_path(ret <= 0)) {
4255
0
                njs_webcrypto_error(vm, "EVP_PKEY_verify_init() failed");
4256
0
                goto fail;
4257
0
            }
4258
0
        }
4259
4260
0
        ret = njs_set_rsa_padding(vm, options, key->u.a.pkey, pctx, alg->type);
4261
0
        if (njs_slow_path(ret != NJS_OK)) {
4262
0
            goto fail;
4263
0
        }
4264
4265
0
        ret = EVP_PKEY_CTX_set_signature_md(pctx, md);
4266
0
        if (njs_slow_path(ret <= 0)) {
4267
0
            njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_signature_md() failed");
4268
0
            goto fail;
4269
0
        }
4270
4271
0
        if (!verify) {
4272
0
            outlen = olen;
4273
0
            ret = EVP_PKEY_sign(pctx, dst, &outlen, m, m_len);
4274
0
            if (njs_slow_path(ret <= 0)) {
4275
0
                njs_webcrypto_error(vm, "EVP_PKEY_sign() failed");
4276
0
                goto fail;
4277
0
            }
4278
4279
0
            if (alg->type == NJS_ALGORITHM_ECDSA) {
4280
0
                ret = njs_convert_der_to_p1363(vm, key->u.a.pkey, dst, outlen,
4281
0
                                               &dst, &outlen);
4282
0
                if (njs_slow_path(ret != NJS_OK)) {
4283
0
                    goto fail;
4284
0
                }
4285
0
            }
4286
4287
0
        } else {
4288
0
            if (alg->type == NJS_ALGORITHM_ECDSA) {
4289
0
                ret = njs_convert_p1363_to_der(vm, key->u.a.pkey, sig.start,
4290
0
                                               sig.length, &sig.start,
4291
0
                                               &sig.length);
4292
0
                if (njs_slow_path(ret != NJS_OK)) {
4293
0
                    goto fail;
4294
0
                }
4295
0
            }
4296
4297
0
            ret = EVP_PKEY_verify(pctx, sig.start, sig.length, m, m_len);
4298
0
            if (njs_slow_path(ret < 0)) {
4299
0
                njs_webcrypto_error(vm, "EVP_PKEY_verify() failed");
4300
0
                goto fail;
4301
0
            }
4302
0
        }
4303
4304
0
        njs_evp_md_ctx_free(mctx);
4305
4306
0
        EVP_PKEY_CTX_free(pctx);
4307
4308
0
        break;
4309
0
    }
4310
4311
0
    if (!verify) {
4312
0
        ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst,
4313
0
                                            outlen);
4314
0
        if (njs_slow_path(ret != NJS_OK)) {
4315
0
            goto fail;
4316
0
        }
4317
4318
0
    } else {
4319
0
        njs_value_boolean_set(njs_value_arg(&result), ret != 0);
4320
0
    }
4321
4322
0
    return njs_webcrypto_result(vm, &result, NJS_OK, retval);
4323
4324
0
fail:
4325
4326
0
    if (mctx != NULL) {
4327
0
        njs_evp_md_ctx_free(mctx);
4328
0
    }
4329
4330
0
    if (pctx != NULL) {
4331
0
        EVP_PKEY_CTX_free(pctx);
4332
0
    }
4333
4334
0
    return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval);
4335
0
}
4336
4337
4338
static njs_int_t
4339
njs_ext_unwrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
4340
    njs_index_t unused, njs_value_t *retval)
4341
0
{
4342
0
    njs_vm_internal_error(vm, "\"unwrapKey\" not implemented");
4343
0
    return NJS_ERROR;
4344
0
}
4345
4346
4347
static njs_int_t
4348
njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
4349
    njs_index_t unused, njs_value_t *retval)
4350
0
{
4351
0
    njs_vm_internal_error(vm, "\"wrapKey\" not implemented");
4352
0
    return NJS_ERROR;
4353
0
}
4354
4355
4356
static njs_int_t
4357
njs_key_ext_algorithm(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
4358
    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
4359
0
{
4360
0
    u_char               *start;
4361
0
    njs_int_t            ret;
4362
0
    njs_str_t            *name;
4363
0
    const BIGNUM         *n_bn, *e_bn;
4364
0
    const EC_GROUP       *group;
4365
0
    njs_opaque_value_t   alg, name_s, val, hash;
4366
0
    njs_webcrypto_key_t  *key;
4367
4368
0
    static const njs_str_t  string_pexponent = njs_str("publicExponent");
4369
4370
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
4371
0
    if (njs_slow_path(key == NULL)) {
4372
0
        njs_value_undefined_set(retval);
4373
0
        return NJS_DECLINED;
4374
0
    }
4375
4376
0
    name = &njs_webcrypto_alg[key->alg->type].name;
4377
0
    ret = njs_vm_value_string_create(vm, njs_value_arg(&alg), name->start,
4378
0
                                     name->length);
4379
0
    if (njs_slow_path(ret != NJS_OK)) {
4380
0
        return NJS_ERROR;
4381
0
    }
4382
4383
0
    (void) njs_vm_value_string_create(vm, njs_value_arg(&name_s),
4384
0
                                     (u_char *) "name", njs_length("name"));
4385
4386
0
    ret = njs_vm_object_alloc(vm, retval, &name_s, &alg, NULL);
4387
0
    if (njs_slow_path(ret != NJS_OK)) {
4388
0
        return NJS_ERROR;
4389
0
    }
4390
4391
0
    switch (key->alg->type) {
4392
0
    case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
4393
0
    case NJS_ALGORITHM_RSA_PSS:
4394
0
    case NJS_ALGORITHM_RSA_OAEP:
4395
        /* RsaHashedKeyGenParams */
4396
4397
0
        njs_assert(key->u.a.pkey != NULL);
4398
0
        njs_assert(EVP_PKEY_id(key->u.a.pkey) == EVP_PKEY_RSA);
4399
4400
0
        njs_rsa_get0_key(njs_pkey_get_rsa_key(key->u.a.pkey), &n_bn, &e_bn,
4401
0
                         NULL);
4402
4403
0
        njs_value_number_set(njs_value_arg(&val), BN_num_bits(n_bn));
4404
4405
0
        ret = njs_vm_object_prop_set(vm, retval, &string_ml, &val);
4406
0
        if (njs_slow_path(ret != NJS_OK)) {
4407
0
            return NJS_ERROR;
4408
0
        }
4409
4410
0
        start = njs_mp_alloc(njs_vm_memory_pool(vm), BN_num_bytes(e_bn));
4411
0
        if (njs_slow_path(start == NULL)) {
4412
0
            njs_vm_memory_error(vm);
4413
0
            return NJS_ERROR;
4414
0
        }
4415
4416
0
        BN_bn2bin(e_bn, start);
4417
4418
0
        ret = njs_vm_value_buffer_set(vm, njs_value_arg(&val), start,
4419
0
                                      BN_num_bytes(e_bn));
4420
0
        if (njs_slow_path(ret != NJS_OK)) {
4421
0
            return NJS_ERROR;
4422
0
        }
4423
4424
0
        ret = njs_vm_object_prop_set(vm, retval, &string_pexponent, &val);
4425
0
        if (njs_slow_path(ret != NJS_OK)) {
4426
0
            return NJS_ERROR;
4427
0
        }
4428
4429
0
        name = njs_algorithm_hash_name(key->hash);
4430
0
        ret = njs_vm_value_string_create(vm, njs_value_arg(&hash), name->start,
4431
0
                                         name->length);
4432
0
        if (njs_slow_path(ret != NJS_OK)) {
4433
0
            return NJS_ERROR;
4434
0
        }
4435
4436
0
        ret = njs_vm_object_alloc(vm, njs_value_arg(&val), NULL);
4437
0
        if (njs_slow_path(ret != NJS_OK)) {
4438
0
            return NJS_ERROR;
4439
0
        }
4440
4441
0
        ret = njs_vm_object_prop_set(vm, njs_value_arg(&val), &string_name,
4442
0
                                     &hash);
4443
0
        if (njs_slow_path(ret != NJS_OK)) {
4444
0
            return NJS_ERROR;
4445
0
        }
4446
4447
0
        ret = njs_vm_object_prop_set(vm, retval, &string_hash, &val);
4448
0
        if (njs_slow_path(ret != NJS_OK)) {
4449
0
            return NJS_ERROR;
4450
0
        }
4451
4452
0
        break;
4453
4454
0
    case NJS_ALGORITHM_AES_GCM:
4455
0
    case NJS_ALGORITHM_AES_CTR:
4456
0
    case NJS_ALGORITHM_AES_CBC:
4457
        /* AesKeyGenParams */
4458
4459
0
        njs_value_number_set(njs_value_arg(&val), key->u.s.raw.length * 8);
4460
4461
0
        ret = njs_vm_object_prop_set(vm, retval, &string_length, &val);
4462
0
        if (njs_slow_path(ret != NJS_OK)) {
4463
0
            return NJS_ERROR;
4464
0
        }
4465
4466
0
        break;
4467
4468
0
    case NJS_ALGORITHM_ECDSA:
4469
0
    case NJS_ALGORITHM_ECDH:
4470
        /* EcKeyGenParams */
4471
4472
0
        njs_assert(key->u.a.pkey != NULL);
4473
0
        njs_assert(EVP_PKEY_id(key->u.a.pkey) == EVP_PKEY_EC);
4474
4475
0
        group = EC_KEY_get0_group(njs_pkey_get_ec_key(key->u.a.pkey));
4476
4477
0
        name = njs_algorithm_curve_name(EC_GROUP_get_curve_name(group));
4478
4479
0
        ret = njs_vm_value_string_create(vm, njs_value_arg(&val), name->start,
4480
0
                                         name->length);
4481
0
        if (njs_slow_path(ret != NJS_OK)) {
4482
0
            return NJS_ERROR;
4483
0
        }
4484
4485
0
        ret = njs_vm_object_prop_set(vm, retval, &string_curve, &val);
4486
0
        if (njs_slow_path(ret != NJS_OK)) {
4487
0
            return NJS_ERROR;
4488
0
        }
4489
4490
0
        break;
4491
4492
0
    case NJS_ALGORITHM_HMAC:
4493
0
    default:
4494
        /* HmacKeyGenParams */
4495
4496
0
        name = njs_algorithm_hash_name(key->hash);
4497
0
        ret = njs_vm_value_string_create(vm, njs_value_arg(&val), name->start,
4498
0
                                         name->length);
4499
0
        if (njs_slow_path(ret != NJS_OK)) {
4500
0
            return NJS_ERROR;
4501
0
        }
4502
4503
0
        ret = njs_vm_object_prop_set(vm, retval, &string_hash, &val);
4504
0
        if (njs_slow_path(ret != NJS_OK)) {
4505
0
            return NJS_ERROR;
4506
0
        }
4507
4508
0
        break;
4509
0
    }
4510
4511
0
    return NJS_OK;
4512
0
}
4513
4514
4515
static njs_int_t
4516
njs_key_ext_extractable(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
4517
    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
4518
0
{
4519
0
    njs_webcrypto_key_t  *key;
4520
4521
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
4522
0
    if (njs_slow_path(key == NULL)) {
4523
0
        njs_value_undefined_set(retval);
4524
0
        return NJS_DECLINED;
4525
0
    }
4526
4527
0
    njs_value_boolean_set(retval, key->extractable);
4528
4529
0
    return NJS_OK;
4530
0
}
4531
4532
4533
static njs_int_t
4534
njs_key_ext_type(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
4535
    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
4536
0
{
4537
0
    const char           *type;
4538
0
    njs_webcrypto_key_t  *key;
4539
4540
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
4541
0
    if (njs_slow_path(key == NULL)) {
4542
0
        njs_value_undefined_set(retval);
4543
0
        return NJS_DECLINED;
4544
0
    }
4545
4546
0
    if (key->alg->raw) {
4547
0
        (void) njs_vm_value_string_create(vm, retval, (u_char *) "secret",
4548
0
                                          njs_length("secret"));
4549
0
    } else {
4550
0
        type = key->u.a.privat ? "private": "public";
4551
0
        (void) njs_vm_value_string_create(vm, retval, (u_char *) type,
4552
0
                                          key->u.a.privat ? 7 : 6);
4553
0
    }
4554
4555
0
    return NJS_OK;
4556
0
}
4557
4558
4559
static njs_int_t
4560
njs_key_ext_usages(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
4561
    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
4562
0
{
4563
0
    njs_webcrypto_key_t  *key;
4564
4565
0
    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
4566
0
    if (njs_slow_path(key == NULL)) {
4567
0
        njs_value_undefined_set(retval);
4568
0
        return NJS_DECLINED;
4569
0
    }
4570
4571
0
    return njs_key_ops(vm, retval, key->usage);
4572
0
}
4573
4574
4575
static njs_int_t
4576
njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
4577
    njs_index_t unused, njs_value_t *retval)
4578
0
{
4579
0
    njs_int_t    ret;
4580
0
    njs_str_t    fill;
4581
0
    njs_value_t  *buffer;
4582
4583
0
    buffer = njs_arg(args, nargs, 1);
4584
4585
0
    ret = njs_vm_value_to_bytes(vm, &fill, buffer);
4586
0
    if (njs_slow_path(ret != NJS_OK)) {
4587
0
        return NJS_ERROR;
4588
0
    }
4589
4590
0
    if (njs_slow_path(fill.length > 65536)) {
4591
0
        njs_vm_type_error(vm, "requested length exceeds 65536 bytes");
4592
0
        return NJS_ERROR;
4593
0
    }
4594
4595
0
    if (RAND_bytes(fill.start, fill.length) != 1) {
4596
0
        njs_webcrypto_error(vm, "RAND_bytes() failed");
4597
0
        return NJS_ERROR;
4598
0
    }
4599
4600
0
    njs_value_assign(retval, buffer);
4601
4602
0
    return NJS_OK;
4603
0
}
4604
4605
4606
static void
4607
njs_webcrypto_cleanup_pkey(void *data)
4608
0
{
4609
0
    njs_webcrypto_key_t  *key = data;
4610
4611
0
    if (!key->alg->raw) {
4612
0
        EVP_PKEY_free(key->u.a.pkey);
4613
0
    }
4614
0
}
4615
4616
4617
static njs_webcrypto_key_t *
4618
njs_webcrypto_key_alloc(njs_vm_t *vm, njs_webcrypto_algorithm_t *alg,
4619
    unsigned usage, njs_bool_t extractable)
4620
0
{
4621
0
    njs_mp_cleanup_t     *cln;
4622
0
    njs_webcrypto_key_t  *key;
4623
4624
0
    key = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_webcrypto_key_t));
4625
0
    if (njs_slow_path(key == NULL)) {
4626
0
        njs_vm_memory_error(vm);
4627
0
        return NULL;
4628
0
    }
4629
4630
0
    cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0);
4631
0
    if (cln == NULL) {
4632
0
        njs_vm_memory_error(vm);
4633
0
        return NULL;
4634
0
    }
4635
4636
0
    cln->handler = njs_webcrypto_cleanup_pkey;
4637
0
    cln->data = key;
4638
4639
0
    key->alg = alg;
4640
0
    key->usage = usage;
4641
0
    key->extractable = extractable;
4642
4643
0
    return key;
4644
0
}
4645
4646
4647
static njs_webcrypto_key_format_t
4648
njs_key_format(njs_vm_t *vm, njs_value_t *value)
4649
0
{
4650
0
    njs_int_t              ret;
4651
0
    njs_str_t              format;
4652
0
    njs_opaque_value_t     string;
4653
0
    njs_webcrypto_entry_t  *e;
4654
4655
0
    ret = njs_value_to_string(vm, njs_value_arg(&string), value);
4656
0
    if (njs_slow_path(ret != NJS_OK)) {
4657
0
        return NJS_KEY_FORMAT_UNKNOWN;
4658
0
    }
4659
4660
0
    njs_value_string_get(vm, njs_value_arg(&string), &format);
4661
4662
0
    for (e = &njs_webcrypto_format[0]; e->name.length != 0; e++) {
4663
0
        if (njs_strstr_eq(&format, &e->name)) {
4664
0
            return e->value;
4665
0
        }
4666
0
    }
4667
4668
0
    njs_vm_type_error(vm, "unknown key format: \"%V\"", &format);
4669
4670
0
    return NJS_KEY_FORMAT_UNKNOWN;
4671
0
}
4672
4673
4674
static njs_str_t *
4675
njs_format_string(njs_webcrypto_key_format_t fmt)
4676
0
{
4677
0
    njs_webcrypto_entry_t  *e;
4678
4679
0
    for (e = &njs_webcrypto_format[0]; e->name.length != 0; e++) {
4680
0
        if (fmt == e->value) {
4681
0
            break;
4682
0
        }
4683
0
    }
4684
4685
0
    return &e->name;
4686
0
}
4687
4688
4689
static njs_int_t
4690
njs_key_usage_array_handler(njs_vm_t *vm, njs_iterator_args_t *args,
4691
    njs_value_t *value, int64_t index, njs_value_t *retval)
4692
0
{
4693
0
    unsigned               *mask;
4694
0
    njs_str_t              u;
4695
0
    njs_int_t              ret;
4696
0
    njs_opaque_value_t     usage;
4697
0
    njs_webcrypto_entry_t  *e;
4698
4699
0
    njs_value_assign(&usage, value);
4700
4701
0
    ret = njs_value_to_string(vm, njs_value_arg(&usage), njs_value_arg(&usage));
4702
0
    if (njs_slow_path(ret != NJS_OK)) {
4703
0
        return NJS_ERROR;
4704
0
    }
4705
4706
0
    njs_value_string_get(vm, njs_value_arg(&usage), &u);
4707
4708
0
    for (e = &njs_webcrypto_usage[0]; e->name.length != 0; e++) {
4709
0
        if (njs_strstr_eq(&u, &e->name)) {
4710
0
            mask = args->data;
4711
0
            *mask |= e->value;
4712
0
            return NJS_OK;
4713
0
        }
4714
0
    }
4715
4716
0
    njs_vm_type_error(vm, "unknown key usage: \"%V\"", &u);
4717
4718
0
    return NJS_ERROR;
4719
0
}
4720
4721
4722
static njs_int_t
4723
njs_key_usage(njs_vm_t *vm, njs_value_t *value, unsigned *mask)
4724
0
{
4725
0
    int64_t              length;
4726
0
    njs_int_t            ret;
4727
0
    njs_opaque_value_t   retval;
4728
0
    njs_iterator_args_t  args;
4729
4730
0
    if (!njs_value_is_array(value)) {
4731
0
        njs_vm_type_error(vm, "\"keyUsages\" argument must be an Array");
4732
0
        return NJS_ERROR;
4733
0
    }
4734
4735
0
    ret = njs_vm_array_length(vm, value, &length);
4736
0
    if (njs_slow_path(ret != NJS_OK)) {
4737
0
        return NJS_ERROR;
4738
0
    }
4739
4740
0
    *mask = 0;
4741
4742
0
    njs_value_assign(&args.value, value);
4743
0
    args.from = 0;
4744
0
    args.to = length;
4745
0
    args.data = mask;
4746
4747
0
    return njs_vm_object_iterate(vm, &args, njs_key_usage_array_handler,
4748
0
                                 njs_value_arg(&retval));
4749
0
}
4750
4751
4752
static njs_int_t
4753
njs_key_ops(njs_vm_t *vm, njs_value_t *retval, unsigned mask)
4754
0
{
4755
0
    njs_int_t              ret;
4756
0
    njs_value_t            *value;
4757
0
    njs_webcrypto_entry_t  *e;
4758
4759
0
    ret = njs_vm_array_alloc(vm, retval, 4);
4760
0
    if (njs_slow_path(ret != NJS_OK)) {
4761
0
        return NJS_ERROR;
4762
0
    }
4763
4764
0
    for (e = &njs_webcrypto_usage[0]; e->name.length != 0; e++) {
4765
0
        if (mask & e->value) {
4766
0
            value = njs_vm_array_push(vm, retval);
4767
0
            if (value == NULL) {
4768
0
                return NJS_ERROR;
4769
0
            }
4770
4771
0
            ret = njs_vm_value_string_create(vm, value, e->name.start,
4772
0
                                             e->name.length);
4773
0
            if (ret != NJS_OK) {
4774
0
                return NJS_ERROR;
4775
0
            }
4776
0
        }
4777
0
    }
4778
4779
0
    return NJS_OK;
4780
0
}
4781
4782
4783
static njs_webcrypto_algorithm_t *
4784
njs_key_algorithm(njs_vm_t *vm, njs_value_t *options)
4785
0
{
4786
0
    njs_int_t                  ret;
4787
0
    njs_str_t                  a;
4788
0
    njs_value_t                *val;
4789
0
    njs_opaque_value_t         name;
4790
0
    njs_webcrypto_entry_t      *e;
4791
0
    njs_webcrypto_algorithm_t  *alg;
4792
4793
0
    if (njs_value_is_object(options)) {
4794
0
        val = njs_vm_object_prop(vm, options, &string_name, &name);
4795
0
        if (njs_slow_path(val == NULL)) {
4796
0
            njs_vm_type_error(vm, "algorithm name is not provided");
4797
0
            return NULL;
4798
0
        }
4799
4800
0
    } else {
4801
0
        njs_value_assign(&name, options);
4802
0
    }
4803
4804
0
    ret = njs_value_to_string(vm, njs_value_arg(&name), njs_value_arg(&name));
4805
0
    if (njs_slow_path(ret != NJS_OK)) {
4806
0
        return NULL;
4807
0
    }
4808
4809
0
    njs_value_string_get(vm, njs_value_arg(&name), &a);
4810
4811
0
    for (e = &njs_webcrypto_alg[0]; e->name.length != 0; e++) {
4812
0
        if (njs_strstr_case_eq(&a, &e->name)) {
4813
0
            alg = (njs_webcrypto_algorithm_t *) e->value;
4814
4815
0
            return alg;
4816
0
        }
4817
0
    }
4818
4819
0
    njs_vm_type_error(vm, "unknown algorithm name: \"%V\"", &a);
4820
4821
0
    return NULL;
4822
0
}
4823
4824
4825
static njs_str_t *
4826
njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm)
4827
0
{
4828
0
    njs_webcrypto_entry_t      *e;
4829
0
    njs_webcrypto_algorithm_t  *alg;
4830
4831
0
    for (e = &njs_webcrypto_alg[0]; e->name.length != 0; e++) {
4832
0
        alg = (njs_webcrypto_algorithm_t *) e->value;
4833
0
        if (alg->type == algorithm->type) {
4834
0
            break;
4835
0
        }
4836
0
    }
4837
4838
0
    return &e->name;
4839
0
}
4840
4841
4842
static njs_int_t
4843
njs_algorithm_hash(njs_vm_t *vm, njs_value_t *options,
4844
    njs_webcrypto_hash_t *hash)
4845
0
{
4846
0
    njs_int_t              ret;
4847
0
    njs_str_t              name;
4848
0
    njs_value_t            *val;
4849
0
    njs_opaque_value_t     value;
4850
0
    njs_webcrypto_entry_t  *e;
4851
4852
0
    if (njs_value_is_object(options)) {
4853
0
        val = njs_vm_object_prop(vm, options, &string_hash, &value);
4854
0
        if (val == NULL) {
4855
0
            *hash = NJS_HASH_SHA256;
4856
0
            return NJS_OK;
4857
0
        }
4858
4859
0
    } else {
4860
0
        njs_value_assign(&value, options);
4861
0
    }
4862
4863
0
    ret = njs_value_to_string(vm, njs_value_arg(&value), njs_value_arg(&value));
4864
0
    if (njs_slow_path(ret != NJS_OK)) {
4865
0
        return NJS_ERROR;
4866
0
    }
4867
4868
0
    njs_value_string_get(vm, njs_value_arg(&value), &name);
4869
4870
0
    for (e = &njs_webcrypto_hash[0]; e->name.length != 0; e++) {
4871
0
        if (njs_strstr_eq(&name, &e->name)) {
4872
0
            *hash = e->value;
4873
0
            return NJS_OK;
4874
0
        }
4875
0
    }
4876
4877
0
    njs_vm_type_error(vm, "unknown hash name: \"%V\"", &name);
4878
4879
0
    return NJS_ERROR;
4880
0
}
4881
4882
4883
static njs_str_t *
4884
njs_algorithm_hash_name(njs_webcrypto_hash_t hash)
4885
0
{
4886
0
    njs_webcrypto_entry_t  *e;
4887
4888
0
    for (e = &njs_webcrypto_hash[0]; e->name.length != 0; e++) {
4889
0
        if (e->value == hash) {
4890
0
            return &e->name;
4891
0
        }
4892
0
    }
4893
4894
0
    return &e->name;
4895
0
}
4896
4897
4898
static const EVP_MD *
4899
njs_algorithm_hash_digest(njs_webcrypto_hash_t hash)
4900
0
{
4901
0
    switch (hash) {
4902
0
    case NJS_HASH_SHA256:
4903
0
        return EVP_sha256();
4904
4905
0
    case NJS_HASH_SHA384:
4906
0
        return EVP_sha384();
4907
4908
0
    case NJS_HASH_SHA512:
4909
0
        return EVP_sha512();
4910
4911
0
    case NJS_HASH_SHA1:
4912
0
    default:
4913
0
        break;
4914
0
    }
4915
4916
0
    return EVP_sha1();
4917
0
}
4918
4919
4920
static njs_int_t
4921
njs_algorithm_curve(njs_vm_t *vm, njs_value_t *options, int *curve)
4922
0
{
4923
0
    njs_int_t              ret;
4924
0
    njs_str_t              name;
4925
0
    njs_value_t            *val;
4926
0
    njs_opaque_value_t     value;
4927
0
    njs_webcrypto_entry_t  *e;
4928
4929
0
    if (*curve != 0) {
4930
0
        return NJS_OK;
4931
0
    }
4932
4933
0
    val = njs_vm_object_prop(vm, options, &string_curve, &value);
4934
0
    if (njs_slow_path(val == NULL)) {
4935
0
        njs_value_undefined_set(njs_value_arg(&value));
4936
0
    }
4937
4938
0
    ret = njs_value_to_string(vm, njs_value_arg(&value), njs_value_arg(&value));
4939
0
    if (njs_slow_path(ret != NJS_OK)) {
4940
0
        return NJS_ERROR;
4941
0
    }
4942
4943
0
    njs_value_string_get(vm, njs_value_arg(&value), &name);
4944
4945
0
    for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) {
4946
0
        if (njs_strstr_eq(&name, &e->name)) {
4947
0
            *curve = e->value;
4948
0
            return NJS_OK;
4949
0
        }
4950
0
    }
4951
4952
0
    njs_vm_type_error(vm, "unknown namedCurve: \"%V\"", &name);
4953
4954
0
    return NJS_ERROR;
4955
0
}
4956
4957
4958
static njs_str_t *
4959
njs_algorithm_curve_name(int curve)
4960
0
{
4961
0
    njs_webcrypto_entry_t  *e;
4962
4963
0
    for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) {
4964
0
        if (e->value == (uintptr_t) curve) {
4965
0
            return &e->name;
4966
0
        }
4967
0
    }
4968
4969
0
    return &e->name;
4970
0
}
4971
4972
4973
static njs_int_t
4974
njs_promise_trampoline(njs_vm_t *vm, njs_value_t *args,
4975
    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
4976
0
{
4977
0
    njs_function_t  *callback;
4978
4979
0
    callback = njs_value_function(njs_argument(args, 1));
4980
4981
0
    if (callback != NULL) {
4982
0
        return njs_vm_invoke(vm, callback, njs_argument(args, 2), 1, retval);
4983
0
    }
4984
4985
0
    return NJS_OK;
4986
0
}
4987
4988
4989
static njs_int_t
4990
njs_webcrypto_result(njs_vm_t *vm, njs_opaque_value_t *result, njs_int_t rc,
4991
    njs_value_t *retval)
4992
0
{
4993
0
    njs_int_t           ret;
4994
0
    njs_function_t      *callback;
4995
0
    njs_opaque_value_t  promise, arguments[2];
4996
4997
0
    ret = njs_vm_promise_create(vm, njs_value_arg(&promise),
4998
0
                                njs_value_arg(&arguments));
4999
0
    if (ret != NJS_OK) {
5000
0
        goto error;
5001
0
    }
5002
5003
0
    callback = njs_vm_function_alloc(vm, njs_promise_trampoline, 0, 0);
5004
0
    if (callback == NULL) {
5005
0
        goto error;
5006
0
    }
5007
5008
0
    njs_value_assign(&arguments[0], &arguments[(rc != NJS_OK)]);
5009
5010
0
    if (rc != NJS_OK) {
5011
0
        njs_vm_exception_get(vm, njs_value_arg(&arguments[1]));
5012
5013
0
    } else {
5014
0
        njs_value_assign(&arguments[1], result);
5015
0
    }
5016
5017
0
    ret = njs_vm_enqueue_job(vm, callback, njs_value_arg(&arguments), 2);
5018
0
    if (ret == NJS_ERROR) {
5019
0
        goto error;
5020
0
    }
5021
5022
0
    njs_value_assign(retval, &promise);
5023
5024
0
    return NJS_OK;
5025
5026
0
error:
5027
5028
0
    njs_vm_internal_error(vm, "cannot make webcrypto result");
5029
5030
0
    return NJS_ERROR;
5031
0
}
5032
5033
5034
static njs_int_t
5035
njs_webcrypto_array_buffer(njs_vm_t *vm, njs_value_t *retval,
5036
    u_char *start, size_t length)
5037
0
{
5038
0
    u_char  *dst;
5039
5040
0
    dst = njs_mp_alloc(njs_vm_memory_pool(vm), length);
5041
0
    if (njs_slow_path(dst == NULL)) {
5042
0
        njs_vm_memory_error(vm);
5043
0
        return NJS_ERROR;
5044
0
    }
5045
5046
0
    memcpy(dst, start, length);
5047
5048
0
    return njs_vm_value_array_buffer_set(vm, retval, dst, length);
5049
0
}
5050
5051
5052
static u_char *
5053
njs_cpystrn(u_char *dst, u_char *src, size_t n)
5054
0
{
5055
0
    if (n == 0) {
5056
0
        return dst;
5057
0
    }
5058
5059
0
    while (--n) {
5060
0
        *dst = *src;
5061
5062
0
        if (*dst == '\0') {
5063
0
            return dst;
5064
0
        }
5065
5066
0
        dst++;
5067
0
        src++;
5068
0
    }
5069
5070
0
    *dst = '\0';
5071
5072
0
    return dst;
5073
0
}
5074
5075
5076
static void
5077
njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...)
5078
0
{
5079
0
    int            flags;
5080
0
    u_char         *p, *last;
5081
0
    va_list        args;
5082
0
    const char     *data;
5083
0
    unsigned long  n;
5084
0
    u_char         errstr[NJS_MAX_ERROR_STR];
5085
5086
0
    last = &errstr[NJS_MAX_ERROR_STR];
5087
5088
0
    va_start(args, fmt);
5089
0
    p = njs_vsprintf(errstr, last - 1, fmt, args);
5090
0
    va_end(args);
5091
5092
0
    if (ERR_peek_error()) {
5093
0
        p = njs_cpystrn(p, (u_char *) " (SSL:", last - p);
5094
5095
0
        for ( ;; ) {
5096
5097
0
            n = ERR_peek_error_data(&data, &flags);
5098
5099
0
            if (n == 0) {
5100
0
                break;
5101
0
            }
5102
5103
            /* ERR_error_string_n() requires at least one byte */
5104
5105
0
            if (p >= last - 1) {
5106
0
                goto next;
5107
0
            }
5108
5109
0
            *p++ = ' ';
5110
5111
0
            ERR_error_string_n(n, (char *) p, last - p);
5112
5113
0
            while (p < last && *p) {
5114
0
                p++;
5115
0
            }
5116
5117
0
            if (p < last && *data && (flags & ERR_TXT_STRING)) {
5118
0
                *p++ = ':';
5119
0
                p = njs_cpystrn(p, (u_char *) data, last - p);
5120
0
            }
5121
5122
0
        next:
5123
5124
0
            (void) ERR_get_error();
5125
0
        }
5126
5127
0
        if (p < last) {
5128
0
            *p++ = ')';
5129
0
        }
5130
0
    }
5131
5132
0
    njs_vm_error(vm, "%*s", p - errstr, errstr);
5133
0
}
5134
5135
5136
static njs_int_t
5137
njs_webcrypto_init(njs_vm_t *vm)
5138
7.30k
{
5139
7.30k
    njs_int_t           ret, proto_id;
5140
7.30k
    njs_str_t           name;
5141
7.30k
    njs_opaque_value_t  value;
5142
5143
#if (OPENSSL_VERSION_NUMBER < 0x10100003L)
5144
    OpenSSL_add_all_algorithms();
5145
#endif
5146
5147
7.30k
    njs_webcrypto_crypto_key_proto_id =
5148
7.30k
        njs_vm_external_prototype(vm, njs_ext_webcrypto_crypto_key,
5149
7.30k
                                  njs_nitems(njs_ext_webcrypto_crypto_key));
5150
7.30k
    if (njs_slow_path(njs_webcrypto_crypto_key_proto_id < 0)) {
5151
0
        return NJS_ERROR;
5152
0
    }
5153
5154
7.30k
    proto_id = njs_vm_external_prototype(vm, njs_ext_webcrypto,
5155
7.30k
                                         njs_nitems(njs_ext_webcrypto));
5156
7.30k
    if (njs_slow_path(proto_id < 0)) {
5157
0
        return NJS_ERROR;
5158
0
    }
5159
5160
7.30k
    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
5161
7.30k
    if (njs_slow_path(ret != NJS_OK)) {
5162
0
        return NJS_ERROR;
5163
0
    }
5164
5165
7.30k
    name.length = njs_length("crypto");
5166
7.30k
    name.start = (u_char *) "crypto";
5167
5168
7.30k
    ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1);
5169
7.30k
    if (njs_slow_path(ret != NJS_OK)) {
5170
0
        return NJS_ERROR;
5171
0
    }
5172
5173
7.30k
    return NJS_OK;
5174
7.30k
}