aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/cert/x509/cert_status.h2
-rw-r--r--src/lib/cert/x509/crl_ent.cpp1
-rw-r--r--src/lib/cert/x509/crl_ent.h3
-rw-r--r--src/lib/cert/x509/x509_crl.h2
-rw-r--r--src/lib/cert/x509/x509_ext.cpp105
-rw-r--r--src/lib/cert/x509/x509_ext.h81
-rw-r--r--src/lib/cert/x509/x509cert.cpp51
-rw-r--r--src/lib/cert/x509/x509cert.h14
-rw-r--r--src/lib/cert/x509/x509path.cpp60
9 files changed, 241 insertions, 78 deletions
diff --git a/src/lib/cert/x509/cert_status.h b/src/lib/cert/x509/cert_status.h
index 6e8635237..52b65fb57 100644
--- a/src/lib/cert/x509/cert_status.h
+++ b/src/lib/cert/x509/cert_status.h
@@ -47,6 +47,8 @@ enum class Certificate_Status_Code {
CERT_NAME_NOMATCH,
+ UNKNOWN_CRITICAL_EXTENSION,
+
// Hard failures
CERT_IS_REVOKED = 5000,
CRL_BAD_SIGNATURE,
diff --git a/src/lib/cert/x509/crl_ent.cpp b/src/lib/cert/x509/crl_ent.cpp
index d6923f714..7074f0609 100644
--- a/src/lib/cert/x509/crl_ent.cpp
+++ b/src/lib/cert/x509/crl_ent.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/crl_ent.h>
+#include <botan/x509cert.h>
#include <botan/x509_ext.h>
#include <botan/der_enc.h>
#include <botan/ber_dec.h>
diff --git a/src/lib/cert/x509/crl_ent.h b/src/lib/cert/x509/crl_ent.h
index 11ab34365..4be508812 100644
--- a/src/lib/cert/x509/crl_ent.h
+++ b/src/lib/cert/x509/crl_ent.h
@@ -8,11 +8,12 @@
#ifndef BOTAN_CRL_ENTRY_H__
#define BOTAN_CRL_ENTRY_H__
-#include <botan/x509cert.h>
#include <botan/asn1_time.h>
namespace Botan {
+class X509_Certificate;
+
/**
* X.509v2 CRL Reason Code.
*/
diff --git a/src/lib/cert/x509/x509_crl.h b/src/lib/cert/x509/x509_crl.h
index 29057e944..dab4d5153 100644
--- a/src/lib/cert/x509/x509_crl.h
+++ b/src/lib/cert/x509/x509_crl.h
@@ -9,7 +9,9 @@
#define BOTAN_X509_CRL_H__
#include <botan/x509_obj.h>
+#include <botan/x509_dn.h>
#include <botan/crl_ent.h>
+#include <botan/datastor.h>
#include <vector>
namespace Botan {
diff --git a/src/lib/cert/x509/x509_ext.cpp b/src/lib/cert/x509/x509_ext.cpp
index 47fd909eb..b54c82b87 100644
--- a/src/lib/cert/x509/x509_ext.cpp
+++ b/src/lib/cert/x509/x509_ext.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
#include <botan/sha160.h>
#include <botan/der_enc.h>
#include <botan/ber_dec.h>
@@ -20,7 +21,7 @@ namespace Botan {
/*
* List of X.509 Certificate Extensions
*/
-Certificate_Extension* Extensions::get_extension(const OID& oid)
+Certificate_Extension* Extensions::get_extension(const OID& oid, bool critical)
{
#define X509_EXTENSION(NAME, TYPE) \
if(OIDS::name_of(oid, NAME)) \
@@ -40,7 +41,7 @@ Certificate_Extension* Extensions::get_extension(const OID& oid)
X509_EXTENSION("X509v3.CRLNumber", CRL_Number);
X509_EXTENSION("X509v3.ReasonCode", CRL_ReasonCode);
- return nullptr;
+ return critical ? new Cert_Extension::Unknown_Critical_Extension(oid) : nullptr;
}
/*
@@ -55,7 +56,7 @@ Extensions::Extensions(const Extensions& extensions) : ASN1_Object()
* Extensions Assignment Operator
*/
Extensions& Extensions::operator=(const Extensions& other)
- {
+ {
m_extensions.clear();
for(size_t i = 0; i != other.m_extensions.size(); ++i)
@@ -63,6 +64,7 @@ Extensions& Extensions::operator=(const Extensions& other)
std::make_pair(std::unique_ptr<Certificate_Extension>(other.m_extensions[i].first->copy()),
other.m_extensions[i].second));
+ m_extensions_raw = other.m_extensions_raw;
m_throw_on_unknown_critical = other.m_throw_on_unknown_critical;
return (*this);
@@ -76,12 +78,31 @@ OID Certificate_Extension::oid_of() const
return OIDS::lookup(oid_name());
}
+/*
+* Validate the extension (the default implementation is a NOP)
+*/
+void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<X509_Certificate>&,
+ std::vector<std::set<Certificate_Status_Code>>&,
+ size_t)
+ {
+ }
+
void Extensions::add(Certificate_Extension* extn, bool critical)
{
m_extensions.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(extn), critical));
m_extensions_raw.emplace(extn->oid_of(), std::make_pair(extn->encode_inner(), critical));
}
+std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> Extensions::extensions() const
+ {
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> exts;
+ for(auto& ext : m_extensions)
+ {
+ exts.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(ext.first->copy()), ext.second));
+ }
+ return exts;
+ }
std::map<OID, std::pair<std::vector<byte>, bool>> Extensions::extensions_raw() const
{
@@ -136,7 +157,7 @@ void Extensions::decode_from(BER_Decoder& from_source)
m_extensions_raw.emplace(oid, std::make_pair(value, critical));
- std::unique_ptr<Certificate_Extension> ext(get_extension(oid));
+ std::unique_ptr<Certificate_Extension> ext(get_extension(oid, critical));
if(!ext && critical && m_throw_on_unknown_critical)
throw Decoding_Error("Encountered unknown X.509 extension marked "
@@ -500,6 +521,68 @@ void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
}
}
+void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<X509_Certificate>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos)
+ {
+ if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+ {
+ if(!subject.is_CA_cert() || !subject.is_critical("X509v3.NameConstraints"))
+ cert_status.at(pos).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+
+ const bool at_self_signed_root = (pos == cert_path.size() - 1);
+
+ // Check that all subordinate certs pass the name constraint
+ for(size_t j = 0; j <= pos; ++j)
+ {
+ if(pos == j && at_self_signed_root)
+ continue;
+
+ bool permitted = m_name_constraints.permitted().empty();
+ bool failed = false;
+
+ for(auto c: m_name_constraints.permitted())
+ {
+ switch(c.base().matches(cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::NotFound:
+ case GeneralName::MatchResult::All:
+ permitted = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer.is_critical("X509v3.NameConstraints");
+ permitted = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for(auto c: m_name_constraints.excluded())
+ {
+ switch(c.base().matches(cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::All:
+ case GeneralName::MatchResult::Some:
+ failed = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer.is_critical("X509v3.NameConstraints");
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(failed || !permitted)
+ {
+ cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+ }
+ }
+ }
+ }
+
namespace {
/*
@@ -730,6 +813,20 @@ void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder&
.end_cons().end_cons();
}
+std::vector<byte> Unknown_Critical_Extension::encode_inner() const
+ {
+ throw Exception("Unknown_Critical_Extension encoding not implemented");
+ }
+
+void Unknown_Critical_Extension::decode_inner(const std::vector<byte>& buf)
+ {
+ }
+
+void Unknown_Critical_Extension::contents_to(Data_Store& info, Data_Store&) const
+ {
+ // TODO: textual representation?
+ }
+
}
}
diff --git a/src/lib/cert/x509/x509_ext.h b/src/lib/cert/x509/x509_ext.h
index ac456b998..caefcb855 100644
--- a/src/lib/cert/x509/x509_ext.h
+++ b/src/lib/cert/x509/x509_ext.h
@@ -10,11 +10,17 @@
#include <botan/asn1_obj.h>
#include <botan/asn1_oid.h>
+#include <botan/asn1_alt_name.h>
+#include <botan/cert_status.h>
#include <botan/datastor.h>
+#include <botan/name_constraint.h>
+#include <botan/key_constraint.h>
#include <botan/crl_ent.h>
namespace Botan {
+class X509_Certificate;
+
/**
* X.509 Certificate Extension
*/
@@ -24,7 +30,7 @@ class BOTAN_DLL Certificate_Extension
/**
* @return OID representing this extension
*/
- OID oid_of() const;
+ virtual OID oid_of() const;
/**
* Make a copy of this extension
@@ -46,6 +52,26 @@ class BOTAN_DLL Certificate_Extension
*/
virtual std::string oid_name() const = 0;
+ /*
+ * Callback visited during path validation.
+ *
+ * An extension can implement this callback to inspect
+ * the path during path validation.
+ *
+ * If an error occurs during validation of this extension,
+ * an appropriate status code shall be added to cert_status.
+ *
+ * @param subject Subject certificate that contains this extension
+ * @param issuer Issuer certificate
+ * @param status Certificate validation status codes for subject certificate
+ * @param cert_path Certificate path which is currently validated
+ * @param pos Position of subject certificate in cert_path
+ */
+ virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<X509_Certificate>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos);
+
virtual ~Certificate_Extension() {}
protected:
friend class Extensions;
@@ -67,6 +93,8 @@ class BOTAN_DLL Extensions : public ASN1_Object
void add(Certificate_Extension* extn, bool critical = false);
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const;
+
std::map<OID, std::pair<std::vector<byte>, bool>> extensions_raw() const;
Extensions& operator=(const Extensions&);
@@ -76,7 +104,7 @@ class BOTAN_DLL Extensions : public ASN1_Object
explicit Extensions(bool st = true) : m_throw_on_unknown_critical(st) {}
private:
- static Certificate_Extension* get_extension(const OID&);
+ static Certificate_Extension* get_extension(const OID&, bool);
std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> m_extensions;
bool m_throw_on_unknown_critical;
@@ -101,6 +129,7 @@ class BOTAN_DLL Basic_Constraints final : public Certificate_Extension
bool get_is_ca() const { return m_is_ca; }
size_t get_path_limit() const;
+
private:
std::string oid_name() const override
{ return "X509v3.BasicConstraints"; }
@@ -124,6 +153,7 @@ class BOTAN_DLL Key_Usage final : public Certificate_Extension
explicit Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : m_constraints(c) {}
Key_Constraints get_constraints() const { return m_constraints; }
+
private:
std::string oid_name() const override { return "X509v3.KeyUsage"; }
@@ -174,6 +204,7 @@ class BOTAN_DLL Authority_Key_ID final : public Certificate_Extension
explicit Authority_Key_ID(const std::vector<byte>& k) : m_key_id(k) {}
std::vector<byte> get_key_id() const { return m_key_id; }
+
private:
std::string oid_name() const override
{ return "X509v3.AuthorityKeyIdentifier"; }
@@ -198,6 +229,7 @@ class BOTAN_DLL Alternative_Name : public Certificate_Extension
Alternative_Name(const AlternativeName&, const std::string& oid_name);
Alternative_Name(const std::string&, const std::string&);
+
private:
std::string oid_name() const override { return m_oid_name_str; }
@@ -247,6 +279,7 @@ class BOTAN_DLL Extended_Key_Usage final : public Certificate_Extension
explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
std::vector<OID> get_oids() const { return m_oids; }
+
private:
std::string oid_name() const override
{ return "X509v3.ExtendedKeyUsage"; }
@@ -270,6 +303,12 @@ class BOTAN_DLL Name_Constraints : public Certificate_Extension
Name_Constraints() {}
Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {}
+
+ void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<X509_Certificate>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override;
+
private:
std::string oid_name() const override
{ return "X509v3.NameConstraints"; }
@@ -295,6 +334,7 @@ class BOTAN_DLL Certificate_Policies final : public Certificate_Extension
explicit Certificate_Policies(const std::vector<OID>& o) : m_oids(o) {}
std::vector<OID> get_oids() const { return m_oids; }
+
private:
std::string oid_name() const override
{ return "X509v3.CertificatePolicies"; }
@@ -344,6 +384,7 @@ class BOTAN_DLL CRL_Number final : public Certificate_Extension
CRL_Number(size_t n) : m_has_value(true), m_crl_number(n) {}
size_t get_crl_number() const;
+
private:
std::string oid_name() const override { return "X509v3.CRLNumber"; }
@@ -368,6 +409,7 @@ class BOTAN_DLL CRL_ReasonCode final : public Certificate_Extension
explicit CRL_ReasonCode(CRL_Code r = UNSPECIFIED) : m_reason(r) {}
CRL_Code get_reason() const { return m_reason; }
+
private:
std::string oid_name() const override { return "X509v3.ReasonCode"; }
@@ -421,6 +463,41 @@ class BOTAN_DLL CRL_Distribution_Points final : public Certificate_Extension
std::vector<Distribution_Point> m_distribution_points;
};
+/**
+* An unknown X.509 extension marked as critical
+* Will always add a failure to the path validation result.
+*/
+class BOTAN_DLL Unknown_Critical_Extension final : public Certificate_Extension
+ {
+ public:
+ explicit Unknown_Critical_Extension(OID oid) : m_oid(oid) {}
+
+ Unknown_Critical_Extension* copy() const override
+ { return new Unknown_Critical_Extension(m_oid); }
+
+ OID oid_of() const override
+ { return m_oid; };
+
+ void validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<X509_Certificate>&,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION);
+ }
+
+ private:
+ std::string oid_name() const override
+ { return "Unknown OID name"; }
+
+ bool should_encode() const { return false; }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ OID m_oid;
+ };
+
}
}
diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp
index 959cddb53..f68956859 100644
--- a/src/lib/cert/x509/x509cert.cpp
+++ b/src/lib/cert/x509/x509cert.cpp
@@ -42,9 +42,10 @@ std::vector<std::string> lookup_oids(const std::vector<std::string>& in)
* X509_Certificate Constructor
*/
X509_Certificate::X509_Certificate(DataSource& in) :
- X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
+ m_self_signed(false),
+ m_v3_extensions(false)
{
- m_self_signed = false;
do_decode();
}
@@ -52,9 +53,10 @@ X509_Certificate::X509_Certificate(DataSource& in) :
* X509_Certificate Constructor
*/
X509_Certificate::X509_Certificate(const std::string& in) :
- X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
+ m_self_signed(false),
+ m_v3_extensions(false)
{
- m_self_signed = false;
do_decode();
}
@@ -62,12 +64,39 @@ X509_Certificate::X509_Certificate(const std::string& in) :
* X509_Certificate Constructor
*/
X509_Certificate::X509_Certificate(const std::vector<byte>& in) :
- X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
+ m_self_signed(false),
+ m_v3_extensions(false)
{
- m_self_signed = false;
do_decode();
}
+X509_Certificate::X509_Certificate(const X509_Certificate& other) :
+ X509_Object(other)
+ {
+ m_subject = other.m_subject;
+ m_issuer = other.m_issuer;
+ m_self_signed = other.m_self_signed;
+ m_v3_extensions = other.m_v3_extensions;
+ }
+
+X509_Certificate& X509_Certificate::operator=(const X509_Certificate& other)
+ {
+ if(&other == this)
+ {
+ return *this;
+ }
+ else
+ {
+ m_subject = other.m_subject;
+ m_issuer = other.m_issuer;
+ m_self_signed = other.m_self_signed;
+ m_v3_extensions = other.m_v3_extensions;
+ }
+ return *this;
+ }
+
+
/*
* Decode the TBSCertificate data
*/
@@ -120,12 +149,8 @@ void X509_Certificate::force_decode()
if(v3_exts_data.type_tag == 3 &&
v3_exts_data.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
{
- Extensions extensions;
-
- BER_Decoder(v3_exts_data.value).decode(extensions).verify_end();
-
- m_v3_extensions = extensions.extensions_raw();
- extensions.contents_to(m_subject, m_issuer);
+ BER_Decoder(v3_exts_data.value).decode(m_v3_extensions).verify_end();
+ m_v3_extensions.contents_to(m_subject, m_issuer);
}
else if(v3_exts_data.type_tag != NO_OBJECT)
throw BER_Bad_Tag("Unknown tag in X.509 cert",
@@ -332,7 +357,7 @@ std::vector<std::string> X509_Certificate::policies() const
return lookup_oids(m_subject.get("X509v3.CertificatePolicies"));
}
-std::map<OID, std::pair<std::vector<byte>, bool>> X509_Certificate::v3_extensions() const
+Extensions X509_Certificate::v3_extensions() const
{
return m_v3_extensions;
}
diff --git a/src/lib/cert/x509/x509cert.h b/src/lib/cert/x509/x509cert.h
index 54d82b1b4..c521cf7ca 100644
--- a/src/lib/cert/x509/x509cert.h
+++ b/src/lib/cert/x509/x509cert.h
@@ -11,11 +11,13 @@
#include <botan/x509_obj.h>
#include <botan/x509_dn.h>
#include <botan/x509_key.h>
+#include <botan/x509_ext.h>
#include <botan/asn1_alt_name.h>
#include <botan/datastor.h>
#include <botan/key_constraint.h>
#include <botan/name_constraint.h>
#include <map>
+#include <memory>
namespace Botan {
@@ -191,10 +193,10 @@ class BOTAN_DLL X509_Certificate final : public X509_Object
std::vector<std::string> policies() const;
/**
- * Get all extensions of this certificate indexed by oid.
- * @return extension values and critical flag
+ * Get all extensions of this certificate.
+ * @return certificate extensions
*/
- std::map<OID, std::pair<std::vector<byte>, bool>> v3_extensions() const;
+ Extensions v3_extensions() const;
/**
* Return the listed address of an OCSP responder, or empty if not set
@@ -250,6 +252,10 @@ class BOTAN_DLL X509_Certificate final : public X509_Object
explicit X509_Certificate(const std::vector<byte>& in);
+ X509_Certificate(const X509_Certificate& other);
+
+ X509_Certificate& operator=(const X509_Certificate& other);
+
private:
void force_decode() override;
friend class X509_CA;
@@ -259,7 +265,7 @@ class BOTAN_DLL X509_Certificate final : public X509_Object
Data_Store m_subject, m_issuer;
bool m_self_signed;
- std::map<OID, std::pair<std::vector<byte>, bool>> m_v3_extensions;
+ Extensions m_v3_extensions;
};
/**
diff --git a/src/lib/cert/x509/x509path.cpp b/src/lib/cert/x509/x509path.cpp
index dd9df6f51..436e27d39 100644
--- a/src/lib/cert/x509/x509path.cpp
+++ b/src/lib/cert/x509/x509path.cpp
@@ -113,7 +113,6 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
// Check issuer constraints
- // Don't require CA bit set on self-signed end entity cert
if(!issuer.is_CA_cert() && !self_signed_ee_cert)
status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
@@ -142,60 +141,11 @@ check_chain(const std::vector<X509_Certificate>& cert_path,
status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
}
- const NameConstraints& name_constr = issuer.name_constraints();
-
- if(!name_constr.permitted().empty() || !name_constr.excluded().empty())
+ // Check cert extensions
+ Extensions extensions = subject.v3_extensions();
+ for(auto& extension : extensions.extensions())
{
- if(!issuer.is_CA_cert() || !issuer.is_critical("X509v3.NameConstraints"))
- cert_status.at(i).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
-
- // Check that all subordinate certs pass the name constraint
- for(size_t j = 0; j <= i; ++j)
- {
- if(i == j && at_self_signed_root)
- continue;
-
- bool permitted = name_constr.permitted().empty();
- bool failed = false;
-
- for(auto c: name_constr.permitted())
- {
- switch(c.base().matches(cert_path.at(j)))
- {
- case GeneralName::MatchResult::NotFound:
- case GeneralName::MatchResult::All:
- permitted = true;
- break;
- case GeneralName::MatchResult::UnknownType:
- failed = issuer.is_critical("X509v3.NameConstraints");
- permitted = true;
- break;
- default:
- break;
- }
- }
-
- for(auto c: name_constr.excluded())
- {
- switch(c.base().matches(cert_path.at(j)))
- {
- case GeneralName::MatchResult::All:
- case GeneralName::MatchResult::Some:
- failed = true;
- break;
- case GeneralName::MatchResult::UnknownType:
- failed = issuer.is_critical("X509v3.NameConstraints");
- break;
- default:
- break;
- }
- }
-
- if(failed || !permitted)
- {
- cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
- }
- }
+ extension.first->validate(subject, issuer, cert_path, cert_status, i);
}
}
@@ -472,6 +422,8 @@ const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
return "Certificate does not match provided name";
case Certificate_Status_Code::NAME_CONSTRAINT_ERROR:
return "Certificate does not pass name constraint";
+ case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION:
+ return "Unknown critical extension encountered";
case Certificate_Status_Code::CERT_IS_REVOKED:
return "Certificate is revoked";