diff --git a/Libraries/LibCrypto/Certificate/Certificate.cpp b/Libraries/LibCrypto/Certificate/Certificate.cpp index c8e684b1249..21a5ef11065 100644 --- a/Libraries/LibCrypto/Certificate/Certificate.cpp +++ b/Libraries/LibCrypto/Certificate/Certificate.cpp @@ -5,43 +5,17 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include "Certificate.h" -#include +#include #include #include #include #include -#include +#include #include namespace Crypto::Certificate { -static ErrorOr parse_certificate_version(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // Version ::= INTEGER {v1(0), v2(1), v3(2)} - if (auto tag = decoder.peek(); !tag.is_error() && tag.value().type == Crypto::ASN1::Type::Constructed) { - ENTER_SCOPE("Version"sv); - READ_OBJECT(Integer, Crypto::UnsignedBigInteger, version); - if (version > 3) { - ERROR_WITH_SCOPE(TRY(String::formatted("Invalid version value at {}", current_scope))); - } - EXIT_SCOPE(); - return version; - } else { - return Crypto::UnsignedBigInteger { 0 }; - } -} - -static ErrorOr parse_serial_number(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // CertificateSerialNumber ::= INTEGER - PUSH_SCOPE("CertificateSerialNumber"sv); - READ_OBJECT(Integer, Crypto::UnsignedBigInteger, serial); - POP_SCOPE(); - return serial; -} - -ErrorOr> parse_ec_parameters(Crypto::ASN1::Decoder& decoder, Vector current_scope) +ErrorOr> parse_ec_parameters(ASN1::Decoder& decoder, Vector current_scope) { // ECParameters ::= CHOICE { // namedCurve OBJECT IDENTIFIER @@ -71,7 +45,7 @@ ErrorOr> parse_ec_parameters(Crypto::ASN1::Decoder& decoder, Vector< return named_curve; } -static ErrorOr parse_algorithm_identifier(Crypto::ASN1::Decoder& decoder, Vector current_scope) +static ErrorOr parse_algorithm_identifier(ASN1::Decoder& decoder, Vector current_scope) { // AlgorithmIdentifier{ALGORITHM:SupportedAlgorithms} ::= SEQUENCE { // algorithm ALGORITHM.&id({SupportedAlgorithms}), @@ -209,100 +183,8 @@ static ErrorOr parse_algorithm_identifier(Crypto::ASN1::Dec ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled parameters for algorithm {}", algorithm))); } -static ErrorOr parse_name(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - RelativeDistinguishedName rdn {}; - // Name ::= Choice { - // rdn_sequence RDNSequence - // } // NOTE: since this is the only alternative, there's no index - // RDNSequence ::= Sequence OF RelativeDistinguishedName - ENTER_TYPED_SCOPE(Sequence, "Name"sv); - while (!decoder.eof()) { - // RelativeDistinguishedName ::= Set OF AttributeTypeAndValue - ENTER_TYPED_SCOPE(Set, "RDNSequence"sv); - while (!decoder.eof()) { - // AttributeTypeAndValue ::= Sequence { - // type AttributeType, - // value AttributeValue - // } - ENTER_TYPED_SCOPE(Sequence, "AttributeTypeAndValue"sv); - // AttributeType ::= ObjectIdentifier - PUSH_SCOPE("AttributeType"sv) - READ_OBJECT(ObjectIdentifier, Vector, attribute_type_oid); - POP_SCOPE(); - - // AttributeValue ::= Any - PUSH_SCOPE("AttributeValue"sv) - READ_OBJECT(PrintableString, StringView, attribute_value); - POP_SCOPE(); - - auto attribute_type_string = TRY(String::join("."sv, attribute_type_oid)); - auto attribute_value_string = TRY(String::from_utf8(attribute_value)); - TRY(rdn.set(move(attribute_type_string), move(attribute_value_string))); - - EXIT_SCOPE(); - } - EXIT_SCOPE(); - } - EXIT_SCOPE(); - - return rdn; -} - -static ErrorOr parse_time(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // Time ::= Choice { - // utc_time UTCTime, - // general_time GeneralizedTime - // } - auto tag = TRY(decoder.peek()); - if (tag.kind == Crypto::ASN1::Kind::UTCTime) { - PUSH_SCOPE("UTCTime"sv); - - READ_OBJECT(UTCTime, StringView, utc_time); - auto parse_result = Crypto::ASN1::parse_utc_time(utc_time); - if (!parse_result.has_value()) { - ERROR_WITH_SCOPE(TRY(String::formatted("Failed to parse UTCTime {}", utc_time))); - } - - POP_SCOPE(); - return parse_result.release_value(); - } - - if (tag.kind == Crypto::ASN1::Kind::GeneralizedTime) { - PUSH_SCOPE("GeneralizedTime"sv); - - READ_OBJECT(UTCTime, StringView, generalized_time); - auto parse_result = Crypto::ASN1::parse_generalized_time(generalized_time); - if (!parse_result.has_value()) { - ERROR_WITH_SCOPE(TRY(String::formatted("Failed to parse GeneralizedTime {}", generalized_time))); - } - - POP_SCOPE(); - return parse_result.release_value(); - } - - ERROR_WITH_SCOPE(TRY(String::formatted("Unrecognised Time format {}", kind_name(tag.kind)))); -} - -static ErrorOr parse_validity(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - Validity validity {}; - - // Validity ::= SEQUENCE { - // notBefore Time, - // notAfter Time } - ENTER_TYPED_SCOPE(Sequence, "Validity"sv); - - validity.not_before = TRY(parse_time(decoder, current_scope)); - validity.not_after = TRY(parse_time(decoder, current_scope)); - - EXIT_SCOPE(); - - return validity; -} - -ErrorOr parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector current_scope) +// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 +ErrorOr parse_subject_public_key_info(ASN1::Decoder& decoder, Vector current_scope) { // SubjectPublicKeyInfo ::= Sequence { // algorithm AlgorithmIdentifier, @@ -366,7 +248,7 @@ ErrorOr parse_subject_public_key_info(Crypto::ASN1::Decoder& d } // https://www.rfc-editor.org/rfc/rfc5208#section-5 -ErrorOr parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vector current_scope) +ErrorOr parse_private_key_info(ASN1::Decoder& decoder, Vector current_scope) { // PrivateKeyInfo ::= SEQUENCE { // version Version, @@ -435,477 +317,4 @@ ErrorOr parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vecto ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled algorithm {}", algo_oid))); } -static ErrorOr parse_unique_identifier(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // UniqueIdentifier ::= BIT STRING - PUSH_SCOPE("UniqueIdentifier"sv); - READ_OBJECT(BitString, Crypto::ASN1::BitStringView, value); - POP_SCOPE(); - - return value; -} - -static ErrorOr parse_general_name(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // GeneralName ::= CHOICE { - // otherName [0] INSTANCE OF OTHER-NAME, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER, - // } - auto tag = TRY(decoder.peek()); - auto tag_value = static_cast(tag.kind); - switch (tag_value) { - case 0: - // Note: We don't know how to use this. - PUSH_SCOPE("otherName"sv) - DROP_OBJECT(); - POP_SCOPE(); - break; - case 1: { - PUSH_SCOPE("rfc822Name"sv) - READ_OBJECT(IA5String, StringView, name); - POP_SCOPE(); - return String::from_utf8(name); - } - case 2: { - PUSH_SCOPE("dNSName"sv) - READ_OBJECT(IA5String, StringView, name); - POP_SCOPE(); - return String::from_utf8(name); - } - case 3: - // Note: We don't know how to use this. - PUSH_SCOPE("x400Address"sv) - DROP_OBJECT(); - POP_SCOPE(); - break; - case 4: { - PUSH_SCOPE("directoryName"sv); - READ_OBJECT(OctetString, StringView, directory_name); - Crypto::ASN1::Decoder decoder { directory_name.bytes() }; - auto names = TRY(parse_name(decoder, current_scope)); - POP_SCOPE(); - return names.to_string(); - } - case 5: - // Note: We don't know how to use this. - PUSH_SCOPE("ediPartyName"); - DROP_OBJECT(); - POP_SCOPE(); - break; - case 6: { - PUSH_SCOPE("uniformResourceIdentifier"sv); - READ_OBJECT(IA5String, StringView, name); - POP_SCOPE(); - return String::from_utf8(name); - } - case 7: { - PUSH_SCOPE("iPAddress"sv); - READ_OBJECT(OctetString, StringView, ip_addr_sv); - IPv4Address ip_addr { ip_addr_sv.bytes().data() }; - POP_SCOPE(); - return ip_addr.to_string(); - } - case 8: { - PUSH_SCOPE("registeredID"sv); - READ_OBJECT(ObjectIdentifier, Vector, identifier); - POP_SCOPE(); - return String::join("."sv, identifier); - } - default: - ERROR_WITH_SCOPE("Unknown tag in GeneralNames choice"sv); - } - - ERROR_WITH_SCOPE("Unknown tag in GeneralNames choice"sv); -} - -static ErrorOr> parse_general_names(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // GeneralNames ::= Sequence OF GeneralName - ENTER_TYPED_SCOPE(Sequence, "GeneralNames"); - - Vector names; - while (!decoder.eof()) { - names.append(TRY(parse_general_name(decoder, current_scope))); - } - - EXIT_SCOPE(); - - return names; -} - -static ErrorOr> parse_subject_alternative_names(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // SubjectAlternativeName ::= GeneralNames - PUSH_SCOPE("SubjectAlternativeName"sv); - auto values = TRY(parse_general_names(decoder, current_scope)); - POP_SCOPE(); - - return values; -} - -static ErrorOr> parse_issuer_alternative_names(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // issuerAltName ::= GeneralNames - PUSH_SCOPE("issuerAltName"sv); - auto values = TRY(parse_general_names(decoder, current_scope)); - POP_SCOPE(); - - return values; -} - -static ErrorOr parse_key_usage(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // KeyUsage ::= BIT STRING { - // digitalSignature (0), - // contentCommitment (1), - // keyEncipherment (2), - // dataEncipherment (3), - // keyAgreement (4), - // keyCertSign (5), - // cRLSign (6), - // encipherOnly (7), - // decipherOnly (8) - // } - - PUSH_SCOPE("KeyUsage"sv); - READ_OBJECT(BitString, Crypto::ASN1::BitStringView, usage); - POP_SCOPE(); - - return usage; -} - -static ErrorOr parse_basic_constraints(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // BasicConstraints ::= SEQUENCE { - // cA BOOLEAN DEFAULT FALSE, - // pathLenConstraint INTEGER (0..MAX) OPTIONAL - // } - - BasicConstraints constraints {}; - - ENTER_TYPED_SCOPE(Sequence, "BasicConstraints"sv); - - if (decoder.eof()) { - EXIT_SCOPE(); - return constraints; - } - - auto ca_tag = TRY(decoder.peek()); - if (ca_tag.kind == Crypto::ASN1::Kind::Boolean) { - PUSH_SCOPE("cA"sv); - READ_OBJECT(Boolean, bool, is_certificate_authority); - constraints.is_certificate_authority = is_certificate_authority; - POP_SCOPE(); - } - - if (decoder.eof()) { - EXIT_SCOPE(); - return constraints; - } - - auto path_length_tag = TRY(decoder.peek()); - if (path_length_tag.kind == Crypto::ASN1::Kind::Integer) { - PUSH_SCOPE("pathLenConstraint"sv); - READ_OBJECT(Integer, Crypto::UnsignedBigInteger, path_length_constraint); - constraints.path_length_constraint = path_length_constraint; - POP_SCOPE(); - } - - EXIT_SCOPE(); - return constraints; -} - -static ErrorOr parse_extension(Crypto::ASN1::Decoder& decoder, Vector current_scope, Certificate& certificate) -{ - // Extension ::= Sequence { - // extension_id ObjectIdentifier, - // critical Boolean DEFAULT false, - // extension_value OctetString (DER-encoded) - // } - ENTER_TYPED_SCOPE(Sequence, "Extension"sv); - - PUSH_SCOPE("extension_id"sv); - READ_OBJECT(ObjectIdentifier, Vector, extension_id); - POP_SCOPE(); - - bool is_critical = false; - auto peek = TRY(decoder.peek()); - if (peek.kind == Crypto::ASN1::Kind::Boolean) { - PUSH_SCOPE("critical"sv); - READ_OBJECT(Boolean, bool, extension_critical); - is_critical = extension_critical; - POP_SCOPE(); - } - - PUSH_SCOPE("extension_value"sv); - READ_OBJECT(OctetString, StringView, extension_value); - POP_SCOPE(); - - bool is_known_extension = false; - - Crypto::ASN1::Decoder extension_decoder { extension_value.bytes() }; - Vector extension_scope {}; - if (extension_id == ASN1::subject_alternative_name_oid) { - is_known_extension = true; - auto alternate_names = TRY(parse_subject_alternative_names(extension_decoder, extension_scope)); - certificate.SAN = alternate_names; - } - - if (extension_id == ASN1::key_usage_oid) { - is_known_extension = true; - auto usage = TRY(parse_key_usage(extension_decoder, extension_scope)); - certificate.is_allowed_to_sign_certificate = usage.get(5); - } - - if (extension_id == ASN1::basic_constraints_oid) { - is_known_extension = true; - auto constraints = TRY(parse_basic_constraints(extension_decoder, extension_scope)); - certificate.is_certificate_authority = constraints.is_certificate_authority; - certificate.path_length_constraint = constraints.path_length_constraint.to_u64(); - } - - if (extension_id == ASN1::issuer_alternative_name_oid) { - is_known_extension = true; - auto alternate_names = TRY(parse_issuer_alternative_names(extension_decoder, extension_scope)); - certificate.IAN = alternate_names; - } - - EXIT_SCOPE(); - - if (is_critical && !is_known_extension) { - ERROR_WITH_SCOPE(TRY(String::formatted("Extension {} is critical, but we do not support it", extension_id))); - } - - if (!is_known_extension) { - dbgln_if(TLS_DEBUG, TRY(String::formatted("{}: Unhandled extension: {}", current_scope, extension_id))); - } - - return {}; -} - -static ErrorOr parse_extensions(Crypto::ASN1::Decoder& decoder, Vector current_scope, Certificate& certificate) -{ - // Extensions ::= Sequence OF Extension - ENTER_TYPED_SCOPE(Sequence, "Extensions"sv); - - while (!decoder.eof()) { - TRY(parse_extension(decoder, current_scope, certificate)); - } - - EXIT_SCOPE(); - - return {}; -} - -static ErrorOr parse_tbs_certificate(Crypto::ASN1::Decoder& decoder, Vector current_scope) -{ - // TBSCertificate ::= SEQUENCE { - // version [0] Version DEFAULT v1, - // serialNumber CertificateSerialNumber, - // signature AlgorithmIdentifier{{SupportedAlgorithms}}, - // issuer Name, - // validity Validity, - // subject Name, - // subjectPublicKeyInfo SubjectPublicKeyInfo, - // issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL, - // ..., - // [[2: -- if present, version shall be v2 or v3 - // subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL]], - // [[3: -- if present, version shall be v2 or v3 - // extensions [3] Extensions OPTIONAL]] - // -- If present, version shall be v3]] - // } - - // Note: Parse out the ASN.1 of this object, since its used for TLS verification. - // To do this, we get the bytes of our parent, the size of ourself, and slice the parent buffer. - auto pre_cert_buffer = TRY(decoder.peek_entry_bytes()); - - // FIXME: Dont assume this value. - // Note: we assume this to be 4. 1 for the tag, and 3 for the length. - auto entry_length_byte_count = 4; - - ENTER_TYPED_SCOPE(Sequence, "TBSCertificate"sv); - - auto post_cert_buffer = TRY(decoder.peek_entry_bytes()); - if (pre_cert_buffer.size() < post_cert_buffer.size() + entry_length_byte_count) { - ERROR_WITH_SCOPE("Unexpected end of file"); - } - - Certificate certificate; - certificate.version = TRY(parse_certificate_version(decoder, current_scope)).to_u64(); - certificate.serial_number = TRY(parse_serial_number(decoder, current_scope)); - certificate.algorithm = TRY(parse_algorithm_identifier(decoder, current_scope)); - certificate.issuer = TRY(parse_name(decoder, current_scope)); - certificate.validity = TRY(parse_validity(decoder, current_scope)); - certificate.subject = TRY(parse_name(decoder, current_scope)); - certificate.public_key = TRY(parse_subject_public_key_info(decoder, current_scope)); - certificate.tbs_asn1 = TRY(ByteBuffer::copy(pre_cert_buffer.slice(0, post_cert_buffer.size() + entry_length_byte_count))); - - if (!decoder.eof()) { - auto tag = TRY(decoder.peek()); - if (static_cast(tag.kind) == 1) { - REWRITE_TAG(BitString) - TRY(parse_unique_identifier(decoder, current_scope)); - } - } - - if (!decoder.eof()) { - auto tag = TRY(decoder.peek()); - if (static_cast(tag.kind) == 2) { - REWRITE_TAG(BitString) - TRY(parse_unique_identifier(decoder, current_scope)); - } - } - - if (!decoder.eof()) { - auto tag = TRY(decoder.peek()); - if (static_cast(tag.kind) == 3) { - REWRITE_TAG(Sequence) - ENTER_TYPED_SCOPE(Sequence, "extensions"sv); - - TRY(parse_extensions(decoder, current_scope, certificate)); - - EXIT_SCOPE(); - } - } - - if (!decoder.eof()) { - ERROR_WITH_SCOPE("Reached end of TBS parse with more data left"sv); - } - - certificate.is_self_issued = TRY(certificate.issuer.to_string()) == TRY(certificate.subject.to_string()); - - EXIT_SCOPE(); - - return certificate; -} - -ErrorOr Certificate::parse_certificate(ReadonlyBytes buffer, bool) -{ - Crypto::ASN1::Decoder decoder { buffer }; - Vector current_scope {}; - - // Certificate ::= SIGNED{TBSCertificate} - - // SIGNED{ToBeSigned} ::= SEQUENCE { - // toBeSigned ToBeSigned, - // COMPONENTS OF SIGNATURE{ToBeSigned}, - // } - - // SIGNATURE{ToBeSigned} ::= SEQUENCE { - // algorithmIdentifier AlgorithmIdentifier{{SupportedAlgorithms}}, - // encrypted ENCRYPTED-HASH{ToBeSigned}, - // } - - // ENCRYPTED-HASH{ToBeSigned} ::= BIT STRING (CONSTRAINED BY { - // -- shall be the result of applying a hashing procedure to the DER-encoded (see 6.2) - // -- octets of a value of -- ToBeSigned -- and then applying an encipherment procedure - // -- to those octets -- } ) - - ENTER_TYPED_SCOPE(Sequence, "Certificate"sv); - - Certificate certificate = TRY(parse_tbs_certificate(decoder, current_scope)); - certificate.original_asn1 = TRY(ByteBuffer::copy(buffer)); - - certificate.signature_algorithm = TRY(parse_algorithm_identifier(decoder, current_scope)); - - PUSH_SCOPE("signature"sv); - READ_OBJECT(BitString, Crypto::ASN1::BitStringView, signature); - certificate.signature_value = TRY(ByteBuffer::copy(TRY(signature.raw_bytes()))); - POP_SCOPE(); - - if (!decoder.eof()) { - ERROR_WITH_SCOPE("Reached end of Certificate parse with more data left"sv); - } - - EXIT_SCOPE(); - - return certificate; -} - -#undef PUSH_SCOPE -#undef ENTER_SCOPE -#undef ENTER_TYPED_SCOPE -#undef POP_SCOPE -#undef EXIT_SCOPE -#undef READ_OBJECT -#undef DROP_OBJECT -#undef REWRITE_TAG - -ErrorOr RelativeDistinguishedName::to_string() const -{ -#define ADD_IF_RECOGNIZED(identifier, shorthand_code) \ - if (member_identifier == identifier) { \ - cert_name.appendff("\\{}={}", shorthand_code, value); \ - continue; \ - } - - StringBuilder cert_name; - - for (auto const& [member_identifier, value] : m_members) { - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::SerialNumber), "SERIALNUMBER"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Email), "MAIL"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Title), "T"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::PostalCode), "PC"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::DnQualifier), "DNQ"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::GivenName), "GIVENNAME"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Surname), "SN"); - - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Cn), "CN"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::L), "L"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::St), "ST"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::O), "O"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Ou), "OU"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::C), "C"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Street), "STREET"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Dc), "DC"); - ADD_IF_RECOGNIZED(enum_value(ASN1::AttributeType::Uid), "UID"); - - cert_name.appendff("\\{}={}", member_identifier, value); - } -#undef ADD_IF_RECOGNIZED - - return cert_name.to_string(); -} - -bool Certificate::is_valid() const -{ - auto now = UnixDateTime::now(); - - if (now < validity.not_before) { - dbgln("certificate expired (not yet valid, signed for {})", Core::DateTime::from_timestamp(validity.not_before.seconds_since_epoch())); - return false; - } - - if (validity.not_after < now) { - dbgln("certificate expired (expiry date {})", Core::DateTime::from_timestamp(validity.not_after.seconds_since_epoch())); - return false; - } - - return true; -} - -// https://www.ietf.org/rfc/rfc5280.html#page-12 -bool Certificate::is_self_signed() -{ - if (m_is_self_signed.has_value()) - return *m_is_self_signed; - - // Self-signed certificates are self-issued certificates where the digital - // signature may be verified by the public key bound into the certificate. - if (!this->is_self_issued) - m_is_self_signed.emplace(false); - - // FIXME: Actually check if we sign ourself - - m_is_self_signed.emplace(true); - return *m_is_self_signed; -} } diff --git a/Libraries/LibCrypto/Certificate/Certificate.h b/Libraries/LibCrypto/Certificate/Certificate.h index e92c91806ff..5317775e5b3 100644 --- a/Libraries/LibCrypto/Certificate/Certificate.h +++ b/Libraries/LibCrypto/Certificate/Certificate.h @@ -6,15 +6,7 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include -#include #include #include @@ -25,126 +17,39 @@ struct AlgorithmIdentifier { { } - explicit AlgorithmIdentifier(Vector identifier) + explicit AlgorithmIdentifier(Vector const& identifier) : identifier(identifier) { } Vector identifier; - Optional> ec_parameters; + Optional> ec_parameters {}; }; ErrorOr> parse_ec_parameters(ASN1::Decoder& decoder, Vector current_scope = {}); -struct BasicConstraints { - bool is_certificate_authority; - Crypto::UnsignedBigInteger path_length_constraint; -}; - -class RelativeDistinguishedName { -public: - ErrorOr to_string() const; - - ErrorOr set(String key, String value) - { - return m_members.try_set(move(key), move(value)); - } - - Optional get(StringView key) const - { - return m_members.get(key); - } - - Optional get(ASN1::AttributeType key) const - { - return m_members.get(enum_value(key)); - } - - Optional get(ASN1::ObjectClass key) const - { - return m_members.get(enum_value(key)); - } - - String common_name() const - { - auto entry = get(ASN1::AttributeType::Cn); - if (entry.has_value()) { - return entry.value(); - } - - return String(); - } - - String organizational_unit() const - { - return get(ASN1::AttributeType::Ou).value_or({}); - } - -private: - HashMap m_members; -}; - -struct Validity { - UnixDateTime not_before; - UnixDateTime not_after; -}; - +// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 class SubjectPublicKey { public: - Crypto::PK::RSAPublicKey rsa; - Crypto::PK::ECPublicKey ec; + PK::RSAPublicKey<> rsa; + PK::ECPublicKey<> ec; AlgorithmIdentifier algorithm; ByteBuffer raw_key; }; -ErrorOr parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector current_scope = {}); +ErrorOr parse_subject_public_key_info(ASN1::Decoder& decoder, Vector current_scope = {}); // https://www.rfc-editor.org/rfc/rfc5208#section-5 class PrivateKey { public: - Crypto::PK::RSAPrivateKey rsa; - Crypto::PK::ECPrivateKey ec; + PK::RSAPrivateKey<> rsa; + PK::ECPrivateKey<> ec; AlgorithmIdentifier algorithm; ByteBuffer raw_key; // FIXME: attributes [0] IMPLICIT Attributes OPTIONAL }; -ErrorOr parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vector current_scope = {}); - -class Certificate { -public: - u16 version { 0 }; - AlgorithmIdentifier algorithm; - SubjectPublicKey public_key; - ByteBuffer exponent {}; - Crypto::PK::RSAPrivateKey private_key {}; - RelativeDistinguishedName issuer, subject; - Validity validity {}; - Vector SAN; - Vector IAN; - u8* ocsp { nullptr }; - Crypto::UnsignedBigInteger serial_number; - ByteBuffer sign_key {}; - ByteBuffer fingerprint {}; - ByteBuffer der {}; - ByteBuffer data {}; - AlgorithmIdentifier signature_algorithm; - ByteBuffer signature_value {}; - ByteBuffer original_asn1 {}; - ByteBuffer tbs_asn1 {}; - bool is_allowed_to_sign_certificate { false }; - bool is_certificate_authority { false }; - Optional path_length_constraint {}; - bool is_self_issued { false }; - - static ErrorOr parse_certificate(ReadonlyBytes, bool client_cert = false); - - bool is_self_signed(); - bool is_valid() const; - -private: - Optional m_is_self_signed; -}; +ErrorOr parse_private_key_info(ASN1::Decoder& decoder, Vector current_scope = {}); } diff --git a/Meta/Lagom/Fuzzers/FuzzASN1.cpp b/Meta/Lagom/Fuzzers/FuzzASN1.cpp index 5c4f34b70e4..b6acfd6eb18 100644 --- a/Meta/Lagom/Fuzzers/FuzzASN1.cpp +++ b/Meta/Lagom/Fuzzers/FuzzASN1.cpp @@ -4,14 +4,17 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include +#include #include #include extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { AK::set_debug_enabled(false); - (void)Crypto::Certificate::Certificate::parse_certificate({ data, size }); + + Crypto::ASN1::Decoder decoder(ReadonlyBytes { data, size }); + while (!decoder.eof()) + (void)decoder.drop(); return 0; } diff --git a/Tests/LibTLS/TestTLSCertificateParser.cpp b/Tests/LibTLS/TestTLSCertificateParser.cpp index 2eca46ca7b2..5de506f3dc0 100644 --- a/Tests/LibTLS/TestTLSCertificateParser.cpp +++ b/Tests/LibTLS/TestTLSCertificateParser.cpp @@ -8,13 +8,6 @@ #include #include -TEST_CASE(certificate_with_malformed_tbscertificate_should_fail_gracefully) -{ - Array invalid_certificate_data { 0xB0, 0x02, 0x70, 0x00 }; - auto parse_result = Crypto::Certificate::Certificate::parse_certificate(invalid_certificate_data); - EXPECT(parse_result.is_error()); -} - TEST_CASE(test_private_key_info_decode) { constexpr auto keyder = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA5HMXMnY+RhEcYXsa"