From 86ea7f88a52c08b9bbf8bb1f0144262a24131764 Mon Sep 17 00:00:00 2001 From: devgianlu Date: Sat, 21 Dec 2024 14:45:29 +0100 Subject: [PATCH] LibWeb: Add Ed448 support in WebCryptoAPI Add full support for Ed448 and import relevant tests. --- Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp | 523 ++++++++++++++++++ Libraries/LibWeb/Crypto/CryptoAlgorithms.h | 32 ++ Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 10 +- .../generateKey/failures_Ed448.https.any.txt | 391 +++++++++++++ .../generateKey/successes_Ed448.https.any.txt | 23 + .../sign_verify/eddsa_curve448.https.any.txt | 18 + .../wrapKey_unwrapKey.https.any.txt | 32 +- .../generateKey/failures_Ed448.https.any.html | 17 + .../generateKey/failures_Ed448.https.any.js | 5 + .../successes_Ed448.https.any.html | 18 + .../generateKey/successes_Ed448.https.any.js | 6 + .../WebCryptoAPI/sign_verify/eddsa.js | 233 ++++++++ .../sign_verify/eddsa_curve448.https.any.html | 17 + .../sign_verify/eddsa_curve448.https.any.js | 6 + .../WebCryptoAPI/sign_verify/eddsa_vectors.js | 197 +++++++ 15 files changed, 1521 insertions(+), 7 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_vectors.js diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 82673143cd4f..de2e4c96e23e 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -626,6 +627,23 @@ JS::ThrowCompletionOr> HmacKeyGenParams::from_val return adopt_own(*new HmacKeyGenParams { hash, maybe_length }); } +Ed448Params::~Ed448Params() = default; + +JS::ThrowCompletionOr> Ed448Params::from_value(JS::VM& vm, JS::Value value) +{ + auto& object = value.as_object(); + + auto maybe_context = Optional {}; + if (MUST(object.has_property("context"))) { + auto context_value = TRY(object.get("context")); + if (!context_value.is_object() || !(is(context_value.as_object()) || is(context_value.as_object()) || is(context_value.as_object()))) + return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "BufferSource"); + maybe_context = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(context_value.as_object())); + } + + return adopt_own(*new Ed448Params { maybe_context }); +} + // https://w3c.github.io/webcrypto/#rsa-oaep-operations WebIDL::ExceptionOr> RSAOAEP::encrypt(AlgorithmParams const& params, GC::Ref key, ByteBuffer const& plaintext) { @@ -4953,6 +4971,511 @@ WebIDL::ExceptionOr ED25519::verify([[maybe_unused]] AlgorithmParams return JS::Value(result); } +// https://wicg.github.io/webcrypto-secure-curves/#ed448-operations +WebIDL::ExceptionOr, GC::Ref>> ED448::generate_key([[maybe_unused]] AlgorithmParams const& params, bool extractable, Vector const& key_usages) +{ + // 1. If usages contains a value which is not one of "sign" or "verify", then throw a SyntaxError. + for (auto const& usage : key_usages) { + if (usage != Bindings::KeyUsage::Sign && usage != Bindings::KeyUsage::Verify) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + // 2. Generate an Ed448 key pair, as defined in [RFC8032], section 5.1.5. + ::Crypto::Curves::Ed448 curve; + auto maybe_private_key = curve.generate_private_key(); + if (maybe_private_key.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to generate private key"_string); + auto private_key_data = maybe_private_key.release_value(); + + auto maybe_public_key = curve.generate_public_key(private_key_data); + if (maybe_public_key.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to generate public key"_string); + auto public_key_data = maybe_public_key.release_value(); + + // 3. Let algorithm be a new KeyAlgorithm object. + auto algorithm = KeyAlgorithm::create(m_realm); + + // 4. Set the name attribute of algorithm to "Ed448". + algorithm->set_name("Ed448"_string); + + // 5. Let publicKey be a new CryptoKey associated with the relevant global object of this [HTML], + // and representing the public key of the generated key pair. + auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key_data }); + + // 6. Set the [[type]] internal slot of publicKey to "public" + public_key->set_type(Bindings::KeyType::Public); + + // 7. Set the [[algorithm]] internal slot of publicKey to algorithm. + public_key->set_algorithm(algorithm); + + // 8. Set the [[extractable]] internal slot of publicKey to true. + public_key->set_extractable(true); + + // 9. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "verify" ]. + public_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Verify } })); + + // 10. Let privateKey be a new CryptoKey associated with the relevant global object of this [HTML], + // and representing the private key of the generated key pair. + auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key_data }); + + // 11. Set the [[type]] internal slot of privateKey to "private" + private_key->set_type(Bindings::KeyType::Private); + + // 12. Set the [[algorithm]] internal slot of privateKey to algorithm. + private_key->set_algorithm(algorithm); + + // 13. Set the [[extractable]] internal slot of privateKey to extractable. + private_key->set_extractable(extractable); + + // 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "sign" ]. + private_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Sign } })); + + // 15. Let result be a new CryptoKeyPair dictionary. + // 16. Set the publicKey attribute of result to be publicKey. + // 17. Set the privateKey attribute of result to be privateKey. + // 18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. + return Variant, GC::Ref> { CryptoKeyPair::create(m_realm, public_key, private_key) }; +} + +// https://wicg.github.io/webcrypto-secure-curves/#ed448-operations +WebIDL::ExceptionOr> ED448::import_key( + [[maybe_unused]] Web::Crypto::AlgorithmParams const& params, + Bindings::KeyFormat format, + CryptoKey::InternalKeyData key_data, + bool extractable, + Vector const& usages) +{ + GC::Ptr key = nullptr; + + // 1. Let keyData be the key data to be imported. + + // 2. If format is "spki": + if (format == Bindings::KeyFormat::Spki) { + // 1. If usages contains a value which is not "verify" then throw a SyntaxError. + for (auto const& usage : usages) { + if (usage != Bindings::KeyUsage::Verify) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + // 2. Let spki be the result of running the parse a subjectPublicKeyInfo algorithm over keyData. + // 3. If an error occurred while parsing, then throw a DataError. + auto spki = TRY(parse_a_subject_public_key_info(m_realm, key_data.get())); + + // 4. If the algorithm object identifier field of the algorithm AlgorithmIdentifier field of spki + // is not equal to the id-Ed448 object identifier defined in [RFC8410], then throw a DataError. + if (spki.algorithm.identifier != ::Crypto::ASN1::ed448_oid) + return WebIDL::DataError::create(m_realm, "Invalid algorithm identifier"_string); + + // 5. If the parameters field of the algorithm AlgorithmIdentifier field of spki is present, then throw a DataError. + if (spki.algorithm.ec_parameters.has_value()) + return WebIDL::DataError::create(m_realm, "Invalid algorithm parameters"_string); + + // 6. Let publicKey be the Ed448 public key identified by the subjectPublicKey field of spki. + auto const& public_key = spki.raw_key; + + // 7. Let key be a new CryptoKey associated with the relevant global object of this [HTML], + // and that represents publicKey. + key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key }); + + // 8. Set the [[type]] internal slot of key to "public" + key->set_type(Bindings::KeyType::Public); + + // 9. Let algorithm be a new KeyAlgorithm. + auto algorithm = KeyAlgorithm::create(m_realm); + + // 10. Set the name attribute of algorithm to "Ed448". + algorithm->set_name("Ed448"_string); + + // 11. Set the [[algorithm]] internal slot of key to algorithm. + key->set_algorithm(algorithm); + } + + // 2. If format is "pkcs8": + else if (format == Bindings::KeyFormat::Pkcs8) { + // 1. If usages contains a value which is not "sign" then throw a SyntaxError. + for (auto const& usage : usages) { + if (usage != Bindings::KeyUsage::Sign) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + // 2. Let privateKeyInfo be the result of running the parse a privateKeyInfo algorithm over keyData. + // 3. If an error occurs while parsing, then throw a DataError. + auto private_key_info = TRY(parse_a_private_key_info(m_realm, key_data.get())); + + // 4. If the algorithm object identifier field of the privateKeyAlgorithm PrivateKeyAlgorithm field + // of privateKeyInfo is not equal to the id-Ed448 object identifier defined in [RFC8410], then throw a DataError. + if (private_key_info.algorithm.identifier != ::Crypto::ASN1::ed448_oid) + return WebIDL::DataError::create(m_realm, "Invalid algorithm identifier"_string); + + // 5. If the parameters field of the privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, + // then throw a DataError. + if (private_key_info.algorithm.ec_parameters.has_value()) + return WebIDL::DataError::create(m_realm, "Invalid algorithm parameters"_string); + + // 6. Let curvePrivateKey be the result of performing the parse an ASN.1 structure algorithm, + // with data as the privateKey field of privateKeyInfo, structure as the ASN.1 CurvePrivateKey structure + // specified in Section 7 of [RFC8410], and exactData set to true. + // 7. If an error occurred while parsing, then throw a DataError. + auto curve_private_key = TRY(parse_an_ASN1_structure(m_realm, private_key_info.raw_key, true)); + auto curve_private_key_bytes = TRY_OR_THROW_OOM(m_realm->vm(), ByteBuffer::copy(curve_private_key.bytes())); + + // 8. Let key be a new CryptoKey associated with the relevant global object of this [HTML], + // and that represents the Ed448 private key identified by curvePrivateKey. + key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { curve_private_key_bytes }); + + // 9. Set the [[type]] internal slot of key to "private" + key->set_type(Bindings::KeyType::Private); + + // 10. Let algorithm be a new KeyAlgorithm. + auto algorithm = KeyAlgorithm::create(m_realm); + + // 11. Set the name attribute of algorithm to "Ed448". + algorithm->set_name("Ed448"_string); + + // 12. Set the [[algorithm]] internal slot of key to algorithm. + key->set_algorithm(algorithm); + } + + // 2. If format is "jwk": + else if (format == Bindings::KeyFormat::Jwk) { + // 1. If keyData is a JsonWebKey dictionary: Let jwk equal keyData. + // Otherwise: Throw a DataError. + if (!key_data.has()) + return WebIDL::DataError::create(m_realm, "keyData is not a JsonWebKey dictionary"_string); + auto& jwk = key_data.get(); + + // 2. If the d field is present and usages contains a value which is not "sign", + // or, if the d field is not present and usages contains a value which is not "verify" then throw a SyntaxError. + if (jwk.d.has_value()) { + for (auto const& usage : usages) { + if (usage != Bindings::KeyUsage::Sign) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + } else { + for (auto const& usage : usages) { + if (usage != Bindings::KeyUsage::Verify) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + } + + // 3. If the kty field of jwk is not "OKP", then throw a DataError. + if (jwk.kty != "OKP"sv) + return WebIDL::DataError::create(m_realm, "Invalid key type"_string); + + // 4. If the crv field of jwk is not "Ed448", then throw a DataError. + if (jwk.crv != "Ed448"sv) + return WebIDL::DataError::create(m_realm, "Invalid curve"_string); + + // 5. If usages is non-empty and the use field of jwk is present and is not "sig", then throw a DataError. + if (!usages.is_empty() && jwk.use.has_value() && jwk.use.value() != "sig") + return WebIDL::DataError::create(m_realm, "Invalid key usage"_string); + + // 6. If the key_ops field of jwk is present, and is invalid according to the requirements of JSON Web Key [JWK], + // or it does not contain all of the specified usages values, then throw a DataError. + TRY(validate_jwk_key_ops(m_realm, jwk, usages)); + + // 7. If the ext field of jwk is present and has the value false and extractable is true, then throw a DataError. + if (jwk.ext.has_value() && !jwk.ext.value() && extractable) + return WebIDL::DataError::create(m_realm, "Invalid extractable"_string); + + // 8. If the d field is present: + if (jwk.d.has_value()) { + // 1. If jwk does not meet the requirements of the JWK private key format described in Section 2 of [RFC8037], + // then throw a DataError. + // o The parameter "kty" MUST be "OKP". + if (jwk.kty != "OKP"sv) + return WebIDL::DataError::create(m_realm, "Invalid key type"_string); + + // https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve + // o The parameter "crv" MUST be present and contain the subtype of the key (from the "JSON Web Elliptic Curve" registry). + if (jwk.crv != "Ed448"sv) + return WebIDL::DataError::create(m_realm, "Invalid curve"_string); + + // o The parameter "x" MUST be present and contain the public key encoded using the base64url [RFC4648] encoding. + if (!jwk.x.has_value()) + return WebIDL::DataError::create(m_realm, "Missing x field"_string); + + // o The parameter "d" MUST be present for private keys and contain the private key encoded using the base64url encoding. + // This parameter MUST NOT be present for public keys. + if (!jwk.d.has_value()) + return WebIDL::DataError::create(m_realm, "Present d field"_string); + + // 2. Let key be a new CryptoKey object that represents the Ed448 private key identified by interpreting jwk according to Section 2 of [RFC8037]. + auto private_key_base_64 = jwk.d.value(); + auto private_key_or_error = decode_base64url(private_key_base_64); + if (private_key_or_error.is_error()) { + return WebIDL::DataError::create(m_realm, "Failed to decode base64"_string); + } + auto private_key = private_key_or_error.release_value(); + key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key }); + + // 3. Set the [[type]] internal slot of Key to "private". + key->set_type(Bindings::KeyType::Private); + } + // Otherwise: + else { + // 1. If jwk does not meet the requirements of the JWK public key format described in Section 2 of [RFC8037], then throw a DataError. + // o The parameter "kty" MUST be "OKP". + if (jwk.kty != "OKP"sv) + return WebIDL::DataError::create(m_realm, "Invalid key type"_string); + + // https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve + // o The parameter "crv" MUST be present and contain the subtype of the key (from the "JSON Web Elliptic Curve" registry). + if (jwk.crv != "Ed448"sv) + return WebIDL::DataError::create(m_realm, "Invalid curve"_string); + + // o The parameter "x" MUST be present and contain the public key encoded using the base64url [RFC4648] encoding. + if (!jwk.x.has_value()) + return WebIDL::DataError::create(m_realm, "Missing x field"_string); + + // o The parameter "d" MUST be present for private keys and contain the private key encoded using the base64url encoding. + // This parameter MUST NOT be present for public keys. + if (jwk.d.has_value()) + return WebIDL::DataError::create(m_realm, "Present d field"_string); + + // 2. Let key be a new CryptoKey object that represents the Ed448 public key identified by interpreting jwk according to Section 2 of [RFC8037]. + auto public_key_base_64 = jwk.x.value(); + auto public_key_or_error = decode_base64url(public_key_base_64); + if (public_key_or_error.is_error()) { + return WebIDL::DataError::create(m_realm, "Failed to decode base64"_string); + } + auto public_key = public_key_or_error.release_value(); + key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key }); + + // 3. Set the [[type]] internal slot of Key to "public". + key->set_type(Bindings::KeyType::Public); + } + + // 9. Let algorithm be a new instance of a KeyAlgorithm object. + auto algorithm = KeyAlgorithm::create(m_realm); + + // 10. Set the name attribute of algorithm to "Ed448". + algorithm->set_name("Ed448"_string); + + // 11. Set the [[algorithm]] internal slot of key to algorithm. + key->set_algorithm(algorithm); + } + + // 2. If format is "raw": + else if (format == Bindings::KeyFormat::Raw) { + // 1. If usages contains a value which is not "verify" then throw a SyntaxError. + for (auto const& usage : usages) { + if (usage != Bindings::KeyUsage::Verify) { + return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); + } + } + + // 2. Let algorithm be a new KeyAlgorithm object. + auto algorithm = KeyAlgorithm::create(m_realm); + + // 3. Set the name attribute of algorithm to "Ed448". + algorithm->set_name("Ed448"_string); + + // 4. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and representing the key data provided in keyData. + key = CryptoKey::create(m_realm, key_data); + + // 5. Set the [[type]] internal slot of key to "public" + key->set_type(Bindings::KeyType::Public); + + // 6. Set the [[algorithm]] internal slot of key to algorithm. + key->set_algorithm(algorithm); + } + + // 2. Otherwise: + else { + // throw a NotSupportedError. + return WebIDL::NotSupportedError::create(m_realm, "Invalid key format"_string); + } + + return GC::Ref { *key }; +} + +// https://wicg.github.io/webcrypto-secure-curves/#ed448-operations +WebIDL::ExceptionOr> ED448::export_key(Bindings::KeyFormat format, GC::Ref key) +{ + auto& vm = m_realm->vm(); + + // 1. Let key be the CryptoKey to be exported. + + // 2. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError. + // Note: In our impl this is always accessible + auto const& key_data = key->handle().get(); + + // 3. If format is "spki": + if (format == Bindings::KeyFormat::Spki) { + // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. + if (key->type() != Bindings::KeyType::Public) + return WebIDL::InvalidAccessError::create(m_realm, "Key is not a public key"_string); + + // 2. Let data be an instance of the subjectPublicKeyInfo ASN.1 structure defined in [RFC5280] with the following properties: + // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the following properties: + // * Set the algorithm object identifier to the id-Ed448 OID defined in [RFC8410]. + // * Set the subjectPublicKey field to keyData. + auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_subject_public_key_info(key_data, ::Crypto::ASN1::ed448_oid)); + + // 3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data. + return JS::ArrayBuffer::create(m_realm, move(data)); + } + + // 3. If format is "pkcs8": + if (format == Bindings::KeyFormat::Pkcs8) { + // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. + if (key->type() != Bindings::KeyType::Private) + return WebIDL::InvalidAccessError::create(m_realm, "Key is not a private key"_string); + + // 2. Let data be an instance of the privateKeyInfo ASN.1 structure defined in [RFC5208] with the following properties: + // * Set the version field to 0. + // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1 type with the following properties: + // * Set the algorithm object identifier to the id-Ed448 OID defined in [RFC8410]. + // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1 type, + // as defined in Section 7 of [RFC8410], that represents the Ed448 private key + // represented by the [[handle]] internal slot of key + ::Crypto::ASN1::Encoder encoder; + TRY_OR_THROW_OOM(vm, encoder.write(key_data.bytes())); + + auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_private_key_info(encoder.finish(), ::Crypto::ASN1::ed448_oid)); + + // 3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data. + return JS::ArrayBuffer::create(m_realm, move(data)); + } + + // 2. If format is "jwk": + if (format == Bindings::KeyFormat::Jwk) { + // 1. Let jwk be a new JsonWebKey dictionary. + Bindings::JsonWebKey jwk; + + // 2. Set the kty attribute of jwk to "OKP". + jwk.kty = "OKP"_string; + + // 3. Set the crv attribute of jwk to "Ed448". + jwk.crv = "Ed448"_string; + + // 4. Set the x attribute of jwk according to the definition in Section 2 of [RFC8037]. + if (key->type() == Bindings::KeyType::Public) { + jwk.x = TRY_OR_THROW_OOM(vm, encode_base64url(key_data, AK::OmitPadding::Yes)); + } else { + // The "x" parameter of the "epk" field is set as follows: + // Apply the appropriate ECDH function to the ephemeral private key (as scalar input) + // and the standard base point (as u-coordinate input). + // The base64url encoding of the output is the value for the "x" parameter of the "epk" field. + ::Crypto::Curves::Ed448 curve; + auto public_key = TRY_OR_THROW_OOM(vm, curve.generate_public_key(key_data)); + jwk.x = TRY_OR_THROW_OOM(vm, encode_base64url(public_key, AK::OmitPadding::Yes)); + } + + // 5. If the [[type]] internal slot of key is "private" + if (key->type() == Bindings::KeyType::Private) { + // 1. Set the d attribute of jwk according to the definition in Section 2 of [RFC8037]. + jwk.d = TRY_OR_THROW_OOM(vm, encode_base64url(key_data, AK::OmitPadding::Yes)); + } + + // 6. Set the key_ops attribute of jwk to the usages attribute of key. + jwk.key_ops = Vector {}; + jwk.key_ops->ensure_capacity(key->internal_usages().size()); + for (auto const& usage : key->internal_usages()) + jwk.key_ops->append(Bindings::idl_enum_to_string(usage)); + + // 7. Set the ext attribute of jwk to the [[extractable]] internal slot of key. + jwk.ext = key->extractable(); + + // 8. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL]. + return TRY(jwk.to_object(m_realm)); + } + + // 2. If format is "raw": + if (format == Bindings::KeyFormat::Raw) { + // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. + if (key->type() != Bindings::KeyType::Public) + return WebIDL::InvalidAccessError::create(m_realm, "Key is not a public key"_string); + + // 2. Let data be an octet string representing the Ed448 public key represented by the [[handle]] internal slot of key. + // 3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data. + return JS::ArrayBuffer::create(m_realm, key_data); + } + + // 2. Otherwise: + // throw a NotSupportedError. + return WebIDL::NotSupportedError::create(m_realm, "Invalid key format"_string); +} + +// https://wicg.github.io/webcrypto-secure-curves/#ed448-operations +WebIDL::ExceptionOr> ED448::sign(AlgorithmParams const& params, GC::Ref key, ByteBuffer const& message) +{ + auto& realm = *m_realm; + + // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. + if (key->type() != Bindings::KeyType::Private) + return WebIDL::InvalidAccessError::create(realm, "Key is not a private key"_string); + + // 2. Let context be the contents of the context member of normalizedAlgorithm + // or the empty octet string if the context member of normalizedAlgorithm is not present. + auto const& algorithm = static_cast(params); + auto context = algorithm.context.value_or(ByteBuffer()); + + // 3. If context has a length greater than 255 bytes, then throw an OperationError. + if (context.size() > 255) + return WebIDL::OperationError::create(realm, "Context is too long"_string); + + // 4. Perform the Ed448 signing process, as specified in [RFC8032], Section 5.2.6, + // with message as M and context as C, using the Ed448 private key associated with key. + ::Crypto::Curves::Ed448 curve; + auto maybe_signature = curve.sign(key->handle().get(), message, context); + if (maybe_signature.is_error()) { + return WebIDL::OperationError::create(realm, "Failed to sign message"_string); + } + + // 5. Return a new ArrayBuffer associated with the relevant global object of this [HTML], + // and containing the bytes of the signature resulting from performing the Ed448 signing process. + return JS::ArrayBuffer::create(m_realm, maybe_signature.release_value()); +} + +// https://wicg.github.io/webcrypto-secure-curves/#ed448-operations +WebIDL::ExceptionOr ED448::verify(AlgorithmParams const& params, GC::Ref key, ByteBuffer const& signature, ByteBuffer const& message) +{ + auto& realm = *m_realm; + + // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. + if (key->type() != Bindings::KeyType::Public) + return WebIDL::InvalidAccessError::create(realm, "Key is not a public key"_string); + + // 2. Let context be the contents of the context member of normalizedAlgorithm + // or the empty octet string if the context member of normalizedAlgorithm is not present. + auto const& algorithm = static_cast(params); + auto context = algorithm.context.value_or(ByteBuffer()); + + // 3. If context has a length greater than 255 bytes, then throw an OperationError. + if (context.size() > 255) + return WebIDL::OperationError::create(realm, "Context is too long"_string); + + // 4. If the key data of key represents an invalid point or a small-order element + // on the Elliptic Curve of Ed448, return false. + // NOTE: https://github.com/WICG/webcrypto-secure-curves/issues/27 + + // 5. If the point R, encoded in the first half of signature, represents an invalid point + // or a small-order element on the Elliptic Curve of Ed448, return false. + // NOTE: https://github.com/WICG/webcrypto-secure-curves/issues/27 + + // 6. Perform the Ed448 verification steps, as specified in [RFC8032], Section 5.2.7, using + // the cofactorless (unbatched) equation, [S]B = R + [k]A', on the signature, + // with message as M and context as C, using the Ed448 public key associated with key. + ::Crypto::Curves::Ed448 curve; + auto maybe_verified = curve.verify(key->handle().get(), signature, message, context); + if (maybe_verified.is_error()) { + auto error_message = MUST(String::from_utf8(maybe_verified.error().string_literal())); + return WebIDL::OperationError::create(realm, error_message); + } + + // 7. Let result be a boolean with the value true if the signature is valid + // and the value false otherwise. + // 8. Return result. + return maybe_verified.release_value(); +} + // https://w3c.github.io/webcrypto/#hkdf-operations WebIDL::ExceptionOr> HKDF::derive_bits(AlgorithmParams const& params, GC::Ref key, Optional length_optional) { diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 2acef1f3e057..24dc9eed4f39 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -544,6 +544,24 @@ class ED25519 : public AlgorithmMethods { } }; +class ED448 : public AlgorithmMethods { +public: + virtual WebIDL::ExceptionOr> sign(AlgorithmParams const&, GC::Ref, ByteBuffer const&) override; + virtual WebIDL::ExceptionOr verify(AlgorithmParams const&, GC::Ref, ByteBuffer const&, ByteBuffer const&) override; + + virtual WebIDL::ExceptionOr, GC::Ref>> generate_key(AlgorithmParams const&, bool, Vector const&) override; + virtual WebIDL::ExceptionOr> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector const&) override; + virtual WebIDL::ExceptionOr> export_key(Bindings::KeyFormat, GC::Ref) override; + + static NonnullOwnPtr create(JS::Realm& realm) { return adopt_own(*new ED448(realm)); } + +private: + explicit ED448(JS::Realm& realm) + : AlgorithmMethods(realm) + { + } +}; + class X25519 : public AlgorithmMethods { public: virtual WebIDL::ExceptionOr> derive_bits(AlgorithmParams const&, GC::Ref, Optional) override; @@ -620,6 +638,20 @@ struct EcKeyImportParams : public AlgorithmParams { static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); }; +// https://wicg.github.io/webcrypto-secure-curves/#dfn-Ed448Params +struct Ed448Params : public AlgorithmParams { + virtual ~Ed448Params() override; + + Ed448Params(Optional& context) + : context(context) + { + } + + Optional context; + + static JS::ThrowCompletionOr> from_value(JS::VM&, JS::Value); +}; + ErrorOr base64_url_uint_encode(::Crypto::UnsignedBigInteger); WebIDL::ExceptionOr base64_url_bytes_decode(JS::Realm&, String const& base64_url_string); WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm&, String const& base64_url_string); diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index f4fedd7e6962..112bdafc151b 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -1181,11 +1181,11 @@ SupportedAlgorithmsMap const& supported_algorithms() define_an_algorithm("exportKey"_string, "Ed25519"_string); // https://wicg.github.io/webcrypto-secure-curves/#ed448-registration - // FIXME: define_an_algorithm("sign"_string, "Ed448"_string); - // FIXME: define_an_algorithm("verify"_string, "Ed448"_string); - // FIXME: define_an_algorithm("generateKey"_string, "Ed448"_string); - // FIXME: define_an_algorithm("importKey"_string, "Ed448"_string); - // FIXME: define_an_algorithm("exportKey"_string, "Ed448"_string); + define_an_algorithm("sign"_string, "Ed448"_string); + define_an_algorithm("verify"_string, "Ed448"_string); + define_an_algorithm("generateKey"_string, "Ed448"_string); + define_an_algorithm("importKey"_string, "Ed448"_string); + define_an_algorithm("exportKey"_string, "Ed448"_string); return internal_object; } diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.txt new file mode 100644 index 000000000000..b315bf61e0fe --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.txt @@ -0,0 +1,391 @@ +Harness status: OK + +Found 386 tests + +386 Pass +Pass Bad algorithm: generateKey(AES, false, [decrypt]) +Pass Bad algorithm: generateKey(AES, true, [decrypt]) +Pass Bad algorithm: generateKey(AES, RED, [decrypt]) +Pass Bad algorithm: generateKey(AES, 7, [decrypt]) +Pass Bad algorithm: generateKey(AES, false, [sign, decrypt]) +Pass Bad algorithm: generateKey(AES, true, [sign, decrypt]) +Pass Bad algorithm: generateKey(AES, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey(AES, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey(AES, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey(AES, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey(AES, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey(AES, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey(AES, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey(AES, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey(AES, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey(AES, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey(AES, false, [sign]) +Pass Bad algorithm: generateKey(AES, true, [sign]) +Pass Bad algorithm: generateKey(AES, RED, [sign]) +Pass Bad algorithm: generateKey(AES, 7, [sign]) +Pass Bad algorithm: generateKey(AES, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey(AES, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey(AES, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey(AES, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey(AES, false, [deriveBits]) +Pass Bad algorithm: generateKey(AES, true, [deriveBits]) +Pass Bad algorithm: generateKey(AES, RED, [deriveBits]) +Pass Bad algorithm: generateKey(AES, 7, [deriveBits]) +Pass Bad algorithm: generateKey(AES, false, []) +Pass Bad algorithm: generateKey(AES, true, []) +Pass Bad algorithm: generateKey(AES, RED, []) +Pass Bad algorithm: generateKey(AES, 7, []) +Pass Bad algorithm: generateKey(AES, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey(AES, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey(AES, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey(AES, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, false, [decrypt]) +Pass Bad algorithm: generateKey({name: AES}, true, [decrypt]) +Pass Bad algorithm: generateKey({name: AES}, RED, [decrypt]) +Pass Bad algorithm: generateKey({name: AES}, 7, [decrypt]) +Pass Bad algorithm: generateKey({name: AES}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: AES}, false, [sign]) +Pass Bad algorithm: generateKey({name: AES}, true, [sign]) +Pass Bad algorithm: generateKey({name: AES}, RED, [sign]) +Pass Bad algorithm: generateKey({name: AES}, 7, [sign]) +Pass Bad algorithm: generateKey({name: AES}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: AES}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: AES}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: AES}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: AES}, false, [deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, true, [deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, false, []) +Pass Bad algorithm: generateKey({name: AES}, true, []) +Pass Bad algorithm: generateKey({name: AES}, RED, []) +Pass Bad algorithm: generateKey({name: AES}, 7, []) +Pass Bad algorithm: generateKey({name: AES}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: AES}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, []) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, []) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, []) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, []) +Pass Bad algorithm: generateKey({length: 128, name: AES}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CMAC}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, []) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({length: 128, name: AES-CFB}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, []) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, []) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, []) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, []) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: MD5, name: HMAC}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, []) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, []) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, []) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, []) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA-256, modulusLength: 2048, name: RSA, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, []) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, []) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, []) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, []) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({hash: SHA, modulusLength: 2048, name: RSA-PSS, publicExponent: {0: 1, 1: 0, 2: 1}}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [deriveBits, sign, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [deriveBits, decrypt]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [deriveBits, sign]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, []) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, []) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, []) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, []) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad algorithm: generateKey({name: EC, namedCurve: P521}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Empty algorithm: generateKey({}, false, [decrypt]) +Pass Empty algorithm: generateKey({}, true, [decrypt]) +Pass Empty algorithm: generateKey({}, RED, [decrypt]) +Pass Empty algorithm: generateKey({}, 7, [decrypt]) +Pass Empty algorithm: generateKey({}, false, [sign, decrypt]) +Pass Empty algorithm: generateKey({}, true, [sign, decrypt]) +Pass Empty algorithm: generateKey({}, RED, [sign, decrypt]) +Pass Empty algorithm: generateKey({}, 7, [sign, decrypt]) +Pass Empty algorithm: generateKey({}, false, [deriveBits, sign, decrypt]) +Pass Empty algorithm: generateKey({}, true, [deriveBits, sign, decrypt]) +Pass Empty algorithm: generateKey({}, RED, [deriveBits, sign, decrypt]) +Pass Empty algorithm: generateKey({}, 7, [deriveBits, sign, decrypt]) +Pass Empty algorithm: generateKey({}, false, [deriveBits, decrypt]) +Pass Empty algorithm: generateKey({}, true, [deriveBits, decrypt]) +Pass Empty algorithm: generateKey({}, RED, [deriveBits, decrypt]) +Pass Empty algorithm: generateKey({}, 7, [deriveBits, decrypt]) +Pass Empty algorithm: generateKey({}, false, [sign]) +Pass Empty algorithm: generateKey({}, true, [sign]) +Pass Empty algorithm: generateKey({}, RED, [sign]) +Pass Empty algorithm: generateKey({}, 7, [sign]) +Pass Empty algorithm: generateKey({}, false, [deriveBits, sign]) +Pass Empty algorithm: generateKey({}, true, [deriveBits, sign]) +Pass Empty algorithm: generateKey({}, RED, [deriveBits, sign]) +Pass Empty algorithm: generateKey({}, 7, [deriveBits, sign]) +Pass Empty algorithm: generateKey({}, false, [deriveBits]) +Pass Empty algorithm: generateKey({}, true, [deriveBits]) +Pass Empty algorithm: generateKey({}, RED, [deriveBits]) +Pass Empty algorithm: generateKey({}, 7, [deriveBits]) +Pass Empty algorithm: generateKey({}, false, []) +Pass Empty algorithm: generateKey({}, true, []) +Pass Empty algorithm: generateKey({}, RED, []) +Pass Empty algorithm: generateKey({}, 7, []) +Pass Empty algorithm: generateKey({}, false, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Empty algorithm: generateKey({}, true, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Empty algorithm: generateKey({}, RED, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Empty algorithm: generateKey({}, 7, [decrypt, sign, deriveBits, decrypt, sign, deriveBits]) +Pass Bad usages: generateKey({name: Ed448}, true, [encrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, encrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [verify, sign, encrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, encrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [decrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, decrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [verify, sign, decrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, decrypt]) +Pass Bad usages: generateKey({name: Ed448}, true, [wrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, wrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [verify, sign, wrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, wrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [unwrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, unwrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [verify, sign, unwrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, unwrapKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [deriveKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, deriveKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [verify, sign, deriveKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, deriveKey]) +Pass Bad usages: generateKey({name: Ed448}, true, [deriveBits]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, deriveBits]) +Pass Bad usages: generateKey({name: Ed448}, true, [verify, sign, deriveBits]) +Pass Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, deriveBits]) +Pass Empty usages: generateKey({name: Ed448}, false, []) +Pass Empty usages: generateKey({name: Ed448}, true, []) \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.txt new file mode 100644 index 000000000000..a87215aa9913 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.txt @@ -0,0 +1,23 @@ +Harness status: OK + +Found 18 tests + +18 Pass +Pass Success: generateKey({name: ED448}, false, [sign]) +Pass Success: generateKey({name: ED448}, true, [sign]) +Pass Success: generateKey({name: ED448}, false, [verify, sign]) +Pass Success: generateKey({name: ED448}, true, [verify, sign]) +Pass Success: generateKey({name: ED448}, false, [sign, verify, sign, sign, verify]) +Pass Success: generateKey({name: ED448}, true, [sign, verify, sign, sign, verify]) +Pass Success: generateKey({name: ed448}, false, [sign]) +Pass Success: generateKey({name: ed448}, true, [sign]) +Pass Success: generateKey({name: ed448}, false, [verify, sign]) +Pass Success: generateKey({name: ed448}, true, [verify, sign]) +Pass Success: generateKey({name: ed448}, false, [sign, verify, sign, sign, verify]) +Pass Success: generateKey({name: ed448}, true, [sign, verify, sign, sign, verify]) +Pass Success: generateKey({name: Ed448}, false, [sign]) +Pass Success: generateKey({name: Ed448}, true, [sign]) +Pass Success: generateKey({name: Ed448}, false, [verify, sign]) +Pass Success: generateKey({name: Ed448}, true, [verify, sign]) +Pass Success: generateKey({name: Ed448}, false, [sign, verify, sign, sign, verify]) +Pass Success: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify]) \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.txt new file mode 100644 index 000000000000..1e74753d8902 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.txt @@ -0,0 +1,18 @@ +Harness status: OK + +Found 13 tests + +13 Pass +Pass EdDSA Ed448 verification +Pass EdDSA Ed448 verification with altered signature after call +Pass EdDSA Ed448 with altered data after call +Pass EdDSA Ed448 using privateKey to verify +Pass EdDSA Ed448 using publicKey to sign +Pass EdDSA Ed448 no verify usage +Pass EdDSA Ed448 round trip +Pass EdDSA Ed448 signing with wrong algorithm name +Pass EdDSA Ed448 verifying with wrong algorithm name +Pass EdDSA Ed448 verification failure due to altered signature +Pass EdDSA Ed448 verification failure due to shortened signature +Pass EdDSA Ed448 verification failure due to altered data +Pass Sign and verify using generated Ed448 keys. \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt index 56e56444c317..c9256f6d8ea6 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.txt @@ -1,8 +1,8 @@ Harness status: OK -Found 281 tests +Found 309 tests -281 Pass +309 Pass Pass setup Pass Can wrap and unwrap RSA-OAEP public key keys using spki and RSA-OAEP Pass Can wrap and unwrap RSA-OAEP public key keys using jwk and RSA-OAEP @@ -27,6 +27,13 @@ Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 Pass Can wrap and unwrap Ed25519 private key keys using jwk and RSA-OAEP Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and RSA-OAEP Pass Can unwrap Ed25519 private key non-extractable keys using jwk and RSA-OAEP +Pass Can wrap and unwrap Ed448 public key keys using spki and RSA-OAEP +Pass Can wrap and unwrap Ed448 public key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap Ed448 private key keys using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using pkcs8 and RSA-OAEP +Pass Can wrap and unwrap Ed448 private key keys using jwk and RSA-OAEP +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using jwk and RSA-OAEP +Pass Can unwrap Ed448 private key non-extractable keys using jwk and RSA-OAEP Pass Can wrap and unwrap X25519 public key keys using spki and RSA-OAEP Pass Can wrap and unwrap X25519 public key keys using jwk and RSA-OAEP Pass Can wrap and unwrap X25519 private key keys using pkcs8 and RSA-OAEP @@ -94,6 +101,13 @@ Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 Pass Can wrap and unwrap Ed25519 private key keys using jwk and AES-CTR Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and AES-CTR Pass Can unwrap Ed25519 private key non-extractable keys using jwk and AES-CTR +Pass Can wrap and unwrap Ed448 public key keys using spki and AES-CTR +Pass Can wrap and unwrap Ed448 public key keys using jwk and AES-CTR +Pass Can wrap and unwrap Ed448 private key keys using pkcs8 and AES-CTR +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using pkcs8 and AES-CTR +Pass Can wrap and unwrap Ed448 private key keys using jwk and AES-CTR +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using jwk and AES-CTR +Pass Can unwrap Ed448 private key non-extractable keys using jwk and AES-CTR Pass Can wrap and unwrap X25519 public key keys using spki and AES-CTR Pass Can wrap and unwrap X25519 public key keys using jwk and AES-CTR Pass Can wrap and unwrap X25519 private key keys using pkcs8 and AES-CTR @@ -161,6 +175,13 @@ Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 Pass Can wrap and unwrap Ed25519 private key keys using jwk and AES-CBC Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and AES-CBC Pass Can unwrap Ed25519 private key non-extractable keys using jwk and AES-CBC +Pass Can wrap and unwrap Ed448 public key keys using spki and AES-CBC +Pass Can wrap and unwrap Ed448 public key keys using jwk and AES-CBC +Pass Can wrap and unwrap Ed448 private key keys using pkcs8 and AES-CBC +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using pkcs8 and AES-CBC +Pass Can wrap and unwrap Ed448 private key keys using jwk and AES-CBC +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using jwk and AES-CBC +Pass Can unwrap Ed448 private key non-extractable keys using jwk and AES-CBC Pass Can wrap and unwrap X25519 public key keys using spki and AES-CBC Pass Can wrap and unwrap X25519 public key keys using jwk and AES-CBC Pass Can wrap and unwrap X25519 private key keys using pkcs8 and AES-CBC @@ -228,6 +249,13 @@ Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using pkcs8 Pass Can wrap and unwrap Ed25519 private key keys using jwk and AES-GCM Pass Can wrap and unwrap Ed25519 private key keys as non-extractable using jwk and AES-GCM Pass Can unwrap Ed25519 private key non-extractable keys using jwk and AES-GCM +Pass Can wrap and unwrap Ed448 public key keys using spki and AES-GCM +Pass Can wrap and unwrap Ed448 public key keys using jwk and AES-GCM +Pass Can wrap and unwrap Ed448 private key keys using pkcs8 and AES-GCM +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using pkcs8 and AES-GCM +Pass Can wrap and unwrap Ed448 private key keys using jwk and AES-GCM +Pass Can wrap and unwrap Ed448 private key keys as non-extractable using jwk and AES-GCM +Pass Can unwrap Ed448 private key non-extractable keys using jwk and AES-GCM Pass Can wrap and unwrap X25519 public key keys using spki and AES-GCM Pass Can wrap and unwrap X25519 public key keys using jwk and AES-GCM Pass Can wrap and unwrap X25519 private key keys using pkcs8 and AES-GCM diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.html b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.html new file mode 100644 index 000000000000..2a13620c6289 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.html @@ -0,0 +1,17 @@ + + +WebCryptoAPI: generateKey() for Failures + + + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.js b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.js new file mode 100644 index 000000000000..b25dcd149094 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/failures_Ed448.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["Ed448"]); diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.html b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.html new file mode 100644 index 000000000000..233ed0293b04 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.html @@ -0,0 +1,18 @@ + + +WebCryptoAPI: generateKey() Successful Calls + + + + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.js b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.js new file mode 100644 index 000000000000..8e37f57b244b --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/generateKey/successes_Ed448.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["Ed448"]); diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa.js b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa.js new file mode 100644 index 000000000000..4024674e3c3d --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa.js @@ -0,0 +1,233 @@ + +function run_test(algorithmName) { + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // Source file [algorithm_name]_vectors.js provides the getTestVectors method + // for the algorithm that drives these tests. + var testVectors = getTestVectors(algorithmName); + + testVectors.forEach(function(vector) { + var algorithm = {name: vector.algorithmName}; + + // Test verification first, because signing tests rely on that working + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " verification"); + + // Test verification with an altered buffer after call + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + var signature = copyBuffer(vector.signature); + [isVerified] = await Promise.all([ + subtle.verify(algorithm, key, signature, vector.data), + signature[0] = 255 - signature[0] + ]); + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " verification with altered signature after call"); + + // Check for successful verification even if data is altered after call. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + var data = copyBuffer(vector.data); + [isVerified] = await Promise.all([ + subtle.verify(algorithm, key, vector.signature, data), + data[0] = 255 - data[0] + ]); + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " with altered data after call"); + + // Check for failures due to using privateKey to verify. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("pkcs8", vector.privateKeyBuffer, algorithm, false, ["sign"]); + isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data) + assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " using privateKey to verify"); + + // Check for failures due to using publicKey to sign. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + let signature = await subtle.sign(algorithm, key, vector.data); + assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }; + }, vector.name + " using publicKey to sign"); + + // Check for failures due to no "verify" usage. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, []); + isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data) + assert_unreached("Should have thrown error for no verify usage in " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " no verify usage"); + + // Check for successful signing and verification. + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let isVerified = false; + let privateKey, publicKey; + let signature; + try { + privateKey = await subtle.importKey("pkcs8", vector.privateKeyBuffer, algorithm, false, ["sign"]); + publicKey = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + signature = await subtle.sign(algorithm, privateKey, vector.data); + isVerified = await subtle.verify(algorithm, publicKey, vector.signature, vector.data) + } catch (err) { + assert_false(publicKey === undefined || privateKey === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_false(signature === undefined, "sign error for test " + vector.name + ": '" + err.message + "'"); + assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Round trip verification works"); + }, vector.name + " round trip"); + + // Test signing with the wrong algorithm + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let wrongKey; + try { + wrongKey = await subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + let signature = await subtle.sign(algorithm, wrongKey, vector.data); + assert_unreached("Signing should not have succeeded for " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(wrongKey === undefined, "Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }; + }, vector.name + " signing with wrong algorithm name"); + + // Test verification with the wrong algorithm + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let wrongKey; + try { + wrongKey = await subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + let isVerified = await subtle.verify(algorithm, wrongKey, vector.signature, vector.data) + assert_unreached("Verifying should not have succeeded for " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(wrongKey === undefined, "Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }; + }, vector.name + " verifying with wrong algorithm name"); + + // Test verification fails with wrong signature + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let key; + let isVerified = true; + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, signature, vector.data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " verification failure due to altered signature"); + + // Test verification fails with short (odd length) signature + promise_test(async() => { + let key; + let isVerified = true; + var signature = vector.signature.slice(1); // Skip the first byte + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, signature, vector.data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " verification failure due to shortened signature"); + + // Test verification fails with wrong data + promise_test(async() => { + let key; + let isVerified = true; + var data = copyBuffer(vector.data); + data[0] = 255 - data[0]; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, vector.signature, data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " verification failure due to altered data"); + + // Test that generated keys are valid for signing and verifying. + promise_test(async() => { + let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]); + let signature = await subtle.sign(algorithm, key.privateKey, vector.data); + let isVerified = await subtle.verify(algorithm, key.publicKey, signature, vector.data); + assert_true(isVerified, "Verificaton failed."); + }, "Sign and verify using generated " + vector.algorithmName + " keys."); + }); + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i + +WebCryptoAPI: sign() and verify() Using EdDSA + + + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.js b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.js new file mode 100644 index 000000000000..6960c296bae6 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_curve448.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: sign() and verify() Using EdDSA +// META: script=eddsa_vectors.js +// META: script=eddsa.js +// META: timeout=long + +run_test("Ed448"); diff --git a/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_vectors.js b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_vectors.js new file mode 100644 index 000000000000..78240586c2e3 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/WebCryptoAPI/sign_verify/eddsa_vectors.js @@ -0,0 +1,197 @@ +// eddsa_vectors.js + +// Data for testing Ed25519 and Ed448. + +// The following function returns an array of test vectors +// for the subtleCrypto sign method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// publicKeyBuffer - an arrayBuffer with the key data +// publicKeyFormat - "spki" "jwk" +// publicKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// privateKeyBuffer - an arrayBuffer with the key data +// privateKeyFormat - "pkcs8" or "jwk" +// privateKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithmName - the name of the AlgorithmIdentifier parameter to provide to sign +// data - the text to sign +// signature - the expected signature +function getTestVectors(algorithmName) { + var pkcs8 = { + "Ed25519": new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]), + "Ed448": new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]), + }; + + var spki = { + "Ed25519": new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]), + "Ed448": new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]), + }; + + // data + var data = new Uint8Array([43, 126, 208, 188, 119, 149, 105, 74, 180, 172, 211, 89, 3, 254, 140, 215, 216, 15, 106, 28, 134, 136, 166, 195, 65, 68, 9, 69, 117, 20, 161, 69, 120, 85, 187, 178, 25, 227, 10, 27, 238, 168, 254, 134, 144, 130, 217, 159, 200, 40, 47, 144, 80, 208, 36, 229, 158, 175, 7, 48, 186, 157, 183, 10]); + + // For verification tests. + var signatures = { + "Ed25519": new Uint8Array([61, 144, 222, 94, 87, 67, 223, 194, 130, 37, 191, 173, 179, 65, 177, 22, 203, 248, 163, 241, 206, 237, 191, 74, 220, 53, 14, 245, 211, 71, 24, 67, 164, 24, 97, 77, 203, 110, 97, 72, 98, 97, 76, 247, 175, 20, 150, 249, 52, 11, 60, 132, 78, 164, 220, 234, 177, 211, 209, 85, 235, 126, 204, 0]), + "Ed448": new Uint8Array([118, 137, 126, 140, 80, 172, 107, 17, 50, 115, 92, 9, 197, 95, 80, 108, 1, 73, 210, 103, 124, 117, 102, 79, 139, 193, 11, 130, 111, 189, 157, 240, 160, 60, 217, 134, 188, 232, 51, 158, 100, 199, 209, 114, 14, 169, 54, 23, 132, 220, 115, 131, 119, 101, 172, 41, 128, 192, 218, 192, 129, 74, 139, 193, 135, 209, 201, 201, 7, 197, 220, 192, 121, 86, 248, 91, 112, 147, 15, 228, 45, 231, 100, 23, 114, 23, 203, 45, 82, 186, 183, 193, 222, 190, 12, 168, 156, 206, 203, 205, 99, 247, 2, 90, 42, 90, 87, 43, 157, 35, 176, 100, 47, 0]), + } + + var vectors = []; + { + var vector = { + name: "EdDSA " + algorithmName, + publicKeyBuffer: spki[algorithmName], + publicKeyFormat: "spki", + publicKey: null, + privateKeyBuffer: pkcs8[algorithmName], + privateKeyFormat: "pkcs8", + privateKey: null, + algorithmName: algorithmName, + data: data, + signature: signatures[algorithmName] + }; + + vectors.push(vector); + } + return vectors; +} + +// https://eprint.iacr.org/2020/1244.pdf#table.caption.3 +var kSmallOrderPoints = [ + // Canonical serializations + [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // #0 - Order 1 + [0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], // #1 - Order 2 + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], // #2 - Order 4 + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // #3 - Order 4 + [0xC7, 0x17, 0x6A, 0x70, 0x3D, 0x4D, 0xD8, 0x4F, 0xBA, 0x3C, 0x0B, 0x76, 0x0D, 0x10, 0x67, 0x0F, 0x2A, 0x20, 0x53, 0xFA, 0x2C, 0x39, 0xCC, 0xC6, 0x4E, 0xC7, 0xFD, 0x77, 0x92, 0xAC, 0x03, 0x7A], // #4 - Order 8 + [0xC7, 0x17, 0x6A, 0x70, 0x3D, 0x4D, 0xD8, 0x4F, 0xBA, 0x3C, 0x0B, 0x76, 0x0D, 0x10, 0x67, 0x0F, 0x2A, 0x20, 0x53, 0xFA, 0x2C, 0x39, 0xCC, 0xC6, 0x4E, 0xC7, 0xFD, 0x77, 0x92, 0xAC, 0x03, 0xFA], // #5 - Order 8 + [0x26, 0xE8, 0x95, 0x8F, 0xC2, 0xB2, 0x27, 0xB0, 0x45, 0xC3, 0xF4, 0x89, 0xF2, 0xEF, 0x98, 0xF0, 0xD5, 0xDF, 0xAC, 0x05, 0xD3, 0xC6, 0x33, 0x39, 0xB1, 0x38, 0x02, 0x88, 0x6D, 0x53, 0xFC, 0x05], // #6 - Order 8 + [0x26, 0xE8, 0x95, 0x8F, 0xC2, 0xB2, 0x27, 0xB0, 0x45, 0xC3, 0xF4, 0x89, 0xF2, 0xEF, 0x98, 0xF0, 0xD5, 0xDF, 0xAC, 0x05, 0xD3, 0xC6, 0x33, 0x39, 0xB1, 0x38, 0x02, 0x88, 0x6D, 0x53, 0xFC, 0x85], // #7 - Order 8 + + // Non-canonical serializatons + [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], // #8 - Order 1 + [0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], // #9 - Order 2 + [0xEE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], // #10 - Order 1 + [0xEE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], // #11 - Order 1 + [0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], // #12 - Order 4 + [0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], // #13 - Order 4 +]; + + +var pubKeys = [ + [0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa], // kSmallOrderPoints #5 + [0xf7, 0xba, 0xde, 0xc5, 0xb8, 0xab, 0xea, 0xf6, 0x99, 0x58, 0x39, 0x92, 0x21, 0x9b, 0x7b, 0x22, 0x3f, 0x1d, 0xf3, 0xfb, 0xbe, 0xa9, 0x19, 0x84, 0x4e, 0x3f, 0x7c, 0x55, 0x4a, 0x43, 0xdd, 0x43], // highest 32 bytes of case "1" signature + [0xcd, 0xb2, 0x67, 0xce, 0x40, 0xc5, 0xcd, 0x45, 0x30, 0x6f, 0xa5, 0xd2, 0xf2, 0x97, 0x31, 0x45, 0x93, 0x87, 0xdb, 0xf9, 0xeb, 0x93, 0x3b, 0x7b, 0xd5, 0xae, 0xd9, 0xa7, 0x65, 0xb8, 0x8d, 0x4d], + [0x44, 0x2a, 0xad, 0x9f, 0x08, 0x9a, 0xd9, 0xe1, 0x46, 0x47, 0xb1, 0xef, 0x90, 0x99, 0xa1, 0xff, 0x47, 0x98, 0xd7, 0x85, 0x89, 0xe6, 0x6f, 0x28, 0xec, 0xa6, 0x9c, 0x11, 0xf5, 0x82, 0xa6, 0x23], + [0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], // kSmallOrderPoints #9 + [0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], // kSmallOrderPoints #1 +] + +// https://eprint.iacr.org/2020/1244.pdf +// signature = (R, S); public key A, h = SHA512(R||A||M ) +// 8(SB) = 8R + 8(hA) => (1) +// SB = R + hA => (2) +var kSmallOrderTestCases = { + "Ed25519": [ + { + id: "0", // S = 0 | A's order = small | R's order = small | (1) = pass | (2) = pass + message : Uint8Array.from([0x8c, 0x93, 0x25, 0x5d, 0x71, 0xdc, 0xab, 0x10, 0xe8, 0xf3, 0x79, 0xc2, 0x62, 0x00, 0xf3, 0xc7, 0xbd, 0x5f, 0x09, 0xd9, 0xbc, 0x30, 0x68, 0xd3, 0xef, 0x4e, 0xde, 0xb4, 0x85, 0x30, 0x22, 0xb6]), + keyData : Uint8Array.from(pubKeys[0]), + signature : Uint8Array.from([0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + verified: false, // small-order signature's R fail in the verification. + }, + { + id: "1", // 0 < S < L | A's order = small | R's order = mixed | (1) = pass | (2) = pass + message : Uint8Array.from([0x9b, 0xd9, 0xf4, 0x4f, 0x4d, 0xcc, 0x75, 0xbd, 0x53, 0x1b, 0x56, 0xb2, 0xcd, 0x28, 0x0b, 0x0b, 0xb3, 0x8f, 0xc1, 0xcd, 0x6d, 0x12, 0x30, 0xe1, 0x48, 0x61, 0xd8, 0x61, 0xde, 0x09, 0x2e, 0x79]), + keyData : Uint8Array.from(pubKeys[0]), + signature : Uint8Array.from([0xf7, 0xba, 0xde, 0xc5, 0xb8, 0xab, 0xea, 0xf6, 0x99, 0x58, 0x39, 0x92, 0x21, 0x9b, 0x7b, 0x22, 0x3f, 0x1d, 0xf3, 0xfb, 0xbe, 0xa9, 0x19, 0x84, 0x4e, 0x3f, 0x7c, 0x55, 0x4a, 0x43, 0xdd, 0x43, 0xa5, 0xbb, 0x70, 0x47, 0x86, 0xbe, 0x79, 0xfc, 0x47, 0x6f, 0x91, 0xd3, 0xf3, 0xf8, 0x9b, 0x03, 0x98, 0x4d, 0x80, 0x68, 0xdc, 0xf1, 0xbb, 0x7d, 0xfc, 0x66, 0x37, 0xb4, 0x54, 0x50, 0xac, 0x04]), + verified: false, // small-order key's data fail in the verification. + }, + { + id: "2", // 0 < S < L | A's order = mixed | R's order = small | (1) = pass | (2) = pass + message : Uint8Array.from([0xae, 0xbf, 0x3f, 0x26, 0x01, 0xa0, 0xc8, 0xc5, 0xd3, 0x9c, 0xc7, 0xd8, 0x91, 0x16, 0x42, 0xf7, 0x40, 0xb7, 0x81, 0x68, 0x21, 0x8d, 0xa8, 0x47, 0x17, 0x72, 0xb3, 0x5f, 0x9d, 0x35, 0xb9, 0xab]), + keyData : Uint8Array.from(pubKeys[1]), + signature : Uint8Array.from([0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa, 0x8c, 0x4b, 0xd4, 0x5a, 0xec, 0xac, 0xa5, 0xb2, 0x4f, 0xb9, 0x7b, 0xc1, 0x0a, 0xc2, 0x7a, 0xc8, 0x75, 0x1a, 0x7d, 0xfe, 0x1b, 0xaf, 0xf8, 0xb9, 0x53, 0xec, 0x9f, 0x58, 0x33, 0xca, 0x26, 0x0e]), + verified: false, // small-order signature's R fail in the verification. + }, + { + id: "3", // 0 < S < L | A's order = mixed | R's order = mixed | (1) = pass | (2) = pass + message : Uint8Array.from([0x9b, 0xd9, 0xf4, 0x4f, 0x4d, 0xcc, 0x75, 0xbd, 0x53, 0x1b, 0x56, 0xb2, 0xcd, 0x28, 0x0b, 0x0b, 0xb3, 0x8f, 0xc1, 0xcd, 0x6d, 0x12, 0x30, 0xe1, 0x48, 0x61, 0xd8, 0x61, 0xde, 0x09, 0x2e, 0x79]), + keyData : Uint8Array.from(pubKeys[2]), + signature : Uint8Array.from([0x90, 0x46, 0xa6, 0x47, 0x50, 0x44, 0x49, 0x38, 0xde, 0x19, 0xf2, 0x27, 0xbb, 0x80, 0x48, 0x5e, 0x92, 0xb8, 0x3f, 0xdb, 0x4b, 0x65, 0x06, 0xc1, 0x60, 0x48, 0x4c, 0x01, 0x6c, 0xc1, 0x85, 0x2f, 0x87, 0x90, 0x9e, 0x14, 0x42, 0x8a, 0x7a, 0x1d, 0x62, 0xe9, 0xf2, 0x2f, 0x3d, 0x3a, 0xd7, 0x80, 0x2d, 0xb0, 0x2e, 0xb2, 0xe6, 0x88, 0xb6, 0xc5, 0x2f, 0xcd, 0x66, 0x48, 0xa9, 0x8b, 0xd0, 0x09]), + verified: true, // mixed-order points are not checked. + }, + { + id: "4", // 0 < S < L | A's order = mixed | R's order = mixed | (1) = pass | (2) = fail + message : Uint8Array.from([0xe4, 0x7d, 0x62, 0xc6, 0x3f, 0x83, 0x0d, 0xc7, 0xa6, 0x85, 0x1a, 0x0b, 0x1f, 0x33, 0xae, 0x4b, 0xb2, 0xf5, 0x07, 0xfb, 0x6c, 0xff, 0xec, 0x40, 0x11, 0xea, 0xcc, 0xd5, 0x5b, 0x53, 0xf5, 0x6c]), + keyData : Uint8Array.from(pubKeys[2]), + signature : Uint8Array.from([0x16, 0x0a, 0x1c, 0xb0, 0xdc, 0x9c, 0x02, 0x58, 0xcd, 0x0a, 0x7d, 0x23, 0xe9, 0x4d, 0x8f, 0xa8, 0x78, 0xbc, 0xb1, 0x92, 0x5f, 0x2c, 0x64, 0x24, 0x6b, 0x2d, 0xee, 0x17, 0x96, 0xbe, 0xd5, 0x12, 0x5e, 0xc6, 0xbc, 0x98, 0x2a, 0x26, 0x9b, 0x72, 0x3e, 0x06, 0x68, 0xe5, 0x40, 0x91, 0x1a, 0x9a, 0x6a, 0x58, 0x92, 0x1d, 0x69, 0x25, 0xe4, 0x34, 0xab, 0x10, 0xaa, 0x79, 0x40, 0x55, 0x1a, 0x09]), + verified: false, // expect a cofactorless verification algorithm. + }, + { + id: "5", // 0 < S < L | A's order = mixed | R's order = L | (1) = pass | (2) = fail + message : Uint8Array.from([0xe4, 0x7d, 0x62, 0xc6, 0x3f, 0x83, 0x0d, 0xc7, 0xa6, 0x85, 0x1a, 0x0b, 0x1f, 0x33, 0xae, 0x4b, 0xb2, 0xf5, 0x07, 0xfb, 0x6c, 0xff, 0xec, 0x40, 0x11, 0xea, 0xcc, 0xd5, 0x5b, 0x53, 0xf5, 0x6c]), + keyData : Uint8Array.from(pubKeys[2]), + signature : Uint8Array.from([0x21, 0x12, 0x2a, 0x84, 0xe0, 0xb5, 0xfc, 0xa4, 0x05, 0x2f, 0x5b, 0x12, 0x35, 0xc8, 0x0a, 0x53, 0x78, 0x78, 0xb3, 0x8f, 0x31, 0x42, 0x35, 0x6b, 0x2c, 0x23, 0x84, 0xeb, 0xad, 0x46, 0x68, 0xb7, 0xe4, 0x0b, 0xc8, 0x36, 0xda, 0xc0, 0xf7, 0x10, 0x76, 0xf9, 0xab, 0xe3, 0xa5, 0x3f, 0x9c, 0x03, 0xc1, 0xce, 0xee, 0xdd, 0xb6, 0x58, 0xd0, 0x03, 0x04, 0x94, 0xac, 0xe5, 0x86, 0x68, 0x74, 0x05]), + verified: false, // expect a cofactorless verification algorithm. + }, + { + id: "6", // S > L | A's order = L | R's order = L | (1) = pass | (2) = pass + message : Uint8Array.from([0x85, 0xe2, 0x41, 0xa0, 0x7d, 0x14, 0x8b, 0x41, 0xe4, 0x7d, 0x62, 0xc6, 0x3f, 0x83, 0x0d, 0xc7, 0xa6, 0x85, 0x1a, 0x0b, 0x1f, 0x33, 0xae, 0x4b, 0xb2, 0xf5, 0x07, 0xfb, 0x6c, 0xff, 0xec, 0x40]), + keyData : Uint8Array.from(pubKeys[3]), + signature : Uint8Array.from([0xe9, 0x6f, 0x66, 0xbe, 0x97, 0x6d, 0x82, 0xe6, 0x01, 0x50, 0xba, 0xec, 0xff, 0x99, 0x06, 0x68, 0x4a, 0xeb, 0xb1, 0xef, 0x18, 0x1f, 0x67, 0xa7, 0x18, 0x9a, 0xc7, 0x8e, 0xa2, 0x3b, 0x6c, 0x0e, 0x54, 0x7f, 0x76, 0x90, 0xa0, 0xe2, 0xdd, 0xcd, 0x04, 0xd8, 0x7d, 0xbc, 0x34, 0x90, 0xdc, 0x19, 0xb3, 0xb3, 0x05, 0x2f, 0x7f, 0xf0, 0x53, 0x8c, 0xb6, 0x8a, 0xfb, 0x36, 0x9b, 0xa3, 0xa5, 0x14]), + verified: false, // S out of bounds + }, + { + id: "7", // S >> L | A's order = L | R's order = L | (1) = pass | (2) = pass + message : Uint8Array.from([0x85, 0xe2, 0x41, 0xa0, 0x7d, 0x14, 0x8b, 0x41, 0xe4, 0x7d, 0x62, 0xc6, 0x3f, 0x83, 0x0d, 0xc7, 0xa6, 0x85, 0x1a, 0x0b, 0x1f, 0x33, 0xae, 0x4b, 0xb2, 0xf5, 0x07, 0xfb, 0x6c, 0xff, 0xec, 0x40]), + keyData : Uint8Array.from(pubKeys[3]), + signature : Uint8Array.from([0x8c, 0xe5, 0xb9, 0x6c, 0x8f, 0x26, 0xd0, 0xab, 0x6c, 0x47, 0x95, 0x8c, 0x9e, 0x68, 0xb9, 0x37, 0x10, 0x4c, 0xd3, 0x6e, 0x13, 0xc3, 0x35, 0x66, 0xac, 0xd2, 0xfe, 0x8d, 0x38, 0xaa, 0x19, 0x42, 0x7e, 0x71, 0xf9, 0x8a, 0x47, 0x34, 0xe7, 0x4f, 0x2f, 0x13, 0xf0, 0x6f, 0x97, 0xc2, 0x0d, 0x58, 0xcc, 0x3f, 0x54, 0xb8, 0xbd, 0x0d, 0x27, 0x2f, 0x42, 0xb6, 0x95, 0xdd, 0x7e, 0x89, 0xa8, 0xc2, 0x02]), + verified: false, // S out of bounds + }, + { + id: "8", // 0 < S < L | A's order = mixed | R's order = small (non-canonical) | (1) = ? | (2) = ? Implementations that reduce A before hashing will accept #8 and accept #9, and viceversa + message : Uint8Array.from([0x9b, 0xed, 0xc2, 0x67, 0x42, 0x37, 0x25, 0xd4, 0x73, 0x88, 0x86, 0x31, 0xeb, 0xf4, 0x59, 0x88, 0xba, 0xd3, 0xdb, 0x83, 0x85, 0x1e, 0xe8, 0x5c, 0x85, 0xe2, 0x41, 0xa0, 0x7d, 0x14, 0x8b, 0x41]), + keyData : Uint8Array.from(pubKeys[1]), + signature : Uint8Array.from([0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xbe, 0x96, 0x78, 0xac, 0x10, 0x2e, 0xdc, 0xd9, 0x2b, 0x02, 0x10, 0xbb, 0x34, 0xd7, 0x42, 0x8d, 0x12, 0xff, 0xc5, 0xdf, 0x5f, 0x37, 0xe3, 0x59, 0x94, 0x12, 0x66, 0xa4, 0xe3, 0x5f, 0x0f]), + verified: false, // non-canonical point should fail in the verificaton (RFC8032) + }, + { + id: "9", // 0 < S < L | A's order = mixed | R's order = small (non-canonical) | (1) = ? | (2) = ? + message : Uint8Array.from([0x9b, 0xed, 0xc2, 0x67, 0x42, 0x37, 0x25, 0xd4, 0x73, 0x88, 0x86, 0x31, 0xeb, 0xf4, 0x59, 0x88, 0xba, 0xd3, 0xdb, 0x83, 0x85, 0x1e, 0xe8, 0x5c, 0x85, 0xe2, 0x41, 0xa0, 0x7d, 0x14, 0x8b, 0x41]), + keyData : Uint8Array.from(pubKeys[1]), + signature : Uint8Array.from([0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0x8c, 0x5b, 0x64, 0xcd, 0x20, 0x89, 0x82, 0xaa, 0x38, 0xd4, 0x93, 0x66, 0x21, 0xa4, 0x77, 0x5a, 0xa2, 0x33, 0xaa, 0x05, 0x05, 0x71, 0x1d, 0x8f, 0xdc, 0xfd, 0xaa, 0x94, 0x3d, 0x49, 0x08]), + verified: false, // non-canonical point should fail in the verificaton (RFC8032) + }, + { + id: "10", // 0 < S < L | A's order = small (non-canonical) | R's order = mixed | (1) = ? | (2) = ? Implementations that reduce A before hashing will accept #10 and accept #11, and viceversa + message : Uint8Array.from([0xe9, 0x6b, 0x70, 0x21, 0xeb, 0x39, 0xc1, 0xa1, 0x63, 0xb6, 0xda, 0x4e, 0x30, 0x93, 0xdc, 0xd3, 0xf2, 0x13, 0x87, 0xda, 0x4c, 0xc4, 0x57, 0x2b, 0xe5, 0x88, 0xfa, 0xfa, 0xe2, 0x3c, 0x15, 0x5b]), + keyData : Uint8Array.from(pubKeys[4]), + signature : Uint8Array.from([0xa9, 0xd5, 0x52, 0x60, 0xf7, 0x65, 0x26, 0x1e, 0xb9, 0xb8, 0x4e, 0x10, 0x6f, 0x66, 0x5e, 0x00, 0xb8, 0x67, 0x28, 0x7a, 0x76, 0x19, 0x90, 0xd7, 0x13, 0x59, 0x63, 0xee, 0x0a, 0x7d, 0x59, 0xdc, 0xa5, 0xbb, 0x70, 0x47, 0x86, 0xbe, 0x79, 0xfc, 0x47, 0x6f, 0x91, 0xd3, 0xf3, 0xf8, 0x9b, 0x03, 0x98, 0x4d, 0x80, 0x68, 0xdc, 0xf1, 0xbb, 0x7d, 0xfc, 0x66, 0x37, 0xb4, 0x54, 0x50, 0xac, 0x04]), + verified: false, // non-canonical point should fail in the verificaton (RFC8032) + }, + { + id: "11", // 0 < S < L | A's order = small (non-canonical) | R's order = mixed | (1) = ? | (2) = ? Implementations that reduce A before hashing will accept #10 and accept #11, and viceversa + message : Uint8Array.from([0x39, 0xa5, 0x91, 0xf5, 0x32, 0x1b, 0xbe, 0x07, 0xfd, 0x5a, 0x23, 0xdc, 0x2f, 0x39, 0xd0, 0x25, 0xd7, 0x45, 0x26, 0x61, 0x57, 0x46, 0x72, 0x7c, 0xee, 0xfd, 0x6e, 0x82, 0xae, 0x65, 0xc0, 0x6f]), + keyData : Uint8Array.from(pubKeys[4]), + signature : Uint8Array.from([0xa9, 0xd5, 0x52, 0x60, 0xf7, 0x65, 0x26, 0x1e, 0xb9, 0xb8, 0x4e, 0x10, 0x6f, 0x66, 0x5e, 0x00, 0xb8, 0x67, 0x28, 0x7a, 0x76, 0x19, 0x90, 0xd7, 0x13, 0x59, 0x63, 0xee, 0x0a, 0x7d, 0x59, 0xdc, 0xa5, 0xbb, 0x70, 0x47, 0x86, 0xbe, 0x79, 0xfc, 0x47, 0x6f, 0x91, 0xd3, 0xf3, 0xf8, 0x9b, 0x03, 0x98, 0x4d, 0x80, 0x68, 0xdc, 0xf1, 0xbb, 0x7d, 0xfc, 0x66, 0x37, 0xb4, 0x54, 0x50, 0xac, 0x04]), + verified: false, // non-canonical point should fail in the verificaton (RFC8032) + }, + // https://eprint.iacr.org/2020/1244.pdf#section.A.2 + // cases breaking non-repudiation + { + id: "12", // 0 < S < L | A's order = small | R's order = mixed | (1) = ? | (2) = ? + message : Uint8Array.from([0x53, 0x65, 0x6e, 0x64, 0x20, 0x31, 0x30, 0x30, 0x20, 0x55, 0x53, 0x44, 0x20, 0x74, 0x6f, 0x20, 0x41, 0x6c, 0x69, 0x63, 0x65]), + keyData : Uint8Array.from(pubKeys[5]), + signature : Uint8Array.from([0xa9, 0xd5, 0x52, 0x60, 0xf7, 0x65, 0x26, 0x1e, 0xb9, 0xb8, 0x4e, 0x10, 0x6f, 0x66, 0x5e, 0x00, 0xb8, 0x67, 0x28, 0x7a, 0x76, 0x19, 0x90, 0xd7, 0x13, 0x59, 0x63, 0xee, 0x0a, 0x7d, 0x59, 0xdc, 0xa5, 0xbb, 0x70, 0x47, 0x86, 0xbe, 0x79, 0xfc, 0x47, 0x6f, 0x91, 0xd3, 0xf3, 0xf8, 0x9b, 0x03, 0x98, 0x4d, 0x80, 0x68, 0xdc, 0xf1, 0xbb, 0x7d, 0xfc, 0x66, 0x37, 0xb4, 0x54, 0x50, 0xac, 0x04]), + verified: false, + }, + { + id: "13", // 0 < S < L | A's order = small | R's order = mixed | (1) = ? | (2) = ? + message : Uint8Array.from([0x53, 0x65, 0x6e, 0x64, 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x55, 0x53, 0x44, 0x20, 0x74, 0x6f, 0x20, 0x41, 0x6c, 0x69, 0x63, 0x65]), + keyData : Uint8Array.from(pubKeys[5]), + signature : Uint8Array.from([0xa9, 0xd5, 0x52, 0x60, 0xf7, 0x65, 0x26, 0x1e, 0xb9, 0xb8, 0x4e, 0x10, 0x6f, 0x66, 0x5e, 0x00, 0xb8, 0x67, 0x28, 0x7a, 0x76, 0x19, 0x90, 0xd7, 0x13, 0x59, 0x63, 0xee, 0x0a, 0x7d, 0x59, 0xdc, 0xa5, 0xbb, 0x70, 0x47, 0x86, 0xbe, 0x79, 0xfc, 0x47, 0x6f, 0x91, 0xd3, 0xf3, 0xf8, 0x9b, 0x03, 0x98, 0x4d, 0x80, 0x68, 0xdc, 0xf1, 0xbb, 0x7d, 0xfc, 0x66, 0x37, 0xb4, 0x54, 0x50, 0xac, 0x04]), + verified: false, + } + ] +};