From d625e120828e0ca506d04bacbb9f6392c5c5bcbc Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 25 Nov 2024 15:56:40 +0100 Subject: [PATCH] LibWeb: Implement the deriveBits algorithm for X448 --- Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp | 63 +++++++++++++++++++ Libraries/LibWeb/Crypto/CryptoAlgorithms.h | 2 +- Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 2 +- .../cfrg_curves_bits_curve448.https.any.txt | 32 +++++----- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index 7f9d5417fa48..38628a3d5cd9 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -3751,6 +3751,69 @@ WebIDL::ExceptionOr> X25519::export_key(Bindings::KeyFormat return GC::Ref { *result }; } +// https://wicg.github.io/webcrypto-secure-curves/#x448-operations +WebIDL::ExceptionOr> X448::derive_bits( + AlgorithmParams const& params, + GC::Ref key, + Optional length_optional) +{ + // 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 publicKey be the public member of normalizedAlgorithm. + auto& public_key = static_cast(params).public_key; + + // 3. If the [[type]] internal slot of publicKey is not "public", then throw an InvalidAccessError. + if (public_key->type() != Bindings::KeyType::Public) + return WebIDL::InvalidAccessError::create(m_realm, "Public key is not a public key"_string); + + // 4. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal to + // the name property of the [[algorithm]] internal slot of key, then throw an InvalidAccessError. + auto& internal_algorithm = static_cast(*key->algorithm()); + auto& public_internal_algorithm = static_cast(*public_key->algorithm()); + if (internal_algorithm.name() != public_internal_algorithm.name()) + return WebIDL::InvalidAccessError::create(m_realm, "Algorithm mismatch"_string); + + // 5. Let secret be the result of performing the X448 function specified in [RFC7748] Section 5 + // with key as the X448 private key k and the X448 public key represented by the [[handle]] + // internal slot of publicKey as the X448 public key u. + auto private_key = key->handle().get(); + auto public_key_data = public_key->handle().get(); + + ::Crypto::Curves::X448 curve; + auto maybe_secret = curve.compute_coordinate(private_key, public_key_data); + if (maybe_secret.is_error()) + return WebIDL::OperationError::create(m_realm, "Failed to compute secret"_string); + + auto secret = maybe_secret.release_value(); + + // 6. If secret is the all-zero value, then throw a OperationError. This check must be performed in constant-time, as per [RFC7748] Section 6.2. + // NOTE: The check may be performed by ORing all the bytes together and checking whether the result is zero, + // as this eliminates standard side-channels in software implementations. + auto or_bytes = 0; + for (auto byte : secret.bytes()) { + or_bytes |= byte; + } + + if (or_bytes == 0) + return WebIDL::OperationError::create(m_realm, "Secret is the all-zero value"_string); + + // 7. If length is null: Return secret + if (!length_optional.has_value()) { + auto result = TRY_OR_THROW_OOM(m_realm->vm(), ByteBuffer::copy(secret)); + return JS::ArrayBuffer::create(m_realm, move(result)); + } + + // Otherwise: Return an octet string containing the first length bits of secret. + auto length = length_optional.value(); + if (secret.size() * 8 < length) + return WebIDL::OperationError::create(m_realm, "Secret is too short"_string); + + auto slice = TRY_OR_THROW_OOM(m_realm->vm(), secret.slice(0, length / 8)); + return JS::ArrayBuffer::create(m_realm, move(slice)); +} + // https://wicg.github.io/webcrypto-secure-curves/#x448-operations WebIDL::ExceptionOr, GC::Ref>> X448::generate_key( AlgorithmParams const&, diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h index 927e3eecd4b4..09cb1a936ada 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.h +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.h @@ -545,7 +545,7 @@ class X25519 : public AlgorithmMethods { class X448 : public AlgorithmMethods { public: - // FIXME: virtual WebIDL::ExceptionOr> derive_bits(AlgorithmParams const&, GC::Ref, Optional) override; + virtual WebIDL::ExceptionOr> derive_bits(AlgorithmParams const&, GC::Ref, Optional) 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; diff --git a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp index 4a0192228683..26cfb882db28 100644 --- a/Libraries/LibWeb/Crypto/SubtleCrypto.cpp +++ b/Libraries/LibWeb/Crypto/SubtleCrypto.cpp @@ -873,7 +873,7 @@ SupportedAlgorithmsMap const& supported_algorithms() define_an_algorithm("exportKey"_string, "X25519"_string); // https://wicg.github.io/webcrypto-secure-curves/#x448-registration - // FIXME: define_an_algorithm("deriveBits"_string, "X448"_string); + define_an_algorithm("deriveBits"_string, "X448"_string); define_an_algorithm("generateKey"_string, "X448"_string); define_an_algorithm("importKey"_string, "X448"_string); define_an_algorithm("exportKey"_string, "X448"_string); diff --git a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.https.any.txt b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.https.any.txt index b22eecc37fd4..f1e04b9ec6cf 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.https.any.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.https.any.txt @@ -6,24 +6,24 @@ Rerun Found 18 tests -1 Pass -17 Fail +15 Pass +3 Fail Details Result Test Name MessagePass setup - define tests -Fail X448 key derivation checks for all-zero value result with a key of order 0 -Fail X448 key derivation checks for all-zero value result with a key of order 1 -Fail X448 key derivation checks for all-zero value result with a key of order p-1 (order 2) -Fail X448 key derivation checks for all-zero value result with a key of order p (=0, order 4) -Fail X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1) -Fail X448 good parameters +Pass X448 key derivation checks for all-zero value result with a key of order 0 +Pass X448 key derivation checks for all-zero value result with a key of order 1 +Pass X448 key derivation checks for all-zero value result with a key of order p-1 (order 2) +Pass X448 key derivation checks for all-zero value result with a key of order p (=0, order 4) +Pass X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1) +Pass X448 good parameters Fail X448 mixed case parameters -Fail X448 short result +Pass X448 short result Fail X448 non-multiple of 8 bits -Fail X448 missing public property -Fail X448 public property of algorithm is not a CryptoKey +Pass X448 missing public property +Pass X448 public property of algorithm is not a CryptoKey Fail X448 mismatched algorithms -Fail X448 no deriveBits usage for base key -Fail X448 base key is not a private key -Fail X448 public property value is a private key -Fail X448 public property value is a secret key -Fail X448 asking for too many bits \ No newline at end of file +Pass X448 no deriveBits usage for base key +Pass X448 base key is not a private key +Pass X448 public property value is a private key +Pass X448 public property value is a secret key +Pass X448 asking for too many bits \ No newline at end of file