diff options
author | stelar7 <dudedbz@gmail.com> | 2023-04-03 19:05:01 +0200 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2023-04-06 09:57:31 +0330 |
commit | d527edf0abaca7482f5b89bb1ba058301c85d7c5 (patch) | |
tree | ab042758fbc6b133cfdef385d169c2c95b7fd3b8 /Userland | |
parent | b1d80b35af1d05e0fff8ce3da76f58dd5b5a4fd0 (diff) | |
download | serenity-d527edf0abaca7482f5b89bb1ba058301c85d7c5.zip |
LibTLS: Change Certificate parsing to use ErrorOr
Loads of changes that are tightly connected... :/
* Change lambdas to static functions
* Add spec docs to those functions
* Keep the current scope around as a parameter
* Add wrapping classes for some Certificate members
* Parse ec and ecdsa data from certificates
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Applications/CertificateSettings/CertificateStore.cpp | 33 | ||||
-rw-r--r-- | Userland/Libraries/LibTLS/Certificate.cpp | 1271 | ||||
-rw-r--r-- | Userland/Libraries/LibTLS/Certificate.h | 275 | ||||
-rw-r--r-- | Userland/Libraries/LibTLS/HandshakeCertificate.cpp | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibTLS/HandshakeClient.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibTLS/HandshakeServer.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibTLS/TLSv12.cpp | 38 |
7 files changed, 1060 insertions, 569 deletions
diff --git a/Userland/Applications/CertificateSettings/CertificateStore.cpp b/Userland/Applications/CertificateSettings/CertificateStore.cpp index 0e434bac9a..86a053e5e0 100644 --- a/Userland/Applications/CertificateSettings/CertificateStore.cpp +++ b/Userland/Applications/CertificateSettings/CertificateStore.cpp @@ -5,6 +5,7 @@ */ #include "CertificateStore.h" +#include <AK/String.h> #include <Applications/CertificateSettings/CertificateStoreGML.h> #include <LibCrypto/ASN1/PEM.h> #include <LibFileSystem/FileSystem.h> @@ -66,12 +67,24 @@ GUI::Variant CertificateStoreModel::data(GUI::ModelIndex const& index, GUI::Mode auto cert = m_certificates.at(index.row()); switch (index.column()) { - case Column::IssuedTo: - return cert.subject.subject.is_empty() ? cert.subject.unit : cert.subject.subject; - case Column::IssuedBy: - return cert.issuer.subject.is_empty() ? cert.issuer.unit : cert.issuer.subject; + case Column::IssuedTo: { + auto issued_to = cert.subject.common_name(); + if (issued_to.is_empty()) { + issued_to = cert.subject.organizational_unit(); + } + + return issued_to; + } + case Column::IssuedBy: { + auto issued_by = cert.issuer.common_name(); + if (issued_by.is_empty()) { + issued_by = cert.issuer.organizational_unit(); + } + + return issued_by; + } case Column::Expire: - return cert.not_after.to_deprecated_string("%Y-%m-%d"sv); + return cert.validity.not_after.to_deprecated_string("%Y-%m-%d"sv); default: VERIFY_NOT_REACHED(); } @@ -128,8 +141,14 @@ ErrorOr<void> CertificateStoreWidget::export_pem() auto real_index = m_root_ca_proxy_model->map_to_source(index); auto cert = m_root_ca_model->get(real_index.row()); - auto filename = cert.subject.subject.is_empty() ? cert.subject.unit : cert.subject.subject; - auto file = FileSystemAccessClient::Client::the().save_file(window(), filename.replace(" "sv, "_"sv), "pem"sv); + String filename = cert.subject.common_name(); + if (filename.is_empty()) { + filename = cert.subject.organizational_unit(); + } + + filename = TRY(filename.replace(" "sv, "_"sv, ReplaceMode::All)); + + auto file = FileSystemAccessClient::Client::the().save_file(window(), filename.to_deprecated_string(), "pem"sv); if (file.is_error()) return {}; diff --git a/Userland/Libraries/LibTLS/Certificate.cpp b/Userland/Libraries/LibTLS/Certificate.cpp index 2b117a445f..f87150defd 100644 --- a/Userland/Libraries/LibTLS/Certificate.cpp +++ b/Userland/Libraries/LibTLS/Certificate.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org> + * Copyright (c) 2023, stelar7 <dudedbz@gmail.com> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,536 +11,878 @@ #include <LibCrypto/ASN1/ASN1.h> #include <LibCrypto/ASN1/DER.h> #include <LibCrypto/ASN1/PEM.h> +#include <LibTLS/CipherSuite.h> namespace TLS { -constexpr static Array<int, 4> - common_name_oid { 2, 5, 4, 3 }, - country_name_oid { 2, 5, 4, 6 }, - locality_name_oid { 2, 5, 4, 7 }, - organization_name_oid { 2, 5, 4, 10 }, - organizational_unit_name_oid { 2, 5, 4, 11 }; - constexpr static Array<int, 7> rsa_encryption_oid { 1, 2, 840, 113549, 1, 1, 1 }, rsa_md5_encryption_oid { 1, 2, 840, 113549, 1, 1, 4 }, rsa_sha1_encryption_oid { 1, 2, 840, 113549, 1, 1, 5 }, rsa_sha256_encryption_oid { 1, 2, 840, 113549, 1, 1, 11 }, rsa_sha384_encryption_oid { 1, 2, 840, 113549, 1, 1, 12 }, - rsa_sha512_encryption_oid { 1, 2, 840, 113549, 1, 1, 13 }; + rsa_sha512_encryption_oid { 1, 2, 840, 113549, 1, 1, 13 }, + rsa_sha224_encryption_oid { 1, 2, 840, 113549, 1, 1, 14 }, + ecdsa_with_sha224_encryption_oid { 1, 2, 840, 10045, 4, 3, 1 }, + ecdsa_with_sha256_encryption_oid { 1, 2, 840, 10045, 4, 3, 2 }, + ecdsa_with_sha384_encryption_oid { 1, 2, 840, 10045, 4, 3, 3 }, + ecdsa_with_sha512_encryption_oid { 1, 2, 840, 10045, 4, 3, 3 }, + ec_public_key_encryption_oid { 1, 2, 840, 10045, 2, 1 }; + +constexpr static Array<Array<int, 7>, 9> known_algorithm_identifiers { + rsa_encryption_oid, + rsa_md5_encryption_oid, + rsa_sha1_encryption_oid, + rsa_sha256_encryption_oid, + rsa_sha384_encryption_oid, + rsa_sha512_encryption_oid, + ecdsa_with_sha256_encryption_oid, + ecdsa_with_sha384_encryption_oid, + ec_public_key_encryption_oid +}; + +constexpr static Array<int, 7> + curve_ansip384r1 { 1, 3, 132, 0, 34 }, + curve_prime256 { 1, 2, 840, 10045, 3, 1, 7 }; + +constexpr static Array<Array<int, 7>, 9> known_curve_identifiers { + curve_ansip384r1, + curve_prime256 +}; constexpr static Array<int, 4> key_usage_oid { 2, 5, 29, 15 }, subject_alternative_name_oid { 2, 5, 29, 17 }, + issuer_alternative_name_oid { 2, 5, 29, 18 }, basic_constraints_oid { 2, 5, 29, 19 }; -Optional<Certificate> Certificate::parse_asn1(ReadonlyBytes buffer, bool) -{ -#define ENTER_SCOPE_WITHOUT_TYPECHECK(scope) \ - do { \ - if (auto result = decoder.enter(); result.is_error()) { \ - dbgln_if(TLS_DEBUG, "Failed to enter object (" scope "): {}", result.error()); \ - return {}; \ - } \ +#define ERROR_WITH_SCOPE(error) \ + do { \ + return Error::from_string_view(TRY(String::formatted("{}: {}", current_scope, error))); \ } while (0) -#define ENTER_SCOPE_OR_FAIL(kind_name, scope) \ - do { \ - if (auto tag = decoder.peek(); tag.is_error() || tag.value().kind != Crypto::ASN1::Kind::kind_name) { \ - if constexpr (TLS_DEBUG) { \ - if (tag.is_error()) \ - dbgln(scope " data was invalid: {}", tag.error()); \ - else \ - dbgln(scope " data was not of kind " #kind_name); \ - } \ - return {}; \ - } \ - ENTER_SCOPE_WITHOUT_TYPECHECK(scope); \ +#define ENTER_TYPED_SCOPE(tag_kind_name, scope) \ + do { \ + if (auto tag = decoder.peek(); tag.is_error() || tag.value().kind != Crypto::ASN1::Kind::tag_kind_name) { \ + if (tag.is_error()) \ + ERROR_WITH_SCOPE(TRY(String::formatted(scope " data was invalid: {}", tag.error()))); \ + else \ + ERROR_WITH_SCOPE(TRY(String::formatted(scope " data was not of kind " #tag_kind_name " was {}", Crypto::ASN1::kind_name(tag.value().kind)))); \ + } \ + ENTER_SCOPE(scope); \ } while (0) -#define EXIT_SCOPE(scope) \ - do { \ - if (auto error = decoder.leave(); error.is_error()) { \ - dbgln_if(TLS_DEBUG, "Error while exiting scope " scope ": {}", error.error()); \ - return {}; \ - } \ +#define ENTER_SCOPE(scope) \ + do { \ + if (auto result = decoder.enter(); result.is_error()) { \ + ERROR_WITH_SCOPE(TRY(String::formatted("Failed to enter scope: {}", scope))); \ + } \ + PUSH_SCOPE(scope) \ } while (0) -#define ENSURE_OBJECT_KIND(_kind_name, scope) \ - do { \ - if (auto tag = decoder.peek(); tag.is_error() || tag.value().kind != Crypto::ASN1::Kind::_kind_name) { \ - if constexpr (TLS_DEBUG) { \ - if (tag.is_error()) \ - dbgln(scope " data was invalid: {}", tag.error()); \ - else \ - dbgln(scope " data was not of kind " #_kind_name ", it was {}", Crypto::ASN1::kind_name(tag.value().kind)); \ - } \ - return {}; \ - } \ +#define PUSH_SCOPE(scope) current_scope.append(#scope##sv); + +#define EXIT_SCOPE() \ + do { \ + if (auto error = decoder.leave(); error.is_error()) { \ + ERROR_WITH_SCOPE(TRY(String::formatted("Failed to exit scope: {}", error.error()))); \ + } \ + POP_SCOPE(); \ } while (0) -#define READ_OBJECT_OR_FAIL(kind_name, type_name, value_name, scope) \ - auto value_name##_result = decoder.read<type_name>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::kind_name); \ - if (value_name##_result.is_error()) { \ - dbgln_if(TLS_DEBUG, scope " read of kind " #kind_name " failed: {}", value_name##_result.error()); \ - return {}; \ - } \ +#define POP_SCOPE() current_scope.remove(current_scope.size() - 1); + +#define READ_OBJECT(kind_name, type_name, value_name) \ + auto value_name##_result = decoder.read<type_name>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::kind_name); \ + if (value_name##_result.is_error()) { \ + ERROR_WITH_SCOPE(TRY(String::formatted("Read of kind " #kind_name " failed: {}", value_name##_result.error()))); \ + } \ auto value_name = value_name##_result.release_value(); -#define DROP_OBJECT_OR_FAIL(scope) \ - do { \ - if (auto error = decoder.drop(); error.is_error()) { \ - dbgln_if(TLS_DEBUG, scope " read failed: {}", error.error()); \ - } \ +#define REWRITE_TAG(kind_name) \ + auto value_name##_result = decoder.rewrite_tag(Crypto::ASN1::Kind::kind_name); \ + if (value_name##_result.is_error()) { \ + ERROR_WITH_SCOPE(TRY(String::formatted("Rewrite of kind " #kind_name " failed: {}", value_name##_result.error()))); \ + } + +#define DROP_OBJECT() \ + do { \ + if (auto error = decoder.drop(); error.is_error()) { \ + ERROR_WITH_SCOPE(TRY(String::formatted("Drop failed: {}", error.error()))); \ + } \ } while (0) - Certificate certificate; - auto copy_buffer_result = ByteBuffer::copy(buffer.data(), buffer.size()); - if (copy_buffer_result.is_error()) - return {}; - certificate.original_asn1 = copy_buffer_result.release_value(); +static ErrorOr<NamedCurve> oid_to_curve(Vector<int> curve) +{ + if (curve == curve_ansip384r1) + return NamedCurve::secp384r1; + else if (curve == curve_prime256) + return NamedCurve::secp256r1; - Crypto::ASN1::Decoder decoder { buffer }; - // Certificate ::= Sequence { - // certificate TBSCertificate, - // signature_algorithm AlgorithmIdentifier, - // signature_value BitString - // } - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate"); - - // TBSCertificate ::= Sequence { - // version (0) EXPLICIT Version DEFAULT v1, - // serial_number CertificateSerialNumber, - // signature AlgorithmIdentifier, - // issuer Name, - // validity Validity, - // subject Name, - // subject_public_key_info SubjectPublicKeyInfo, - // issuer_unique_id (1) IMPLICIT UniqueIdentifier OPTIONAL (if present, version > v1), - // subject_unique_id (2) IMPLICIT UniqueIdentifier OPTIONAL (if present, version > v1), - // extensions (3) EXPLICIT Extensions OPTIONAL (if present, version > v2) + return Error::from_string_view(TRY(String::formatted("Unknown curve oid {}", curve))); +} + +static ErrorOr<CertificateKeyAlgorithm> oid_to_algorithm(Vector<int> algorithm) +{ + if (algorithm == rsa_encryption_oid) + return CertificateKeyAlgorithm::RSA_RSA; + else if (algorithm == rsa_md5_encryption_oid) + return CertificateKeyAlgorithm::RSA_MD5; + else if (algorithm == rsa_sha1_encryption_oid) + return CertificateKeyAlgorithm::RSA_SHA1; + else if (algorithm == rsa_sha256_encryption_oid) + return CertificateKeyAlgorithm::RSA_SHA256; + else if (algorithm == rsa_sha384_encryption_oid) + return CertificateKeyAlgorithm::RSA_SHA384; + else if (algorithm == rsa_sha512_encryption_oid) + return CertificateKeyAlgorithm::RSA_SHA512; + else if (algorithm == rsa_sha224_encryption_oid) + return CertificateKeyAlgorithm::RSA_SHA224; + else if (algorithm == ecdsa_with_sha224_encryption_oid) + return CertificateKeyAlgorithm::ECDSA_SHA224; + else if (algorithm == ecdsa_with_sha256_encryption_oid) + return CertificateKeyAlgorithm::ECDSA_SHA256; + else if (algorithm == ecdsa_with_sha384_encryption_oid) + return CertificateKeyAlgorithm::ECDSA_SHA384; + else if (algorithm == ecdsa_with_sha512_encryption_oid) + return CertificateKeyAlgorithm::ECDSA_SHA512; + + return Error::from_string_view(TRY(String::formatted("Unknown algorithm oid {}", algorithm))); +} + +static ErrorOr<Crypto::UnsignedBigInteger> parse_version(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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<Crypto::UnsignedBigInteger> parse_serial_number(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // CertificateSerialNumber ::= INTEGER + PUSH_SCOPE("CertificateSerialNumber"sv); + READ_OBJECT(Integer, Crypto::UnsignedBigInteger, serial); + POP_SCOPE(); + return serial; +} + +static ErrorOr<NamedCurve> parse_ec_parameters(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // ECParameters ::= CHOICE { + // namedCurve OBJECT IDENTIFIER // } - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate"); - - // version - { - // Version :: Integer { v1(0), v2(1), v3(2) } (Optional) - if (auto tag = decoder.peek(); !tag.is_error() && tag.value().type == Crypto::ASN1::Type::Constructed) { - ENTER_SCOPE_WITHOUT_TYPECHECK("Certificate::version"); - READ_OBJECT_OR_FAIL(Integer, Crypto::UnsignedBigInteger, value, "Certificate::version"); - if (!(value < 3)) { - dbgln_if(TLS_DEBUG, "Certificate::version Invalid value for version: {}", value.to_base_deprecated(10)); - return {}; - } - certificate.version = value.words()[0]; - EXIT_SCOPE("Certificate::version"); - } else { - certificate.version = 0; + PUSH_SCOPE("ECParameters"sv); + READ_OBJECT(ObjectIdentifier, Vector<int>, named_curve); + // Note: namedCurve sometimes has 5 nodes, but we need 7 for the comparison below to work. + while (named_curve.size() < 7) { + named_curve.append(0); + } + POP_SCOPE(); + + bool is_known_curve = false; + for (auto const& curves : known_curve_identifiers) { + if (curves.span() == named_curve.span()) { + is_known_curve = true; + break; + } + } + + if (!is_known_curve) { + ERROR_WITH_SCOPE(TRY(String::formatted("Unknown named curve {}", named_curve))); + } + + return oid_to_curve(named_curve); +} + +static ErrorOr<CertificateKeyAlgorithm> parse_algorithm_identifier(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // AlgorithmIdentifier{ALGORITHM:SupportedAlgorithms} ::= SEQUENCE { + // algorithm ALGORITHM.&id({SupportedAlgorithms}), + // parameters ALGORITHM.&Type({SupportedAlgorithms}{@algorithm}) OPTIONAL, + // ... } + ENTER_TYPED_SCOPE(Sequence, "AlgorithmIdentifier"sv); + PUSH_SCOPE("algorithm"sv); + READ_OBJECT(ObjectIdentifier, Vector<int>, algorithm); + // Note: ecPublicKey only has 6 nodes, but we need 7 for the comparison below to work. + while (algorithm.size() < 7) { + algorithm.append(0); + } + POP_SCOPE(); + + bool is_known_algorithm = false; + for (auto const& inner : known_algorithm_identifiers) { + if (inner.span() == algorithm.span()) { + is_known_algorithm = true; + break; } } - // serial_number - { - // CertificateSerialNumber :: Integer - READ_OBJECT_OR_FAIL(Integer, Crypto::UnsignedBigInteger, value, "Certificate::serial_number"); - certificate.serial_number = move(value); - } - - auto parse_algorithm_identifier = [&](CertificateKeyAlgorithm& field) -> Optional<bool> { - // AlgorithmIdentifier ::= Sequence { - // algorithm ObjectIdentifier, - // parameters ANY OPTIONAL - // } - ENTER_SCOPE_OR_FAIL(Sequence, "AlgorithmIdentifier"); - READ_OBJECT_OR_FAIL(ObjectIdentifier, Vector<int>, identifier, "AlgorithmIdentifier::algorithm"); - if (identifier == rsa_encryption_oid) - field = CertificateKeyAlgorithm ::RSA_RSA; - else if (identifier == rsa_md5_encryption_oid) - field = CertificateKeyAlgorithm ::RSA_MD5; - else if (identifier == rsa_sha1_encryption_oid) - field = CertificateKeyAlgorithm ::RSA_SHA1; - else if (identifier == rsa_sha256_encryption_oid) - field = CertificateKeyAlgorithm ::RSA_SHA256; - else if (identifier == rsa_sha384_encryption_oid) - field = CertificateKeyAlgorithm ::RSA_SHA384; - else if (identifier == rsa_sha512_encryption_oid) - field = CertificateKeyAlgorithm ::RSA_SHA512; - else - return {}; - - EXIT_SCOPE("AlgorithmIdentifier"); - return true; + if (!is_known_algorithm) { + ERROR_WITH_SCOPE(TRY(String::formatted("Unknown algorithm {}", algorithm))); + } + + // -- When the following OIDs are used in an AlgorithmIdentifier, the + // -- parameters MUST be present and MUST be NULL. + // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } + // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } + // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } + // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } + // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } + // sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } + // sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 } + Array<Array<int, 7>, 8> rsa_null_algorithms = { + rsa_encryption_oid, + rsa_md5_encryption_oid, + rsa_sha1_encryption_oid, + rsa_sha256_encryption_oid, + rsa_sha384_encryption_oid, + rsa_sha512_encryption_oid, + rsa_sha224_encryption_oid, }; - // signature - { - if (!parse_algorithm_identifier(certificate.algorithm).has_value()) - return {}; + bool is_rsa_null_algorithm = false; + for (auto const& inner : rsa_null_algorithms) { + if (inner.span() == algorithm.span()) { + is_rsa_null_algorithm = true; + break; + } + } + + if (is_rsa_null_algorithm) { + PUSH_SCOPE("RSA null parameter"sv); + READ_OBJECT(Null, void*, forced_null); + (void)forced_null; + POP_SCOPE(); + + EXIT_SCOPE(); + return oid_to_algorithm(algorithm); + } + + // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or + // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field + // as an AlgorithmIdentifier, the encoding MUST omit the parameters + // field. + Array<Array<int, 7>, 8> no_parameter_algorithms = { + ecdsa_with_sha224_encryption_oid, + ecdsa_with_sha256_encryption_oid, + ecdsa_with_sha384_encryption_oid, + ecdsa_with_sha512_encryption_oid, + }; + + bool is_no_parameter_algorithm = false; + for (auto const& inner : no_parameter_algorithms) { + if (inner.span() == algorithm.span()) { + is_no_parameter_algorithm = true; + } + } + + if (is_no_parameter_algorithm) { + EXIT_SCOPE(); + + return oid_to_algorithm(algorithm); } - auto parse_name = [&](auto& name_struct) -> Optional<bool> { - // Name ::= Choice { - // rdn_sequence RDNSequence - // } // NOTE: since this is the only alternative, there's no index - // RDNSequence ::= Sequence OF RelativeDistinguishedName - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::issuer/subject"); + if (algorithm.span() == ec_public_key_encryption_oid.span()) { + // The parameters associated with id-ecPublicKey SHOULD be absent or ECParameters, + // and NULL is allowed to support legacy implementations. + if (decoder.eof()) { + EXIT_SCOPE(); + return oid_to_algorithm(algorithm); + } + + auto tag = TRY(decoder.peek()); + if (tag.kind == Crypto::ASN1::Kind::Null) { + PUSH_SCOPE("ecPublicKey null parameter"sv); + READ_OBJECT(Null, void*, forced_null); + (void)forced_null; + POP_SCOPE(); + + EXIT_SCOPE(); + return oid_to_algorithm(algorithm); + } + + auto ec_parameters = TRY(parse_ec_parameters(decoder, current_scope)); + EXIT_SCOPE(); + + if (ec_parameters == NamedCurve::secp256r1) + return CertificateKeyAlgorithm::ECDSA_SECP256R1; + else if (ec_parameters == NamedCurve::secp384r1) + return CertificateKeyAlgorithm::ECDSA_SECP384R1; + } + + ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled parameters for algorithm {}", algorithm))); +} + +static ErrorOr<RelativeDistinguishedName> parse_name(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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 - // AttributeTypeAndValue ::= Sequence { - // type AttributeType, - // value AttributeValue - // } - // AttributeType ::= ObjectIdentifier - // AttributeValue ::= Any + ENTER_TYPED_SCOPE(Set, "RDNSequence"sv); while (!decoder.eof()) { - // Parse only the required fields, and ignore the rest. - ENTER_SCOPE_OR_FAIL(Set, "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName"); - while (!decoder.eof()) { - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue"); - ENSURE_OBJECT_KIND(ObjectIdentifier, "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::type"); - - if (auto type_identifier_or_error = decoder.read<Vector<int>>(); !type_identifier_or_error.is_error()) { - // Figure out what type of identifier this is - auto& identifier = type_identifier_or_error.value(); - if (identifier == common_name_oid) { - READ_OBJECT_OR_FAIL(PrintableString, StringView, name, - "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value"); - name_struct.subject = name; - } else if (identifier == country_name_oid) { - READ_OBJECT_OR_FAIL(PrintableString, StringView, name, - "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value"); - name_struct.country = name; - } else if (identifier == locality_name_oid) { - READ_OBJECT_OR_FAIL(PrintableString, StringView, name, - "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value"); - name_struct.location = name; - } else if (identifier == organization_name_oid) { - READ_OBJECT_OR_FAIL(PrintableString, StringView, name, - "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value"); - name_struct.entity = name; - } else if (identifier == organizational_unit_name_oid) { - READ_OBJECT_OR_FAIL(PrintableString, StringView, name, - "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value"); - name_struct.unit = name; - } - } else { - dbgln_if(TLS_DEBUG, "Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::type data was invalid: {}", type_identifier_or_error.error()); - return {}; - } - - EXIT_SCOPE("Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue"); - } - EXIT_SCOPE("Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName"); + // AttributeTypeAndValue ::= Sequence { + // type AttributeType, + // value AttributeValue + // } + ENTER_TYPED_SCOPE(Sequence, "AttributeTypeAndValue"sv); + // AttributeType ::= ObjectIdentifier + PUSH_SCOPE("AttributeType"sv) + READ_OBJECT(ObjectIdentifier, Vector<int>, 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(attribute_type_string, attribute_value_string)); + + EXIT_SCOPE(); } + EXIT_SCOPE(); + } + EXIT_SCOPE(); - EXIT_SCOPE("Certificate::TBSCertificate::issuer/subject"); - return true; - }; + return rdn; +} - // issuer - { - if (!parse_name(certificate.issuer).has_value()) - return {}; +static ErrorOr<Core::DateTime> parse_time(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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(); } - // validity - { - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::Validity"); + if (tag.kind == Crypto::ASN1::Kind::GeneralizedTime) { + PUSH_SCOPE("GeneralizedTime"sv); - auto parse_time = [&](Core::DateTime& datetime) -> Optional<bool> { - // Time ::= Choice { - // utc_time UTCTime, - // general_time GeneralizedTime - // } - auto tag = decoder.peek(); - if (tag.is_error()) { - dbgln_if(1, "Certificate::TBSCertificate::Validity::$::Time failed to read tag: {}", tag.error()); - return {}; - }; - - if (tag.value().kind == Crypto::ASN1::Kind::UTCTime) { - READ_OBJECT_OR_FAIL(UTCTime, StringView, time, "Certificate::TBSCertificate::Validity::$"); - auto result = Crypto::ASN1::parse_utc_time(time); - if (!result.has_value()) { - dbgln_if(1, "Certificate::TBSCertificate::Validity::$::Time Invalid UTC Time: {}", time); - return {}; - } - datetime = result.release_value(); - return true; - } - - if (tag.value().kind == Crypto::ASN1::Kind::GeneralizedTime) { - READ_OBJECT_OR_FAIL(UTCTime, StringView, time, "Certificate::TBSCertificate::Validity::$"); - auto result = Crypto::ASN1::parse_generalized_time(time); - if (!result.has_value()) { - dbgln_if(1, "Certificate::TBSCertificate::Validity::$::Time Invalid Generalized Time: {}", time); - return {}; - } - datetime = result.release_value(); - return true; - } - - dbgln_if(1, "Unrecognised Time format {}", Crypto::ASN1::kind_name(tag.value().kind)); - return {}; - }; - - if (!parse_time(certificate.not_before).has_value()) - return {}; - - if (!parse_time(certificate.not_after).has_value()) - return {}; - - EXIT_SCOPE("Certificate::TBSCertificate::Validity"); - } - - // subject - { - if (!parse_name(certificate.subject).has_value()) - return {}; - } - - // subject_public_key_info - { - // SubjectPublicKeyInfo ::= Sequence { - // algorithm AlgorithmIdentifier, - // subject_public_key BitString - // } - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::subject_public_key_info"); - - if (!parse_algorithm_identifier(certificate.key_algorithm).has_value()) - return {}; - - READ_OBJECT_OR_FAIL(BitString, Crypto::ASN1::BitStringView, value, "Certificate::TBSCertificate::subject_public_key_info::subject_public_key_info"); - // Note: Once we support other kinds of keys, make sure to check the kind here! + 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<Validity> parse_validity(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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; +} + +static ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // SubjectPublicKeyInfo ::= Sequence { + // algorithm AlgorithmIdentifier, + // subject_public_key BitString + // } + + SubjectPublicKey public_key; + ENTER_TYPED_SCOPE(Sequence, "SubjectPublicKeyInfo"sv); + + public_key.algorithm = TRY(parse_algorithm_identifier(decoder, current_scope)); + + PUSH_SCOPE("subjectPublicKey"sv); + READ_OBJECT(BitString, Crypto::ASN1::BitStringView, value); + POP_SCOPE(); + + switch (public_key.algorithm) { + case CertificateKeyAlgorithm::ECDSA_SECP256R1: + case CertificateKeyAlgorithm::ECDSA_SECP384R1: { + public_key.raw_key = TRY(ByteBuffer::copy(value.raw_bytes())); + break; + } + case CertificateKeyAlgorithm::RSA_RSA: { + public_key.raw_key = TRY(ByteBuffer::copy(value.raw_bytes())); auto key = Crypto::PK::RSA::parse_rsa_key(value.raw_bytes()); if (!key.public_key.length()) { - dbgln_if(TLS_DEBUG, "Certificate::TBSCertificate::subject_public_key_info::subject_public_key_info: Invalid key"); - return {}; + return Error::from_string_literal("Invalid RSA key"); } - certificate.public_key = move(key.public_key); - EXIT_SCOPE("Certificate::TBSCertificate::subject_public_key_info"); + + public_key.rsa = move(key.public_key); + break; + } + default: { + ERROR_WITH_SCOPE(TRY(String::formatted("Unknown algorithm {}", static_cast<u8>(public_key.algorithm)))); + } } - auto parse_unique_identifier = [&]() -> Optional<bool> { - if (certificate.version == 0) - return true; + EXIT_SCOPE(); + return public_key; +} - auto tag = decoder.peek(); - if (tag.is_error()) { - dbgln_if(TLS_DEBUG, "Certificate::TBSCertificate::*::UniqueIdentifier could not read tag: {}", tag.error()); - return {}; - } +static ErrorOr<Crypto::ASN1::BitStringView> parse_unique_identifier(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // UniqueIdentifier ::= BIT STRING + PUSH_SCOPE("UniqueIdentifier"sv); + READ_OBJECT(BitString, Crypto::ASN1::BitStringView, value); + POP_SCOPE(); - // The spec says to just ignore these. - if (static_cast<u8>(tag.value().kind) == 1 || static_cast<u8>(tag.value().kind) == 2) - DROP_OBJECT_OR_FAIL("UniqueIdentifier"); + return value; +} - return true; - }; +static ErrorOr<String> parse_general_name(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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<u8>(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<int>, identifier); + POP_SCOPE(); + return String::join("."sv, identifier); + } + default: + ERROR_WITH_SCOPE("Unknown tag in GeneralNames choice"sv); + } - // issuer_unique_identifier - { - if (!parse_unique_identifier().has_value()) - return {}; - } - - // subject_unique_identifier - { - if (!parse_unique_identifier().has_value()) - return {}; - } - - // self issued - { - certificate.is_self_issued = certificate.issuer_identifier_string() == certificate.subject_identifier_string(); - } - - // extensions - { - if (certificate.version == 2) { - auto tag = decoder.peek(); - if (tag.is_error()) { - dbgln_if(TLS_DEBUG, "Certificate::TBSCertificate::*::UniqueIdentifier could not read tag: {}", tag.error()); - return {}; - } - if (static_cast<u8>(tag.value().kind) == 3) { - // Extensions ::= Sequence OF Extension - // Extension ::= Sequence { - // extension_id ObjectIdentifier, - // critical Boolean DEFAULT false, - // extension_value OctetString (DER-encoded) - // } - ENTER_SCOPE_WITHOUT_TYPECHECK("Certificate::TBSCertificate::Extensions(IMPLICIT)"); - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::Extensions"); - - while (!decoder.eof()) { - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::Extensions::$::Extension"); - READ_OBJECT_OR_FAIL(ObjectIdentifier, Vector<int>, extension_id, "Certificate::TBSCertificate::Extensions::$::Extension::extension_id"); - bool is_critical = false; - if (auto tag = decoder.peek(); !tag.is_error() && tag.value().kind == Crypto::ASN1::Kind::Boolean) { - // Read the 'critical' property - READ_OBJECT_OR_FAIL(Boolean, bool, critical, "Certificate::TBSCertificate::Extensions::$::Extension::critical"); - is_critical = critical; - } - READ_OBJECT_OR_FAIL(OctetString, StringView, extension_value, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value"); - - // Figure out what this extension is. - if (extension_id == subject_alternative_name_oid) { - Crypto::ASN1::Decoder decoder { extension_value.bytes() }; - // SubjectAlternativeName ::= GeneralNames - // GeneralNames ::= Sequence OF GeneralName - // GeneralName ::= CHOICE { - // other_name (0) OtherName, - // rfc_822_name (1) IA5String, - // dns_name (2) IA5String, - // x400Address (3) ORAddress, - // directory_name (4) Name, - // edi_party_name (5) EDIPartyName, - // uri (6) IA5String, - // ip_address (7) OctetString, - // registered_id (8) ObjectIdentifier, - // } - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName"); - - while (!decoder.eof()) { - auto tag = decoder.peek(); - if (tag.is_error()) { - dbgln_if(TLS_DEBUG, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$ could not read tag: {}", tag.error()); - return {}; - } - - auto tag_value = static_cast<u8>(tag.value().kind); - switch (tag_value) { - case 0: - // OtherName - // We don't know how to use this. - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::OtherName"); - break; - case 1: - // RFC 822 name - // We don't know how to use this. - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::RFC822Name"); - break; - case 2: { - // DNS Name - READ_OBJECT_OR_FAIL(IA5String, StringView, name, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::Name"); - certificate.SAN.append(name); - break; - } - case 3: - // x400Address - // We don't know how to use this. - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::X400Address"); - break; - case 4: - // Directory name - // We don't know how to use this. - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::DirectoryName"); - break; - case 5: - // edi party name - // We don't know how to use this. - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::EDIPartyName"); - break; - case 6: { - // URI - READ_OBJECT_OR_FAIL(IA5String, StringView, name, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::URI"); - certificate.SAN.append(name); - break; - } - case 7: { - // IP Address - READ_OBJECT_OR_FAIL(OctetString, StringView, ip_addr_sv, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::IPAddress"); - IPv4Address ip_addr { ip_addr_sv.bytes().data() }; - certificate.SAN.append(ip_addr.to_deprecated_string()); - break; - } - case 8: - // Registered ID - // We can't handle these. - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::RegisteredID"); - break; - default: - dbgln_if(TLS_DEBUG, "Unknown tag in SAN choice {}", tag_value); - if (is_critical) - return {}; - else - DROP_OBJECT_OR_FAIL("Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::???"); - } - } - } else if (extension_id == key_usage_oid) { - // RFC5280 section 4.2.1.3: The keyCertSign bit is asserted when the subject public key is used - // for verifying signatures on public key certificates. If the keyCertSign bit is asserted, - // then the cA bit in the basic constraints extension MUST also be asserted. - Crypto::ASN1::Decoder decoder { extension_value.bytes() }; - READ_OBJECT_OR_FAIL(BitString, Crypto::ASN1::BitStringView, usage, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::KeyUsage"); - - // keyCertSign (5) - certificate.is_allowed_to_sign_certificate = usage.get(5); - } else if (extension_id == basic_constraints_oid) { - // RFC5280 section 4.2.1.9: The cA boolean indicates whether the certified public key may be - // used to verify certificate signatures. If the cA boolean is not asserted, then the keyCertSign - // bit in the key usage extension MUST NOT be asserted. If the basic constraints extension is - // not present in a version 3 certificate, or the extension is present but the cA boolean is - // not asserted, then the certified public key MUST NOT be used to verify certificate signatures. - Crypto::ASN1::Decoder decoder { extension_value.bytes() }; - ENTER_SCOPE_OR_FAIL(Sequence, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::BasicConstraints"); - - if (auto tag = decoder.peek(); !tag.is_error() && tag.value().kind == Crypto::ASN1::Kind::Boolean) { - READ_OBJECT_OR_FAIL(Boolean, bool, is_certificate_authority, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::BasicConstraints::cA"); - certificate.is_certificate_authority = is_certificate_authority; - - if (auto tag = decoder.peek(); !tag.is_error() && tag.value().kind == Crypto::ASN1::Kind::Integer) { - READ_OBJECT_OR_FAIL(Integer, Crypto::UnsignedBigInteger, path_length_constraint, "Certificate::TBSCertificate::Extensions::$::Extension::extension_value::BasicConstraints::pathLenConstraint"); - certificate.path_length_constraint = path_length_constraint.to_u64(); - } - } - } else { - dbgln_if(TLS_DEBUG, "Certificate::TBSCertificate::Extensions::$::Extension::extension_id: unknown extension {} (critical: {})", extension_id, is_critical); - if (is_critical) - return {}; - } - - EXIT_SCOPE("Certificate::TBSCertificate::Extensions::$::Extension"); - } - - EXIT_SCOPE("Certificate::TBSCertificate::Extensions"); - EXIT_SCOPE("Certificate::TBSCertificate::Extensions(IMPLICIT)"); - } - } + ERROR_WITH_SCOPE("Unknown tag in GeneralNames choice"sv); +} + +static ErrorOr<Vector<String>> parse_general_names(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // GeneralNames ::= Sequence OF GeneralName + ENTER_TYPED_SCOPE(Sequence, "GeneralNames"); + + Vector<String> names; + while (!decoder.eof()) { + names.append(TRY(parse_general_name(decoder, current_scope))); + } + + EXIT_SCOPE(); + + return names; +} + +static ErrorOr<Vector<String>> parse_subject_alternative_names(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // SubjectAlternativeName ::= GeneralNames + PUSH_SCOPE("SubjectAlternativeName"sv); + auto values = TRY(parse_general_names(decoder, current_scope)); + POP_SCOPE(); + + return values; +} + +static ErrorOr<Vector<String>> parse_issuer_alternative_names(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope) +{ + // issuerAltName ::= GeneralNames + PUSH_SCOPE("issuerAltName"sv); + auto values = TRY(parse_general_names(decoder, current_scope)); + POP_SCOPE(); + + return values; +} + +static ErrorOr<Crypto::ASN1::BitStringView> parse_key_usage(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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<BasicConstraints> parse_basic_constraints(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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<void> parse_extension(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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<int>, 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<StringView, 8> extension_scope {}; + if (extension_id == 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 == 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 == 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(); } - EXIT_SCOPE("Certificate::TBSCertificate"); + if (extension_id == 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<void> parse_extensions(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope, Certificate& certificate) +{ + // Extensions ::= Sequence OF Extension + ENTER_TYPED_SCOPE(Sequence, "Extensions"sv); - // signature_algorithm - { - if (!parse_algorithm_identifier(certificate.signature_algorithm).has_value()) - return {}; + while (!decoder.eof()) { + TRY(parse_extension(decoder, current_scope, certificate)); } - // signature_value - { - READ_OBJECT_OR_FAIL(BitString, Crypto::ASN1::BitStringView, value, "Certificate"); - auto signature_data_result = ByteBuffer::copy(value.raw_bytes()); - if (signature_data_result.is_error()) { - dbgln("Certificate::signature_value: out of memory"); - return {}; + EXIT_SCOPE(); + + return {}; +} + +static ErrorOr<Certificate> parse_tbs_certificate(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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]] + // } + + ENTER_TYPED_SCOPE(Sequence, "TBSCertificate"sv); + + Certificate certificate; + certificate.version = TRY(parse_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)); + + if (!decoder.eof()) { + auto tag = TRY(decoder.peek()); + if (static_cast<u8>(tag.kind) == 1) { + REWRITE_TAG(BitString) + TRY(parse_unique_identifier(decoder, current_scope)); + } + } + + if (!decoder.eof()) { + auto tag = TRY(decoder.peek()); + if (static_cast<u8>(tag.kind) == 2) { + REWRITE_TAG(BitString) + TRY(parse_unique_identifier(decoder, current_scope)); } - certificate.signature_value = signature_data_result.release_value(); } - EXIT_SCOPE("Certificate"); + if (!decoder.eof()) { + auto tag = TRY(decoder.peek()); + if (static_cast<u8>(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); + } - dbgln_if(TLS_DEBUG, "Certificate issued for {} by {}", certificate.subject.subject, certificate.issuer.subject); + certificate.is_self_issued = TRY(certificate.issuer.to_string()) == TRY(certificate.subject.to_string()); + + EXIT_SCOPE(); return certificate; +} -#undef DROP_OBJECT_OR_FAIL -#undef ENSURE_OBJECT_KIND -#undef ENTER_SCOPE_OR_FAIL -#undef ENTER_SCOPE_WITHOUT_TYPECHECK -#undef EXIT_SCOPE -#undef READ_OBJECT_OR_FAIL +ErrorOr<Certificate> Certificate::parse_certificate(ReadonlyBytes buffer, bool) +{ + Crypto::ASN1::Decoder decoder { buffer }; + Vector<StringView, 8> 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)); + + CertificateKeyAlgorithm signature_algorithm = TRY(parse_algorithm_identifier(decoder, current_scope)); + certificate.signature_algorithm = signature_algorithm; + + PUSH_SCOPE("signature"sv); + READ_OBJECT(BitString, Crypto::ASN1::BitStringView, signature); + certificate.signature_value = TRY(ByteBuffer::copy(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<String> RelativeDistinguishedName::to_string() +{ +#define ADD_IF_RECOGNIZED(identifier, shorthand_code) \ + do { \ + if (it->key == identifier) { \ + cert_name.appendff("\\{}={}", shorthand_code, it->value); \ + continue; \ + } \ + } while (0); + + StringBuilder cert_name; + + for (auto it = m_members.begin(); it != m_members.end(); ++it) { + ADD_IF_RECOGNIZED(enum_value(AttributeType::SerialNumber), "SERIALNUMBER"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Email), "MAIL"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Title), "T"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::PostalCode), "PC"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::DnQualifier), "DNQ"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::GivenName), "GIVENNAME"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Surname), "SN"); + + ADD_IF_RECOGNIZED(enum_value(AttributeType::Cn), "CN"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::L), "L"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::St), "ST"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::O), "O"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Ou), "OU"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::C), "C"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Street), "STREET"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Dc), "DC"); + ADD_IF_RECOGNIZED(enum_value(AttributeType::Uid), "UID"); + + cert_name.appendff("\\{}={}", it->key, it->value); + } +#undef ADD_IF_RECOGNIZED + + return cert_name.to_string(); +} } diff --git a/Userland/Libraries/LibTLS/Certificate.h b/Userland/Libraries/LibTLS/Certificate.h index 97f84de0a4..dbc2549c85 100644 --- a/Userland/Libraries/LibTLS/Certificate.h +++ b/Userland/Libraries/LibTLS/Certificate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2020-2023, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -18,36 +18,219 @@ namespace TLS { -enum class CertificateKeyAlgorithm { +#define _ENUM(key, value) key, + +#define __ENUM_OBJECT_CLASS \ + _ENUM(ApplicationProcess, "2.5.6.11"sv) \ + _ENUM(Country, "2.5.6.2"sv) \ + _ENUM(DcObject, "1.3.6.1.4.1.1466.344"sv) \ + _ENUM(Device, "2.5.6.14"sv) \ + _ENUM(GroupOfNames, "2.5.6.9"sv) \ + _ENUM(GroupOfUniqueNames, "2.5.6.17"sv) \ + _ENUM(Locality, "2.5.6.3"sv) \ + _ENUM(Organization, "2.5.6.4"sv) \ + _ENUM(OrganizationalPerson, "2.5.6.7"sv) \ + _ENUM(OrganizationalRole, "2.5.6.8"sv) \ + _ENUM(OrganizationalUnit, "2.5.6.5"sv) \ + _ENUM(Person, "2.5.6.6"sv) \ + _ENUM(ResidentialPerson, "2.5.6.10"sv) \ + _ENUM(UidObject, "1.3.6.1.1.3.1"sv) + +// NOTE: Type = O +// NOTE: This list is not exhaustive. If more members are needed, find them at the link below. +// https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3 +enum class ObjectClass { + __ENUM_OBJECT_CLASS +}; + +#define __ENUM_ATTRIBUTE_TYPE \ + _ENUM(BusinessCategory, "2.5.4.15"sv) \ + _ENUM(C, "2.5.4.6"sv) \ + _ENUM(Cn, "2.5.4.3"sv) \ + _ENUM(Dc, "0.9.2342.19200300.100.1.25"sv) \ + _ENUM(Description, "2.5.4.13"sv) \ + _ENUM(DestinationIndicator, "2.5.4.27"sv) \ + _ENUM(DistinguishedName, "2.5.4.49"sv) \ + _ENUM(DnQualifier, "2.5.4.46"sv) \ + _ENUM(EnhancedSearchGuide, "2.5.4.47"sv) \ + _ENUM(Email, "1.2.840.113549.1.9.1"sv) \ + _ENUM(FacsimileTelephoneNumber, "2.5.4.23"sv) \ + _ENUM(GenerationQualifier, "2.5.4.44"sv) \ + _ENUM(GivenName, "2.5.4.42"sv) \ + _ENUM(HouseIdentifier, "2.5.4.51"sv) \ + _ENUM(Initials, "2.5.4.43"sv) \ + _ENUM(InternationalISDNNumber, "2.5.4.25"sv) \ + _ENUM(L, "2.5.4.7"sv) \ + _ENUM(Member, "2.5.4.31"sv) \ + _ENUM(Name, "2.5.4.41"sv) \ + _ENUM(O, "2.5.4.10"sv) \ + _ENUM(Ou, "2.5.4.11"sv) \ + _ENUM(Owner, "2.5.4.32"sv) \ + _ENUM(PhysicalDeliveryOfficeName, "2.5.4.19"sv) \ + _ENUM(PostalAddress, "2.5.4.16"sv) \ + _ENUM(PostalCode, "2.5.4.17"sv) \ + _ENUM(PostOfficeBox, "2.5.4.18"sv) \ + _ENUM(PreferredDeliveryMethod, "2.5.4.28"sv) \ + _ENUM(RegisteredAddress, "2.5.4.26"sv) \ + _ENUM(RoleOccupant, "2.5.4.33"sv) \ + _ENUM(SearchGuide, "2.5.4.14"sv) \ + _ENUM(SeeAlso, "2.5.4.34"sv) \ + _ENUM(SerialNumber, "2.5.4.5"sv) \ + _ENUM(Sn, "2.5.4.4"sv) \ + _ENUM(St, "2.5.4.8"sv) \ + _ENUM(Street, "2.5.4.9"sv) \ + _ENUM(Surname, "2.5.4.4"sv) \ + _ENUM(TelephoneNumber, "2.5.4.20"sv) \ + _ENUM(TeletexTerminalIdentifier, "2.5.4.22"sv) \ + _ENUM(TelexNumber, "2.5.4.21"sv) \ + _ENUM(Title, "2.5.4.12"sv) \ + _ENUM(Uid, "0.9.2342.19200300.100.1.1"sv) \ + _ENUM(UniqueMember, "2.5.4.50"sv) \ + _ENUM(UserPassword, "2.5.4.35"sv) \ + _ENUM(X121Address, "2.5.4.24"sv) \ + _ENUM(X500UniqueIdentifier, "2.5.4.45"sv) + +// NOTE: Type = A +// NOTE: This list is not exhaustive. If more members are needed, find them at the link below. +// https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3 +enum class AttributeType { + __ENUM_ATTRIBUTE_TYPE +}; + +#undef _ENUM + +constexpr static StringView enum_value(ObjectClass object_class) +{ +#define _ENUM(key, value) \ + case ObjectClass::key: \ + return value; + + switch (object_class) { + __ENUM_OBJECT_CLASS + } + + return "Unknown"sv; +#undef _ENUM +#undef __ENUM_OBJECT_CLASS +} + +constexpr static StringView enum_value(AttributeType object_class) +{ +#define _ENUM(key, value) \ + case AttributeType::key: \ + return value; + + switch (object_class) { + __ENUM_ATTRIBUTE_TYPE + } + + return "Unknown"sv; +#undef _ENUM +#undef __ENUM_ATTRIBUTE_TYPE +} + +enum class CertificateKeyAlgorithm : u8 { Unsupported = 0x00, RSA_RSA = 0x01, + RSA_MD2 = 0x2, + RSA_MD4 = 0x3, RSA_MD5 = 0x04, RSA_SHA1 = 0x05, + RSA_OAEP = 0x6, + RSAES_OAEP = 0x7, + RSA_MGF1 = 0x8, + RSA_SPECIFIED = 0x9, + RSA_PSS = 0xa, RSA_SHA256 = 0x0b, RSA_SHA384 = 0x0c, RSA_SHA512 = 0x0d, + RSA_SHA224 = 0xe, + ECDSA_SHA224 = 0x10, + ECDSA_SHA256 = 0x11, + ECDSA_SHA384 = 0x12, + ECDSA_SHA512 = 0x13, + ECDSA_SECP256R1 = 0x14, + ECDSA_SECP384R1 = 0x15, +}; + +struct BasicConstraints { + bool is_certificate_authority; + Crypto::UnsignedBigInteger path_length_constraint; +}; + +class RelativeDistinguishedName { +public: + ErrorOr<String> to_string(); + + ErrorOr<AK::HashSetResult> set(String key, String value) + { + return m_members.try_set(key, value); + } + + Optional<String> get(StringView key) + { + return m_members.get(key); + } + + Optional<String> get(AttributeType key) + { + return m_members.get(enum_value(key)); + } + + Optional<String> get(ObjectClass key) + { + return m_members.get(enum_value(key)); + } + + String common_name() + { + auto entry = get(AttributeType::Cn); + if (entry.has_value()) { + return entry.value(); + } + + return String(); + } + + String organizational_unit() + { + auto entry = get(AttributeType::Ou); + if (entry.has_value()) { + return entry.value(); + } + + return String(); + } + +private: + HashMap<String, String> m_members; +}; + +struct Validity { + Core::DateTime not_before; + Core::DateTime not_after; +}; + +class SubjectPublicKey { +public: + Crypto::PK::RSAPublicKey<Crypto::UnsignedBigInteger> rsa; + + CertificateKeyAlgorithm algorithm { CertificateKeyAlgorithm::Unsupported }; + ByteBuffer raw_key; }; class Certificate { public: u16 version { 0 }; CertificateKeyAlgorithm algorithm { CertificateKeyAlgorithm::Unsupported }; - CertificateKeyAlgorithm key_algorithm { CertificateKeyAlgorithm::Unsupported }; CertificateKeyAlgorithm ec_algorithm { CertificateKeyAlgorithm::Unsupported }; + SubjectPublicKey public_key {}; ByteBuffer exponent {}; - Crypto::PK::RSAPublicKey<Crypto::UnsignedBigInteger> public_key {}; Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger> private_key {}; - struct Name { - DeprecatedString country; - DeprecatedString state; - DeprecatedString location; - DeprecatedString entity; - DeprecatedString subject; - DeprecatedString unit; - } issuer, subject; - Core::DateTime not_before; - Core::DateTime not_after; - Vector<DeprecatedString> SAN; + RelativeDistinguishedName issuer, subject; + Validity validity {}; + Vector<String> SAN; + Vector<String> IAN; u8* ocsp { nullptr }; Crypto::UnsignedBigInteger serial_number; ByteBuffer sign_key {}; @@ -62,71 +245,11 @@ public: Optional<size_t> path_length_constraint {}; bool is_self_issued { false }; - static Optional<Certificate> parse_asn1(ReadonlyBytes, bool client_cert = false); + static ErrorOr<Certificate> parse_certificate(ReadonlyBytes, bool client_cert = false); bool is_self_signed(); bool is_valid() const; - DeprecatedString subject_identifier_string() const - { - StringBuilder cert_name; - if (!subject.country.is_empty()) { - cert_name.append("/C="sv); - cert_name.append(subject.country); - } - if (!subject.state.is_empty()) { - cert_name.append("/ST="sv); - cert_name.append(subject.state); - } - if (!subject.location.is_empty()) { - cert_name.append("/L="sv); - cert_name.append(subject.location); - } - if (!subject.entity.is_empty()) { - cert_name.append("/O="sv); - cert_name.append(subject.entity); - } - if (!subject.unit.is_empty()) { - cert_name.append("/OU="sv); - cert_name.append(subject.unit); - } - if (!subject.subject.is_empty()) { - cert_name.append("/CN="sv); - cert_name.append(subject.subject); - } - return cert_name.to_deprecated_string(); - } - - DeprecatedString issuer_identifier_string() const - { - StringBuilder cert_name; - if (!issuer.country.is_empty()) { - cert_name.append("/C="sv); - cert_name.append(issuer.country); - } - if (!issuer.state.is_empty()) { - cert_name.append("/ST="sv); - cert_name.append(issuer.state); - } - if (!issuer.location.is_empty()) { - cert_name.append("/L="sv); - cert_name.append(issuer.location); - } - if (!issuer.entity.is_empty()) { - cert_name.append("/O="sv); - cert_name.append(issuer.entity); - } - if (!issuer.unit.is_empty()) { - cert_name.append("/OU="sv); - cert_name.append(issuer.unit); - } - if (!issuer.subject.is_empty()) { - cert_name.append("/CN="sv); - cert_name.append(issuer.subject); - } - return cert_name.to_deprecated_string(); - } - private: Optional<bool> m_is_self_signed; }; diff --git a/Userland/Libraries/LibTLS/HandshakeCertificate.cpp b/Userland/Libraries/LibTLS/HandshakeCertificate.cpp index 40bb4c6f1b..c10817dfe0 100644 --- a/Userland/Libraries/LibTLS/HandshakeCertificate.cpp +++ b/Userland/Libraries/LibTLS/HandshakeCertificate.cpp @@ -76,10 +76,14 @@ ssize_t TLSv12::handle_certificate(ReadonlyBytes buffer) } remaining -= certificate_size_specific; - auto certificate = Certificate::parse_asn1(buffer.slice(res_cert, certificate_size_specific), false); - if (certificate.has_value()) { + auto certificate = Certificate::parse_certificate(buffer.slice(res_cert, certificate_size_specific), false); + if (!certificate.is_error()) { m_context.certificates.append(certificate.value()); valid_certificate = true; + } else { + dbgln("Failed to parse client cert: {}", certificate.error()); + dbgln("{:hex-dump}", buffer.slice(res_cert, certificate_size_specific)); + dbgln(""); } res_cert += certificate_size_specific; } while (remaining > 0); diff --git a/Userland/Libraries/LibTLS/HandshakeClient.cpp b/Userland/Libraries/LibTLS/HandshakeClient.cpp index ec4e1a44df..25cab8cf30 100644 --- a/Userland/Libraries/LibTLS/HandshakeClient.cpp +++ b/Userland/Libraries/LibTLS/HandshakeClient.cpp @@ -186,7 +186,7 @@ void TLSv12::build_rsa_pre_master_secret(PacketBuilder& builder) print_buffer(m_context.premaster_key); } - Crypto::PK::RSA_PKCS1_EME rsa(certificate.public_key.modulus(), 0, certificate.public_key.public_exponent()); + Crypto::PK::RSA_PKCS1_EME rsa(certificate.public_key.rsa.modulus(), 0, certificate.public_key.rsa.public_exponent()); Vector<u8, 32> out; out.resize(rsa.output_size()); diff --git a/Userland/Libraries/LibTLS/HandshakeServer.cpp b/Userland/Libraries/LibTLS/HandshakeServer.cpp index f2591bf86a..2d4f40c0a0 100644 --- a/Userland/Libraries/LibTLS/HandshakeServer.cpp +++ b/Userland/Libraries/LibTLS/HandshakeServer.cpp @@ -362,7 +362,7 @@ ssize_t TLSv12::verify_rsa_server_key_exchange(ReadonlyBytes server_key_info_buf // RFC5246 section 7.4.2: The sender's certificate MUST come first in the list. auto certificate_public_key = m_context.certificates.first().public_key; Crypto::PK::RSAPrivateKey dummy_private_key; - auto rsa = Crypto::PK::RSA(certificate_public_key, dummy_private_key); + auto rsa = Crypto::PK::RSA(certificate_public_key.rsa, dummy_private_key); auto signature_verify_buffer_result = ByteBuffer::create_uninitialized(signature_length); if (signature_verify_buffer_result.is_error()) { diff --git a/Userland/Libraries/LibTLS/TLSv12.cpp b/Userland/Libraries/LibTLS/TLSv12.cpp index 7dc10f0c7f..8a6364fadf 100644 --- a/Userland/Libraries/LibTLS/TLSv12.cpp +++ b/Userland/Libraries/LibTLS/TLSv12.cpp @@ -102,13 +102,13 @@ bool Certificate::is_valid() const { auto now = Core::DateTime::now(); - if (now < not_before) { - dbgln("certificate expired (not yet valid, signed for {})", not_before.to_deprecated_string()); + if (now < validity.not_before) { + dbgln("certificate expired (not yet valid, signed for {})", validity.not_before.to_deprecated_string()); return false; } - if (not_after < now) { - dbgln("certificate expired (expiry date {})", not_after.to_deprecated_string()); + if (validity.not_after < now) { + dbgln("certificate expired (expiry date {})", validity.not_after.to_deprecated_string()); return false; } @@ -201,11 +201,12 @@ void TLSv12::set_root_certificates(Vector<Certificate> certificates) } for (auto& cert : certificates) { - if (!cert.is_valid()) - dbgln("Certificate for {} by {} is invalid, things may or may not work!", cert.subject.subject, cert.issuer.subject); + if (!cert.is_valid()) { + dbgln("Certificate for {} by {} is invalid, things may or may not work!", cert.subject.common_name(), cert.issuer.common_name()); + } // FIXME: Figure out what we should do when our root certs are invalid. - m_context.root_certificates.set(cert.subject_identifier_string(), cert); + m_context.root_certificates.set(MUST(cert.subject.to_string()).to_deprecated_string(), cert); } dbgln_if(TLS_DEBUG, "{}: Set {} root certificates", this, m_context.root_certificates.size()); } @@ -228,7 +229,7 @@ static bool wildcard_matches(StringView host, StringView subject) static bool certificate_subject_matches_host(Certificate& cert, StringView host) { - if (wildcard_matches(host, cert.subject.subject)) + if (wildcard_matches(host, cert.subject.common_name())) return true; for (auto& san : cert.SAN) { @@ -279,15 +280,15 @@ bool Context::verify_chain(StringView host) const for (size_t cert_index = 0; cert_index < local_chain->size(); ++cert_index) { auto cert = local_chain->at(cert_index); - auto subject_string = cert.subject_identifier_string(); - auto issuer_string = cert.issuer_identifier_string(); + auto subject_string = MUST(cert.subject.to_string()); + auto issuer_string = MUST(cert.issuer.to_string()); if (!cert.is_valid()) { dbgln("verify_chain: Certificate is not valid {}", subject_string); return false; } - auto maybe_root_certificate = root_certificates.get(issuer_string); + auto maybe_root_certificate = root_certificates.get(issuer_string.to_deprecated_string()); if (maybe_root_certificate.has_value()) { auto& root_certificate = *maybe_root_certificate; auto verification_correct = verify_certificate_pair(cert, root_certificate); @@ -312,7 +313,7 @@ bool Context::verify_chain(StringView host) const } auto parent_certificate = local_chain->at(cert_index + 1); - if (issuer_string != parent_certificate.subject_identifier_string()) { + if (issuer_string != MUST(parent_certificate.subject.to_string())) { dbgln("verify_chain: Next certificate in the chain is not the issuer of this certificate"); return false; } @@ -359,7 +360,7 @@ bool Context::verify_certificate_pair(Certificate const& subject, Certificate co } Crypto::PK::RSAPrivateKey dummy_private_key; - Crypto::PK::RSAPublicKey public_key_copy { issuer.public_key }; + Crypto::PK::RSAPublicKey public_key_copy { issuer.public_key.rsa }; auto rsa = Crypto::PK::RSA(public_key_copy, dummy_private_key); auto verification_buffer_result = ByteBuffer::create_uninitialized(subject.signature_value.size()); if (verification_buffer_result.is_error()) { @@ -471,8 +472,8 @@ Vector<Certificate> TLSv12::parse_pem_certificate(ReadonlyBytes certificate_pem_ return {}; } - auto maybe_certificate = Certificate::parse_asn1(decoded_certificate); - if (!maybe_certificate.has_value()) { + auto maybe_certificate = Certificate::parse_certificate(decoded_certificate); + if (!maybe_certificate.is_error()) { dbgln("Invalid certificate"); return {}; } @@ -516,19 +517,20 @@ ErrorOr<Vector<Certificate>> DefaultRootCACertificates::reload_certificates(Byte auto certs = TRY(Crypto::decode_pems(data)); for (auto& cert : certs) { - auto certificate_result = Certificate::parse_asn1(cert.bytes()); + auto certificate_result = Certificate::parse_certificate(cert.bytes()); // If the certificate does not parse it is likely using elliptic curve keys/signatures, which are not // supported right now. It might make sense to cleanup cacert.pem before adding it to the system. - if (!certificate_result.has_value()) { + if (certificate_result.is_error()) { // FIXME: It would be nice to have more informations about the certificate we failed to parse. // Like: Issuer, Algorithm, CN, etc + dbgln("Failed to load certificate: {}", certificate_result.error()); continue; } auto certificate = certificate_result.release_value(); if (certificate.is_certificate_authority && certificate.is_self_signed()) { TRY(certificates.try_append(move(certificate))); } else { - dbgln("Skipped '{}' because it is not a valid root CA", certificate.subject_identifier_string()); + dbgln("Skipped '{}' because it is not a valid root CA", MUST(certificate.subject.to_string())); } } |