Skip to content

Commit

Permalink
LibCrypto+LibWeb: Allow serializing key info without params
Browse files Browse the repository at this point in the history
Previously, if `nullptr` was passed as params for
`wrap_in_private_key_info` or `wrap_in_subject_public_key_info` an ASN1
null was serialized. This was not the intended behaviour for many.

The issue was discovered while implementing `wrapKey` and `unwrapKey` in
the next commits.
  • Loading branch information
devgianlu authored and awesomekling committed Dec 16, 2024
1 parent 718b78d commit 08af878
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 26 deletions.
98 changes: 98 additions & 0 deletions Libraries/LibCrypto/PK/PK.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@

namespace Crypto::PK {

template<class ByteBuffer>
ErrorOr<ByteBuffer> wrap_in_private_key_info(ByteBuffer key, Span<int const> algorithm_identifier)
{
ASN1::Encoder encoder;
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
TRY(encoder.write(0x00u)); // version

// AlgorithmIdentifier
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
TRY(encoder.write(algorithm_identifier)); // algorithm

return {};
}));

// PrivateKey
TRY(encoder.write(key));

return {};
}));

return encoder.finish();
}

template<class ByteBuffer, typename Params>
ErrorOr<ByteBuffer> wrap_in_private_key_info(ByteBuffer key, Span<int const> algorithm_identifier, Params params)
{
Expand All @@ -38,6 +61,33 @@ ErrorOr<ByteBuffer> wrap_in_private_key_info(ByteBuffer key, Span<int const> alg
return encoder.finish();
}

template<typename ExportableKey>
ErrorOr<ByteBuffer> wrap_in_private_key_info(ExportableKey key, Span<int const> algorithm_identifier)
requires requires(ExportableKey k) {
k.export_as_der();
}
{
ASN1::Encoder encoder;
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
TRY(encoder.write(0x00u)); // version

// AlgorithmIdentifier
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
TRY(encoder.write(algorithm_identifier)); // algorithm

return {};
}));

// PrivateKey
auto data = TRY(key.export_as_der());
TRY(encoder.write(data));

return {};
}));

return encoder.finish();
}

template<typename ExportableKey, typename Params>
ErrorOr<ByteBuffer> wrap_in_private_key_info(ExportableKey key, Span<int const> algorithm_identifier, Params params)
requires requires(ExportableKey k) {
Expand Down Expand Up @@ -67,6 +117,28 @@ requires requires(ExportableKey k) {
return encoder.finish();
}

template<class ByteBuffer>
ErrorOr<ByteBuffer> wrap_in_subject_public_key_info(ByteBuffer key, Span<int const> algorithm_identifier)
{
ASN1::Encoder encoder;
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
// AlgorithmIdentifier
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
TRY(encoder.write(algorithm_identifier)); // algorithm

return {};
}));

// subjectPublicKey
auto bitstring = ::Crypto::ASN1::BitStringView(key, 0);
TRY(encoder.write(bitstring));

return {};
}));

return encoder.finish();
}

template<class ByteBuffer, typename ParamsType>
ErrorOr<ByteBuffer> wrap_in_subject_public_key_info(ByteBuffer key, Span<int const> algorithm_identifier, ParamsType const& params)
{
Expand All @@ -91,6 +163,32 @@ ErrorOr<ByteBuffer> wrap_in_subject_public_key_info(ByteBuffer key, Span<int con
return encoder.finish();
}

template<typename ExportableKey>
ErrorOr<ByteBuffer> wrap_in_subject_public_key_info(ExportableKey key, Span<int const> algorithm_identifier)
requires requires(ExportableKey k) {
k.export_as_der();
}
{
ASN1::Encoder encoder;
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
// AlgorithmIdentifier
TRY(encoder.write_constructed(ASN1::Class::Universal, ASN1::Kind::Sequence, [&]() -> ErrorOr<void> {
TRY(encoder.write(algorithm_identifier)); // algorithm

return {};
}));

// subjectPublicKey
auto data = TRY(key.export_as_der());
auto bitstring = ::Crypto::ASN1::BitStringView(data, 0);
TRY(encoder.write(bitstring));

return {};
}));

return encoder.finish();
}

template<typename ExportableKey, typename ParamsType>
ErrorOr<ByteBuffer> wrap_in_subject_public_key_info(ExportableKey key, Span<int const> algorithm_identifier, ParamsType const& params)
requires requires(ExportableKey k) {
Expand Down
12 changes: 6 additions & 6 deletions Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4491,7 +4491,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> ED25519::export_key(Bindings::KeyFormat
// * Set the algorithm object identifier to the id-Ed25519 OID defined in [RFC8410].
// * Set the subjectPublicKey field to keyData.
auto ed25519_oid = ::Crypto::ASN1::ed25519_oid;
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_subject_public_key_info(key_data, ed25519_oid, nullptr));
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));
Expand All @@ -4514,7 +4514,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> ED25519::export_key(Bindings::KeyFormat
TRY_OR_THROW_OOM(vm, encoder.write(key_data.bytes()));

auto ed25519_oid = ::Crypto::ASN1::ed25519_oid;
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_private_key_info(encoder.finish(), ed25519_oid, nullptr));
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_private_key_info(encoder.finish(), 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));
Expand Down Expand Up @@ -5183,7 +5183,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> X25519::export_key(Bindings::KeyFormat
// Set the algorithm object identifier to the id-X25519 OID defined in [RFC8410].
// Set the subjectPublicKey field to keyData.
auto public_key = handle.get<ByteBuffer>();
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_subject_public_key_info(public_key, ::Crypto::ASN1::x25519_oid, nullptr));
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_subject_public_key_info(public_key, ::Crypto::ASN1::x25519_oid));

