Преглед изворни кода

LibWeb: Implement the generateKey algorithm for X448

Andreas Kling пре 8 месеци
родитељ
комит
5a8b0a2610

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

@@ -2,6 +2,7 @@
  * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
  * Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
  * Copyright (c) 2024, Jelle Raaijmakers <jelle@ladybird.org>
+ * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -17,6 +18,7 @@
 #include <LibCrypto/Curves/Ed25519.h>
 #include <LibCrypto/Curves/SECPxxxr1.h>
 #include <LibCrypto/Curves/X25519.h>
+#include <LibCrypto/Curves/X448.h>
 #include <LibCrypto/Hash/HKDF.h>
 #include <LibCrypto/Hash/HashManager.h>
 #include <LibCrypto/Hash/MGF.h>
@@ -3749,6 +3751,75 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> X25519::export_key(Bindings::KeyFormat
     return GC::Ref { *result };
 }
 
+// https://wicg.github.io/webcrypto-secure-curves/#x448-operations
+WebIDL::ExceptionOr<Variant<GC::Ref<CryptoKey>, GC::Ref<CryptoKeyPair>>> X448::generate_key(
+    AlgorithmParams const&,
+    bool extractable,
+    Vector<Bindings::KeyUsage> const& usages)
+{
+    // 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.
+    for (auto const& usage : usages) {
+        if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) {
+            return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage))));
+        }
+    }
+
+    // 2. Generate an X448 key pair, with the private key being 56 random bytes, and the public key being X448(a, 5), as defined in [RFC7748], section 6.2.
+    ::Crypto::Curves::X448 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 "X448".
+    algorithm->set_name("X448"_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 empty list.
+    public_key->set_usages({});
+
+    // 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 [ "deriveKey", "deriveBits" ].
+    private_key->set_usages(usage_intersection(usages, { { Bindings::KeyUsage::Derivekey, Bindings::KeyUsage::Derivebits } }));
+
+    // 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.
+    auto result = CryptoKeyPair::create(m_realm, public_key, private_key);
+
+    // 18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL].
+    return Variant<GC::Ref<CryptoKey>, GC::Ref<CryptoKeyPair>> { result };
+}
+
 static WebIDL::ExceptionOr<ByteBuffer> hmac_calculate_message_digest(JS::Realm& realm, GC::Ptr<KeyAlgorithm> hash, ReadonlyBytes key, ReadonlyBytes message)
 {
     auto calculate_digest = [&]<typename T>() -> ByteBuffer {

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

@@ -543,6 +543,22 @@ private:
     }
 };
 
+class X448 : public AlgorithmMethods {
+public:
+    // FIXME: virtual WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> derive_bits(AlgorithmParams const&, GC::Ref<CryptoKey>, Optional<u32>) override;
+    virtual WebIDL::ExceptionOr<Variant<GC::Ref<CryptoKey>, GC::Ref<CryptoKeyPair>>> generate_key(AlgorithmParams const&, bool, Vector<Bindings::KeyUsage> const&) override;
+    // FIXME: virtual WebIDL::ExceptionOr<GC::Ref<CryptoKey>> import_key(AlgorithmParams const&, Bindings::KeyFormat, CryptoKey::InternalKeyData, bool, Vector<Bindings::KeyUsage> const&) override;
+    // FIXME: 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 X448(realm)); }
+
+private:
+    explicit X448(JS::Realm& realm)
+        : AlgorithmMethods(realm)
+    {
+    }
+};
+
 class HMAC : public AlgorithmMethods {
 public:
     virtual WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> sign(AlgorithmParams const&, GC::Ref<CryptoKey>, ByteBuffer const&) override;

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

@@ -874,7 +874,7 @@ SupportedAlgorithmsMap const& supported_algorithms()
 
     // https://wicg.github.io/webcrypto-secure-curves/#x448-registration
     // FIXME: define_an_algorithm<X448, EcdhKeyDerivePrams>("deriveBits"_string, "X448"_string);
-    // FIXME: define_an_algorithm<X448>("generateKey"_string, "X448"_string);
+    define_an_algorithm<X448>("generateKey"_string, "X448"_string);
     // FIXME: define_an_algorithm<X448>("importKey"_string, "X448"_string);
     // FIXME: define_an_algorithm<X448>("exportKey"_string, "X448"_string);
 

+ 34 - 34
Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/generateKey/failures_X448.https.any.txt

@@ -6,8 +6,8 @@ Rerun
 
 Found 392 tests
 
-332 Pass
-60 Fail
+364 Pass
+28 Fail
 Details
 Result	Test Name	MessagePass	Bad algorithm: generateKey(AES, false, [decrypt])	
 Pass	Bad algorithm: generateKey(AES, true, [decrypt])	
@@ -369,35 +369,35 @@ Pass	Empty algorithm: generateKey({}, false, [decrypt, sign, deriveBits, decrypt
 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])	
-Fail	Bad usages: generateKey({name: X448}, true, [encrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [decrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])	
-Fail	Bad usages: generateKey({name: X448}, true, [sign])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, sign])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, sign])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])	
-Fail	Bad usages: generateKey({name: X448}, true, [verify])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, verify])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, verify])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])	
-Fail	Bad usages: generateKey({name: X448}, true, [wrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [unwrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey])	
-Fail	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])	
-Fail	Empty usages: generateKey({name: X448}, false, [])	
-Fail	Empty usages: generateKey({name: X448}, true, [])	
+Pass	Bad usages: generateKey({name: X448}, true, [encrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, encrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [decrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, decrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, decrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])	
+Pass	Bad usages: generateKey({name: X448}, true, [sign])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, sign])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, sign])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, sign])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])	
+Pass	Bad usages: generateKey({name: X448}, true, [verify])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, verify])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, verify])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, verify])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])	
+Pass	Bad usages: generateKey({name: X448}, true, [wrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, wrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, wrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, wrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [unwrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, unwrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, unwrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveBits, unwrapKey])	
+Pass	Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])	
+Pass	Empty usages: generateKey({name: X448}, false, [])	
+Pass	Empty usages: generateKey({name: X448}, true, [])