1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00

LibCrypto: Remove unused Certificate class

This commit is contained in:
devgianlu 2025-02-15 14:06:25 +01:00 committed by Alexander Kalenik
parent 24d3da64e5
commit bc0bb0d535
Notes: github-actions[bot] 2025-02-17 23:04:11 +00:00
4 changed files with 21 additions and 711 deletions

View file

@ -5,43 +5,17 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Certificate.h"
#include <AK/Debug.h>
#include <AK/ByteBuffer.h>
#include <AK/IPv4Address.h>
#include <LibCore/DateTime.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/ASN1/PEM.h>
#include <LibCrypto/Certificate/Certificate.h>
#include <LibCrypto/PK/EC.h>
namespace Crypto::Certificate {
static ErrorOr<Crypto::UnsignedBigInteger> parse_certificate_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;
}
ErrorOr<Vector<int>> parse_ec_parameters(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
ErrorOr<Vector<int>> parse_ec_parameters(ASN1::Decoder& decoder, Vector<StringView> current_scope)
{
// ECParameters ::= CHOICE {
// namedCurve OBJECT IDENTIFIER
@ -71,7 +45,7 @@ ErrorOr<Vector<int>> parse_ec_parameters(Crypto::ASN1::Decoder& decoder, Vector<
return named_curve;
}
static ErrorOr<AlgorithmIdentifier> parse_algorithm_identifier(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
static ErrorOr<AlgorithmIdentifier> parse_algorithm_identifier(ASN1::Decoder& decoder, Vector<StringView> current_scope)
{
// AlgorithmIdentifier{ALGORITHM:SupportedAlgorithms} ::= SEQUENCE {
// algorithm ALGORITHM.&id({SupportedAlgorithms}),
@ -209,100 +183,8 @@ static ErrorOr<AlgorithmIdentifier> parse_algorithm_identifier(Crypto::ASN1::Dec
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
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<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(move(attribute_type_string), move(attribute_value_string)));
EXIT_SCOPE();
}
EXIT_SCOPE();
}
EXIT_SCOPE();
return rdn;
}
static ErrorOr<UnixDateTime> 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();
}
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<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;
}
ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
ErrorOr<SubjectPublicKey> parse_subject_public_key_info(ASN1::Decoder& decoder, Vector<StringView> current_scope)
{
// SubjectPublicKeyInfo ::= Sequence {
// algorithm AlgorithmIdentifier,
@ -366,7 +248,7 @@ ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& d
}
// https://www.rfc-editor.org/rfc/rfc5208#section-5
ErrorOr<PrivateKey> parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope)
ErrorOr<PrivateKey> parse_private_key_info(ASN1::Decoder& decoder, Vector<StringView> current_scope)
{
// PrivateKeyInfo ::= SEQUENCE {
// version Version,
@ -435,477 +317,4 @@ ErrorOr<PrivateKey> parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vecto
ERROR_WITH_SCOPE(TRY(String::formatted("Unhandled algorithm {}", algo_oid)));
}
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();
return value;
}
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);
}
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 == 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<void> parse_extensions(Crypto::ASN1::Decoder& decoder, Vector<StringView> 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<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]]
// }
// 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<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));
}
}
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);
}
certificate.is_self_issued = TRY(certificate.issuer.to_string()) == TRY(certificate.subject.to_string());
EXIT_SCOPE();
return certificate;
}
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));
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<String> 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;
}
}

View file

@ -6,15 +6,7 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Forward.h>
#include <AK/Optional.h>
#include <AK/Time.h>
#include <AK/Types.h>
#include <LibCore/ConfigFile.h>
#include <LibCrypto/ASN1/Constants.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibCrypto/PK/EC.h>
#include <LibCrypto/PK/RSA.h>
@ -25,126 +17,39 @@ struct AlgorithmIdentifier {
{
}
explicit AlgorithmIdentifier(Vector<int, 9> identifier)
explicit AlgorithmIdentifier(Vector<int, 9> const& identifier)
: identifier(identifier)
{
}
Vector<int, 9> identifier;
Optional<Vector<int>> ec_parameters;
Optional<Vector<int>> ec_parameters {};
};
ErrorOr<Vector<int>> parse_ec_parameters(ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
struct BasicConstraints {
bool is_certificate_authority;
Crypto::UnsignedBigInteger path_length_constraint;
};
class RelativeDistinguishedName {
public:
ErrorOr<String> to_string() const;
ErrorOr<AK::HashSetResult> set(String key, String value)
{
return m_members.try_set(move(key), move(value));
}
Optional<String const&> get(StringView key) const
{
return m_members.get(key);
}
Optional<String const&> get(ASN1::AttributeType key) const
{
return m_members.get(enum_value(key));
}
Optional<String const&> 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<String, String> 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<Crypto::UnsignedBigInteger> rsa;
Crypto::PK::ECPublicKey<Crypto::UnsignedBigInteger> ec;
PK::RSAPublicKey<> rsa;
PK::ECPublicKey<> ec;
AlgorithmIdentifier algorithm;
ByteBuffer raw_key;
};
ErrorOr<SubjectPublicKey> parse_subject_public_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
ErrorOr<SubjectPublicKey> parse_subject_public_key_info(ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
// https://www.rfc-editor.org/rfc/rfc5208#section-5
class PrivateKey {
public:
Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger> rsa;
Crypto::PK::ECPrivateKey<Crypto::UnsignedBigInteger> ec;
PK::RSAPrivateKey<> rsa;
PK::ECPrivateKey<> ec;
AlgorithmIdentifier algorithm;
ByteBuffer raw_key;
// FIXME: attributes [0] IMPLICIT Attributes OPTIONAL
};
ErrorOr<PrivateKey> parse_private_key_info(Crypto::ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
class Certificate {
public:
u16 version { 0 };
AlgorithmIdentifier algorithm;
SubjectPublicKey public_key;
ByteBuffer exponent {};
Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger> private_key {};
RelativeDistinguishedName issuer, subject;
Validity validity {};
Vector<String> SAN;
Vector<String> 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<size_t> path_length_constraint {};
bool is_self_issued { false };
static ErrorOr<Certificate> parse_certificate(ReadonlyBytes, bool client_cert = false);
bool is_self_signed();
bool is_valid() const;
private:
Optional<bool> m_is_self_signed;
};
ErrorOr<PrivateKey> parse_private_key_info(ASN1::Decoder& decoder, Vector<StringView> current_scope = {});
}

View file

@ -4,14 +4,17 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCrypto/Certificate/Certificate.h>
#include <LibCrypto/ASN1/DER.h>
#include <stddef.h>
#include <stdint.h>
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;
}

View file

@ -8,13 +8,6 @@
#include <LibCrypto/Certificate/Certificate.h>
#include <LibTest/TestCase.h>
TEST_CASE(certificate_with_malformed_tbscertificate_should_fail_gracefully)
{
Array<u8, 4> 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"