[njs] WebCrypto: introduced CryptoKey properties.

Dmitry Volyntsev xeioex at nginx.com
Wed May 24 04:21:30 UTC 2023


details:   https://hg.nginx.org/njs/rev/873830a0a78f
branches:  
changeset: 2132:873830a0a78f
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue May 23 20:58:40 2023 -0700
description:
WebCrypto: introduced CryptoKey properties.

The following properties for CryptoKey were added:
    algorithm, extractable, type, usages.

diffstat:

 external/njs_webcrypto_module.c |  376 +++++++++++++++++++++++++++++++++++----
 test/ts/test.ts                 |    6 +
 test/webcrypto/export.t.js      |  117 ++++++++++++
 ts/njs_webcrypto.d.ts           |   51 +++++-
 4 files changed, 502 insertions(+), 48 deletions(-)

diffs (751 lines):

diff -r 646374a97d8c -r 873830a0a78f external/njs_webcrypto_module.c
--- a/external/njs_webcrypto_module.c	Mon May 22 22:48:59 2023 -0700
+++ b/external/njs_webcrypto_module.c	Tue May 23 20:58:40 2023 -0700
@@ -128,6 +128,14 @@ static njs_int_t njs_ext_unwrap_key(njs_
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 static njs_int_t njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_key_ext_algorithm(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+static njs_int_t njs_key_ext_extractable(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+static njs_int_t njs_key_ext_type(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+static njs_int_t njs_key_ext_usages(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
 static njs_int_t njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 
@@ -138,16 +146,17 @@ static njs_webcrypto_key_format_t njs_ke
 static njs_str_t *njs_format_string(njs_webcrypto_key_format_t fmt);
 static njs_int_t njs_key_usage(njs_vm_t *vm, njs_value_t *value,
     unsigned *mask);
-static njs_int_t njs_key_ops(njs_vm_t *vm, njs_opaque_value_t *retval,
-    unsigned mask);
+static njs_int_t njs_key_ops(njs_vm_t *vm, njs_value_t *retval, unsigned mask);
 static njs_webcrypto_algorithm_t *njs_key_algorithm(njs_vm_t *vm,
     njs_value_t *value);
 static njs_str_t *njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm);
 static njs_int_t njs_algorithm_hash(njs_vm_t *vm, njs_value_t *value,
     njs_webcrypto_hash_t *hash);
+static njs_str_t *njs_algorithm_hash_name(njs_webcrypto_hash_t hash);
 static const EVP_MD *njs_algorithm_hash_digest(njs_webcrypto_hash_t hash);
 static njs_int_t njs_algorithm_curve(njs_vm_t *vm, njs_value_t *value,
     int *curve);
+static njs_str_t *njs_algorithm_curve_name(int curve);
 
 static njs_int_t njs_webcrypto_result(njs_vm_t *vm, njs_opaque_value_t *result,
     njs_int_t rc, njs_value_t *retval);
@@ -414,18 +423,6 @@ static njs_str_t njs_webcrypto_alg_aes_n
 };
 
 
-static njs_external_t  njs_ext_webcrypto_crypto_key[] = {
-
-    {
-        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
-        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
-        .u.property = {
-            .value = "CryptoKey",
-        }
-    },
-};
-
-
 static njs_external_t  njs_ext_subtle_webcrypto[] = {
 
     {
@@ -575,6 +572,55 @@ static njs_external_t  njs_ext_subtle_we
 
 };
 
+
+static njs_external_t  njs_ext_webcrypto_crypto_key[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "CryptoKey",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("algorithm"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_key_ext_algorithm,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("extractable"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_key_ext_extractable,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("type"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_key_ext_type,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("usages"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_key_ext_usages,
+        }
+    },
+};
+
+
 static njs_external_t  njs_ext_webcrypto[] = {
 
     {
@@ -633,7 +679,11 @@ static const njs_str_t  string_ext = njs
 static const njs_str_t  string_crv = njs_str("crv");
 static const njs_str_t  string_kty = njs_str("kty");
 static const njs_str_t  key_ops = njs_str("key_ops");
+static const njs_str_t  string_hash = njs_str("hash");
+static const njs_str_t  string_name = njs_str("name");
 static const njs_str_t  string_length = njs_str("length");
+static const njs_str_t  string_ml = njs_str("modulusLength");
+static const njs_str_t  string_curve = njs_str("namedCurve");
 
 
 static njs_int_t    njs_webcrypto_crypto_key_proto_id;
@@ -1865,15 +1915,15 @@ njs_export_jwk_rsa(njs_vm_t *vm, njs_web
 static njs_int_t
 njs_export_jwk_ec(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval)
 {
-    int                    nid, group_bits, group_bytes;
-    BIGNUM                 *x_bn, *y_bn;
-    njs_int_t              ret;
-    const EC_KEY           *ec;
-    const BIGNUM           *d_bn;
-    const EC_POINT         *pub;
-    const EC_GROUP         *group;
-    njs_opaque_value_t     xvalue, yvalue, dvalue, name, ec_s;
-    njs_webcrypto_entry_t  *e;
+    int                 nid, group_bits, group_bytes;
+    BIGNUM              *x_bn, *y_bn;
+    njs_int_t           ret;
+    njs_str_t           *cname;
+    const EC_KEY        *ec;
+    const BIGNUM        *d_bn;
+    const EC_POINT      *pub;
+    const EC_GROUP      *group;
+    njs_opaque_value_t  xvalue, yvalue, dvalue, name, ec_s;
 
     x_bn = NULL;
     y_bn = NULL;
@@ -1920,15 +1970,11 @@ njs_export_jwk_ec(njs_vm_t *vm, njs_webc
 
     nid = EC_GROUP_get_curve_name(group);
 
-    for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) {
-        if ((uintptr_t) nid == e->value) {
-            (void) njs_vm_value_string_set(vm, njs_value_arg(&name),
-                                           e->name.start, e->name.length);
-            break;
-        }
-    }
-
-    if (e->name.length == 0) {
+    cname = njs_algorithm_curve_name(nid);
+    (void) njs_vm_value_string_set(vm, njs_value_arg(&name),
+                                   cname->start, cname->length);
+
+    if (cname->length == 0) {
         njs_vm_error(vm, "Unsupported JWK EC curve: %s", OBJ_nid2sn(nid));
         goto fail;
     }
@@ -2069,7 +2115,7 @@ njs_export_jwk_asymmetric(njs_vm_t *vm, 
         return NJS_ERROR;
     }
 
-    ret = njs_key_ops(vm, &ops, key->usage);
+    ret = njs_key_ops(vm, njs_value_arg(&ops), key->usage);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -2124,7 +2170,7 @@ njs_export_jwk_oct(njs_vm_t *vm, njs_web
         }
     }
 
-    ret = njs_key_ops(vm, &ops, key->usage);
+    ret = njs_key_ops(vm, njs_value_arg(&ops), key->usage);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -2356,7 +2402,6 @@ njs_ext_generate_key(njs_vm_t *vm, njs_v
     njs_opaque_value_t         value, pub, priv;
     njs_webcrypto_algorithm_t  *alg;
 
-    static const njs_str_t  string_ml = njs_str("modulusLength");
     static const njs_str_t  string_priv = njs_str("privateKey");
     static const njs_str_t  string_pub = njs_str("publicKey");
 
@@ -4076,6 +4121,225 @@ njs_ext_wrap_key(njs_vm_t *vm, njs_value
 
 
 static njs_int_t
+njs_key_ext_algorithm(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval)
+{
+    u_char               *start;
+    njs_int_t            ret;
+    njs_str_t            *name;
+    const BIGNUM         *n_bn, *e_bn;
+    const EC_GROUP       *group;
+    njs_opaque_value_t   alg, name_s, val, hash;
+    njs_webcrypto_key_t  *key;
+
+    static const njs_str_t  string_pexponent = njs_str("publicExponent");
+
+    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
+    if (njs_slow_path(key == NULL)) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    name = &njs_webcrypto_alg[key->alg->type].name;
+    ret = njs_vm_value_string_set(vm, njs_value_arg(&alg), name->start,
+                                  name->length);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    (void) njs_vm_value_string_set(vm, njs_value_arg(&name_s),
+                                   (u_char *) "name", njs_length("name"));
+
+    ret = njs_vm_object_alloc(vm, retval, &name_s, &alg, NULL);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    switch (key->alg->type) {
+    case NJS_ALGORITHM_RSASSA_PKCS1_v1_5:
+    case NJS_ALGORITHM_RSA_PSS:
+    case NJS_ALGORITHM_RSA_OAEP:
+        /* RsaHashedKeyGenParams */
+
+        njs_assert(key->u.a.pkey != NULL);
+        njs_assert(EVP_PKEY_id(key->u.a.pkey) == EVP_PKEY_RSA);
+
+        njs_rsa_get0_key(njs_pkey_get_rsa_key(key->u.a.pkey), &n_bn, &e_bn,
+                         NULL);
+
+        njs_value_number_set(njs_value_arg(&val), BN_num_bits(n_bn));
+
+        ret = njs_vm_object_prop_set(vm, retval, &string_ml, &val);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        start = njs_mp_alloc(njs_vm_memory_pool(vm), BN_num_bytes(e_bn));
+        if (njs_slow_path(start == NULL)) {
+            njs_vm_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        BN_bn2bin(e_bn, start);
+
+        ret = njs_vm_value_buffer_set(vm, njs_value_arg(&val), start,
+                                      BN_num_bytes(e_bn));
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_vm_object_prop_set(vm, retval, &string_pexponent, &val);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        name = njs_algorithm_hash_name(key->hash);
+        ret = njs_vm_value_string_set(vm, njs_value_arg(&hash), name->start,
+                                      name->length);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_vm_object_alloc(vm, njs_value_arg(&val), NULL);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_vm_object_prop_set(vm, njs_value_arg(&val), &string_name,
+                                     &hash);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_vm_object_prop_set(vm, retval, &string_hash, &val);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        break;
+
+    case NJS_ALGORITHM_AES_GCM:
+    case NJS_ALGORITHM_AES_CTR:
+    case NJS_ALGORITHM_AES_CBC:
+        /* AesKeyGenParams */
+
+        njs_value_number_set(njs_value_arg(&val), key->u.s.raw.length * 8);
+
+        ret = njs_vm_object_prop_set(vm, retval, &string_length, &val);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        break;
+
+    case NJS_ALGORITHM_ECDSA:
+    case NJS_ALGORITHM_ECDH:
+        /* EcKeyGenParams */
+
+        njs_assert(key->u.a.pkey != NULL);
+        njs_assert(EVP_PKEY_id(key->u.a.pkey) == EVP_PKEY_EC);
+
+        group = EC_KEY_get0_group(njs_pkey_get_ec_key(key->u.a.pkey));
+
+        name = njs_algorithm_curve_name(EC_GROUP_get_curve_name(group));
+
+        ret = njs_vm_value_string_set(vm, njs_value_arg(&val), name->start,
+                                      name->length);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_vm_object_prop_set(vm, retval, &string_curve, &val);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        break;
+
+    case NJS_ALGORITHM_HMAC:
+    default:
+        /* HmacKeyGenParams */
+
+        name = njs_algorithm_hash_name(key->hash);
+        ret = njs_vm_value_string_set(vm, njs_value_arg(&val), name->start,
+                                      name->length);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_vm_object_prop_set(vm, retval, &string_hash, &val);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        break;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_key_ext_extractable(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    njs_webcrypto_key_t  *key;
+
+    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
+    if (njs_slow_path(key == NULL)) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    njs_value_boolean_set(retval, key->extractable);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_key_ext_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval)
+{
+    const char           *type;
+    njs_webcrypto_key_t  *key;
+
+    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
+    if (njs_slow_path(key == NULL)) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    if (key->alg->raw) {
+        (void) njs_vm_value_string_set(vm, retval, (u_char *) "secret",
+                                       njs_length("secret"));
+    } else {
+        type = key->u.a.privat ? "private": "public";
+        (void) njs_vm_value_string_set(vm, retval, (u_char *) type,
+                                       key->u.a.privat ? 7 : 6);
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_key_ext_usages(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval)
+{
+    njs_webcrypto_key_t  *key;
+
+    key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value);
+    if (njs_slow_path(key == NULL)) {
+        njs_value_undefined_set(retval);
+        return NJS_DECLINED;
+    }
+
+    return njs_key_ops(vm, retval, key->usage);
+}
+
+
+static njs_int_t
 njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
@@ -4253,20 +4517,20 @@ njs_key_usage(njs_vm_t *vm, njs_value_t 
 
 
 static njs_int_t
-njs_key_ops(njs_vm_t *vm, njs_opaque_value_t *retval, unsigned mask)
+njs_key_ops(njs_vm_t *vm, njs_value_t *retval, unsigned mask)
 {
     njs_int_t              ret;
     njs_value_t            *value;
     njs_webcrypto_entry_t  *e;
 
-    ret = njs_vm_array_alloc(vm, njs_value_arg(retval), 4);
+    ret = njs_vm_array_alloc(vm, retval, 4);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
     for (e = &njs_webcrypto_usage[0]; e->name.length != 0; e++) {
         if (mask & e->value) {
-            value = njs_vm_array_push(vm, njs_value_arg(retval));
+            value = njs_vm_array_push(vm, retval);
             if (value == NULL) {
                 return NJS_ERROR;
             }
@@ -4293,8 +4557,6 @@ njs_key_algorithm(njs_vm_t *vm, njs_valu
     njs_webcrypto_entry_t      *e;
     njs_webcrypto_algorithm_t  *alg;
 
-    static const njs_str_t  string_name = njs_str("name");
-
     if (njs_value_is_object(options)) {
         val = njs_vm_object_prop(vm, options, &string_name, &name);
         if (njs_slow_path(val == NULL)) {
@@ -4358,8 +4620,6 @@ njs_algorithm_hash(njs_vm_t *vm, njs_val
     njs_opaque_value_t     value;
     njs_webcrypto_entry_t  *e;
 
-    static const njs_str_t  string_hash = njs_str("hash");
-
     if (njs_value_is_object(options)) {
         val = njs_vm_object_prop(vm, options, &string_hash, &value);
         if (njs_slow_path(val == NULL)) {
@@ -4390,6 +4650,21 @@ njs_algorithm_hash(njs_vm_t *vm, njs_val
 }
 
 
+static njs_str_t *
+njs_algorithm_hash_name(njs_webcrypto_hash_t hash)
+{
+    njs_webcrypto_entry_t  *e;
+
+    for (e = &njs_webcrypto_hash[0]; e->name.length != 0; e++) {
+        if (e->value == hash) {
+            return &e->name;
+        }
+    }
+
+    return &e->name;
+}
+
+
 static const EVP_MD *
 njs_algorithm_hash_digest(njs_webcrypto_hash_t hash)
 {
@@ -4421,8 +4696,6 @@ njs_algorithm_curve(njs_vm_t *vm, njs_va
     njs_opaque_value_t     value;
     njs_webcrypto_entry_t  *e;
 
-    static const njs_str_t  string_curve = njs_str("namedCurve");
-
     if (*curve != 0) {
         return NJS_OK;
     }
@@ -4452,6 +4725,21 @@ njs_algorithm_curve(njs_vm_t *vm, njs_va
 }
 
 
+static njs_str_t *
+njs_algorithm_curve_name(int curve)
+{
+    njs_webcrypto_entry_t  *e;
+
+    for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) {
+        if (e->value == (uintptr_t) curve) {
+            return &e->name;
+        }
+    }
+
+    return &e->name;
+}
+
+
 static njs_int_t
 njs_promise_trampoline(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
diff -r 646374a97d8c -r 873830a0a78f test/ts/test.ts
--- a/test/ts/test.ts	Mon May 22 22:48:59 2023 -0700
+++ b/test/ts/test.ts	Tue May 23 20:58:40 2023 -0700
@@ -246,13 +246,19 @@ async function crypto_object(keyData: Ar
                                                 publicExponent: new Uint8Array([1, 0, 1])},
                                                 true, ['sign', 'verify']);
 
+    pair.privateKey.extractable;
+    pair.publicKey.algorithm.name;
+
     let hkey = await crypto.subtle.generateKey({name: "HMAC",
                                                 hash: "SHA-384"},
                                                 true, ['sign', 'verify']);
+    hkey.algorithm.name;
 
     let akey = await crypto.subtle.generateKey({name: "AES-GCM",
                                                 length: 256},
                                                 true, ['encrypt', 'decrypt']);
+
+    akey.type;
 }
 
 function buffer(b: Buffer) {
diff -r 646374a97d8c -r 873830a0a78f test/webcrypto/export.t.js
--- a/test/webcrypto/export.t.js	Mon May 22 22:48:59 2023 -0700
+++ b/test/webcrypto/export.t.js	Tue May 23 20:58:40 2023 -0700
@@ -35,6 +35,8 @@ async function test(params) {
     let key = await load_key(params);
     let exp = await crypto.subtle.exportKey(params.export.fmt, key);
 
+    validate_key(key, params);
+
     if (params.check && !params.check(exp, params)) {
         throw Error(`failed check`);
     }
@@ -92,6 +94,121 @@ function p(args, default_opts) {
     return params;
 }
 
+function validate_key(key, params) {
+    let opts;
+
+    if (params.generate_keys) {
+        opts = params.generate_keys;
+
+    } else if (params.generate_key) {
+        opts = params.generate_key;
+
+    } else {
+        opts = params.key;
+    }
+
+    if (opts.extractable != key.extractable) {
+        throw Error(`unexpected generated key.extractable: ${key.extractable}`);
+    }
+
+    switch (key.type) {
+    case "secret":
+        /* AesKeyGenParams */
+
+        switch (key.algorithm.name) {
+        case "AES-CBC":
+        case "AES-CTR":
+        case "AES-GCM":
+            let length = key.algorithm.length;
+
+            switch (length) {
+            case 128:
+            case 192:
+            case 256:
+                break;
+            default:
+                throw Error(`unexpected symmetric generated key.algorithm.length: ${length}`);
+            }
+
+            /* FALLTHROUGH */
+
+        case "HMAC":
+            if (!compareObjects(opts.usage, key.usages)) {
+                throw Error(`unexpected symmetric generated key.usages: ${key.usages}`);
+            }
+
+            break;
+
+        default:
+            throw Error(`unexpected symmetric generated key.algorithm.name: ${key.algorithm.name}`);
+        }
+
+        break;
+    case "private":
+    case "public":
+        if (opts.expected && !compareObjects(opts.expected.key_ops, key.usages)) {
+            throw Error(`unexpected asymmetric generated key.usages: ${key.usages}`);
+        }
+
+        switch (key.algorithm.name) {
+        case "RSA-OAEP":
+        case "RSA-PSS":
+        case "RSASSA-PKCS1-v1_5":
+            /* RsaHashedKeyGenParams */
+
+            let mlength = key.algorithm.modulusLength;
+            let pexp = key.algorithm.publicExponent;
+            let hash = key.algorithm.hash.name;
+
+            switch (mlength) {
+            case 1024:
+            case 2048:
+            case 4096:
+                break;
+            default:
+                throw Error(`unexpected asymmetric generated key.algorithm.modulusLength: ${mlength}`);
+            }
+
+            if (!compareObjects(new Uint8Array([1, 0, 1]), pexp)) {
+                throw Error(`unexpected asymmetric generated key.algorithm.publicExponent: ${pexp}`);
+            }
+
+            switch (hash) {
+            case "SHA-1":
+            case "SHA-256":
+            case "SHA-384":
+            case "SHA-512":
+                break;
+            default:
+                throw Error(`unexpected asymmetric generated key.algorithm.hash.name: ${hash}`);
+            }
+
+            break;
+        case "ECDSA":
+        case "ECDH":
+            /* EcKeyGenParams */
+
+            let crv = key.algorithm.namedCurve;
+            switch (crv) {
+            case "P-256":
+            case "P-384":
+            case "P-521":
+                break;
+            default:
+                throw Error(`unexpected asymmetric generated key.algorithm.namedCurve: ${crv}`);
+            }
+
+            break;
+        default:
+            throw Error(`unexpected asymmetric generated key.algorithm.name: ${key.algorithm.name}`);
+        }
+
+        break;
+    default:
+        throw Error(`unexpected generated key.type: ${key.type}`);
+    }
+}
+
 function validate_property(exp, p, exp_len) {
     if (!exp[p]) {
         throw Error(`"${p}" is not found in ${JSON.stringify(exp)}`);
diff -r 646374a97d8c -r 873830a0a78f ts/njs_webcrypto.d.ts
--- a/ts/njs_webcrypto.d.ts	Mon May 22 22:48:59 2023 -0700
+++ b/ts/njs_webcrypto.d.ts	Tue May 23 20:58:40 2023 -0700
@@ -136,6 +136,35 @@ type SignOrVerifyAlgorithm =
     | "RSASSA-PKCS1-v1_5";
 
 interface CryptoKey {
+    /*
+     * An object describing the algorithm for which this key can be used
+     * and any associated extra parameters.
+     * @since 0.8.0
+     */
+    readonly algorithm: GenerateAlgorithm;
+    /*
+     * A boolean value that is true if the key can be exported and false if not.
+     * @since 0.8.0
+     */
+    readonly extractable: boolean;
+    /*
+     * A string value indicates which kind of key is represented by the object.
+     *
+     * It can have the following values:
+     *  "secret": This key is a secret key for use with a symmetric algorithm.
+     *  "private": This key is the private half of an asymmetric algorithm's CryptoKeyPair.
+     *  "public": This key is the public half of an asymmetric algorithm's CryptoKeyPair.
+     * @since 0.8.0
+     */
+    readonly type: string;
+
+    /*
+     * An array of strings indicating what this key can be used for.
+     * Possible array values: "encrypt", "decrypt", "sign", "verify",
+     *  "deriveKey", "deriveBits", "wrapKey", "unwrapKey".
+     * @since 0.8.0
+     */
+    readonly usages: Array<string>;
 }
 
 type CryptoKeyPair = { privateKey: CryptoKey, publicKey: CryptoKey };
@@ -233,8 +262,7 @@ interface SubtleCrypto {
               key: CryptoKey): Promise<ArrayBuffer|Object>;
 
     /**
-     * Generates a key for symmetric algorithms or a keypair
-     *  for asymmetric algorithms.
+     * Generates a key for symmetric algorithms.
      *
      * @since 0.7.10
      * @param algorithm Dictionary object defining the type of key to generate
@@ -244,9 +272,24 @@ interface SubtleCrypto {
      *  Possible array values: "encrypt", "decrypt", "sign", "verify",
      *  "deriveKey", "deriveBits", "wrapKey", "unwrapKey".
      */
-    generateKey(algorithm: GenerateAlgorithm,
+    generateKey(algorithm: HmacKeyGenParams | AesKeyGenParams,
                 extractable: boolean,
-                usage: Array<string>): Promise<CryptoKey|CryptoKeyPair>;
+                usage: Array<string>): Promise<CryptoKey>;
+
+    /**
+     * Generates a key for asymmetric algorithms.
+     *
+     * @since 0.7.10
+     * @param algorithm Dictionary object defining the type of key to generate
+     *  and providing extra algorithm-specific parameters.
+     * @param extractable Boolean indicating whether a key can be exported.
+     * @param usage Array indicating what can be done with the key.
+     *  Possible array values: "encrypt", "decrypt", "sign", "verify",
+     *  "deriveKey", "deriveBits", "wrapKey", "unwrapKey".
+     */
+    generateKey(algorithm: RsaHashedKeyGenParams | EcKeyGenParams,
+                extractable: boolean,
+                usage: Array<string>): Promise<CryptoKeyPair>;
 
     /**
      * Generates a digital signature.


More information about the nginx-devel mailing list