diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2019-01-21 22:36:25 -0600 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2019-01-21 23:36:25 -0500 |
commit | 767fa8511caade795457b23ea9d3d85af1ed12bb (patch) | |
tree | 917394e703ca72b35c82b2c9b06e1db22c0363a5 | |
parent | 5b3e735253d4cc1c7f51dedc11c9ca5eeb6f451f (diff) | |
download | platform_external_python_cryptography-767fa8511caade795457b23ea9d3d85af1ed12bb.tar.gz platform_external_python_cryptography-767fa8511caade795457b23ea9d3d85af1ed12bb.tar.bz2 platform_external_python_cryptography-767fa8511caade795457b23ea9d3d85af1ed12bb.zip |
allow 32-bit platforms to encode certs with dates > unix epoch (#4727)
Previously we used unix timestamps, but now we are switching to using
ASN1_TIME_set_string and automatically formatting the string based on
the year. The rule is as follows:
Per RFC 5280 (section 4.1.2.5.), the valid input time
strings should be encoded with the following rules:
1. UTC: YYMMDDHHMMSSZ, if YY < 50 (20YY) --> UTC: YYMMDDHHMMSSZ
2. UTC: YYMMDDHHMMSSZ, if YY >= 50 (19YY) --> UTC: YYMMDDHHMMSSZ
3. G'd: YYYYMMDDHHMMSSZ, if YYYY >= 2050 --> G'd: YYYYMMDDHHMMSSZ
4. G'd: YYYYMMDDHHMMSSZ, if YYYY < 2050 --> UTC: YYMMDDHHMMSSZ
Notably, Dates < 1950 are not valid UTCTime. At the moment we still
reject dates < Jan 1, 1970 in all cases but a followup PR can fix
that.
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 21 | ||||
-rw-r--r-- | tests/x509/test_x509.py | 21 |
2 files changed, 19 insertions, 23 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 1d1e0446..0a9bc53a 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function import base64 -import calendar import collections import contextlib import itertools @@ -854,20 +853,12 @@ class Backend(object): return _Certificate(self, x509_cert) def _set_asn1_time(self, asn1_time, time): - timestamp = calendar.timegm(time.timetuple()) - res = self._lib.ASN1_TIME_set(asn1_time, timestamp) - if res == self._ffi.NULL: - errors = self._consume_errors() - self.openssl_assert( - errors[0]._lib_reason_match( - self._lib.ERR_LIB_ASN1, - self._lib.ASN1_R_ERROR_GETTING_TIME - ) - ) - raise ValueError( - "Invalid time. This error can occur if you set a time too far " - "in the future on Windows." - ) + if time.year >= 2050: + asn1_str = time.strftime('%Y%m%d%H%M%SZ').encode('ascii') + else: + asn1_str = time.strftime('%y%m%d%H%M%SZ').encode('ascii') + res = self._lib.ASN1_TIME_set_string(asn1_time, asn1_str) + self.openssl_assert(res == 1) def _create_asn1_time(self, time): asn1_time = self._lib.ASN1_TIME_new() diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 1d483ac2..55f5ddda 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -8,7 +8,6 @@ import binascii import datetime import ipaddress import os -import sys from asn1crypto.x509 import Certificate @@ -1722,18 +1721,16 @@ class TestCertificateBuilder(object): oid )[0]._type == asn1_type - @pytest.mark.skipif(sys.platform != "win32", reason="Requires windows") @pytest.mark.parametrize( ("not_valid_before", "not_valid_after"), [ - [datetime.datetime(1999, 1, 1), datetime.datetime(9999, 1, 1)], - [datetime.datetime(9999, 1, 1), datetime.datetime(9999, 12, 31)], + [datetime.datetime(1970, 2, 1), datetime.datetime(9999, 1, 1)], + [datetime.datetime(1970, 2, 1), datetime.datetime(9999, 12, 31)], ] ) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) - def test_invalid_time_windows(self, not_valid_before, not_valid_after, - backend): + def test_extreme_times(self, not_valid_before, not_valid_after, backend): private_key = RSA_KEY_2048.private_key(backend) builder = x509.CertificateBuilder().subject_name(x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'), @@ -1748,8 +1745,16 @@ class TestCertificateBuilder(object): ).not_valid_after( not_valid_after ) - with pytest.raises(ValueError): - builder.sign(private_key, hashes.SHA256(), backend) + cert = builder.sign(private_key, hashes.SHA256(), backend) + assert cert.not_valid_before == not_valid_before + assert cert.not_valid_after == not_valid_after + parsed = Certificate.load( + cert.public_bytes(serialization.Encoding.DER) + ) + not_before = parsed['tbs_certificate']['validity']['not_before'] + not_after = parsed['tbs_certificate']['validity']['not_after'] + assert not_before.chosen.tag == 23 # UTCTime + assert not_after.chosen.tag == 24 # GeneralizedTime @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) |