Procházet zdrojové kódy

LibWeb: Implement the exportKey algorithm for Ed25519

Andreas Kling před 8 měsíci
rodič
revize
ed7b1caba2

+ 105 - 0
Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp

@@ -2950,6 +2950,111 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED25519::import_key(
     return GC::Ref { *key };
 }
 
+WebIDL::ExceptionOr<GC::Ref<JS::Object>> ED25519::export_key(Bindings::KeyFormat format, GC::Ref<CryptoKey> 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<ByteBuffer>();
+
+    // 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<String> {};
+        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<GC::Ref<JS::ArrayBuffer>> ED25519::sign([[maybe_unused]] AlgorithmParams const& params, GC::Ref<CryptoKey> key, ByteBuffer const& message)
 {
     auto& realm = *m_realm;

+ 1 - 0
Libraries/LibWeb/Crypto/CryptoAlgorithms.h

@@ -516,6 +516,7 @@ public:
 
     virtual WebIDL::ExceptionOr<Variant<GC::Ref<CryptoKey>, GC::Ref<CryptoKeyPair>>> generate_key(AlgorithmParams const&, bool, Vector<Bindings::KeyUsage> const&) override;
     virtual WebIDL::ExceptionOr<GC::Ref<CryptoKey>> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector<Bindings::KeyUsage> const&) override;
+    virtual WebIDL::ExceptionOr<GC::Ref<JS::Object>> export_key(Bindings::KeyFormat, GC::Ref<CryptoKey>) override;
 
     static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new ED25519(realm)); }
 

+ 1 - 1
Libraries/LibWeb/Crypto/SubtleCrypto.cpp

@@ -879,7 +879,7 @@ SupportedAlgorithmsMap supported_algorithms()
     define_an_algorithm<ED25519>("verify"_string, "Ed25519"_string);
     define_an_algorithm<ED25519>("generateKey"_string, "Ed25519"_string);
     define_an_algorithm<ED25519>("importKey"_string, "Ed25519"_string);
-    // FIXME: define_an_algorithm<ED25519>("exportKey"_string, "Ed25519"_string);
+    define_an_algorithm<ED25519>("exportKey"_string, "Ed25519"_string);
 
     // https://wicg.github.io/webcrypto-secure-curves/#ed448-registration
     // FIXME: define_an_algorithm<ED448, Ed448Params>("sign"_string, "Ed448"_string);

+ 8 - 8
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])