diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index b1c83cb3ad2..2b8766754fd 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -2950,6 +2950,111 @@ WebIDL::ExceptionOr> ED25519::import_key( return GC::Ref { *key }; } +WebIDL::ExceptionOr> ED25519::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-Ed25519 OID defined in [RFC8410]. + // * Set the subjectPublicKey field to keyData. + auto ed25519_oid = TLS::ed25519_oid; + auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_subject_public_key_info(key_data, ed25519_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-Ed25519 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 Ed25519 private key represented by the [[handle]] internal slot of key + + auto ed25519_oid = TLS::ed25519_oid; + auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_private_key_info(key_data, ed25519_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 "Ed25519". + jwk.crv = "Ed25519"_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)); + } 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::Ed25519 curve; + auto public_key = TRY_OR_THROW_OOM(vm, curve.generate_public_key(key_data)); + jwk.x = TRY_OR_THROW_OOM(vm, encode_base64url(key_data)); + } + + // 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)); + } + + // 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 Ed25519 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); +} + WebIDL::ExceptionOr> ED25519::sign([[maybe_unused]] AlgorithmParams const& params, GC::Ref key, ByteBuffer const& message) { auto& realm = *m_realm; diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index ca780f6e821..92d539f4383 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -516,6 +516,7 @@ public: 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 ED25519(realm)); } diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index ef5986386cb..856f7fdbb32 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -879,7 +879,7 @@ SupportedAlgorithmsMap supported_algorithms() define_an_algorithm("verify"_string, "Ed25519"_string); define_an_algorithm("generateKey"_string, "Ed25519"_string); define_an_algorithm("importKey"_string, "Ed25519"_string); - // FIXME: define_an_algorithm("exportKey"_string, "Ed25519"_string); + define_an_algorithm("exportKey"_string, "Ed25519"_string); // https://wicg.github.io/webcrypto-secure-curves/#ed448-registration // FIXME: define_an_algorithm("sign"_string, "Ed448"_string); diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.txt index fa1f7917e2c..8da6599caab 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.txt @@ -6,8 +6,8 @@ Rerun Found 62 tests -26 Pass -36 Fail +32 Pass +30 Fail Details Result Test Name MessageFail Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [verify]) Fail Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, [verify]) @@ -15,24 +15,24 @@ Fail Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, t Fail Good parameters: Ed25519 bits (jwk, object(kty, crv, x), Ed25519, true, [verify]) Fail Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify]) Fail Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify]) -Fail Good parameters: Ed25519 bits (raw, buffer(32), {name: Ed25519}, true, [verify]) -Fail Good parameters: Ed25519 bits (raw, buffer(32), Ed25519, true, [verify]) +Pass Good parameters: Ed25519 bits (raw, buffer(32), {name: Ed25519}, true, [verify]) +Pass Good parameters: Ed25519 bits (raw, buffer(32), Ed25519, true, [verify]) Fail Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, []) Fail Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, []) Fail Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, []) Fail Good parameters: Ed25519 bits (jwk, object(kty, crv, x), Ed25519, true, []) Fail Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, []) Fail Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, []) -Fail Good parameters: Ed25519 bits (raw, buffer(32), {name: Ed25519}, true, []) -Fail Good parameters: Ed25519 bits (raw, buffer(32), Ed25519, true, []) +Pass Good parameters: Ed25519 bits (raw, buffer(32), {name: Ed25519}, true, []) +Pass Good parameters: Ed25519 bits (raw, buffer(32), Ed25519, true, []) Fail Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [verify, verify]) Fail Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, [verify, verify]) Fail Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify]) Fail Good parameters: Ed25519 bits (jwk, object(kty, crv, x), Ed25519, true, [verify, verify]) Fail Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify]) Fail Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify, verify]) -Fail Good parameters: Ed25519 bits (raw, buffer(32), {name: Ed25519}, true, [verify, verify]) -Fail Good parameters: Ed25519 bits (raw, buffer(32), Ed25519, true, [verify, verify]) +Pass Good parameters: Ed25519 bits (raw, buffer(32), {name: Ed25519}, true, [verify, verify]) +Pass Good parameters: Ed25519 bits (raw, buffer(32), Ed25519, true, [verify, verify]) Fail Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign]) Fail Good parameters: Ed25519 bits (pkcs8, buffer(48), Ed25519, true, [sign]) Fail Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])