Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 108 additions & 11 deletions src/cryptography/hazmat/backends/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,23 @@ def _asn1_string_to_utf8(backend, asn1_string):
return backend._ffi.buffer(buf[0], res)[:].decode('utf8')


def _build_x509_name_entry(backend, x509_name_entry):
obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry)
assert obj != backend._ffi.NULL
data = backend._lib.X509_NAME_ENTRY_get_data(x509_name_entry)
assert data != backend._ffi.NULL
value = _asn1_string_to_utf8(backend, data)
oid = _obj2txt(backend, obj)

return x509.NameAttribute(x509.ObjectIdentifier(oid), value)


def _build_x509_name(backend, x509_name):
count = backend._lib.X509_NAME_entry_count(x509_name)
attributes = []
for x in range(count):
entry = backend._lib.X509_NAME_get_entry(x509_name, x)
obj = backend._lib.X509_NAME_ENTRY_get_object(entry)
assert obj != backend._ffi.NULL
data = backend._lib.X509_NAME_ENTRY_get_data(entry)
assert data != backend._ffi.NULL
value = _asn1_string_to_utf8(backend, data)
oid = _obj2txt(backend, obj)
attributes.append(
x509.NameAttribute(
x509.ObjectIdentifier(oid), value
)
)
attributes.append(_build_x509_name_entry(backend, entry))

return x509.Name(attributes)

Expand Down Expand Up @@ -292,6 +293,8 @@ def extensions(self):
value = self._build_authority_information_access(ext)
elif oid == x509.OID_CERTIFICATE_POLICIES:
value = self._build_certificate_policies(ext)
elif oid == x509.OID_CRL_DISTRIBUTION_POINTS:
value = self._build_crl_distribution_points(ext)
elif critical:
raise x509.UnsupportedExtension(
"{0} is not currently supported".format(oid), oid
Expand Down Expand Up @@ -518,6 +521,100 @@ def _build_extended_key_usage(self, ext):

return x509.ExtendedKeyUsage(ekus)

def _build_crl_distribution_points(self, ext):
cdps = self._backend._ffi.cast(
"Cryptography_STACK_OF_DIST_POINT *",
self._backend._lib.X509V3_EXT_d2i(ext)
)
assert cdps != self._backend._ffi.NULL
cdps = self._backend._ffi.gc(
cdps, self._backend._lib.sk_DIST_POINT_free)
num = self._backend._lib.sk_DIST_POINT_num(cdps)

dist_points = []
for i in range(num):
full_name = None
relative_name = None
crl_issuer = None
reasons = None
cdp = self._backend._lib.sk_DIST_POINT_value(cdps, i)
if cdp.reasons != self._backend._ffi.NULL:
# We will check each bit from RFC 5280
# ReasonFlags ::= BIT STRING {
# unused (0),
# keyCompromise (1),
# cACompromise (2),
# affiliationChanged (3),
# superseded (4),
# cessationOfOperation (5),
# certificateHold (6),
# privilegeWithdrawn (7),
# aACompromise (8) }
reasons = []
get_bit = self._backend._lib.ASN1_BIT_STRING_get_bit
if get_bit(cdp.reasons, 1):
reasons.append(x509.ReasonFlags.key_compromise)

if get_bit(cdp.reasons, 2):
reasons.append(x509.ReasonFlags.ca_compromise)

if get_bit(cdp.reasons, 3):
reasons.append(x509.ReasonFlags.affiliation_changed)

if get_bit(cdp.reasons, 4):
reasons.append(x509.ReasonFlags.superseded)

if get_bit(cdp.reasons, 5):
reasons.append(x509.ReasonFlags.cessation_of_operation)

if get_bit(cdp.reasons, 6):
reasons.append(x509.ReasonFlags.certificate_hold)

if get_bit(cdp.reasons, 7):
reasons.append(x509.ReasonFlags.privilege_withdrawn)

if get_bit(cdp.reasons, 8):
reasons.append(x509.ReasonFlags.aa_compromise)

reasons = frozenset(reasons)

if cdp.CRLissuer != self._backend._ffi.NULL:
crl_issuer = _build_general_names(self._backend, cdp.CRLissuer)

# Certificates may have a crl_issuer/reasons and no distribution
# point so make sure it's not null.
if cdp.distpoint != self._backend._ffi.NULL:
# Type 0 is fullName, there is no #define for it in the code.
if cdp.distpoint.type == 0:
full_name = _build_general_names(
self._backend, cdp.distpoint.name.fullname
)
# OpenSSL code doesn't test for a specific type for
# relativename, everything that isn't fullname is considered
# relativename.
else:
rns = cdp.distpoint.name.relativename
rnum = self._backend._lib.sk_X509_NAME_ENTRY_num(rns)
attributes = []
for i in range(rnum):
rn = self._backend._lib.sk_X509_NAME_ENTRY_value(
rns, i
)
assert rn != self._backend._ffi.NULL
attributes.append(
_build_x509_name_entry(self._backend, rn)
)

relative_name = x509.Name(attributes)

dist_points.append(
x509.DistributionPoint(
full_name, relative_name, reasons, crl_issuer
)
)

return x509.CRLDistributionPoints(dist_points)


@utils.register_interface(x509.CertificateSigningRequest)
class _CertificateSigningRequest(object):
Expand Down
215 changes: 215 additions & 0 deletions tests/test_x509_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -1929,3 +1929,218 @@ def test_ne(self):
assert cdp != cdp3
assert cdp != cdp4
assert cdp != object()


@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestCRLDistributionPointsExtension(object):
def test_fullname_and_crl_issuer(self, backend):
cert = _load_cert(
os.path.join(
"x509", "PKITS_data", "certs", "ValidcRLIssuerTest28EE.crt"
),
x509.load_der_x509_certificate,
backend
)

cdps = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value

assert cdps == x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.DirectoryName(
x509.Name([
x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
x509.NameAttribute(
x509.OID_ORGANIZATION_NAME,
"Test Certificates 2011"
),
x509.NameAttribute(
x509.OID_ORGANIZATIONAL_UNIT_NAME,
"indirectCRL CA3 cRLIssuer"
),
x509.NameAttribute(
x509.OID_COMMON_NAME,
"indirect CRL for indirectCRL CA3"
),
])
)],
relative_name=None,
reasons=None,
crl_issuer=[x509.DirectoryName(
x509.Name([
x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
x509.NameAttribute(
x509.OID_ORGANIZATION_NAME,
"Test Certificates 2011"
),
x509.NameAttribute(
x509.OID_ORGANIZATIONAL_UNIT_NAME,
"indirectCRL CA3 cRLIssuer"
),
])
)],
)
])

