From 39a2034967207b89069cf64a76308e1446b1ad26 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 7 Oct 2024 16:34:40 +0530 Subject: [PATCH] feat(ext/crypto): X448 support (#26043) Signed-off-by: Divy Srivastava --- Cargo.lock | 21 +- ext/crypto/00_crypto.js | 377 ++++++++++++++++++++++++++++++ ext/crypto/Cargo.toml | 1 + ext/crypto/lib.rs | 11 +- ext/crypto/x448.rs | 147 ++++++++++++ tests/wpt/runner/expectation.json | 194 +++++++-------- 6 files changed, 640 insertions(+), 111 deletions(-) create mode 100644 ext/crypto/x448.rs diff --git a/Cargo.lock b/Cargo.lock index a679504108..a63da555de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,7 +1060,7 @@ dependencies = [ "cpufeatures", "curve25519-dalek-derive", "digest", - "fiat-crypto", + "fiat-crypto 0.2.7", "rustc_version 0.4.0", "subtle", "zeroize", @@ -1493,6 +1493,7 @@ dependencies = [ "curve25519-dalek", "deno_core", "deno_web", + "ed448-goldilocks", "elliptic-curve", "num-traits", "once_cell", @@ -2716,6 +2717,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed448-goldilocks" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" +dependencies = [ + "fiat-crypto 0.1.20", + "hex", + "subtle", + "zeroize", +] + [[package]] name = "editpe" version = "0.1.0" @@ -2978,6 +2991,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "fiat-crypto" version = "0.2.7" diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 8e43b76f7c..63b1905145 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -18,21 +18,27 @@ import { op_crypto_decrypt, op_crypto_derive_bits, op_crypto_derive_bits_x25519, + op_crypto_derive_bits_x448, op_crypto_encrypt, op_crypto_export_key, op_crypto_export_pkcs8_ed25519, op_crypto_export_pkcs8_x25519, + op_crypto_export_pkcs8_x448, op_crypto_export_spki_ed25519, op_crypto_export_spki_x25519, + op_crypto_export_spki_x448, op_crypto_generate_ed25519_keypair, op_crypto_generate_key, op_crypto_generate_x25519_keypair, + op_crypto_generate_x448_keypair, op_crypto_get_random_values, op_crypto_import_key, op_crypto_import_pkcs8_ed25519, op_crypto_import_pkcs8_x25519, + op_crypto_import_pkcs8_x448, op_crypto_import_spki_ed25519, op_crypto_import_spki_x25519, + op_crypto_import_spki_x448, op_crypto_jwk_x_ed25519, op_crypto_random_uuid, op_crypto_sign_ed25519, @@ -134,6 +140,7 @@ const supportedAlgorithms = { "AES-KW": "AesKeyGenParams", "HMAC": "HmacKeyGenParams", "X25519": null, + "X448": null, "Ed25519": null, }, "sign": { @@ -165,12 +172,14 @@ const supportedAlgorithms = { "AES-KW": null, "Ed25519": null, "X25519": null, + "X448": null, }, "deriveBits": { "HKDF": "HkdfParams", "PBKDF2": "Pbkdf2Params", "ECDH": "EcdhKeyDeriveParams", "X25519": "EcdhKeyDeriveParams", + "X448": "EcdhKeyDeriveParams", }, "encrypt": { "RSA-OAEP": "RsaOaepParams", @@ -1037,6 +1046,10 @@ class SubtleCrypto { result = exportKeyEd25519(format, key, innerKey); break; } + case "X448": { + result = exportKeyX448(format, key, innerKey); + break; + } case "X25519": { result = exportKeyX25519(format, key, innerKey); break; @@ -1954,6 +1967,48 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { return generateKeyAES(normalizedAlgorithm, extractable, usages); } + case "X448": { + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + const privateKeyData = new Uint8Array(56); + const publicKeyData = new Uint8Array(56); + + op_crypto_generate_x448_keypair(privateKeyData, publicKeyData); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const publicHandle = {}; + WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData); + + const algorithm = { + name: algorithmName, + }; + + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, []), + algorithm, + publicHandle, + ); + + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["deriveKey", "deriveBits"]), + algorithm, + handle, + ); + + return { publicKey, privateKey }; + } case "X25519": { if ( ArrayPrototypeFind( @@ -2100,6 +2155,211 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { } } +function importKeyX448( + format, + keyData, + extractable, + keyUsages, +) { + switch (format) { + case "raw": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, keyData); + + // 2-3. + const algorithm = { + name: "X448", + }; + + // 4-6. + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "spki": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + const publicKeyData = new Uint8Array(56); + if (!op_crypto_import_spki_x448(keyData, publicKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + const privateKeyData = new Uint8Array(32); + if (!op_crypto_import_pkcs8_x448(keyData, privateKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "private", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + } + case "jwk": { + // 1. + const jwk = keyData; + + // 2. + if (jwk.d !== undefined) { + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + ["deriveKey", "deriveBits"], + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + } + + // 3. + if (jwk.d === undefined && keyUsages.length > 0) { + throw new DOMException("Invalid key usage", "SyntaxError"); + } + + // 4. + if (jwk.kty !== "OKP") { + throw new DOMException("Invalid key type", "DataError"); + } + + // 5. + if (jwk.crv !== "X448") { + throw new DOMException("Invalid curve", "DataError"); + } + + // 6. + if (keyUsages.length > 0 && jwk.use !== undefined) { + if (jwk.use !== "enc") { + throw new DOMException("Invalid key use", "DataError"); + } + } + + // 7. + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 8. + if (jwk.ext !== undefined && jwk.ext === false && extractable) { + throw new DOMException("Invalid key extractability", "DataError"); + } + + // 9. + if (jwk.d !== undefined) { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const privateKeyData = op_crypto_base64url_decode(jwk.d); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "private", + extractable, + usageIntersection(keyUsages, ["deriveKey", "deriveBits"]), + algorithm, + handle, + ); + } else { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const publicKeyData = op_crypto_base64url_decode(jwk.x); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "X448", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + function importKeyEd25519( format, keyData, @@ -3358,6 +3618,14 @@ async function importKeyInner( ["wrapKey", "unwrapKey"], ); } + case "X448": { + return importKeyX448( + format, + keyData, + extractable, + keyUsages, + ); + } case "X25519": { return importKeyX25519( format, @@ -4162,6 +4430,66 @@ function exportKeyEd25519(format, key, innerKey) { } } +function exportKeyX448(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + // 2-3. + return TypedArrayPrototypeGetBuffer(innerKey); + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + const spkiDer = op_crypto_export_spki_x448(innerKey); + return TypedArrayPrototypeGetBuffer(spkiDer); + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a private key", + "InvalidAccessError", + ); + } + + const pkcs8Der = op_crypto_export_pkcs8_x448( + new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), + ); + pkcs8Der[15] = 0x20; + return TypedArrayPrototypeGetBuffer(pkcs8Der); + } + case "jwk": { + if (key[_type] === "private") { + throw new DOMException("Not implemented", "NotSupportedError"); + } + const x = op_crypto_base64url_encode(innerKey); + const jwk = { + kty: "OKP", + crv: "X448", + x, + "key_ops": key.usages, + ext: key[_extractable], + }; + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } +} + function exportKeyX25519(format, key, innerKey) { switch (format) { case "raw": { @@ -4519,6 +4847,55 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { return TypedArrayPrototypeGetBuffer(buf); } + case "X448": { + // 1. + if (baseKey[_type] !== "private") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 2. + const publicKey = normalizedAlgorithm.public; + // 3. + if (publicKey[_type] !== "public") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 4. + if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { + throw new DOMException( + "Algorithm mismatch", + "InvalidAccessError", + ); + } + + // 5. + const kHandle = baseKey[_handle]; + const k = WeakMapPrototypeGet(KEY_STORE, kHandle); + + const uHandle = publicKey[_handle]; + const u = WeakMapPrototypeGet(KEY_STORE, uHandle); + + const secret = new Uint8Array(56); + const isIdentity = op_crypto_derive_bits_x448(k, u, secret); + + // 6. + if (isIdentity) { + throw new DOMException("Invalid key", "OperationError"); + } + + // 7. + if (length === null) { + return TypedArrayPrototypeGetBuffer(secret); + } else if ( + TypedArrayPrototypeGetByteLength(secret) * 8 < length + ) { + throw new DOMException("Invalid length", "OperationError"); + } else { + return ArrayBufferPrototypeSlice( + TypedArrayPrototypeGetBuffer(secret), + 0, + MathCeil(length / 8), + ); + } + } case "X25519": { // 1. if (baseKey[_type] !== "private") { diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 667867efa7..0425b1e5e8 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -24,6 +24,7 @@ ctr = "0.9.1" curve25519-dalek = "4.1.3" deno_core.workspace = true deno_web.workspace = true +ed448-goldilocks = { version = "0.8.3", features = ["zeroize"] } elliptic-curve = { version = "0.13.1", features = ["std", "pem"] } num-traits = "0.2.14" once_cell.workspace = true diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index 7aa3462c76..c96029bf4a 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -64,6 +64,7 @@ mod import_key; mod key; mod shared; mod x25519; +mod x448; pub use crate::decrypt::op_crypto_decrypt; pub use crate::encrypt::op_crypto_encrypt; @@ -98,6 +99,14 @@ deno_core::extension!(deno_crypto, x25519::op_crypto_derive_bits_x25519, x25519::op_crypto_import_spki_x25519, x25519::op_crypto_import_pkcs8_x25519, + x25519::op_crypto_export_spki_x25519, + x25519::op_crypto_export_pkcs8_x25519, + x448::op_crypto_generate_x448_keypair, + x448::op_crypto_derive_bits_x448, + x448::op_crypto_import_spki_x448, + x448::op_crypto_import_pkcs8_x448, + x448::op_crypto_export_spki_x448, + x448::op_crypto_export_pkcs8_x448, ed25519::op_crypto_generate_ed25519_keypair, ed25519::op_crypto_import_spki_ed25519, ed25519::op_crypto_import_pkcs8_ed25519, @@ -106,8 +115,6 @@ deno_core::extension!(deno_crypto, ed25519::op_crypto_export_spki_ed25519, ed25519::op_crypto_export_pkcs8_ed25519, ed25519::op_crypto_jwk_x_ed25519, - x25519::op_crypto_export_spki_x25519, - x25519::op_crypto_export_pkcs8_x25519, ], esm = [ "00_crypto.js" ], options = { diff --git a/ext/crypto/x448.rs b/ext/crypto/x448.rs new file mode 100644 index 0000000000..3c8f24c319 --- /dev/null +++ b/ext/crypto/x448.rs @@ -0,0 +1,147 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::error::custom_error; +use deno_core::error::AnyError; +use deno_core::op2; +use deno_core::ToJsBuffer; +use ed448_goldilocks::curve::MontgomeryPoint; +use ed448_goldilocks::Scalar; +use elliptic_curve::pkcs8::PrivateKeyInfo; +use elliptic_curve::subtle::ConstantTimeEq; +use rand::rngs::OsRng; +use rand::RngCore; +use spki::der::asn1::BitString; +use spki::der::Decode; +use spki::der::Encode; + +#[op2(fast)] +pub fn op_crypto_generate_x448_keypair( + #[buffer] pkey: &mut [u8], + #[buffer] pubkey: &mut [u8], +) { + let mut rng = OsRng; + rng.fill_bytes(pkey); + + // x448(pkey, 5) + let point = &MontgomeryPoint::generator() + * &Scalar::from_bytes(pkey.try_into().unwrap()); + pubkey.copy_from_slice(&point.0); +} + +const MONTGOMERY_IDENTITY: MontgomeryPoint = MontgomeryPoint([0; 56]); + +#[op2(fast)] +pub fn op_crypto_derive_bits_x448( + #[buffer] k: &[u8], + #[buffer] u: &[u8], + #[buffer] secret: &mut [u8], +) -> bool { + let k: [u8; 56] = k.try_into().expect("Expected byteLength 56"); + let u: [u8; 56] = u.try_into().expect("Expected byteLength 56"); + + // x448(k, u) + let point = &MontgomeryPoint(u) * &Scalar::from_bytes(k); + if point.ct_eq(&MONTGOMERY_IDENTITY).unwrap_u8() == 1 { + return true; + } + + secret.copy_from_slice(&point.0); + false +} + +// id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 } +const X448_OID: const_oid::ObjectIdentifier = + const_oid::ObjectIdentifier::new_unwrap("1.3.101.111"); + +#[op2] +#[serde] +pub fn op_crypto_export_spki_x448( + #[buffer] pubkey: &[u8], +) -> Result { + let key_info = spki::SubjectPublicKeyInfo { + algorithm: spki::AlgorithmIdentifierRef { + oid: X448_OID, + parameters: None, + }, + subject_public_key: BitString::from_bytes(pubkey)?, + }; + Ok( + key_info + .to_der() + .map_err(|_| { + custom_error("DOMExceptionOperationError", "Failed to export key") + })? + .into(), + ) +} + +#[op2] +#[serde] +pub fn op_crypto_export_pkcs8_x448( + #[buffer] pkey: &[u8], +) -> Result { + use rsa::pkcs1::der::Encode; + + let pk_info = rsa::pkcs8::PrivateKeyInfo { + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifierRef { + oid: X448_OID, + parameters: None, + }, + private_key: pkey, // OCTET STRING + }; + + let mut buf = Vec::new(); + pk_info.encode_to_vec(&mut buf)?; + Ok(buf.into()) +} + +#[op2(fast)] +pub fn op_crypto_import_spki_x448( + #[buffer] key_data: &[u8], + #[buffer] out: &mut [u8], +) -> bool { + // 2-3. + let pk_info = match spki::SubjectPublicKeyInfoRef::try_from(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != X448_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + out.copy_from_slice(pk_info.subject_public_key.raw_bytes()); + true +} + +#[op2(fast)] +pub fn op_crypto_import_pkcs8_x448( + #[buffer] key_data: &[u8], + #[buffer] out: &mut [u8], +) -> bool { + // 2-3. + let pk_info = match PrivateKeyInfo::from_der(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != X448_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + // 6. + // CurvePrivateKey ::= OCTET STRING + if pk_info.private_key.len() != 56 { + return false; + } + out.copy_from_slice(&pk_info.private_key[2..]); + true +} diff --git a/tests/wpt/runner/expectation.json b/tests/wpt/runner/expectation.json index f7da5e51f0..ce73f5d05d 100644 --- a/tests/wpt/runner/expectation.json +++ b/tests/wpt/runner/expectation.json @@ -59,14 +59,12 @@ "X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)", "X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)", "X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)", - "X25519 mismatched algorithms", "X448 good parameters", "X448 mixed case parameters", "X448 short result", "X448 non-multiple of 8 bits", "X448 mismatched algorithms", "X448 no deriveBits usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key", "X448 asking for too many bits" @@ -77,14 +75,12 @@ "X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)", "X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)", "X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)", - "X25519 mismatched algorithms", "X448 good parameters", "X448 mixed case parameters", "X448 short result", "X448 non-multiple of 8 bits", "X448 mismatched algorithms", "X448 no deriveBits usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key", "X448 asking for too many bits" @@ -95,13 +91,10 @@ "X448 deriveBits checks for all-zero value result with a key of order p-1 (order 2)", "X448 deriveBits checks for all-zero value result with a key of order p (=0, order 4)", "X448 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)", - "Key derivation using a X448 generated keys.", - "X25519 mismatched algorithms", "X448 good parameters", "X448 mixed case parameters", "X448 mismatched algorithms", "X448 no deriveKey usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key" ], @@ -111,13 +104,10 @@ "X448 deriveBits checks for all-zero value result with a key of order p-1 (order 2)", "X448 deriveBits checks for all-zero value result with a key of order p (=0, order 4)", "X448 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)", - "Key derivation using a X448 generated keys.", - "X25519 mismatched algorithms", "X448 good parameters", "X448 mixed case parameters", "X448 mismatched algorithms", "X448 no deriveKey usage for base key", - "X448 base key is not a private key", "X448 public property value is a private key", "X448 public property value is a secret key" ], @@ -767,82 +757,16 @@ ], "failures_X25519.https.any.html": true, "failures_X25519.https.any.worker.html": true, - "failures_X448.https.any.html": [ - "Bad usages: generateKey({name: X448}, true, [encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])", - "Empty usages: generateKey({name: X448}, false, [])", - "Empty usages: generateKey({name: X448}, true, [])" - ], - "failures_X448.https.any.worker.html": [ - "Bad usages: generateKey({name: X448}, true, [encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])", - "Bad usages: generateKey({name: X448}, true, [decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])", - "Bad usages: generateKey({name: X448}, true, [sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])", - "Bad usages: generateKey({name: X448}, true, [verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])", - "Bad usages: generateKey({name: X448}, true, [wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])", - "Bad usages: generateKey({name: X448}, true, [unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey])", - "Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])", - "Empty usages: generateKey({name: X448}, false, [])", - "Empty usages: generateKey({name: X448}, true, [])" - ], + "failures_X448.https.any.html": true, + "failures_X448.https.any.worker.html": true, "successes_Ed25519.https.any.html": true, "successes_Ed25519.https.any.worker.html": true, "successes_Ed448.https.any.html": false, "successes_Ed448.https.any.worker.html": false, "successes_X25519.https.any.html": true, "successes_X25519.https.any.worker.html": true, - "successes_X448.https.any.html": false, - "successes_X448.https.any.worker.html": false + "successes_X448.https.any.html": true, + "successes_X448.https.any.worker.html": true }, "historical.any.html": false, "historical.any.worker.html": false, @@ -929,10 +853,6 @@ "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters with ignored JWK alg: X448 (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, true, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", @@ -945,17 +865,10 @@ "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, false, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, false, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, false, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits, deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits, deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" ], "okp_importKey.https.any.worker.html": [ "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, true, [verify])", @@ -997,10 +910,6 @@ "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X25519 (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters with ignored JWK alg: X448 (jwk, object(kty, crv, x), {name: X448}, true, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, true, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", @@ -1013,17 +922,10 @@ "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", "Good parameters with ignored JWK alg: X448 (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (spki, buffer(68), {name: X448}, false, [])", - "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, false, [])", - "Good parameters: X448 bits (raw, buffer(56), {name: X448}, false, [])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits, deriveKey])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits, deriveKey])", "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])", - "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", - "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])" ], "okp_importKey_failures_Ed25519.https.any.html": [ "Bad key length: importKey(raw, {name: Ed25519}, true, [verify])", @@ -1103,8 +1005,62 @@ "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveBits])", "Invalid key pair: importKey(jwk(private), {name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" ], - "okp_importKey_failures_X448.https.any.html": false, - "okp_importKey_failures_X448.https.any.worker.html": false, + "okp_importKey_failures_X448.https.any.html": [ + "Empty usages: importKey(pkcs8, {name: X448}, true, [])", + "Empty usages: importKey(pkcs8, {name: X448}, false, [])", + "Bad key length: importKey(raw, {name: X448}, true, [])", + "Bad key length: importKey(raw, {name: X448}, false, [])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", + "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" + ], + "okp_importKey_failures_X448.https.any.worker.html": [ + "Empty usages: importKey(pkcs8, {name: X448}, true, [])", + "Empty usages: importKey(pkcs8, {name: X448}, false, [])", + "Bad key length: importKey(raw, {name: X448}, true, [])", + "Bad key length: importKey(raw, {name: X448}, false, [])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Bad key length: importKey(jwk (public) , {name: X448}, true, [])", + "Bad key length: importKey(jwk (public) , {name: X448}, false, [])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Missing JWK 'x' parameter: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveBits])", + "Invalid key pair: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits])" + ], "crashtests": { "importKey-unsettled-promise.https.any.html": true, "importKey-unsettled-promise.https.any.worker.html": true @@ -1478,8 +1434,30 @@ "crypto-subtle-secure-context-available.https.sub.html": true }, "wrapKey_unwrapKey": { - "wrapKey_unwrapKey.https.any.html": true, - "wrapKey_unwrapKey.https.any.worker.html": true + "wrapKey_unwrapKey.https.any.html": [ + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys using pkcs8 and RSA-OAEP", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and RSA-OAEP" + ], + "wrapKey_unwrapKey.https.any.worker.html": [ + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CTR", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-CBC", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-GCM", + "Can wrap and unwrap X448 private key keys using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and AES-KW", + "Can wrap and unwrap X448 private key keys using pkcs8 and RSA-OAEP", + "Can wrap and unwrap X448 private key keys as non-extractable using pkcs8 and RSA-OAEP" + ] } }, "console": {