LibWeb: Validate JWK key length for OKP importKey

After base64url-decoding the x and d fields during JWK import,
verify the decoded byte length matches the expected key size for
the curve (32 for Ed25519/X25519, 57 for Ed448, 56 for X448).
A truncated value does not "contain the public/private key" as
required by RFC 8037 Section 2.
This commit is contained in:
Tete17
2026-04-12 23:16:09 +02:00
committed by Jelle Raaijmakers
parent 00e9396cfe
commit 32af84b698
Notes: github-actions[bot] 2026-04-19 11:36:39 +00:00
7 changed files with 671 additions and 123 deletions

View File

@@ -6082,19 +6082,19 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED25519::import_key(
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 32)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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"_utf16);
auto private_key = TRY(base64_url_bytes_decode(m_realm, jwk.d.value()));
if (private_key.size() != 32)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 2. Let key be a new CryptoKey object that represents the Ed25519 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"_utf16);
}
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".
@@ -6115,6 +6115,9 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED25519::import_key(
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 32)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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.
@@ -6122,12 +6125,6 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED25519::import_key(
return WebIDL::DataError::create(m_realm, "Present d field"_utf16);
// 2. Let key be a new CryptoKey object that represents the Ed25519 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"_utf16);
}
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".
@@ -6590,19 +6587,19 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED448::import_key(
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 57)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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"_utf16);
auto private_key = TRY(base64_url_bytes_decode(m_realm, jwk.d.value()));
if (private_key.size() != 57)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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"_utf16);
}
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".
@@ -6623,6 +6620,9 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED448::import_key(
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 57)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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.
@@ -6630,12 +6630,6 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> ED448::import_key(
return WebIDL::DataError::create(m_realm, "Present d field"_utf16);
// 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"_utf16);
}
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".
@@ -7321,19 +7315,19 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> X25519::import_key([[maybe_unused]] Web:
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 32)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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, "Missing d field"_utf16);
auto private_key = TRY(base64_url_bytes_decode(m_realm, jwk.d.value()));
if (private_key.size() != 32)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 2. Let key be a new CryptoKey object that represents the X25519 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"_utf16);
}
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".
@@ -7354,19 +7348,15 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> X25519::import_key([[maybe_unused]] Web:
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 32)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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"_utf16);
// 2. Let key be a new CryptoKey object that represents the X25519 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"_utf16);
}
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".
@@ -7938,19 +7928,19 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> X448::import_key(
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 56)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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, "Missing d field"_utf16);
auto private_key = TRY(base64_url_bytes_decode(m_realm, jwk.d.value()));
if (private_key.size() != 56)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 2. Let key be a new CryptoKey object that represents the X25519 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"_utf16);
}
auto private_key = private_key_or_error.release_value();
// 2. Let key be a new CryptoKey object that represents the X448 private key identified by interpreting jwk according to Section 2 of [RFC8037].
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key });
// 3. Set the [[type]] internal slot of Key to "private".
@@ -7971,19 +7961,16 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> X448::import_key(
// 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"_utf16);
auto public_key = TRY(base64_url_bytes_decode(m_realm, jwk.x.value()));
if (public_key.size() != 56)
return WebIDL::DataError::create(m_realm, "Invalid key length"_utf16);
// 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"_utf16);
// 2. Let key be a new CryptoKey object that represents the Ed25519 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"_utf16);
}
auto public_key = public_key_or_error.release_value();
// 2. Let key be a new CryptoKey object that represents the X448 public key identified by interpreting jwk according to Section 2 of [RFC8037].
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
// 3. Set the [[type]] internal slot of Key to "public".