// 3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.
result = JS::ArrayBuffer::create(m_realm, data);
Expand All @@ -5202,7 +5202,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> X25519::export_key(Bindings::KeyFormat
// 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 X25519 private key represented by the [[handle]] internal slot of key
auto private_key = handle.get<ByteBuffer>();
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_private_key_info(private_key, ::Crypto::ASN1::x25519_oid, nullptr));
auto data = TRY_OR_THROW_OOM(vm, ::Crypto::PK::wrap_in_private_key_info(private_key, ::Crypto::ASN1::x25519_oid));

// 3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.
result = JS::ArrayBuffer::create(m_realm, data);
Expand Down Expand Up @@ -5427,7 +5427,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> X448::export_key(Bindings::KeyFormat fo
// * Set the algorithm object identifier to the id-X448 OID defined in [RFC8410].
// * Set the subjectPublicKey field to keyData.
auto x448_oid = ::Crypto::ASN1::x448_oid;
auto data = TRY_OR_THROW_OOM(m_realm->vm(), ::Crypto::PK::wrap_in_subject_public_key_info(key_data, x448_oid, nullptr));
auto data = TRY_OR_THROW_OOM(m_realm->vm(), ::Crypto::PK::wrap_in_subject_public_key_info(key_data, x448_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, data);
Expand All @@ -5445,7 +5445,7 @@ WebIDL::ExceptionOr<GC::Ref<JS::Object>> X448::export_key(Bindings::KeyFormat fo
// * Set the algorithm object identifier to the id-X448 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 X448 private key represented by the [[handle]] internal slot of key
auto x448_oid = ::Crypto::ASN1::x448_oid;
auto data = TRY_OR_THROW_OOM(m_realm->vm(), ::Crypto::PK::wrap_in_private_key_info(key_data, x448_oid, nullptr));
auto data = TRY_OR_THROW_OOM(m_realm->vm(), ::Crypto::PK::wrap_in_private_key_info(key_data, x448_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, data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,39 @@ Harness status: OK

Found 62 tests

52 Pass
10 Fail
Fail Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [verify])
Fail Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, [verify])
62 Pass
Pass Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [verify])
Pass Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, [verify])
Pass Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify])
Pass Good parameters: Ed25519 bits (jwk, object(kty, crv, x), Ed25519, true, [verify])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), 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, [])
Pass Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [])
Pass Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, [])
Pass Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [])
Pass Good parameters: Ed25519 bits (jwk, object(kty, crv, x), Ed25519, true, [])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), 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])
Pass Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [verify, verify])
Pass Good parameters: Ed25519 bits (spki, buffer(44), Ed25519, true, [verify, verify])
Pass Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify])
Pass Good parameters: Ed25519 bits (jwk, object(kty, crv, x), Ed25519, true, [verify, verify])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(kty, crv, x), 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])
Pass Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign])
Pass Good parameters: Ed25519 bits (pkcs8, buffer(48), Ed25519, true, [sign])
Pass Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])
Pass Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), Ed25519, true, [sign])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign])
Fail Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign, sign])
Fail Good parameters: Ed25519 bits (pkcs8, buffer(48), Ed25519, true, [sign, sign])
Pass Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign, sign])
Pass Good parameters: Ed25519 bits (pkcs8, buffer(48), Ed25519, true, [sign, sign])
Pass Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign, sign])
Pass Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), Ed25519, true, [sign, sign])
Pass Good parameters with ignored JWK alg: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign, sign])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ Harness status: OK

Found 54 tests

24 Pass
30 Fail
Fail Good parameters: X25519 bits (spki, buffer(44), {name: X25519}, true, [])
Fail Good parameters: X25519 bits (spki, buffer(44), X25519, true, [])
26 Pass
28 Fail
Pass Good parameters: X25519 bits (spki, buffer(44), {name: X25519}, true, [])
Pass Good parameters: X25519 bits (spki, buffer(44), X25519, true, [])
Fail Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, true, [])
Fail Good parameters: X25519 bits (jwk, object(kty, crv, x), X25519, true, [])
Fail Good parameters with ignored JWK alg: X25519 (jwk, object(kty, crv, x), {name: X25519}, true, [])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ Harness status: OK

Found 54 tests

24 Pass
30 Fail
Fail Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])
Fail Good parameters: X448 bits (spki, buffer(68), X448, true, [])
26 Pass
28 Fail
Pass Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])
Pass Good parameters: X448 bits (spki, buffer(68), X448, true, [])
Fail Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])
Fail Good parameters: X448 bits (jwk, object(kty, crv, x), X448, true, [])
Fail Good parameters with ignored JWK alg: X448 (jwk, object(kty, crv, x), {name: X448}, true, [])
Expand Down

0 comments on commit 08af878

Please sign in to comment.