diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2017-06-04 13:36:58 -0400 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2017-06-04 07:36:58 -1000 |
commit | 6a0718faddbc7b6b57f86417f6daa468c18ea248 (patch) | |
tree | 624fe16cf368a13cbbd7370b2a4780fa5da76c91 /src | |
parent | 140ec5d6e2167692ba5619b368f44a1b07f96a4a (diff) | |
download | cryptography-6a0718faddbc7b6b57f86417f6daa468c18ea248.tar.gz cryptography-6a0718faddbc7b6b57f86417f6daa468c18ea248.tar.bz2 cryptography-6a0718faddbc7b6b57f86417f6daa468c18ea248.zip |
Refs #3461 -- parse SCTs from x.509 extension (#3480)
* Stub API for SCTs, feedback wanted
* grr, flake8
* finish up the __init__
* Initial implementation and tests
* write a test. it fails because computer
* get the tests passing and fix some TODOs
* changelog entry
* This can go now
* Put a skip in this test
* grump
* Removed unreachable code
* moved changelog to the correct section
* Use the deocrator for expressing requirements
* This needs f for the right entry_type
* coverage
* syntax error
* tests for coverage
* better sct eq tests
* docs
* technically correct, the most useless kind of correct
* typo and more details
* bug
* drop __eq__
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/decode_asn1.py | 18 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 41 | ||||
-rw-r--r-- | src/cryptography/x509/__init__.py | 7 | ||||
-rw-r--r-- | src/cryptography/x509/extensions.py | 36 |
4 files changed, 99 insertions, 3 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 282e30f0..ab97dc19 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -597,6 +597,21 @@ def _decode_inhibit_any_policy(backend, asn1_int): return x509.InhibitAnyPolicy(skip_certs) +def _decode_precert_signed_certificate_timestamps(backend, asn1_scts): + from cryptography.hazmat.backends.openssl.x509 import ( + _SignedCertificateTimestamp + ) + asn1_scts = backend._ffi.cast("Cryptography_STACK_OF_SCT *", asn1_scts) + asn1_scts = backend._ffi.gc(asn1_scts, backend._lib.SCT_LIST_free) + + scts = [] + for i in range(backend._lib.sk_SCT_num(asn1_scts)): + sct = backend._lib.sk_SCT_value(asn1_scts, i) + + scts.append(_SignedCertificateTimestamp(backend, asn1_scts, sct)) + return x509.PrecertificateSignedCertificateTimestamps(scts) + + # CRLReason ::= ENUMERATED { # unspecified (0), # keyCompromise (1), @@ -751,6 +766,9 @@ _EXTENSION_HANDLERS = { ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name, ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints, ExtensionOID.POLICY_CONSTRAINTS: _decode_policy_constraints, + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + _decode_precert_signed_certificate_timestamps + ), } _REVOKED_EXTENSION_HANDLERS = { diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 5b3304f3..43456382 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function +import datetime import operator import warnings @@ -433,3 +434,43 @@ class _CertificateSigningRequest(object): return False return True + + +@utils.register_interface( + x509.certificate_transparency.SignedCertificateTimestamp +) +class _SignedCertificateTimestamp(object): + def __init__(self, backend, sct_list, sct): + self._backend = backend + # Keep the SCT_LIST that this SCT came from alive. + self._sct_list = sct_list + self._sct = sct + + @property + def version(self): + version = self._backend._lib.SCT_get_version(self._sct) + assert version == self._backend._lib.SCT_VERSION_V1 + return x509.certificate_transparency.Version.v1 + + @property + def log_id(self): + out = self._backend._ffi.new("unsigned char **") + log_id_length = self._backend._lib.SCT_get0_log_id(self._sct, out) + assert log_id_length >= 0 + return self._backend._ffi.buffer(out[0], log_id_length)[:] + + @property + def timestamp(self): + timestamp = self._backend._lib.SCT_get_timestamp(self._sct) + milliseconds = timestamp % 1000 + return datetime.datetime.utcfromtimestamp( + timestamp // 1000 + ).replace(microsecond=milliseconds * 1000) + + @property + def entry_type(self): + entry_type = self._backend._lib.SCT_get_log_entry_type(self._sct) + # We currently only support loading SCTs from the X.509 extension, so + # we only have precerts. + assert entry_type == self._backend._lib.CT_LOG_ENTRY_TYPE_PRECERT + return x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index c5465fbb..b1a32ef6 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -23,9 +23,9 @@ from cryptography.x509.extensions import ( ExtensionNotFound, ExtensionType, Extensions, GeneralNames, InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage, NameConstraints, NoticeReference, OCSPNoCheck, PolicyConstraints, - PolicyInformation, ReasonFlags, SubjectAlternativeName, - SubjectKeyIdentifier, UnrecognizedExtension, UnsupportedExtension, - UserNotice + PolicyInformation, PrecertificateSignedCertificateTimestamps, ReasonFlags, + SubjectAlternativeName, SubjectKeyIdentifier, UnrecognizedExtension, + UnsupportedExtension, UserNotice ) from cryptography.x509.general_name import ( DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, @@ -185,4 +185,5 @@ __all__ = [ "InvalidityDate", "UnrecognizedExtension", "PolicyConstraints", + "PrecertificateSignedCertificateTimestamps", ] diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index aa30f8ff..1b64f4a5 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -18,6 +18,9 @@ from cryptography import utils from cryptography.hazmat.primitives import constant_time, serialization from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey +from cryptography.x509.certificate_transparency import ( + SignedCertificateTimestamp +) from cryptography.x509.general_name import GeneralName, IPAddress, OtherName from cryptography.x509.name import RelativeDistinguishedName from cryptography.x509.oid import ( @@ -1152,6 +1155,39 @@ class InvalidityDate(object): @utils.register_interface(ExtensionType) +class PrecertificateSignedCertificateTimestamps(object): + oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__(self, signed_certificate_timestamps): + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + def __iter__(self): + return iter(self._signed_certificate_timestamps) + + def __len__(self): + return len(self._signed_certificate_timestamps) + + def __getitem__(self, idx): + return self._signed_certificate_timestamps[idx] + + def __repr__(self): + return ( + "<PrecertificateSignedCertificateTimestamps({0})>".format( + list(self) + ) + ) + + +@utils.register_interface(ExtensionType) class UnrecognizedExtension(object): def __init__(self, oid, value): if not isinstance(oid, ObjectIdentifier): |