def test_relativename_and_crl_issuer(self, backend):
cert = _load_cert(
os.path.join(
"x509", "PKITS_data", "certs", "ValidcRLIssuerTest29EE.crt"
),
x509.load_der_x509_certificate,
backend
)

cdps = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value

assert cdps == x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=None,
relative_name=x509.Name([
x509.NameAttribute(
x509.OID_COMMON_NAME,
"indirect CRL for indirectCRL CA3"
),
]),
reasons=None,
crl_issuer=[x509.DirectoryName(
x509.Name([
x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
x509.NameAttribute(
x509.OID_ORGANIZATION_NAME,
"Test Certificates 2011"
),
x509.NameAttribute(
x509.OID_ORGANIZATIONAL_UNIT_NAME,
"indirectCRL CA3 cRLIssuer"
),
])
)],
)
])

def test_fullname_crl_issuer_reasons(self, backend):
cert = _load_cert(
os.path.join(
"x509", "custom", "cdp_fullname_reasons_crl_issuer.pem"
),
x509.load_pem_x509_certificate,
backend
)

cdps = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value

assert cdps == x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.UniformResourceIdentifier(
u"http://myhost.com/myca.crl"
)],
relative_name=None,
reasons=frozenset([
x509.ReasonFlags.key_compromise,
x509.ReasonFlags.ca_compromise
]),
crl_issuer=[x509.DirectoryName(
x509.Name([
x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
x509.NameAttribute(
x509.OID_ORGANIZATION_NAME, "PyCA"
),
x509.NameAttribute(
x509.OID_COMMON_NAME, "cryptography CA"
),
])
)],
)
])

def test_all_reasons(self, backend):
cert = _load_cert(
os.path.join(
"x509", "custom", "cdp_all_reasons.pem"
),
x509.load_pem_x509_certificate,
backend
)

cdps = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value

assert cdps == x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.UniformResourceIdentifier(
u"http://domain.com/some.crl"
)],
relative_name=None,
reasons=frozenset([
x509.ReasonFlags.key_compromise,
x509.ReasonFlags.ca_compromise,
x509.ReasonFlags.affiliation_changed,
x509.ReasonFlags.superseded,
x509.ReasonFlags.privilege_withdrawn,
x509.ReasonFlags.cessation_of_operation,
x509.ReasonFlags.aa_compromise,
x509.ReasonFlags.certificate_hold,
]),
crl_issuer=None
)
])

def test_single_reason(self, backend):
cert = _load_cert(
os.path.join(
"x509", "custom", "cdp_reason_aa_compromise.pem"
),
x509.load_pem_x509_certificate,
backend
)

cdps = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value

assert cdps == x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=[x509.UniformResourceIdentifier(
u"http://domain.com/some.crl"
)],
relative_name=None,
reasons=frozenset([x509.ReasonFlags.aa_compromise]),
crl_issuer=None
)
])

def test_crl_issuer_only(self, backend):
cert = _load_cert(
os.path.join(
"x509", "custom", "cdp_crl_issuer.pem"
),
x509.load_pem_x509_certificate,
backend
)

cdps = cert.extensions.get_extension_for_oid(
x509.OID_CRL_DISTRIBUTION_POINTS
).value

assert cdps == x509.CRLDistributionPoints([
x509.DistributionPoint(
full_name=None,
relative_name=None,
reasons=None,
crl_issuer=[x509.DirectoryName(
x509.Name([
x509.NameAttribute(
x509.OID_COMMON_NAME, "cryptography CA"
),
])
)],
)
])