Coverage Report

Created: 2026-07-16 07:11

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