From 4d77fb9d3edab81652210da81a5af7fe6aa79d60 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 13 Feb 2021 15:44:43 -0600 Subject: [PATCH] 3.4.5 backports and changelog/version bump (#5827) * Bump pyo3 and lower MSRV (#5823) * fix signature of EllipticCurvePublicKey.verify() (#5808) The signature change was introduced in https://github.com/pyca/cryptography/pull/5729 but is inconsistent with respect to related methods, breaks backward compatibility and compatibility with the OpenSSL backend (and maybe other backends) when named arguments are used. * Name: update get_attributes_for_oid return type (#5809) `List` gives more power to the caller. Note that `RelativeDistinguishedName`, the same function returns a `List`. Is there a reason this was `Iterable` only for `Name`? If we don't want to promise `List`, `Sequence` is another alternative. * Start typing a bunch of stuff from x509 extensions (#5812) * part 2 of typing x509 extensions (#5815) * 3.4.5 changelog and version bump * spelling * fix a false positive from the latest clippy (#5813) Co-authored-by: Alex Gaynor Co-authored-by: Markus Wamser Co-authored-by: Dan Halperin --- .github/workflows/ci.yml | 3 +- CHANGELOG.rst | 13 ++ docs/installation.rst | 101 ++++++++-- docs/spelling_wordlist.txt | 2 + setup.py | 2 +- src/cryptography/__about__.py | 2 +- .../hazmat/backends/openssl/decode_asn1.py | 2 +- .../hazmat/primitives/asymmetric/ec.py | 2 +- src/cryptography/x509/extensions.py | 157 ++++++++++----- src/cryptography/x509/general_name.py | 21 +- src/cryptography/x509/name.py | 2 +- src/rust/Cargo.lock | 74 ++++++-- src/rust/src/lib.rs | 2 + tests/x509/test_ocsp.py | 4 +- tests/x509/test_x509.py | 4 +- tests/x509/test_x509_ext.py | 179 +++++++++++++----- vectors/cryptography_vectors/__about__.py | 2 +- 17 files changed, 414 insertions(+), 158 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8c17e9b2761..cd967a3a084c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,7 +143,8 @@ jobs: PYTHON: - {VERSION: "3.9", TOXENV: "py39"} RUST: - # Cover MSRV and in-dev versions + # Cover MSRV (and likely next MSRV) and in-dev versions + - 1.41.0 - 1.45.0 - beta - nightly diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b1481a7cceda..aa3d738b58e4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,19 @@ Changelog ========= +.. _v3-4-5: + +3.4.5 - 2021-02-13 +~~~~~~~~~~~~~~~~~~ + +* Various improvements to type hints. +* Lower the minimum supported Rust version (MSRV) to >=1.41.0. This change + improves compatibility with system-provided Rust on several Linux + distributions. +* ``cryptography`` will be switching to a new versioning scheme with its next + feature release. More information is available in our + :doc:`/api-stability` documentation. + .. _v3-4-4: 3.4.4 - 2021-02-09 diff --git a/docs/installation.rst b/docs/installation.rst index e3de10340a7c..278e680ce1f5 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -7,6 +7,9 @@ You can install ``cryptography`` with ``pip``: $ pip install cryptography +If this does not work please **upgrade your pip** first, as that is the +single most common cause of installation problems. + Supported platforms ------------------- @@ -72,18 +75,26 @@ local `wheel cache`_. Building cryptography on Linux ------------------------------ +.. note:: + + If you are on RHEL/CentOS/Fedora/Debian/Ubuntu or another distribution + derived from the preceding list, then you should **upgrade pip** and + attempt to install ``cryptography`` again before following the instructions + to compile it below. These platforms will receive a binary wheel and + require no compiler if you have an updated ``pip``! + ``cryptography`` ships ``manylinux`` wheels (as of 2.0) so all dependencies -are included. For users on pip 19.0 or above running on a ``manylinux2010`` (or -greater) compatible distribution (almost everything except Alpine) all you -should need to do is: +are included. For users on **pip 19.0** or above running on a ``manylinux2010`` +(or greater) compatible distribution (almost everything **except Alpine**) all +you should need to do is: .. code-block:: console $ pip install cryptography If you are on Alpine or just want to compile it yourself then -``cryptography`` requires a compiler, headers for Python (if you're not -using ``pypy``), and headers for the OpenSSL and ``libffi`` libraries +``cryptography`` requires a C compiler, a Rust compiler, headers for Python (if +you're not using ``pypy``), and headers for the OpenSSL and ``libffi`` libraries available on your system. On all Linux distributions you will need to have :ref:`Rust installed and @@ -92,21 +103,57 @@ available`. Alpine ~~~~~~ +.. warning:: + + The Rust available by default in Alpine < 3.12 is older than the minimum + supported version. See the :ref:`Rust installation instructions + ` for information about installing a newer Rust. + .. code-block:: console - $ sudo apk add gcc musl-dev python3-dev libffi-dev openssl-dev + $ sudo apk add gcc musl-dev python3-dev libffi-dev openssl-dev cargo If you get an error with ``openssl-dev`` you may have to use ``libressl-dev``. Debian/Ubuntu ~~~~~~~~~~~~~ +.. warning:: + + The Rust available in current Debian stable and some Ubuntu versions is + older than the minimum supported version. Ubuntu 18.04 and 20.04 are + sufficiently new, but otherwise please see the + :ref:`Rust installation instructions ` for information + about installing a newer Rust. + .. code-block:: console - $ sudo apt-get install build-essential libssl-dev libffi-dev python3-dev + $ sudo apt-get install build-essential libssl-dev libffi-dev \ + python3-dev cargo + +Fedora/RHEL 8/CentOS 8 +~~~~~~~~~~~~~~~~~~~~~~ -RHEL/CentOS -~~~~~~~~~~~ +.. warning:: + + For RHEL and CentOS you must be on version 8.3 or newer for the command + below to install a sufficiently new Rust. If your Rust is less than 1.41.0 + please see the :ref:`Rust installation instructions ` + for information about installing a newer Rust. + +.. code-block:: console + + $ sudo dnf install redhat-rpm-config gcc libffi-devel python3-devel \ + openssl-devel cargo + +RHEL 7/CentOS 7 +~~~~~~~~~~~~~~~ + +.. warning:: + + You must install Rust using the :ref:`Rust installation instructions + `. ``cryptography`` requires a Rust version newer than + what is provided in the distribution packages. .. code-block:: console @@ -226,10 +273,12 @@ This will install a compiler (clang) along with (most of) the required development headers. You will also need to have :ref:`Rust installed and -available`. +available`, which can be obtained from `Homebrew`_, +`MacPorts`_, or directly from the Rust website. -You'll also need OpenSSL, which you can obtain from `Homebrew`_ or `MacPorts`_. -Cryptography does **not** support Apple's deprecated OpenSSL distribution. +Finally you need OpenSSL, which you can obtain from `Homebrew`_ or `MacPorts`_. +Cryptography does **not** support the OpenSSL/LibreSSL libraries Apple ships +in its base operating system. To build cryptography and dynamically link it: @@ -237,14 +286,14 @@ To build cryptography and dynamically link it: .. code-block:: console - $ brew install openssl@1.1 + $ brew install openssl@1.1 rust $ env LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" CFLAGS="-I$(brew --prefix openssl@1.1)/include" pip install cryptography `MacPorts`_: .. code-block:: console - $ sudo port install openssl + $ sudo port install openssl rust $ env LDFLAGS="-L/opt/local/lib" CFLAGS="-I/opt/local/include" pip install cryptography You can also build cryptography statically: @@ -253,14 +302,14 @@ You can also build cryptography statically: .. code-block:: console - $ brew install openssl@1.1 + $ brew install openssl@1.1 rust $ env CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 LDFLAGS="$(brew --prefix openssl@1.1)/lib/libssl.a $(brew --prefix openssl@1.1)/lib/libcrypto.a" CFLAGS="-I$(brew --prefix openssl@1.1)/include" pip install cryptography `MacPorts`_: .. code-block:: console - $ sudo port install openssl + $ sudo port install openssl rust $ env CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 LDFLAGS="/opt/local/lib/libssl.a /opt/local/lib/libcrypto.a" CFLAGS="-I/opt/local/include" pip install cryptography If you need to rebuild ``cryptography`` for any reason be sure to clear the @@ -269,13 +318,31 @@ local `wheel cache`_. Rust ---- +.. note:: + + If you are on RHEL/CentOS/Fedora/Debian/Ubuntu or another distribution + derived from the preceding list, then you should **upgrade pip** (in + a virtual environment!) and attempt to install ``cryptography`` again + before trying to install the Rust toolchain. These platforms will receive + a binary wheel and require no compiler if you have an updated ``pip``! + Building ``cryptography`` requires having a working Rust toolchain. The current -minimum supported Rust version is 1.45.0. +minimum supported Rust version is 1.41.0. **This is newer than the Rust most +package managers ship**, so users will likely need to install with the +instructions below. Instructions for installing Rust can be found on `the Rust Project's website`_. We recommend installing Rust with ``rustup`` (as documented by the Rust Project) in order to ensure you have a recent version. +Rust is only required when building ``cryptography``, meaning that you may +install it for the duration of your ``pip install`` command and then remove it +from a system. A Rust toolchain is not required to **use** ``cryptography``. In +deployments such as ``docker``, you may use a multi-stage ``Dockerfile`` where +you install Rust during the build phase but do not install it in the runtime +image. This is the same as the C compiler toolchain which is also required to +build ``cryptography``, but not afterwards. + .. _`Homebrew`: https://brew.sh .. _`MacPorts`: https://www.macports.org .. _`a binary distribution`: https://wiki.openssl.org/index.php/Binaries diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index f59c3e413506..cad47e53677a 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -14,6 +14,7 @@ Botan Brainpool Bullseye Capitan +CentOS changelog Changelog ciphertext @@ -80,6 +81,7 @@ online paddings Parallelization personalization +RHEL pickleable plaintext Poly diff --git a/setup.py b/setup.py index 1a22695427a4..74f69e7148a6 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ if platform.python_implementation() == "PyPy" else ["pyo3/abi3-py36"] ), - rust_version=">=1.45.0", + rust_version=">=1.41.0", ) ] diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index 63adf698706d..385d21957534 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -21,7 +21,7 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.4.4" +__version__ = "3.4.5" __author__ = "The Python Cryptographic Authority and individual contributors" __email__ = "cryptography-dev@python.org" diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 96ba4cdbc42c..167acc078743 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -768,7 +768,7 @@ def _asn1_string_to_ascii(backend, asn1_string): return _asn1_string_to_bytes(backend, asn1_string).decode("ascii") -def _asn1_string_to_utf8(backend, asn1_string): +def _asn1_string_to_utf8(backend, asn1_string) -> str: buf = backend._ffi.new("unsigned char **") res = backend._lib.ASN1_STRING_to_UTF8(buf, asn1_string) if res == -1: diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index 6374305d8754..56c5f9a02c21 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -176,7 +176,7 @@ def verify( self, signature: bytes, data: bytes, - algorithm: EllipticCurveSignatureAlgorithm, + signature_algorithm: EllipticCurveSignatureAlgorithm, ) -> None: """ Verifies the signature of the data. diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 9f3d8f62d084..6cae016a1c60 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -17,6 +17,7 @@ OBJECT_IDENTIFIER, SEQUENCE, ) +from cryptography.hazmat._types import _PUBLIC_KEY_TYPES from cryptography.hazmat.primitives import constant_time, serialization from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey @@ -33,7 +34,7 @@ ) -def _key_identifier_from_public_key(public_key): +def _key_identifier_from_public_key(public_key: _PUBLIC_KEY_TYPES) -> bytes: if isinstance(public_key, RSAPublicKey): data = public_key.public_bytes( serialization.Encoding.DER, @@ -54,7 +55,7 @@ def _key_identifier_from_public_key(public_key): reader = DERReader(serialized) with reader.read_single_element(SEQUENCE) as public_key_info: algorithm = public_key_info.read_element(SEQUENCE) - public_key = public_key_info.read_element(BIT_STRING) + public_key_data = public_key_info.read_element(BIT_STRING) # Double-check the algorithm structure. with algorithm: @@ -65,10 +66,10 @@ def _key_identifier_from_public_key(public_key): # BIT STRING contents begin with the number of padding bytes added. It # must be zero for SubjectPublicKeyInfo structures. - if public_key.read_byte() != 0: + if public_key_data.read_byte() != 0: raise ValueError("Invalid public key encoding") - data = public_key.data + data = public_key_data.data return hashlib.sha1(data).digest() @@ -110,14 +111,14 @@ class Extensions(object): def __init__(self, extensions: typing.List["Extension"]): self._extensions = extensions - def get_extension_for_oid(self, oid): + def get_extension_for_oid(self, oid: ObjectIdentifier) -> "Extension": for ext in self: if ext.oid == oid: return ext raise ExtensionNotFound("No {} extension was found".format(oid), oid) - def get_extension_for_class(self, extclass): + def get_extension_for_class(self, extclass) -> "Extension": if extclass is UnrecognizedExtension: raise TypeError( "UnrecognizedExtension can't be used with " @@ -142,7 +143,7 @@ def __repr__(self): class CRLNumber(ExtensionType): oid = ExtensionOID.CRL_NUMBER - def __init__(self, crl_number): + def __init__(self, crl_number: int): if not isinstance(crl_number, int): raise TypeError("crl_number must be an integer") @@ -171,9 +172,9 @@ class AuthorityKeyIdentifier(ExtensionType): def __init__( self, - key_identifier, - authority_cert_issuer, - authority_cert_serial_number, + key_identifier: typing.Optional[bytes], + authority_cert_issuer: typing.Optional[typing.Iterable[GeneralName]], + authority_cert_serial_number: typing.Optional[int], ): if (authority_cert_issuer is None) != ( authority_cert_serial_number is None @@ -203,7 +204,9 @@ def __init__( self._authority_cert_serial_number = authority_cert_serial_number @classmethod - def from_issuer_public_key(cls, public_key): + def from_issuer_public_key( + cls, public_key: _PUBLIC_KEY_TYPES + ) -> "AuthorityKeyIdentifier": digest = _key_identifier_from_public_key(public_key) return cls( key_identifier=digest, @@ -212,7 +215,9 @@ def from_issuer_public_key(cls, public_key): ) @classmethod - def from_issuer_subject_key_identifier(cls, ski): + def from_issuer_subject_key_identifier( + cls, ski: "SubjectKeyIdentifier" + ) -> "AuthorityKeyIdentifier": return cls( key_identifier=ski.digest, authority_cert_issuer=None, @@ -260,11 +265,13 @@ def __hash__(self): class SubjectKeyIdentifier(ExtensionType): oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER - def __init__(self, digest): + def __init__(self, digest: bytes): self._digest = digest @classmethod - def from_public_key(cls, public_key): + def from_public_key( + cls, public_key: _PUBLIC_KEY_TYPES + ) -> "SubjectKeyIdentifier": return cls(_key_identifier_from_public_key(public_key)) digest = utils.read_only_property("_digest") @@ -288,7 +295,7 @@ def __hash__(self): class AuthorityInformationAccess(ExtensionType): oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS - def __init__(self, descriptions): + def __init__(self, descriptions: typing.Iterable["AccessDescription"]): descriptions = list(descriptions) if not all(isinstance(x, AccessDescription) for x in descriptions): raise TypeError( @@ -319,7 +326,7 @@ def __hash__(self): class SubjectInformationAccess(ExtensionType): oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS - def __init__(self, descriptions): + def __init__(self, descriptions: typing.Iterable["AccessDescription"]): descriptions = list(descriptions) if not all(isinstance(x, AccessDescription) for x in descriptions): raise TypeError( @@ -348,7 +355,9 @@ def __hash__(self): class AccessDescription(object): - def __init__(self, access_method, access_location): + def __init__( + self, access_method: ObjectIdentifier, access_location: GeneralName + ): if not isinstance(access_method, ObjectIdentifier): raise TypeError("access_method must be an ObjectIdentifier") @@ -386,7 +395,7 @@ def __hash__(self): class BasicConstraints(ExtensionType): oid = ExtensionOID.BASIC_CONSTRAINTS - def __init__(self, ca, path_length): + def __init__(self, ca: bool, path_length: typing.Optional[int]): if not isinstance(ca, bool): raise TypeError("ca must be a boolean value") @@ -427,7 +436,7 @@ def __hash__(self): class DeltaCRLIndicator(ExtensionType): oid = ExtensionOID.DELTA_CRL_INDICATOR - def __init__(self, crl_number): + def __init__(self, crl_number: int): if not isinstance(crl_number, int): raise TypeError("crl_number must be an integer") @@ -454,7 +463,9 @@ def __repr__(self): class CRLDistributionPoints(ExtensionType): oid = ExtensionOID.CRL_DISTRIBUTION_POINTS - def __init__(self, distribution_points): + def __init__( + self, distribution_points: typing.Iterable["DistributionPoint"] + ): distribution_points = list(distribution_points) if not all( isinstance(x, DistributionPoint) for x in distribution_points @@ -489,7 +500,9 @@ def __hash__(self): class FreshestCRL(ExtensionType): oid = ExtensionOID.FRESHEST_CRL - def __init__(self, distribution_points): + def __init__( + self, distribution_points: typing.Iterable["DistributionPoint"] + ): distribution_points = list(distribution_points) if not all( isinstance(x, DistributionPoint) for x in distribution_points @@ -522,7 +535,13 @@ def __hash__(self): class DistributionPoint(object): - def __init__(self, full_name, relative_name, reasons, crl_issuer): + def __init__( + self, + full_name: typing.Optional[typing.Iterable[GeneralName]], + relative_name: typing.Optional[RelativeDistinguishedName], + reasons: typing.Optional[typing.FrozenSet["ReasonFlags"]], + crl_issuer: typing.Optional[typing.Iterable[GeneralName]], + ): if full_name and relative_name: raise ValueError( "You cannot provide both full_name and relative_name, at " @@ -631,7 +650,11 @@ class ReasonFlags(Enum): class PolicyConstraints(ExtensionType): oid = ExtensionOID.POLICY_CONSTRAINTS - def __init__(self, require_explicit_policy, inhibit_policy_mapping): + def __init__( + self, + require_explicit_policy: typing.Optional[int], + inhibit_policy_mapping: typing.Optional[int], + ): if require_explicit_policy is not None and not isinstance( require_explicit_policy, int ): @@ -691,7 +714,7 @@ def __hash__(self): class CertificatePolicies(ExtensionType): oid = ExtensionOID.CERTIFICATE_POLICIES - def __init__(self, policies): + def __init__(self, policies: typing.Iterable["PolicyInformation"]): policies = list(policies) if not all(isinstance(x, PolicyInformation) for x in policies): raise TypeError( @@ -720,7 +743,13 @@ def __hash__(self): class PolicyInformation(object): - def __init__(self, policy_identifier, policy_qualifiers): + def __init__( + self, + policy_identifier: ObjectIdentifier, + policy_qualifiers: typing.Optional[ + typing.Iterable[typing.Union[str, "UserNotice"]] + ], + ): if not isinstance(policy_identifier, ObjectIdentifier): raise TypeError("policy_identifier must be an ObjectIdentifier") @@ -769,7 +798,11 @@ def __hash__(self): class UserNotice(object): - def __init__(self, notice_reference, explicit_text): + def __init__( + self, + notice_reference: typing.Optional["NoticeReference"], + explicit_text: typing.Optional[str], + ): if notice_reference and not isinstance( notice_reference, NoticeReference ): @@ -806,7 +839,11 @@ def __hash__(self): class NoticeReference(object): - def __init__(self, organization, notice_numbers): + def __init__( + self, + organization: typing.Optional[str], + notice_numbers: typing.Iterable[int], + ): self._organization = organization notice_numbers = list(notice_numbers) if not all(isinstance(x, int) for x in notice_numbers): @@ -842,7 +879,7 @@ def __hash__(self): class ExtendedKeyUsage(ExtensionType): oid = ExtensionOID.EXTENDED_KEY_USAGE - def __init__(self, usages): + def __init__(self, usages: typing.Iterable[ObjectIdentifier]): usages = list(usages) if not all(isinstance(x, ObjectIdentifier) for x in usages): raise TypeError( @@ -910,7 +947,7 @@ def __repr__(self): class TLSFeature(ExtensionType): oid = ExtensionOID.TLS_FEATURE - def __init__(self, features): + def __init__(self, features: typing.Iterable["TLSFeatureType"]): features = list(features) if ( not all(isinstance(x, TLSFeatureType) for x in features) @@ -958,7 +995,7 @@ class TLSFeatureType(Enum): class InhibitAnyPolicy(ExtensionType): oid = ExtensionOID.INHIBIT_ANY_POLICY - def __init__(self, skip_certs): + def __init__(self, skip_certs: int): if not isinstance(skip_certs, int): raise TypeError("skip_certs must be an integer") @@ -990,15 +1027,15 @@ class KeyUsage(ExtensionType): def __init__( self, - digital_signature, - content_commitment, - key_encipherment, - data_encipherment, - key_agreement, - key_cert_sign, - crl_sign, - encipher_only, - decipher_only, + digital_signature: bool, + content_commitment: bool, + key_encipherment: bool, + data_encipherment: bool, + key_agreement: bool, + key_cert_sign: bool, + crl_sign: bool, + encipher_only: bool, + decipher_only: bool, ): if not key_agreement and (encipher_only or decipher_only): raise ValueError( @@ -1101,7 +1138,11 @@ def __hash__(self): class NameConstraints(ExtensionType): oid = ExtensionOID.NAME_CONSTRAINTS - def __init__(self, permitted_subtrees, excluded_subtrees): + def __init__( + self, + permitted_subtrees: typing.Optional[typing.Iterable[GeneralName]], + excluded_subtrees: typing.Optional[typing.Iterable[GeneralName]], + ): if permitted_subtrees is not None: permitted_subtrees = list(permitted_subtrees) if not all(isinstance(x, GeneralName) for x in permitted_subtrees): @@ -1180,7 +1221,9 @@ def __hash__(self): class Extension(object): - def __init__(self, oid, critical, value): + def __init__( + self, oid: ObjectIdentifier, critical: bool, value: ExtensionType + ): if not isinstance(oid, ObjectIdentifier): raise TypeError( "oid argument must be an ObjectIdentifier instance." @@ -1221,7 +1264,7 @@ def __hash__(self): class GeneralNames(object): - def __init__(self, general_names): + def __init__(self, general_names: typing.Iterable[GeneralName]): general_names = list(general_names) if not all(isinstance(x, GeneralName) for x in general_names): raise TypeError( @@ -1233,7 +1276,7 @@ def __init__(self, general_names): __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - def get_values_for_type(self, type): + def get_values_for_type(self, type: typing.Type[GeneralName]): # Return the value of each GeneralName, except for OtherName instances # which we return directly because it has two important properties not # just one value. @@ -1261,7 +1304,7 @@ def __hash__(self): class SubjectAlternativeName(ExtensionType): oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME - def __init__(self, general_names): + def __init__(self, general_names: typing.Iterable[GeneralName]): self._general_names = GeneralNames(general_names) __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") @@ -1288,7 +1331,7 @@ def __hash__(self): class IssuerAlternativeName(ExtensionType): oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME - def __init__(self, general_names): + def __init__(self, general_names: typing.Iterable[GeneralName]): self._general_names = GeneralNames(general_names) __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") @@ -1315,7 +1358,7 @@ def __hash__(self): class CertificateIssuer(ExtensionType): oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER - def __init__(self, general_names): + def __init__(self, general_names: typing.Iterable[GeneralName]): self._general_names = GeneralNames(general_names) __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") @@ -1342,7 +1385,7 @@ def __hash__(self): class CRLReason(ExtensionType): oid = CRLEntryExtensionOID.CRL_REASON - def __init__(self, reason): + def __init__(self, reason: ReasonFlags): if not isinstance(reason, ReasonFlags): raise TypeError("reason must be an element from ReasonFlags") @@ -1369,7 +1412,7 @@ def __hash__(self): class InvalidityDate(ExtensionType): oid = CRLEntryExtensionOID.INVALIDITY_DATE - def __init__(self, invalidity_date): + def __init__(self, invalidity_date: datetime.datetime): if not isinstance(invalidity_date, datetime.datetime): raise TypeError("invalidity_date must be a datetime.datetime") @@ -1398,7 +1441,12 @@ def __hash__(self): class PrecertificateSignedCertificateTimestamps(ExtensionType): oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS - def __init__(self, signed_certificate_timestamps): + def __init__( + self, + signed_certificate_timestamps: typing.Iterable[ + SignedCertificateTimestamp + ], + ): signed_certificate_timestamps = list(signed_certificate_timestamps) if not all( isinstance(sct, SignedCertificateTimestamp) @@ -1438,7 +1486,12 @@ def __ne__(self, other): class SignedCertificateTimestamps(ExtensionType): oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS - def __init__(self, signed_certificate_timestamps): + def __init__( + self, + signed_certificate_timestamps: typing.Iterable[ + SignedCertificateTimestamp + ], + ): signed_certificate_timestamps = list(signed_certificate_timestamps) if not all( isinstance(sct, SignedCertificateTimestamp) @@ -1476,7 +1529,7 @@ def __ne__(self, other): class OCSPNonce(ExtensionType): oid = OCSPExtensionOID.NONCE - def __init__(self, nonce): + def __init__(self, nonce: bytes): if not isinstance(nonce, bytes): raise TypeError("nonce must be bytes") @@ -1642,7 +1695,7 @@ def __hash__(self): class UnrecognizedExtension(ExtensionType): - def __init__(self, oid, value): + def __init__(self, oid: ObjectIdentifier, value: bytes): if not isinstance(oid, ObjectIdentifier): raise TypeError("oid must be an ObjectIdentifier") self._oid = oid diff --git a/src/cryptography/x509/general_name.py b/src/cryptography/x509/general_name.py index 6683e9313ce8..a83471e93131 100644 --- a/src/cryptography/x509/general_name.py +++ b/src/cryptography/x509/general_name.py @@ -40,8 +40,7 @@ def value(self): """ -@utils.register_interface(GeneralName) -class RFC822Name(object): +class RFC822Name(GeneralName): def __init__(self, value: str): if isinstance(value, str): try: @@ -87,8 +86,7 @@ def __hash__(self) -> int: return hash(self.value) -@utils.register_interface(GeneralName) -class DNSName(object): +class DNSName(GeneralName): def __init__(self, value: str): if isinstance(value, str): try: @@ -128,8 +126,7 @@ def __hash__(self) -> int: return hash(self.value) -@utils.register_interface(GeneralName) -class UniformResourceIdentifier(object): +class UniformResourceIdentifier(GeneralName): def __init__(self, value: str): if isinstance(value, str): try: @@ -169,8 +166,7 @@ def __hash__(self) -> int: return hash(self.value) -@utils.register_interface(GeneralName) -class DirectoryName(object): +class DirectoryName(GeneralName): def __init__(self, value: Name): if not isinstance(value, Name): raise TypeError("value must be a Name") @@ -195,8 +191,7 @@ def __hash__(self) -> int: return hash(self.value) -@utils.register_interface(GeneralName) -class RegisteredID(object): +class RegisteredID(GeneralName): def __init__(self, value: ObjectIdentifier): if not isinstance(value, ObjectIdentifier): raise TypeError("value must be an ObjectIdentifier") @@ -221,8 +216,7 @@ def __hash__(self) -> int: return hash(self.value) -@utils.register_interface(GeneralName) -class IPAddress(object): +class IPAddress(GeneralName): def __init__( self, value: typing.Union[ @@ -267,8 +261,7 @@ def __hash__(self) -> int: return hash(self.value) -@utils.register_interface(GeneralName) -class OtherName(object): +class OtherName(GeneralName): def __init__(self, type_id: ObjectIdentifier, value: bytes): if not isinstance(type_id, ObjectIdentifier): raise TypeError("type_id must be an ObjectIdentifier") diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py index c183160e0aca..a579aa219638 100644 --- a/src/cryptography/x509/name.py +++ b/src/cryptography/x509/name.py @@ -216,7 +216,7 @@ def rfc4514_string(self) -> str: attr.rfc4514_string() for attr in reversed(self._attributes) ) - def get_attributes_for_oid(self, oid) -> typing.Iterable[NameAttribute]: + def get_attributes_for_oid(self, oid) -> typing.List[NameAttribute]: return [i for i in self if i.oid == oid] @property diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index d4b90b8c4203..afacd5a6e793 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "cfg-if" version = "1.0.0" @@ -36,10 +42,24 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.3" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", "unindent", ] @@ -102,9 +122,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ "cfg-if", "instant", @@ -116,9 +136,28 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.4" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" @@ -131,9 +170,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ca634cf3acd58a599b535ed6cb188223298977d471d146121792bfa23b754c" +checksum = "4837b8e8e18a102c23f79d1e9a110b597ea3b684c95e874eb1ad88f8683109c3" dependencies = [ "cfg-if", "ctor", @@ -148,9 +187,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483ac516dbda6789a5b4be0271e7a31b9ad4ec8c0a5955050e8076f72bdbef8f" +checksum = "a47f2c300ceec3e58064fd5f8f5b61230f2ffd64bde4970c81fdd0563a2db1bb" dependencies = [ "pyo3-macros-backend", "quote", @@ -159,9 +198,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15230cabcda008f03565ed8bac40f094cbb5ee1b46e6551f1ec3a0e922cf7df9" +checksum = "87b097e5d84fcbe3e167f400fbedd657820a375b034c78bd852050749a575d66" dependencies = [ "proc-macro2", "quote", @@ -170,18 +209,21 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] [[package]] name = "scopeguard" diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index f06ac5f02125..1580ca4fcef7 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -3,6 +3,8 @@ // for complete details. #[pyo3::prelude::pymodule] +// False positive: https://github.com/rust-lang/rust-clippy/issues/6721 +#[allow(clippy::unnecessary_wraps)] fn _rust(_py: pyo3::Python<'_>, _m: &pyo3::types::PyModule) -> pyo3::PyResult<()> { Ok(()) } diff --git a/tests/x509/test_ocsp.py b/tests/x509/test_ocsp.py index 5793f6d62be3..5d9da790af9f 100644 --- a/tests/x509/test_ocsp.py +++ b/tests/x509/test_ocsp.py @@ -726,7 +726,9 @@ def test_invalid_build_successful_status(self): class TestSignedCertificateTimestampsExtension(object): def test_init(self): with pytest.raises(TypeError): - x509.SignedCertificateTimestamps([object()]) + x509.SignedCertificateTimestamps( + [object()] # type: ignore[list-item] + ) def test_repr(self): assert repr(x509.SignedCertificateTimestamps([])) == ( diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 39f7bb951d41..b1e86f43647e 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -4070,7 +4070,9 @@ def test_subject_alt_name_unsupported_general_name(self, backend): x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "SAN")]) ) .add_extension( - x509.SubjectAlternativeName([FakeGeneralName("")]), + x509.SubjectAlternativeName( + [FakeGeneralName("")] # type:ignore[list-item] + ), critical=False, ) ) diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py index 011649f4ecd9..b8f226d5f848 100644 --- a/tests/x509/test_x509_ext.py +++ b/tests/x509/test_x509_ext.py @@ -7,6 +7,7 @@ import datetime import ipaddress import os +import typing import pretend @@ -56,12 +57,16 @@ class TestExtension(object): def test_not_an_oid(self): bc = x509.BasicConstraints(ca=False, path_length=None) with pytest.raises(TypeError): - x509.Extension("notanoid", True, bc) + x509.Extension("notanoid", True, bc) # type:ignore[arg-type] def test_critical_not_a_bool(self): bc = x509.BasicConstraints(ca=False, path_length=None) with pytest.raises(TypeError): - x509.Extension(ExtensionOID.BASIC_CONSTRAINTS, "notabool", bc) + x509.Extension( + ExtensionOID.BASIC_CONSTRAINTS, + "notabool", # type:ignore[arg-type] + bc, + ) def test_repr(self): bc = x509.BasicConstraints(ca=False, path_length=None) @@ -73,16 +78,38 @@ def test_repr(self): ) def test_eq(self): - ext1 = x509.Extension(x509.ObjectIdentifier("1.2.3.4"), False, "value") - ext2 = x509.Extension(x509.ObjectIdentifier("1.2.3.4"), False, "value") + ext1 = x509.Extension( + x509.ObjectIdentifier("1.2.3.4"), + False, + x509.BasicConstraints(ca=False, path_length=None), + ) + ext2 = x509.Extension( + x509.ObjectIdentifier("1.2.3.4"), + False, + x509.BasicConstraints(ca=False, path_length=None), + ) assert ext1 == ext2 def test_ne(self): - ext1 = x509.Extension(x509.ObjectIdentifier("1.2.3.4"), False, "value") - ext2 = x509.Extension(x509.ObjectIdentifier("1.2.3.5"), False, "value") - ext3 = x509.Extension(x509.ObjectIdentifier("1.2.3.4"), True, "value") + ext1 = x509.Extension( + x509.ObjectIdentifier("1.2.3.4"), + False, + x509.BasicConstraints(ca=False, path_length=None), + ) + ext2 = x509.Extension( + x509.ObjectIdentifier("1.2.3.5"), + False, + x509.BasicConstraints(ca=False, path_length=None), + ) + ext3 = x509.Extension( + x509.ObjectIdentifier("1.2.3.4"), + True, + x509.BasicConstraints(ca=False, path_length=None), + ) ext4 = x509.Extension( - x509.ObjectIdentifier("1.2.3.4"), False, "value4" + x509.ObjectIdentifier("1.2.3.4"), + False, + x509.BasicConstraints(ca=True, path_length=None), ) assert ext1 != ext2 assert ext1 != ext3 @@ -112,7 +139,7 @@ def test_hash(self): class TestTLSFeature(object): def test_not_enum_type(self): with pytest.raises(TypeError): - x509.TLSFeature([3]) + x509.TLSFeature([3]) # type:ignore[list-item] def test_empty_list(self): with pytest.raises(TypeError): @@ -181,7 +208,9 @@ def test_indexing(self): class TestUnrecognizedExtension(object): def test_invalid_oid(self): with pytest.raises(TypeError): - x509.UnrecognizedExtension("notanoid", b"somedata") + x509.UnrecognizedExtension( + "notanoid", b"somedata" # type:ignore[arg-type] + ) def test_eq(self): ext1 = x509.UnrecognizedExtension( @@ -289,7 +318,7 @@ def test_hash(self): class TestCRLReason(object): def test_invalid_reason_flags(self): with pytest.raises(TypeError): - x509.CRLReason("notareason") + x509.CRLReason("notareason") # type:ignore[arg-type] def test_eq(self): reason1 = x509.CRLReason(x509.ReasonFlags.unspecified) @@ -318,7 +347,7 @@ def test_repr(self): class TestDeltaCRLIndicator(object): def test_not_int(self): with pytest.raises(TypeError): - x509.DeltaCRLIndicator("notanint") + x509.DeltaCRLIndicator("notanint") # type:ignore[arg-type] def test_eq(self): delta1 = x509.DeltaCRLIndicator(1) @@ -346,7 +375,7 @@ def test_hash(self): class TestInvalidityDate(object): def test_invalid_invalidity_date(self): with pytest.raises(TypeError): - x509.InvalidityDate("notadate") + x509.InvalidityDate("notadate") # type:ignore[arg-type] def test_eq(self): invalid1 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) @@ -376,11 +405,13 @@ def test_hash(self): class TestNoticeReference(object): def test_notice_numbers_not_all_int(self): with pytest.raises(TypeError): - x509.NoticeReference("org", [1, 2, "three"]) + x509.NoticeReference( + "org", [1, 2, "three"] # type:ignore[list-item] + ) def test_notice_numbers_none(self): with pytest.raises(TypeError): - x509.NoticeReference("org", None) + x509.NoticeReference("org", None) # type:ignore[arg-type] def test_iter_input(self): numbers = [1, 3, 4] @@ -419,7 +450,7 @@ def test_hash(self): class TestUserNotice(object): def test_notice_reference_invalid(self): with pytest.raises(TypeError): - x509.UserNotice("invalid", None) + x509.UserNotice("invalid", None) # type:ignore[arg-type] def test_notice_reference_none(self): un = x509.UserNotice(None, "text") @@ -463,7 +494,7 @@ def test_hash(self): class TestPolicyInformation(object): def test_invalid_policy_identifier(self): with pytest.raises(TypeError): - x509.PolicyInformation("notanoid", None) + x509.PolicyInformation("notanoid", None) # type:ignore[arg-type] def test_none_policy_qualifiers(self): pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), None) @@ -478,7 +509,10 @@ def test_policy_qualifiers(self): def test_invalid_policy_identifiers(self): with pytest.raises(TypeError): - x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), [1, 2]) + x509.PolicyInformation( + x509.ObjectIdentifier("1.2.3"), + [1, 2], # type:ignore[list-item] + ) def test_iter_input(self): qual = ["foo", "bar"] @@ -486,7 +520,10 @@ def test_iter_input(self): assert list(pi.policy_qualifiers) == qual def test_repr(self): - pq = ["string", x509.UserNotice(None, "hi")] + pq: typing.List[typing.Union[str, x509.UserNotice]] = [ + "string", + x509.UserNotice(None, "hi"), + ] pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), pq) assert repr(pi) == ( "