diff options
author | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-01-07 00:26:20 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-01-07 00:26:20 +0000 |
commit | f9bf9b4162753bac4752faab67bd082e57c6145f (patch) | |
tree | 3a63d537d43732e48136170d9829fdde144cf6e9 | |
parent | 941ebad703fb7c7373712c86889ec0843b01eaf6 (diff) | |
parent | 67a55d70b5793782d570553f94212d3d0e8b9fbc (diff) | |
download | asn1crypto-android11-qpr1-d-release.tar.gz |
Upgrade python/asn1crypto to 1.3.0 am: 4918382e86 am: a9a4b88828 am: 67a55d70b5r_aml_301500702android-mainline-12.0.0_r55android-mainline-11.0.0_r9android-mainline-11.0.0_r8android-mainline-11.0.0_r7android-mainline-11.0.0_r6android-mainline-11.0.0_r5android-mainline-11.0.0_r45android-mainline-11.0.0_r44android-mainline-11.0.0_r43android-mainline-11.0.0_r42android-mainline-11.0.0_r41android-mainline-11.0.0_r40android-mainline-11.0.0_r4android-mainline-11.0.0_r39android-mainline-11.0.0_r38android-mainline-11.0.0_r37android-mainline-11.0.0_r36android-mainline-11.0.0_r35android-mainline-11.0.0_r34android-mainline-11.0.0_r33android-mainline-11.0.0_r32android-mainline-11.0.0_r31android-mainline-11.0.0_r30android-mainline-11.0.0_r3android-mainline-11.0.0_r29android-mainline-11.0.0_r28android-mainline-11.0.0_r27android-mainline-11.0.0_r26android-mainline-11.0.0_r25android-mainline-11.0.0_r24android-mainline-11.0.0_r23android-mainline-11.0.0_r22android-mainline-11.0.0_r21android-mainline-11.0.0_r20android-mainline-11.0.0_r2android-mainline-11.0.0_r19android-mainline-11.0.0_r18android-mainline-11.0.0_r17android-mainline-11.0.0_r16android-mainline-11.0.0_r15android-mainline-11.0.0_r14android-mainline-11.0.0_r13android-mainline-11.0.0_r12android-mainline-11.0.0_r10android-mainline-11.0.0_r1android-11.0.0_r9android-11.0.0_r8android-11.0.0_r7android-11.0.0_r48android-11.0.0_r47android-11.0.0_r46android-11.0.0_r45android-11.0.0_r44android-11.0.0_r43android-11.0.0_r42android-11.0.0_r41android-11.0.0_r40android-11.0.0_r39android-11.0.0_r38android-11.0.0_r37android-11.0.0_r36android-11.0.0_r35android-11.0.0_r34android-11.0.0_r33android-11.0.0_r32android-11.0.0_r31android-11.0.0_r30android-11.0.0_r29android-11.0.0_r28android-11.0.0_r27android-11.0.0_r26android-11.0.0_r24android-11.0.0_r23android-11.0.0_r22android-11.0.0_r21android-11.0.0_r20android-11.0.0_r19android-11.0.0_r18android-11.0.0_r16android-11.0.0_r15android-11.0.0_r14android-11.0.0_r13android-11.0.0_r12android-11.0.0_r11android-11.0.0_r10android11-qpr3-s1-releaseandroid11-qpr3-releaseandroid11-qpr2-releaseandroid11-qpr1-s2-releaseandroid11-qpr1-s1-releaseandroid11-qpr1-releaseandroid11-qpr1-d-s1-releaseandroid11-qpr1-d-releaseandroid11-qpr1-c-releaseandroid11-mainline-tethering-releaseandroid11-mainline-sparse-2021-jan-releaseandroid11-mainline-sparse-2020-dec-releaseandroid11-mainline-releaseandroid11-mainline-permission-releaseandroid11-mainline-os-statsd-releaseandroid11-mainline-networkstack-releaseandroid11-mainline-media-swcodec-releaseandroid11-mainline-media-releaseandroid11-mainline-extservices-releaseandroid11-mainline-documentsui-releaseandroid11-mainline-conscrypt-releaseandroid11-mainline-cellbroadcast-releaseandroid11-mainline-captiveportallogin-releaseandroid11-devandroid11-d2-releaseandroid11-d1-s7-releaseandroid11-d1-s6-releaseandroid11-d1-s5-releaseandroid11-d1-s1-releaseandroid11-d1-releaseandroid11-d1-b-release
Change-Id: I755be65c30a23d78d712b75efcac32c11f8d9524
-rw-r--r-- | .circleci/config.yml | 9 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 17 | ||||
-rw-r--r-- | .travis.yml | 5 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | asn1crypto/__init__.py | 38 | ||||
-rw-r--r-- | asn1crypto/cms.py | 23 | ||||
-rw-r--r-- | asn1crypto/keys.py | 98 | ||||
-rw-r--r-- | asn1crypto/util.py | 10 | ||||
-rw-r--r-- | asn1crypto/version.py | 4 | ||||
-rw-r--r-- | asn1crypto/x509.py | 9 | ||||
-rw-r--r-- | changelog.md | 28 | ||||
-rw-r--r-- | dev/__init__.py | 2 | ||||
-rw-r--r-- | dev/_import.py | 35 | ||||
-rw-r--r-- | dev/_task.py | 163 | ||||
-rw-r--r-- | dev/coverage.py | 2 | ||||
-rw-r--r-- | dev/tests.py | 16 | ||||
-rw-r--r-- | dev/version.py | 8 | ||||
-rw-r--r-- | readme.md | 2 | ||||
-rw-r--r-- | requires/coverage | 3 | ||||
-rw-r--r-- | requires/lint | 17 | ||||
-rw-r--r-- | requires/release | 6 | ||||
-rw-r--r-- | run.py | 67 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | tests/__init__.py | 6 | ||||
-rw-r--r-- | tests/_unittest_compat.py | 85 | ||||
-rw-r--r-- | tests/fixtures/rfc3739.crt | bin | 0 -> 788 bytes | |||
-rw-r--r-- | tests/setup.py | 2 | ||||
-rw-r--r-- | tests/test_core.py | 11 | ||||
-rw-r--r-- | tests/test_init.py | 137 | ||||
-rw-r--r-- | tests/test_pem.py | 6 | ||||
-rw-r--r-- | tests/test_x509.py | 191 | ||||
-rw-r--r-- | tox.ini | 2 |
32 files changed, 882 insertions, 130 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 500e4d6..874d959 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ jobs: py26: macos: # macOS 10.12, last version with Python 2.6 - xcode: 9.2.0 + xcode: 9.0.1 steps: - checkout - run: /usr/bin/python2.6 run.py deps @@ -14,7 +14,14 @@ jobs: xcode: 10.3.0 steps: - checkout + - restore_cache: + keys: + - homebrew - run: brew install pypy + - save_cache: + key: homebrew + paths: + - /usr/local/Homebrew - run: pypy run.py deps - run: pypy run.py ci workflows: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab5738c..8180e66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: matrix: os: - ubuntu-18.04 - - macOS-10.14 + - macOS-latest - windows-2019 python: - '2.7' @@ -20,7 +20,7 @@ jobs: exclude: - os: ubuntu-18.04 arch: x86 - - os: macOS-10.14 + - os: macOS-latest arch: x86 steps: - uses: actions/checkout@master @@ -32,7 +32,18 @@ jobs: run: python run.py deps - name: Run test suite run: python run.py ci - - name: Run test suite (OpenSSL/macOS) + env: + OSCRYPTO_USE_CTYPES: 'true' + - name: Run test suite (Mac cffi) + run: python run.py ci + if: runner.os == 'macOS' + - name: Run test suite (Mac OpenSSL) + run: python run.py ci + if: runner.os == 'macOS' + env: + OSCRYPTO_USE_OPENSSL: /usr/lib/libcrypto.dylib,/usr/lib/libssl.dylib + OSCRYPTO_USE_CTYPES: 'true' + - name: Run test suite (Mac OpenSSL/cffi) run: python run.py ci if: runner.os == 'macOS' env: diff --git a/.travis.yml b/.travis.yml index 3c5b0d6..93c1a7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,11 @@ matrix: language: python python: "3.7" - os: linux + arch: arm64 + dist: bionic + language: python + python: "3.7" + - os: linux dist: xenial language: python python: "pypy" @@ -9,11 +9,11 @@ third_party { type: GIT value: "https://github.com/wbond/asn1crypto" } - version: "1.0.0" + version: "1.3.0" license_type: NOTICE last_upgrade_date { - year: 2019 - month: 10 - day: 2 + year: 2020 + month: 1 + day: 4 } } diff --git a/asn1crypto/__init__.py b/asn1crypto/__init__.py index afdeb43..2c93f00 100644 --- a/asn1crypto/__init__.py +++ b/asn1crypto/__init__.py @@ -6,4 +6,42 @@ from .version import __version__, __version_info__ __all__ = [ '__version__', '__version_info__', + 'load_order', ] + + +def load_order(): + """ + Returns a list of the module and sub-module names for asn1crypto in + dependency load order, for the sake of live reloading code + + :return: + A list of unicode strings of module names, as they would appear in + sys.modules, ordered by which module should be reloaded first + """ + + return [ + 'asn1crypto._errors', + 'asn1crypto._int', + 'asn1crypto._ordereddict', + 'asn1crypto._teletex_codec', + 'asn1crypto._types', + 'asn1crypto._inet', + 'asn1crypto._iri', + 'asn1crypto.version', + 'asn1crypto.pem', + 'asn1crypto.util', + 'asn1crypto.parser', + 'asn1crypto.core', + 'asn1crypto.algos', + 'asn1crypto.keys', + 'asn1crypto.x509', + 'asn1crypto.crl', + 'asn1crypto.csr', + 'asn1crypto.ocsp', + 'asn1crypto.cms', + 'asn1crypto.pdf', + 'asn1crypto.pkcs12', + 'asn1crypto.tsp', + 'asn1crypto', + ] diff --git a/asn1crypto/cms.py b/asn1crypto/cms.py index 1fabc13..2115aed 100644 --- a/asn1crypto/cms.py +++ b/asn1crypto/cms.py @@ -100,6 +100,8 @@ class CMSAttributeType(ObjectIdentifier): '1.2.840.113549.1.9.4': 'message_digest', '1.2.840.113549.1.9.5': 'signing_time', '1.2.840.113549.1.9.6': 'counter_signature', + # https://tools.ietf.org/html/rfc2633#page-26 + '1.2.840.113549.1.9.16.2.11': 'encrypt_key_pref', # https://tools.ietf.org/html/rfc3161#page-20 '1.2.840.113549.1.9.16.2.14': 'signature_time_stamp_token', # https://tools.ietf.org/html/rfc6211#page-5 @@ -924,6 +926,26 @@ class CompressedData(Sequence): return self._decompressed +class RecipientKeyIdentifier(Sequence): + _fields = [ + ('subjectKeyIdentifier', OctetString), + ('date', GeneralizedTime, {'optional': True}), + ('other', OtherKeyAttribute, {'optional': True}), + ] + + +class SMIMEEncryptionKeyPreference(Choice): + _alternatives = [ + ('issuer_and_serial_number', IssuerAndSerialNumber, {'implicit': 0}), + ('recipientKeyId', RecipientKeyIdentifier, {'implicit': 1}), + ('subjectAltKeyIdentifier', PublicKeyInfo, {'implicit': 2}), + ] + + +class SMIMEEncryptionKeyPreferences(SetOf): + _child_spec = SMIMEEncryptionKeyPreference + + ContentInfo._oid_specs = { 'data': OctetString, 'signed_data': SignedData, @@ -958,4 +980,5 @@ CMSAttribute._oid_specs = { 'cms_algorithm_protection': SetOfCMSAlgorithmProtection, 'microsoft_nested_signature': SetOfContentInfo, 'microsoft_time_stamp_token': SetOfContentInfo, + 'encrypt_key_pref': SMIMEEncryptionKeyPreferences, } diff --git a/asn1crypto/keys.py b/asn1crypto/keys.py index 3d447e3..599929f 100644 --- a/asn1crypto/keys.py +++ b/asn1crypto/keys.py @@ -21,7 +21,7 @@ import math from ._errors import unwrap, APIException from ._types import type_name, byte_cls -from .algos import _ForceNullParameters, DigestAlgorithm, EncryptionAlgorithm, RSAESOAEPParams +from .algos import _ForceNullParameters, DigestAlgorithm, EncryptionAlgorithm, RSAESOAEPParams, RSASSAPSSParams from .core import ( Any, Asn1Value, @@ -40,7 +40,6 @@ from .core import ( SetOf, ) from .util import int_from_bytes, int_to_bytes -from asn1crypto.algos import RSASSAPSSParams class OtherPrimeInfo(Sequence): @@ -355,23 +354,55 @@ class NamedCurve(ObjectIdentifier): '1.2.840.10045.3.1.5': 'prime239v2', '1.2.840.10045.3.1.6': 'prime239v3', # https://tools.ietf.org/html/rfc5480#page-5 - # http://www.secg.org/sec2-v2.pdf + # http://www.secg.org/SEC2-Ver-1.0.pdf + '1.2.840.10045.3.1.1': 'secp192r1', + '1.2.840.10045.3.1.7': 'secp256r1', '1.3.132.0.1': 'sect163k1', + '1.3.132.0.2': 'sect163r1', + '1.3.132.0.3': 'sect239k1', + '1.3.132.0.4': 'sect113r1', + '1.3.132.0.5': 'sect113r2', + '1.3.132.0.6': 'secp112r1', + '1.3.132.0.7': 'secp112r2', + '1.3.132.0.8': 'secp160r1', + '1.3.132.0.9': 'secp160k1', '1.3.132.0.10': 'secp256k1', '1.3.132.0.15': 'sect163r2', - '1.2.840.10045.3.1.1': 'secp192r1', - '1.3.132.0.33': 'secp224r1', - '1.3.132.0.26': 'sect233k1', - '1.2.840.10045.3.1.7': 'secp256r1', - '1.3.132.0.27': 'sect233r1', '1.3.132.0.16': 'sect283k1', '1.3.132.0.17': 'sect283r1', + '1.3.132.0.22': 'sect131r1', + '1.3.132.0.23': 'sect131r2', + '1.3.132.0.24': 'sect193r1', + '1.3.132.0.25': 'sect193r2', + '1.3.132.0.26': 'sect233k1', + '1.3.132.0.27': 'sect233r1', + '1.3.132.0.28': 'secp128r1', + '1.3.132.0.29': 'secp128r2', + '1.3.132.0.30': 'secp160r2', + '1.3.132.0.31': 'secp192k1', + '1.3.132.0.32': 'secp224k1', + '1.3.132.0.33': 'secp224r1', '1.3.132.0.34': 'secp384r1', + '1.3.132.0.35': 'secp521r1', '1.3.132.0.36': 'sect409k1', '1.3.132.0.37': 'sect409r1', - '1.3.132.0.35': 'secp521r1', '1.3.132.0.38': 'sect571k1', '1.3.132.0.39': 'sect571r1', + # https://tools.ietf.org/html/rfc5639#section-4.1 + '1.3.36.3.3.2.8.1.1.1': 'brainpoolp160r1', + '1.3.36.3.3.2.8.1.1.2': 'brainpoolp160t1', + '1.3.36.3.3.2.8.1.1.3': 'brainpoolp192r1', + '1.3.36.3.3.2.8.1.1.4': 'brainpoolp192t1', + '1.3.36.3.3.2.8.1.1.5': 'brainpoolp224r1', + '1.3.36.3.3.2.8.1.1.6': 'brainpoolp224t1', + '1.3.36.3.3.2.8.1.1.7': 'brainpoolp256r1', + '1.3.36.3.3.2.8.1.1.8': 'brainpoolp256t1', + '1.3.36.3.3.2.8.1.1.9': 'brainpoolp320r1', + '1.3.36.3.3.2.8.1.1.10': 'brainpoolp320t1', + '1.3.36.3.3.2.8.1.1.11': 'brainpoolp384r1', + '1.3.36.3.3.2.8.1.1.12': 'brainpoolp384t1', + '1.3.36.3.3.2.8.1.1.13': 'brainpoolp512r1', + '1.3.36.3.3.2.8.1.1.14': 'brainpoolp512t1', } _key_sizes = { @@ -404,22 +435,57 @@ class NamedCurve(ObjectIdentifier): '1.2.840.10045.3.1.6': 30, # Order values used to compute these sourced from # http://www.secg.org/SEC2-Ver-1.0.pdf + # ceil(n.bit_length() / 8) + '1.2.840.10045.3.1.1': 24, + '1.2.840.10045.3.1.7': 32, '1.3.132.0.1': 21, + '1.3.132.0.2': 21, + '1.3.132.0.3': 30, + '1.3.132.0.4': 15, + '1.3.132.0.5': 15, + '1.3.132.0.6': 14, + '1.3.132.0.7': 14, + '1.3.132.0.8': 21, + '1.3.132.0.9': 21, '1.3.132.0.10': 32, '1.3.132.0.15': 21, - '1.2.840.10045.3.1.1': 24, - '1.3.132.0.33': 28, - '1.3.132.0.26': 29, - '1.2.840.10045.3.1.7': 32, - '1.3.132.0.27': 29, '1.3.132.0.16': 36, '1.3.132.0.17': 36, + '1.3.132.0.22': 17, + '1.3.132.0.23': 17, + '1.3.132.0.24': 25, + '1.3.132.0.25': 25, + '1.3.132.0.26': 29, + '1.3.132.0.27': 30, + '1.3.132.0.28': 16, + '1.3.132.0.29': 16, + '1.3.132.0.30': 21, + '1.3.132.0.31': 24, + '1.3.132.0.32': 29, + '1.3.132.0.33': 28, '1.3.132.0.34': 48, - '1.3.132.0.36': 51, - '1.3.132.0.37': 51, '1.3.132.0.35': 66, + '1.3.132.0.36': 51, + '1.3.132.0.37': 52, '1.3.132.0.38': 72, '1.3.132.0.39': 72, + # Order values used to compute these sourced from + # https://tools.ietf.org/html/rfc5639#section-3 + # ceil(q.bit_length() / 8) + '1.3.36.3.3.2.8.1.1.1': 20, + '1.3.36.3.3.2.8.1.1.2': 20, + '1.3.36.3.3.2.8.1.1.3': 24, + '1.3.36.3.3.2.8.1.1.4': 24, + '1.3.36.3.3.2.8.1.1.5': 28, + '1.3.36.3.3.2.8.1.1.6': 28, + '1.3.36.3.3.2.8.1.1.7': 32, + '1.3.36.3.3.2.8.1.1.8': 32, + '1.3.36.3.3.2.8.1.1.9': 40, + '1.3.36.3.3.2.8.1.1.10': 40, + '1.3.36.3.3.2.8.1.1.11': 48, + '1.3.36.3.3.2.8.1.1.12': 48, + '1.3.36.3.3.2.8.1.1.13': 64, + '1.3.36.3.3.2.8.1.1.14': 64, } @classmethod diff --git a/asn1crypto/util.py b/asn1crypto/util.py index 4d743df..7196897 100644 --- a/asn1crypto/util.py +++ b/asn1crypto/util.py @@ -161,6 +161,16 @@ if sys.version_info <= (3,): return False return self._offset == other._offset + def __getinitargs__(self): + """ + Called by tzinfo.__reduce__ to support pickle and copy. + + :return: + offset and name, to be used for __init__ + """ + + return self._offset, self._name + def tzname(self, dt): """ :param dt: diff --git a/asn1crypto/version.py b/asn1crypto/version.py index 0c08d01..b7c352c 100644 --- a/asn1crypto/version.py +++ b/asn1crypto/version.py @@ -2,5 +2,5 @@ from __future__ import unicode_literals, division, absolute_import, print_function -__version__ = '1.0.0' -__version_info__ = (1, 0, 0) +__version__ = '1.3.0' +__version_info__ = (1, 3, 0) diff --git a/asn1crypto/x509.py b/asn1crypto/x509.py index 8341bb2..2cce9a5 100644 --- a/asn1crypto/x509.py +++ b/asn1crypto/x509.py @@ -537,6 +537,8 @@ class NameType(ObjectIdentifier): '1.3.6.1.4.1.311.60.2.1.1': 'incorporation_locality', '1.3.6.1.4.1.311.60.2.1.2': 'incorporation_state_or_province', '1.3.6.1.4.1.311.60.2.1.3': 'incorporation_country', + # https://tools.ietf.org/html/rfc4519#section-2.39 + '0.9.2342.19200300.100.1.1': 'user_id', # https://tools.ietf.org/html/rfc2247#section-4 '0.9.2342.19200300.100.1.25': 'domain_component', # http://www.alvestrand.no/objectid/0.2.262.1.10.7.20.html @@ -561,6 +563,7 @@ class NameType(ObjectIdentifier): 'organizational_unit_name', 'title', 'common_name', + 'user_id', 'initials', 'generation_qualifier', 'surname', @@ -642,6 +645,7 @@ class NameType(ObjectIdentifier): 'platform_manufacturer': 'Platform Manufacturer', 'platform_model': 'Platform Model', 'platform_version': 'Platform Version', + 'user_id': 'User ID', }.get(self.native, self.native) @@ -688,6 +692,7 @@ class NameTypeAndValue(Sequence): 'platform_manufacturer': UTF8String, 'platform_model': UTF8String, 'platform_version': UTF8String, + 'user_id': DirectoryString, } _prepped = None @@ -2140,7 +2145,7 @@ class Certificate(Sequence): _processed_extensions = False _critical_extensions = None - _subject_directory_attributes = None + _subject_directory_attributes_value = None _key_identifier_value = None _key_usage_value = None _subject_alt_name_value = None @@ -2229,7 +2234,7 @@ class Certificate(Sequence): if not self._processed_extensions: self._set_extensions() - return self._subject_directory_attributes + return self._subject_directory_attributes_value @property def key_identifier_value(self): diff --git a/changelog.md b/changelog.md index 3792d84..67d1766 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,33 @@ # changelog +## 1.3.0 + + - Added `encrypt_key_pref` (`1.2.840.113549.1.9.16.2.11`) to + `cms.CMSAttributeType()`, along with related structures + - Added Brainpool curves from RFC 5639 to `keys.NamedCurve()` + - Fixed `x509.Certificate().subject_directory_attributes_value` + - Fixed some incorrectly computed minimum elliptic curve primary key + encoding sizes in `keys.NamedCurve()` + - Fixed a `TypeError` when trying to call `.untag()` or `.copy()` on a + `core.UTCTime()` or `core.GeneralizedTime()`, or a value containing one, + when using Python 2 + +## 1.2.0 + + - Added `asn1crypto.load_order()`, which returns a `list` of unicode strings + of the names of the fully-qualified module names for all of submodules of + the package. The module names are listed in their dependency load order. + This is primarily intended for the sake of implementing hot reloading. + +## 1.1.0 + + - Added User ID (`0.9.2342.19200300.100.1.1`) to `x509.NameType()` + - Added various EC named curves to `keys.NamedCurve()` + +## 1.0.1 + + - Fix an absolute import in `keys` to a relative import + ## 1.0.0 - Backwards Compatibility Breaks diff --git a/dev/__init__.py b/dev/__init__.py index 02e9c6c..a4b5cb4 100644 --- a/dev/__init__.py +++ b/dev/__init__.py @@ -15,6 +15,8 @@ other_packages = [ "ocspbuilder" ] +task_keyword_args = [] + requires_oscrypto = False has_tests_package = True diff --git a/dev/_import.py b/dev/_import.py index 2599588..caad219 100644 --- a/dev/_import.py +++ b/dev/_import.py @@ -5,10 +5,15 @@ import imp import sys import os -from . import build_root +from . import build_root, package_name, package_root +if sys.version_info < (3,): + getcwd = os.getcwdu +else: + getcwd = os.getcwd -def _import_from(mod, path, mod_dir=None): + +def _import_from(mod, path, mod_dir=None, allow_error=False): """ Imports a module from a specific path @@ -22,23 +27,33 @@ def _import_from(mod, path, mod_dir=None): If the sub directory of "path" is different than the "mod" name, pass the sub directory as a unicode string + :param allow_error: + If an ImportError should be raised when the module can't be imported + :return: None if not loaded, otherwise the module """ if mod_dir is None: - mod_dir = mod + mod_dir = mod.replace('.', os.sep) if not os.path.exists(path): return None - if not os.path.exists(os.path.join(path, mod_dir)): + if not os.path.exists(os.path.join(path, mod_dir)) \ + and not os.path.exists(os.path.join(path, mod_dir + '.py')): return None + if os.sep in mod_dir: + append, mod_dir = mod_dir.rsplit(os.sep, 1) + path = os.path.join(path, append) + try: mod_info = imp.find_module(mod_dir, [path]) return imp.load_module(mod, *mod_info) except ImportError: + if allow_error: + raise return None @@ -55,13 +70,18 @@ def _preload(require_oscrypto, print_info): """ if print_info: + print('Working dir: ' + getcwd()) print('Python ' + sys.version.replace('\n', '')) asn1crypto = None oscrypto = None if require_oscrypto: - oscrypto_dir = os.path.join(build_root, 'oscrypto') + # Some CI services don't use the package name for the dir + if package_name == 'oscrypto': + oscrypto_dir = package_root + else: + oscrypto_dir = os.path.join(build_root, 'oscrypto') oscrypto_tests = None if os.path.exists(oscrypto_dir): oscrypto_tests = _import_from('oscrypto_tests', oscrypto_dir, 'tests') @@ -70,7 +90,10 @@ def _preload(require_oscrypto, print_info): asn1crypto, oscrypto = oscrypto_tests.local_oscrypto() else: - asn1crypto_dir = os.path.join(build_root, 'asn1crypto') + if package_name == 'asn1crypto': + asn1crypto_dir = package_root + else: + asn1crypto_dir = os.path.join(build_root, 'asn1crypto') if os.path.exists(asn1crypto_dir): asn1crypto = _import_from('asn1crypto', asn1crypto_dir) if asn1crypto is None: diff --git a/dev/_task.py b/dev/_task.py new file mode 100644 index 0000000..5cc257a --- /dev/null +++ b/dev/_task.py @@ -0,0 +1,163 @@ +# coding: utf-8 +from __future__ import unicode_literals, division, absolute_import, print_function + +import ast +import _ast +import os +import sys + +from . import package_root, task_keyword_args +from ._import import _import_from + + +if sys.version_info < (3,): + byte_cls = str +else: + byte_cls = bytes + + +def _list_tasks(): + """ + Fetches a list of all valid tasks that may be run, and the args they + accept. Does not actually import the task module to prevent errors if a + user does not have the dependencies installed for every task. + + :return: + A list of 2-element tuples: + 0: a unicode string of the task name + 1: a list of dicts containing the parameter definitions + """ + + out = [] + dev_path = os.path.join(package_root, 'dev') + for fname in sorted(os.listdir(dev_path)): + if fname.startswith('.') or fname.startswith('_'): + continue + if not fname.endswith('.py'): + continue + name = fname[:-3] + args = () + + full_path = os.path.join(package_root, 'dev', fname) + with open(full_path, 'rb') as f: + full_code = f.read() + if sys.version_info >= (3,): + full_code = full_code.decode('utf-8') + + task_node = ast.parse(full_code, filename=full_path) + for node in ast.iter_child_nodes(task_node): + if isinstance(node, _ast.Assign): + if len(node.targets) == 1 \ + and isinstance(node.targets[0], _ast.Name) \ + and node.targets[0].id == 'run_args': + args = ast.literal_eval(node.value) + break + + out.append((name, args)) + return out + + +def show_usage(): + """ + Prints to stderr the valid options for invoking tasks + """ + + valid_tasks = [] + for task in _list_tasks(): + usage = task[0] + for run_arg in task[1]: + usage += ' ' + name = run_arg.get('name', '') + if run_arg.get('required', False): + usage += '{%s}' % name + else: + usage += '[%s]' % name + valid_tasks.append(usage) + + out = 'Usage: run.py' + for karg in task_keyword_args: + out += ' [%s=%s]' % (karg['name'], karg['placeholder']) + out += ' (%s)' % ' | '.join(valid_tasks) + + print(out, file=sys.stderr) + sys.exit(1) + + +def _get_arg(num): + """ + :return: + A unicode string of the requested command line arg + """ + + if len(sys.argv) < num + 1: + return None + arg = sys.argv[num] + if isinstance(arg, byte_cls): + arg = arg.decode('utf-8') + return arg + + +def run_task(): + """ + Parses the command line args, invoking the requested task + """ + + arg_num = 1 + task = None + args = [] + kwargs = {} + + # We look for the task name, processing any global task keyword args + # by setting the appropriate env var + while True: + val = _get_arg(arg_num) + if val is None: + break + + next_arg = False + for karg in task_keyword_args: + if val.startswith(karg['name'] + '='): + os.environ[karg['env_var']] = val[len(karg['name']) + 1:] + next_arg = True + break + + if next_arg: + arg_num += 1 + continue + + task = val + break + + if task is None: + show_usage() + + task_mod = _import_from('dev.%s' % task, package_root, allow_error=True) + if task_mod is None: + show_usage() + + run_args = task_mod.__dict__.get('run_args', []) + max_args = arg_num + 1 + len(run_args) + + if len(sys.argv) > max_args: + show_usage() + + for i, run_arg in enumerate(run_args): + val = _get_arg(arg_num + 1 + i) + if val is None: + if run_arg.get('required', False): + show_usage() + break + + if run_arg.get('cast') == 'int' and val.isdigit(): + val = int(val) + + kwarg = run_arg.get('kwarg') + if kwarg: + kwargs[kwarg] = val + else: + args.append(val) + + run = task_mod.__dict__.get('run') + + result = run(*args, **kwargs) + sys.exit(int(not result)) diff --git a/dev/coverage.py b/dev/coverage.py index b9a55de..eb03b53 100644 --- a/dev/coverage.py +++ b/dev/coverage.py @@ -54,7 +54,7 @@ def run(ci=False): cov.start() from .tests import run as run_tests - result = run_tests() + result = run_tests(ci=ci) print() if ci: diff --git a/dev/tests.py b/dev/tests.py index a065c38..101c691 100644 --- a/dev/tests.py +++ b/dev/tests.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals, division, absolute_import, print_functi import unittest import re import sys +import warnings from . import requires_oscrypto from ._import import _preload @@ -17,6 +18,19 @@ else: from io import StringIO +run_args = [ + { + 'name': 'regex', + 'kwarg': 'matcher', + }, + { + 'name': 'repeat_count', + 'kwarg': 'repeat', + 'cast': 'int', + }, +] + + def run(matcher=None, repeat=1, ci=False): """ Runs the tests @@ -37,6 +51,8 @@ def run(matcher=None, repeat=1, ci=False): _preload(requires_oscrypto, not ci) + warnings.filterwarnings("error") + loader = unittest.TestLoader() # We have to manually track the list of applicable tests because for # some reason with Python 3.4 on Windows, the tests in a suite are replaced diff --git a/dev/version.py b/dev/version.py index 3027431..fe37d3d 100644 --- a/dev/version.py +++ b/dev/version.py @@ -8,6 +8,14 @@ import re from . import package_root, package_name, has_tests_package +run_args = [ + { + 'name': 'pep440_version', + 'required': True + }, +] + + def run(new_version): """ Updates the package version in the various locations @@ -112,7 +112,7 @@ faster to an order of magnitude or more. ## Current Release -1.0.0 - [changelog](changelog.md) +1.3.0 - [changelog](changelog.md) ## Dependencies diff --git a/requires/coverage b/requires/coverage index 8a80dcc..39126eb 100644 --- a/requires/coverage +++ b/requires/coverage @@ -1 +1,2 @@ -coverage >= 4.3.4 ; python_version != '3.2'
\ No newline at end of file +coverage == 4.4.1 ; python_version == '2.6' +coverage == 4.5.4 ; python_version != '3.2' and python_version != '2.6' diff --git a/requires/lint b/requires/lint index f5d0e74..121788b 100644 --- a/requires/lint +++ b/requires/lint @@ -1,7 +1,14 @@ -setuptools == 39.0.1 ; python_version == '2.7' or python_version >= '3.3' +setuptools >= 39.0.1 ; python_version == '2.7' or python_version >= '3.3' enum34 == 1.1.6 ; python_version == '2.7' or python_version == '3.3' -mccabe == 0.6.1 ; python_version == '2.7' or python_version >= '3.3' -pycodestyle == 2.3.1 ; python_version == '2.7' or python_version >= '3.3' -pyflakes == 1.6.0 ; python_version == '2.7' or python_version >= '3.3' configparser == 3.5.0 ; python_version == '2.7' -flake8 == 3.5.0 ; python_version == '2.7' or python_version >= '3.3'
\ No newline at end of file +mccabe == 0.6.1 ; python_version == '3.3' +pycodestyle == 2.3.1 ; python_version == '3.3' +pyflakes == 1.6.0 ; python_version == '3.3' +flake8 == 3.5.0 ; python_version == '3.3' +mccabe == 0.6.1 ; python_version == '2.7' or python_version >= '3.4' +pycodestyle == 2.5.0 ; python_version == '2.7' or python_version >= '3.4' +pyflakes == 2.1.1 ; python_version == '2.7' or python_version >= '3.4' +functools32 == 3.2.3-2 ; python_version == '2.7' +typing == 3.7.4.1 ; python_version == '2.7' or python_version == '3.4' +entrypoints == 0.3 ; python_version == '2.7' or python_version >= '3.4' +flake8 == 3.7.9 ; python_version == '2.7' or python_version >= '3.4' diff --git a/requires/release b/requires/release index 91cff65..3b81655 100644 --- a/requires/release +++ b/requires/release @@ -1,3 +1,3 @@ -wheel>=0.31.0 -twine>=1.11.0 -setuptools>=38.6.0 +wheel >= 0.31.0 +twine >= 1.11.0 +setuptools >= 38.6.0 @@ -2,70 +2,7 @@ # coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function -import sys +from dev._task import run_task -if sys.version_info < (3,): - byte_cls = str -else: - byte_cls = bytes - -def show_usage(): - print('Usage: run.py (lint | tests [regex] | coverage | deps | ci | version {pep440_version} | build | release)', file=sys.stderr) - sys.exit(1) - - -def get_arg(num): - if len(sys.argv) < num + 1: - return None, num - arg = sys.argv[num] - if isinstance(arg, byte_cls): - arg = arg.decode('utf-8') - return arg, num + 1 - - -if len(sys.argv) < 2 or len(sys.argv) > 3: - show_usage() - -task, next_arg = get_arg(1) - -if task not in set(['lint', 'tests', 'coverage', 'deps', 'ci', 'version', 'build', 'release']): - show_usage() - -if task != 'tests' and task != 'version' and len(sys.argv) == 3: - show_usage() - -params = [] -if task == 'lint': - from dev.lint import run - -elif task == 'tests': - from dev.tests import run - matcher, next_arg = get_arg(next_arg) - if matcher: - params.append(matcher) - -elif task == 'coverage': - from dev.coverage import run - -elif task == 'deps': - from dev.deps import run - -elif task == 'ci': - from dev.ci import run - -elif task == 'version': - from dev.version import run - if len(sys.argv) != 3: - show_usage() - pep440_version, next_arg = get_arg(next_arg) - params.append(pep440_version) - -elif task == 'build': - from dev.build import run - -elif task == 'release': - from dev.release import run - -result = run(*params) -sys.exit(int(not result)) +run_task() @@ -10,7 +10,7 @@ from setuptools.command.egg_info import egg_info PACKAGE_NAME = 'asn1crypto' -PACKAGE_VERSION = '1.0.0' +PACKAGE_VERSION = '1.3.0' PACKAGE_ROOT = os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/__init__.py b/tests/__init__.py index d267878..b669e5a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -6,8 +6,8 @@ import os import unittest -__version__ = '1.0.0' -__version_info__ = (1, 0, 0) +__version__ = '1.3.0' +__version_info__ = (1, 3, 0) def _import_from(mod, path, mod_dir=None): @@ -92,6 +92,7 @@ def test_classes(): from .test_cms import CMSTests from .test_crl import CRLTests from .test_csr import CSRTests + from .test_init import InitTests from .test_keys import KeysTests from .test_ocsp import OCSPTests from .test_pem import PEMTests @@ -107,6 +108,7 @@ def test_classes(): CMSTests, CRLTests, CSRTests, + InitTests, KeysTests, OCSPTests, PEMTests, diff --git a/tests/_unittest_compat.py b/tests/_unittest_compat.py index 2d4985d..11715c2 100644 --- a/tests/_unittest_compat.py +++ b/tests/_unittest_compat.py @@ -6,40 +6,105 @@ import unittest import re +if sys.version_info < (3,): + str_cls = unicode # noqa +else: + str_cls = str + + _non_local = {'patched': False} def patch(): - if not sys.version_info < (2, 7): + if sys.version_info >= (3, 0): return if _non_local['patched']: return - unittest.TestCase.assertIsInstance = _assert_is_instance - unittest.TestCase.assertRaises = _assert_raises - unittest.TestCase.assertRaisesRegexp = _assert_raises_regexp + if sys.version_info < (2, 7): + unittest.TestCase.assertIsInstance = _assert_is_instance + unittest.TestCase.assertRegex = _assert_regex + unittest.TestCase.assertRaises = _assert_raises + unittest.TestCase.assertRaisesRegex = _assert_raises_regex + unittest.TestCase.assertGreaterEqual = _assert_greater_equal + unittest.TestCase.assertLess = _assert_less + unittest.TestCase.assertLessEqual = _assert_less_equal + unittest.TestCase.assertIn = _assert_in + unittest.TestCase.assertNotIn = _assert_not_in + else: + unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches + unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp _non_local['patched'] = True +def _safe_repr(obj): + try: + return repr(obj) + except Exception: + return object.__repr__(obj) + + +def _format_message(msg, standard_msg): + return msg or standard_msg + + +def _assert_greater_equal(self, a, b, msg=None): + if not a >= b: + standard_msg = '%s not greater than or equal to %s' % (_safe_repr(a), _safe_repr(b)) + self.fail(_format_message(msg, standard_msg)) + + +def _assert_less(self, a, b, msg=None): + if not a < b: + standard_msg = '%s not less than %s' % (_safe_repr(a), _safe_repr(b)) + self.fail(_format_message(msg, standard_msg)) + + +def _assert_less_equal(self, a, b, msg=None): + if not a <= b: + standard_msg = '%s not less than or equal to %s' % (_safe_repr(a), _safe_repr(b)) + self.fail(_format_message(msg, standard_msg)) + + def _assert_is_instance(self, obj, cls, msg=None): - """Same as self.assertTrue(isinstance(obj, cls)), with a nicer - default message.""" if not isinstance(obj, cls): if not msg: msg = '%s is not an instance of %r' % (obj, cls) self.fail(msg) -def _assert_raises(self, expected_exception, callableObj=None, *args, **kwargs): # noqa - context = _AssertRaisesContext(expected_exception, self) +def _assert_in(self, member, container, msg=None): + if member not in container: + standard_msg = '%s not found in %s' % (_safe_repr(member), _safe_repr(container)) + self.fail(_format_message(msg, standard_msg)) + + +def _assert_not_in(self, member, container, msg=None): + if member in container: + standard_msg = '%s found in %s' % (_safe_repr(member), _safe_repr(container)) + self.fail(_format_message(msg, standard_msg)) + + +def _assert_regex(self, text, expected_regexp, msg=None): + """Fail the test unless the text matches the regular expression.""" + if isinstance(expected_regexp, str_cls): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(text): + msg = msg or "Regexp didn't match" + msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text) + self.fail(msg) + + +def _assert_raises(self, excClass, callableObj=None, *args, **kwargs): # noqa + context = _AssertRaisesContext(excClass, self) if callableObj is None: return context with context: callableObj(*args, **kwargs) -def _assert_raises_regexp(self, expected_exception, expected_regexp, callable_obj=None, *args, **kwargs): +def _assert_raises_regex(self, expected_exception, expected_regexp, callable_obj=None, *args, **kwargs): if expected_regexp is not None: expected_regexp = re.compile(expected_regexp) context = _AssertRaisesContext(expected_exception, self, expected_regexp) @@ -50,8 +115,6 @@ def _assert_raises_regexp(self, expected_exception, expected_regexp, callable_ob class _AssertRaisesContext(object): - """A context manager used to implement TestCase.assertRaises* methods.""" - def __init__(self, expected, test_case, expected_regexp=None): self.expected = expected self.failureException = test_case.failureException diff --git a/tests/fixtures/rfc3739.crt b/tests/fixtures/rfc3739.crt Binary files differnew file mode 100644 index 0000000..93ec6ba --- /dev/null +++ b/tests/fixtures/rfc3739.crt diff --git a/tests/setup.py b/tests/setup.py index 9f69479..fc20401 100644 --- a/tests/setup.py +++ b/tests/setup.py @@ -10,7 +10,7 @@ from setuptools.command.egg_info import egg_info PACKAGE_NAME = 'asn1crypto' -PACKAGE_VERSION = '1.0.0' +PACKAGE_VERSION = '1.3.0' TEST_PACKAGE_NAME = '%s_tests' % PACKAGE_NAME TESTS_ROOT = os.path.dirname(os.path.abspath(__file__)) PACKAGE_ROOT = os.path.abspath(os.path.join(TESTS_ROOT, '..')) diff --git a/tests/test_core.py b/tests/test_core.py index aaff9f5..b9a7a82 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -409,6 +409,15 @@ class CoreTests(unittest.TestCase): # Is past 2050 core.UTCTime(datetime(2106, 2, 7, 6, 28, 16, tzinfo=util.timezone.utc)) + def test_utctime_copy(self): + a = core.UTCTime(datetime(2019, 11, 11, 17, 45, 18, tzinfo=util.timezone.utc)) + # Ensure _native is set because we want to test copy on the nested timezone object. + a.native + b = a.copy() + self.assertEqual(a.native, b.native) + self.assertEqual(a.contents, b.contents) + self.assertEqual(a.dump(), b.dump()) + @staticmethod def generalized_time_info(): def tz(hours, minutes=0): @@ -1173,7 +1182,7 @@ class CoreTests(unittest.TestCase): self.assertEqual(b'\x6a\x03\x02\x01\x00', ati.dump(force=True)) def test_required_field(self): - with self.assertRaisesRegexp(ValueError, '"id" is missing from structure'): + with self.assertRaisesRegex(ValueError, '"id" is missing from structure'): Seq({'value': core.Integer(5)}).dump() def test_explicit_application_tag_nested(self): diff --git a/tests/test_init.py b/tests/test_init.py new file mode 100644 index 0000000..b986458 --- /dev/null +++ b/tests/test_init.py @@ -0,0 +1,137 @@ +# coding: utf-8 +from __future__ import unicode_literals, division, absolute_import, print_function + +import ast +import _ast +import unittest +import os +import sys + +import asn1crypto as module + + +# This handles situations where an import is importing a function from a +# dotted path, e.g. "from . import ident", and ident is a function, not a +# submodule +MOD_MAP = { +} + + +def add_mod(mod_name, imports): + """ + Maps pre-defined module.function to module import names + + :param mod_name: + A unicode string of a fully-qualified module name being imported + + :param imports: + A set of unicode strings of the modules that are being imported + """ + + imports.add(MOD_MAP.get(mod_name, mod_name)) + + +def walk_ast(parent_node, modname, imports): + """ + Walks the AST for a module finding any imports and recording them + + :param parent_node: + A node from the _ast module + + :param modname: + A unicode string of the module we are walking the AST of + + :param imports: + A set of unicode strings of the imports that have been found so far + """ + + for node in ast.iter_child_nodes(parent_node): + if isinstance(node, _ast.Import): + if node.names[0].name.startswith(module.__name__): + add_mod(node.names[0].name, imports) + + elif isinstance(node, _ast.ImportFrom): + if node.level > 0: + if modname == module.__name__: + base_mod = module.__name__ + else: + base_mod = '.'.join(modname.split('.')[:-node.level]) + if node.module: + base_mod += '.' + node.module + else: + base_mod = node.module + + if not base_mod.startswith(module.__name__): + continue + + if node.level > 0 and not node.module: + for n in node.names: + add_mod(base_mod + '.' + n.name, imports) + else: + add_mod(base_mod, imports) + + elif isinstance(node, _ast.If): + for subast in node.body: + walk_ast(subast, modname, imports) + for subast in node.orelse: + walk_ast(subast, modname, imports) + + elif sys.version_info >= (3, 3) and isinstance(node, _ast.Try): + for subast in node.body: + walk_ast(subast, modname, imports) + for subast in node.orelse: + walk_ast(subast, modname, imports) + for subast in node.finalbody: + walk_ast(subast, modname, imports) + + elif sys.version_info < (3, 3) and isinstance(node, _ast.TryFinally): + for subast in node.body: + walk_ast(subast, modname, imports) + for subast in node.finalbody: + walk_ast(subast, modname, imports) + + elif sys.version_info < (3, 3) and isinstance(node, _ast.TryExcept): + for subast in node.body: + walk_ast(subast, modname, imports) + for subast in node.orelse: + walk_ast(subast, modname, imports) + + +class InitTests(unittest.TestCase): + + def test_load_order(self): + deps = {} + + mod_root = os.path.abspath(os.path.dirname(module.__file__)) + files = [] + for root, dnames, fnames in os.walk(mod_root): + for f in fnames: + if f.endswith('.py'): + full_path = os.path.join(root, f) + rel_path = full_path.replace(mod_root + os.sep, '') + files.append((full_path, rel_path)) + + for full_path, rel_path in sorted(files): + with open(full_path, 'rb') as f: + full_code = f.read() + if sys.version_info >= (3,): + full_code = full_code.decode('utf-8') + + modname = rel_path.replace('.py', '').replace(os.sep, '.') + if modname == '__init__': + modname = module.__name__ + else: + modname = '%s.%s' % (module.__name__, modname) + + imports = set([]) + module_node = ast.parse(full_code, filename=full_path) + walk_ast(module_node, modname, imports) + + deps[modname] = imports + + load_order = module.load_order() + prev = set([]) + for mod in load_order: + self.assertEqual(True, mod in deps) + self.assertEqual((mod, set([])), (mod, deps[mod] - prev)) + prev.add(mod) diff --git a/tests/test_pem.py b/tests/test_pem.py index 8d7f274..db9857c 100644 --- a/tests/test_pem.py +++ b/tests/test_pem.py @@ -149,13 +149,13 @@ class PEMTests(unittest.TestCase): self.assertEqual(expected_bytes, encoded_bytes) def test_armor_wrong_type(self): - with self.assertRaisesRegexp(TypeError, 'type_name must be a unicode string'): + with self.assertRaisesRegex(TypeError, 'type_name must be a unicode string'): pem.armor(b'CERTIFICATE', b'') def test_armor_wrong_type2(self): - with self.assertRaisesRegexp(TypeError, 'der_bytes must be a byte string'): + with self.assertRaisesRegex(TypeError, 'der_bytes must be a byte string'): pem.armor('CERTIFICATE', '') def test_detect_wrong_type(self): - with self.assertRaisesRegexp(TypeError, 'byte_string must be a byte string'): + with self.assertRaisesRegex(TypeError, 'byte_string must be a byte string'): pem.detect('CERTIFICATE') diff --git a/tests/test_x509.py b/tests/test_x509.py index f933911..cfeb485 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -620,6 +620,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', set(['key_usage', 'extended_key_usage', 'basic_constraints']) ), + ( + 'rfc3739.crt', + set(['key_usage']) + ), ) @data('critical_extensions_info') @@ -628,6 +632,87 @@ class X509Tests(unittest.TestCase): self.assertEqual(critical_extensions, cert.critical_extensions) @staticmethod + def subject_directory_attributes_value_info(): + return ( + ( + 'keys/test-der.crt', + None + ), + ( + 'keys/test-inter-der.crt', + None + ), + ( + 'keys/test-third-der.crt', + None + ), + ( + 'geotrust_certs/GeoTrust_Universal_CA.crt', + None + ), + ( + 'geotrust_certs/GeoTrust_Primary_CA.crt', + None + ), + ( + 'geotrust_certs/GeoTrust_EV_SSL_CA_-_G4.crt', + None + ), + ( + 'geotrust_certs/codex.crt', + None + ), + ( + 'lets_encrypt/isrgrootx1.pem', + None + ), + ( + 'lets_encrypt/letsencryptauthorityx1.pem', + None + ), + ( + 'lets_encrypt/letsencryptauthorityx2.pem', + None + ), + ( + 'globalsign_example_keys/IssuingCA-der.cer', + None + ), + ( + 'globalsign_example_keys/rootCA.cer', + None + ), + ( + 'globalsign_example_keys/SSL1.cer', + None + ), + ( + 'globalsign_example_keys/SSL2.cer', + None + ), + ( + 'globalsign_example_keys/SSL3.cer', + None + ), + ( + 'rfc3739.crt', + [ + util.OrderedDict([('type', 'pda_country_of_citizenship'), ('values', ['DE'])]), + util.OrderedDict([('type', 'pda_gender'), ('values', ['F'])]), + util.OrderedDict([('type', 'pda_date_of_birth'), ('values', [ + datetime(1971, 10, 14, 12, 0, tzinfo=util.timezone.utc)])]), + util.OrderedDict([('type', 'pda_place_of_birth'), ('values', ['Darmstadt'])]), + ] + ), + ) + + @data('subject_directory_attributes_value_info') + def subject_directory_attributes_value(self, relative_path, sda_value): + cert = self._load_cert(relative_path) + value = cert.subject_directory_attributes_value + self.assertEqual(sda_value, value.native if value else None) + + @staticmethod def key_identifier_value_info(): return ( ( @@ -690,6 +775,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', b'G\xde\xa4\xe7\xea`\xe7\xee6\xc8\xf1\xd5\xb0F\x07\x07\x9eBh\xce' ), + ( + 'rfc3739.crt', + None + ), ) @data('key_identifier_value_info') @@ -761,6 +850,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', set(['digital_signature', 'key_encipherment']) ), + ( + 'rfc3739.crt', + set(['non_repudiation']) + ), ) @data('key_usage_value_info') @@ -836,6 +929,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None, + ), ) @data('subject_alt_name_value_info') @@ -907,6 +1004,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', {'ca': False, 'path_len_constraint': None} ), + ( + 'rfc3739.crt', + None, + ), ) @data('basic_constraints_value_info') @@ -1017,6 +1118,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None, + ), ) @data('name_constraints_value_info') @@ -1122,6 +1227,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None, + ), ) @data('crl_distribution_points_value_info') @@ -1315,6 +1424,15 @@ class X509Tests(unittest.TestCase): ]) ] ), + ( + 'rfc3739.crt', + [ + util.OrderedDict([ + ('policy_identifier', '1.3.36.8.1.1'), + ('policy_qualifiers', None) + ]), + ] + ), ) @data('certificate_policies_value_info') @@ -1386,6 +1504,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None, + ), ) @data('policy_mappings_value_info') @@ -1518,6 +1640,14 @@ class X509Tests(unittest.TestCase): ('authority_cert_serial_number', None) ]) ), + ( + 'rfc3739.crt', + util.OrderedDict([ + ('key_identifier', b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\xfe\xdc\xba\x98"), + ('authority_cert_issuer', None), + ('authority_cert_serial_number', None) + ]) + ), ) @data('authority_key_identifier_value_info') @@ -1589,6 +1719,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None, + ), ) @data('policy_constraints_value_info') @@ -1659,6 +1793,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', ['server_auth', 'client_auth'] ), + ( + 'rfc3739.crt', + None, + ), ) @data('extended_key_usage_value_info') @@ -1789,6 +1927,10 @@ class X509Tests(unittest.TestCase): ]) ] ), + ( + 'rfc3739.crt', + None, + ), ) @data('authority_information_access_value_info') @@ -1860,6 +2002,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None, + ), ) @data('ocsp_no_check_value_info') @@ -1945,6 +2091,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', 425155524522 ), + ( + 'rfc3739.crt', + 1234567890, + ), ) @data('serial_number_info') @@ -2015,6 +2165,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', b'G\xde\xa4\xe7\xea`\xe7\xee6\xc8\xf1\xd5\xb0F\x07\x07\x9eBh\xce' ), + ( + 'rfc3739.crt', + None, + ), ) @data('key_identifier_info') @@ -2099,6 +2253,11 @@ class X509Tests(unittest.TestCase): b'_\xc0S\xb1\xeb}\xe3\x8e\xe4{\xdb\xd7\xe2\xd9}=3\x97|\x0c\x1e\xecz\xcc\x92u\x1f' b'\xf0\x1d\xbc\x9f\xe4:425155524522' ), + ( + 'rfc3739.crt', + b"@\xde\x1b\xdb\xdc3a\x89:'D\xaf.G' \xb4<\xb3R8\xca;y\x8e\xfb\xef\x14\xbcE\x05F" + b":1234567890" + ), ) @data('issuer_serial_info') @@ -2169,6 +2328,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', b"'\xf8/\xe9]\xd7\r\xf4\xa8\xea\x87\x99=\xfd\x8e\xb3\x9e@\xd0\x91" ), + ( + 'rfc3739.crt', + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\xfe\xdc\xba\x98" + ), ) @data('authority_key_identifier_info') @@ -2240,6 +2403,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', None ), + ( + 'rfc3739.crt', + None + ), ) @data('authority_issuer_serial_info') @@ -2310,6 +2477,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', ['http://ocsp.exampleovca.com/'] ), + ( + 'rfc3739.crt', + [] + ), ) @data('ocsp_urls_info') @@ -2416,6 +2587,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', [] ), + ( + 'rfc3739.crt', + [] + ), ) @data('crl_distribution_points_info') @@ -2487,6 +2662,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', ['*.google.com'] ), + ( + 'rfc3739.crt', + [] + ), ) @data('valid_domains_info') @@ -2557,6 +2736,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', [] ), + ( + 'rfc3739.crt', + [] + ), ) @data('valid_ips_info') @@ -2627,6 +2810,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', False ), + ( + 'rfc3739.crt', + False + ), ) @data('self_issued_info') @@ -2697,6 +2884,10 @@ class X509Tests(unittest.TestCase): 'globalsign_example_keys/SSL3.cer', 'no' ), + ( + 'rfc3739.crt', + 'no' + ), ) @data('self_signed_info') @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,py32,py33,py34,py35,py36,py37,pypy +envlist = py26,py27,py32,py33,py34,py35,py36,py37,py38,pypy [testenv] deps = -rrequires/ci |