Skip to content

diff review of cryptography update from 2.6.1 to 3.2.1

rmol edited this page Nov 3, 2020 · 1 revision

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256

Comparing cryptography-2.6.1-cp34-abi3-manylinux1_x86_64.whl & cryptography-3.2.1-cp35-abi3-manylinux2010_x86_64.whl

45a4f4cf4f4e6a55c8128f8b76b4c057027b27d4c67e3fe157fa02f27e37830d  cryptography-2.6.1-cp34-abi3-manylinux1_x86_64.whl
32434673d8505b42c0de4de86da8c1620651abd24afe91ae0335597683ed1b77  cryptography-3.2.1-cp35-abi3-manylinux2010_x86_64.whl

zipinfo /dev/stdin

@@ -1,89 +1,96 @@
-Zip file size: 2267787 bytes, number of entries: 87
--rw-r--r--  2.0 unx       46 b- defN 19-Feb-27 23:31 cryptography-2.6.1.dist-info/top_level.txt
--rw-r--r--  2.0 unx     8709 b- defN 19-Feb-27 23:32 cryptography-2.6.1.dist-info/RECORD
--rw-r--r--  2.0 unx      108 b- defN 19-Feb-27 23:32 cryptography-2.6.1.dist-info/WHEEL
--rw-r--r--  2.0 unx     5086 b- defN 19-Feb-27 23:32 cryptography-2.6.1.dist-info/METADATA
--rw-r--r--  2.0 unx      816 b- defN 19-Feb-27 23:27 cryptography/__about__.py
--rw-r--r--  2.0 unx     5220 b- defN 19-Feb-27 23:27 cryptography/fernet.py
--rw-r--r--  2.0 unx     4962 b- defN 19-Feb-27 23:27 cryptography/utils.py
--rw-r--r--  2.0 unx      527 b- defN 19-Feb-27 23:27 cryptography/__init__.py
--rw-r--r--  2.0 unx     1234 b- defN 19-Feb-27 23:27 cryptography/exceptions.py
--rw-r--r--  2.0 unx     2178 b- defN 19-Feb-27 23:27 cryptography/hazmat/_oid.py
--rw-r--r--  2.0 unx      483 b- defN 19-Feb-27 23:27 cryptography/hazmat/__init__.py
--rw-r--r--  2.0 unx      496 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/__init__.py
--rw-r--r--  2.0 unx    10789 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/interfaces.py
--rw-r--r--  2.0 unx    10228 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/dsa.py
--rw-r--r--  2.0 unx     5679 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/aead.py
--rw-r--r--  2.0 unx    18021 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/rsa.py
--rw-r--r--  2.0 unx    32758 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/decode_asn1.py
--rw-r--r--  2.0 unx     6043 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/ed448.py
--rw-r--r--  2.0 unx    13654 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/ocsp.py
--rw-r--r--  2.0 unx     5578 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/x25519.py
--rw-r--r--  2.0 unx    20097 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/x509.py
--rw-r--r--  2.0 unx     3196 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/hashes.py
--rw-r--r--  2.0 unx    94525 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/backend.py
--rw-r--r--  2.0 unx     6079 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/ed25519.py
--rw-r--r--  2.0 unx     2902 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/cmac.py
--rw-r--r--  2.0 unx    10814 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/dh.py
--rw-r--r--  2.0 unx     2339 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/utils.py
--rw-r--r--  2.0 unx      336 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/__init__.py
--rw-r--r--  2.0 unx     4524 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/x448.py
--rw-r--r--  2.0 unx    23412 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/encode_asn1.py
--rw-r--r--  2.0 unx    12158 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/ec.py
--rw-r--r--  2.0 unx     3045 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/hmac.py
--rw-r--r--  2.0 unx     9314 b- defN 19-Feb-27 23:27 cryptography/hazmat/backends/openssl/ciphers.py
--rwxr-xr-x  2.0 unx    23127 b- defN 19-Feb-27 23:32 cryptography/hazmat/bindings/_padding.abi3.so
--rwxr-xr-x  2.0 unx    21048 b- defN 19-Feb-27 23:32 cryptography/hazmat/bindings/_constant_time.abi3.so
--rwxr-xr-x  2.0 unx  5703926 b- defN 19-Feb-27 23:32 cryptography/hazmat/bindings/_openssl.abi3.so
--rw-r--r--  2.0 unx      246 b- defN 19-Feb-27 23:27 cryptography/hazmat/bindings/__init__.py
--rw-r--r--  2.0 unx     7120 b- defN 19-Feb-27 23:27 cryptography/hazmat/bindings/openssl/binding.py
--rw-r--r--  2.0 unx    10899 b- defN 19-Feb-27 23:27 cryptography/hazmat/bindings/openssl/_conditional.py
--rw-r--r--  2.0 unx      246 b- defN 19-Feb-27 23:27 cryptography/hazmat/bindings/openssl/__init__.py
--rw-r--r--  2.0 unx     6206 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/hashes.py
--rw-r--r--  2.0 unx     1136 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/constant_time.py
--rw-r--r--  2.0 unx     2122 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/cmac.py
--rw-r--r--  2.0 unx     5644 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/padding.py
--rw-r--r--  2.0 unx      246 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/__init__.py
--rw-r--r--  2.0 unx     2243 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/hmac.py
--rw-r--r--  2.0 unx     5462 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/keywrap.py
--rw-r--r--  2.0 unx      884 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/mac.py
--rw-r--r--  2.0 unx     3463 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/hkdf.py
--rw-r--r--  2.0 unx     4905 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/kbkdf.py
--rw-r--r--  2.0 unx     2280 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/x963kdf.py
--rw-r--r--  2.0 unx      771 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/__init__.py
--rw-r--r--  2.0 unx     3981 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/concatkdf.py
--rw-r--r--  2.0 unx     2088 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/pbkdf2.py
--rw-r--r--  2.0 unx     2155 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/kdf/scrypt.py
--rw-r--r--  2.0 unx     6437 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/ciphers/aead.py
--rw-r--r--  2.0 unx     4190 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/ciphers/algorithms.py
--rw-r--r--  2.0 unx     7144 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/ciphers/base.py
--rw-r--r--  2.0 unx     6641 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/ciphers/modes.py
--rw-r--r--  2.0 unx      626 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/ciphers/__init__.py
--rw-r--r--  2.0 unx      377 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/serialization/pkcs12.py
--rw-r--r--  2.0 unx     1904 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/serialization/base.py
--rw-r--r--  2.0 unx     1046 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/serialization/__init__.py
--rw-r--r--  2.0 unx     4381 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/serialization/ssh.py
--rw-r--r--  2.0 unx      954 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/twofactor/utils.py
--rw-r--r--  2.0 unx     2589 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/twofactor/hotp.py
--rw-r--r--  2.0 unx     1594 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/twofactor/totp.py
--rw-r--r--  2.0 unx      288 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/twofactor/__init__.py
--rw-r--r--  2.0 unx     6891 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/dsa.py
--rw-r--r--  2.0 unx    10317 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/rsa.py
--rw-r--r--  2.0 unx     2322 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/ed448.py
--rw-r--r--  2.0 unx     2281 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/x25519.py
--rw-r--r--  2.0 unx     2395 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/ed25519.py
--rw-r--r--  2.0 unx     5454 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/dh.py
--rw-r--r--  2.0 unx     1101 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/utils.py
--rw-r--r--  2.0 unx     2261 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/padding.py
--rw-r--r--  2.0 unx     1020 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/__init__.py
--rw-r--r--  2.0 unx     2249 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/x448.py
--rw-r--r--  2.0 unx    13758 b- defN 19-Feb-27 23:27 cryptography/hazmat/primitives/asymmetric/ec.py
--rw-r--r--  2.0 unx     1000 b- defN 19-Feb-27 23:27 cryptography/x509/certificate_transparency.py
--rw-r--r--  2.0 unx    12960 b- defN 19-Feb-27 23:27 cryptography/x509/ocsp.py
--rw-r--r--  2.0 unx    49959 b- defN 19-Feb-27 23:27 cryptography/x509/extensions.py
--rw-r--r--  2.0 unx    23739 b- defN 19-Feb-27 23:27 cryptography/x509/base.py
--rw-r--r--  2.0 unx     8016 b- defN 19-Feb-27 23:27 cryptography/x509/name.py
--rw-r--r--  2.0 unx    10462 b- defN 19-Feb-27 23:27 cryptography/x509/general_name.py
--rw-r--r--  2.0 unx    10632 b- defN 19-Feb-27 23:27 cryptography/x509/oid.py
--rw-r--r--  2.0 unx     7305 b- defN 19-Feb-27 23:27 cryptography/x509/__init__.py
-87 files, 6347847 bytes uncompressed, 2253731 bytes compressed:  64.5%
+Zip file size: 2606314 bytes, number of entries: 94
+-rw-r--r--  2.0 unx     2475 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/AUTHORS.rst
+-rw-rw-r--  2.0 unx     9397 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/RECORD
+-rw-r--r--  2.0 unx     5176 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/METADATA
+-rw-r--r--  2.0 unx     2415 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/LICENSE.PSF
+-rw-r--r--  2.0 unx       31 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/top_level.txt
+-rw-r--r--  2.0 unx      111 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/WHEEL
+-rw-r--r--  2.0 unx    11360 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/LICENSE.APACHE
+-rw-r--r--  2.0 unx     1532 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/LICENSE.BSD
+-rw-r--r--  2.0 unx      352 b- defN 20-Oct-28 03:21 cryptography-3.2.1.dist-info/LICENSE
+-rw-r--r--  2.0 unx     5980 b- defN 20-Oct-28 03:11 cryptography/fernet.py
+-rw-r--r--  2.0 unx     1208 b- defN 20-Oct-28 03:11 cryptography/__init__.py
+-rw-r--r--  2.0 unx      835 b- defN 20-Oct-28 03:11 cryptography/__about__.py
+-rw-r--r--  2.0 unx     4745 b- defN 20-Oct-28 03:11 cryptography/utils.py
+-rw-r--r--  2.0 unx     1259 b- defN 20-Oct-28 03:11 cryptography/exceptions.py
+-rw-r--r--  2.0 unx     5205 b- defN 20-Oct-28 03:11 cryptography/hazmat/_der.py
+-rw-r--r--  2.0 unx      483 b- defN 20-Oct-28 03:11 cryptography/hazmat/__init__.py
+-rw-r--r--  2.0 unx     2432 b- defN 20-Oct-28 03:11 cryptography/hazmat/_oid.py
+-rw-r--r--  2.0 unx      616 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/__init__.py
+-rw-r--r--  2.0 unx    10770 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/interfaces.py
+-rw-r--r--  2.0 unx     3015 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/hmac.py
+-rw-r--r--  2.0 unx     3169 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/hashes.py
+-rw-r--r--  2.0 unx     4011 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/x448.py
+-rw-r--r--  2.0 unx    10239 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/dh.py
+-rw-r--r--  2.0 unx     4488 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/x25519.py
+-rw-r--r--  2.0 unx     5626 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/ed448.py
+-rw-r--r--  2.0 unx      336 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/__init__.py
+-rw-r--r--  2.0 unx    10036 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/dsa.py
+-rw-r--r--  2.0 unx    23553 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/encode_asn1.py
+-rw-r--r--  2.0 unx     8608 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/ciphers.py
+-rw-r--r--  2.0 unx   106907 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/backend.py
+-rw-r--r--  2.0 unx    17378 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/rsa.py
+-rw-r--r--  2.0 unx    14028 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/ocsp.py
+-rw-r--r--  2.0 unx    32345 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/decode_asn1.py
+-rw-r--r--  2.0 unx     5765 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/aead.py
+-rw-r--r--  2.0 unx     5670 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/ed25519.py
+-rw-r--r--  2.0 unx     2419 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/poly1305.py
+-rw-r--r--  2.0 unx     2304 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/utils.py
+-rw-r--r--  2.0 unx    21620 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/x509.py
+-rw-r--r--  2.0 unx     2855 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/cmac.py
+-rw-r--r--  2.0 unx    12085 b- defN 20-Oct-28 03:11 cryptography/hazmat/backends/openssl/ec.py
+-rwxr-xr-x  2.0 unx    37232 b- defN 20-Oct-28 03:21 cryptography/hazmat/bindings/_padding.abi3.so
+-rw-r--r--  2.0 unx      246 b- defN 20-Oct-28 03:11 cryptography/hazmat/bindings/__init__.py
+-rwxr-xr-x  2.0 unx  7050912 b- defN 20-Oct-28 03:21 cryptography/hazmat/bindings/_openssl.abi3.so
+-rw-r--r--  2.0 unx      246 b- defN 20-Oct-28 03:11 cryptography/hazmat/bindings/openssl/__init__.py
+-rw-r--r--  2.0 unx     7947 b- defN 20-Oct-28 03:11 cryptography/hazmat/bindings/openssl/binding.py
+-rw-r--r--  2.0 unx     8762 b- defN 20-Oct-28 03:11 cryptography/hazmat/bindings/openssl/_conditional.py
+-rw-r--r--  2.0 unx     2306 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/hmac.py
+-rw-r--r--  2.0 unx     6315 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/hashes.py
+-rw-r--r--  2.0 unx      430 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/constant_time.py
+-rw-r--r--  2.0 unx      246 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/__init__.py
+-rw-r--r--  2.0 unx     5730 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/keywrap.py
+-rw-r--r--  2.0 unx     5743 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/padding.py
+-rw-r--r--  2.0 unx     1679 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/poly1305.py
+-rw-r--r--  2.0 unx     2130 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/cmac.py
+-rw-r--r--  2.0 unx     4625 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/serialization/pkcs7.py
+-rw-r--r--  2.0 unx     2249 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/serialization/base.py
+-rw-r--r--  2.0 unx     1132 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/serialization/__init__.py
+-rw-r--r--  2.0 unx     1853 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/serialization/pkcs12.py
+-rw-r--r--  2.0 unx    21682 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/serialization/ssh.py
+-rw-r--r--  2.0 unx      288 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/twofactor/__init__.py
+-rw-r--r--  2.0 unx     1780 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/twofactor/totp.py
+-rw-r--r--  2.0 unx      982 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/twofactor/utils.py
+-rw-r--r--  2.0 unx     2679 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/twofactor/hotp.py
+-rw-r--r--  2.0 unx     7253 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/ciphers/base.py
+-rw-r--r--  2.0 unx      647 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/ciphers/__init__.py
+-rw-r--r--  2.0 unx     6107 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/ciphers/aead.py
+-rw-r--r--  2.0 unx     4225 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/ciphers/algorithms.py
+-rw-r--r--  2.0 unx     6730 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/ciphers/modes.py
+-rw-r--r--  2.0 unx     2255 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/x448.py
+-rw-r--r--  2.0 unx     5661 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/dh.py
+-rw-r--r--  2.0 unx     2277 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/x25519.py
+-rw-r--r--  2.0 unx     2328 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/ed448.py
+-rw-r--r--  2.0 unx     1020 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/__init__.py
+-rw-r--r--  2.0 unx     7181 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/dsa.py
+-rw-r--r--  2.0 unx    10494 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/rsa.py
+-rw-r--r--  2.0 unx     2401 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/ed25519.py
+-rw-r--r--  2.0 unx     2250 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/padding.py
+-rw-r--r--  2.0 unx     1201 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/utils.py
+-rw-r--r--  2.0 unx    14006 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/asymmetric/ec.py
+-rw-r--r--  2.0 unx      771 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/__init__.py
+-rw-r--r--  2.0 unx     2407 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/x963kdf.py
+-rw-r--r--  2.0 unx     4095 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/concatkdf.py
+-rw-r--r--  2.0 unx     3598 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/hkdf.py
+-rw-r--r--  2.0 unx     2220 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/pbkdf2.py
+-rw-r--r--  2.0 unx     2268 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/scrypt.py
+-rw-r--r--  2.0 unx     5100 b- defN 20-Oct-28 03:11 cryptography/hazmat/primitives/kdf/kbkdf.py
+-rw-r--r--  2.0 unx    12594 b- defN 20-Oct-28 03:11 cryptography/x509/oid.py
+-rw-r--r--  2.0 unx    52924 b- defN 20-Oct-28 03:11 cryptography/x509/extensions.py
+-rw-r--r--  2.0 unx    26409 b- defN 20-Oct-28 03:11 cryptography/x509/base.py
+-rw-r--r--  2.0 unx     7699 b- defN 20-Oct-28 03:11 cryptography/x509/__init__.py
+-rw-r--r--  2.0 unx     8291 b- defN 20-Oct-28 03:11 cryptography/x509/name.py
+-rw-r--r--  2.0 unx     1000 b- defN 20-Oct-28 03:11 cryptography/x509/certificate_transparency.py
+-rw-r--r--  2.0 unx    13245 b- defN 20-Oct-28 03:11 cryptography/x509/ocsp.py
+-rw-r--r--  2.0 unx     7942 b- defN 20-Oct-28 03:11 cryptography/x509/general_name.py
+94 files, 7756632 bytes uncompressed, 2591164 bytes compressed:  66.6%

zipnote {}

@@ -1,262 +1,283 @@
-Filename: cryptography-2.6.1.dist-info/top_level.txt
+Filename: cryptography-3.2.1.dist-info/AUTHORS.rst
 Comment:

-Filename: cryptography-2.6.1.dist-info/RECORD
+Filename: cryptography-3.2.1.dist-info/RECORD
 Comment:

-Filename: cryptography-2.6.1.dist-info/WHEEL
+Filename: cryptography-3.2.1.dist-info/METADATA
 Comment:

-Filename: cryptography-2.6.1.dist-info/METADATA
+Filename: cryptography-3.2.1.dist-info/LICENSE.PSF
 Comment:

-Filename: cryptography/__about__.py
+Filename: cryptography-3.2.1.dist-info/top_level.txt
 Comment:

-Filename: cryptography/fernet.py
+Filename: cryptography-3.2.1.dist-info/WHEEL
 Comment:

-Filename: cryptography/utils.py
+Filename: cryptography-3.2.1.dist-info/LICENSE.APACHE
+Comment:
+
+Filename: cryptography-3.2.1.dist-info/LICENSE.BSD
+Comment:
+
+Filename: cryptography-3.2.1.dist-info/LICENSE
+Comment:
+
+Filename: cryptography/fernet.py
 Comment:

 Filename: cryptography/__init__.py
 Comment:

+Filename: cryptography/__about__.py
+Comment:
+
+Filename: cryptography/utils.py
+Comment:
+
 Filename: cryptography/exceptions.py
 Comment:

-Filename: cryptography/hazmat/_oid.py
+Filename: cryptography/hazmat/_der.py
 Comment:

 Filename: cryptography/hazmat/__init__.py
 Comment:

+Filename: cryptography/hazmat/_oid.py
+Comment:
+
 Filename: cryptography/hazmat/backends/__init__.py
 Comment:

 Filename: cryptography/hazmat/backends/interfaces.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/dsa.py
+Filename: cryptography/hazmat/backends/openssl/hmac.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/aead.py
+Filename: cryptography/hazmat/backends/openssl/hashes.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/rsa.py
+Filename: cryptography/hazmat/backends/openssl/x448.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/decode_asn1.py
+Filename: cryptography/hazmat/backends/openssl/dh.py
+Comment:
+
+Filename: cryptography/hazmat/backends/openssl/x25519.py
 Comment:

 Filename: cryptography/hazmat/backends/openssl/ed448.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/ocsp.py
+Filename: cryptography/hazmat/backends/openssl/__init__.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/x25519.py
+Filename: cryptography/hazmat/backends/openssl/dsa.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/x509.py
+Filename: cryptography/hazmat/backends/openssl/encode_asn1.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/hashes.py
+Filename: cryptography/hazmat/backends/openssl/ciphers.py
 Comment:

 Filename: cryptography/hazmat/backends/openssl/backend.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/ed25519.py
+Filename: cryptography/hazmat/backends/openssl/rsa.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/cmac.py
+Filename: cryptography/hazmat/backends/openssl/ocsp.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/dh.py
+Filename: cryptography/hazmat/backends/openssl/decode_asn1.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/utils.py
+Filename: cryptography/hazmat/backends/openssl/aead.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/__init__.py
+Filename: cryptography/hazmat/backends/openssl/ed25519.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/x448.py
+Filename: cryptography/hazmat/backends/openssl/poly1305.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/encode_asn1.py
+Filename: cryptography/hazmat/backends/openssl/utils.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/ec.py
+Filename: cryptography/hazmat/backends/openssl/x509.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/hmac.py
+Filename: cryptography/hazmat/backends/openssl/cmac.py
 Comment:

-Filename: cryptography/hazmat/backends/openssl/ciphers.py
+Filename: cryptography/hazmat/backends/openssl/ec.py
 Comment:

 Filename: cryptography/hazmat/bindings/_padding.abi3.so
 Comment:

-Filename: cryptography/hazmat/bindings/_constant_time.abi3.so
+Filename: cryptography/hazmat/bindings/__init__.py
 Comment:

 Filename: cryptography/hazmat/bindings/_openssl.abi3.so
 Comment:

-Filename: cryptography/hazmat/bindings/__init__.py
+Filename: cryptography/hazmat/bindings/openssl/__init__.py
 Comment:

 Filename: cryptography/hazmat/bindings/openssl/binding.py
 Comment:

 Filename: cryptography/hazmat/bindings/openssl/_conditional.py
 Comment:

-Filename: cryptography/hazmat/bindings/openssl/__init__.py
+Filename: cryptography/hazmat/primitives/hmac.py
 Comment:

 Filename: cryptography/hazmat/primitives/hashes.py
 Comment:

 Filename: cryptography/hazmat/primitives/constant_time.py
 Comment:

-Filename: cryptography/hazmat/primitives/cmac.py
-Comment:
-
-Filename: cryptography/hazmat/primitives/padding.py
-Comment:
-
 Filename: cryptography/hazmat/primitives/__init__.py
 Comment:

-Filename: cryptography/hazmat/primitives/hmac.py
+Filename: cryptography/hazmat/primitives/keywrap.py
 Comment:

-Filename: cryptography/hazmat/primitives/keywrap.py
+Filename: cryptography/hazmat/primitives/padding.py
 Comment:

-Filename: cryptography/hazmat/primitives/mac.py
+Filename: cryptography/hazmat/primitives/poly1305.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/hkdf.py
+Filename: cryptography/hazmat/primitives/cmac.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/kbkdf.py
+Filename: cryptography/hazmat/primitives/serialization/pkcs7.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/x963kdf.py
+Filename: cryptography/hazmat/primitives/serialization/base.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/__init__.py
+Filename: cryptography/hazmat/primitives/serialization/__init__.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/concatkdf.py
+Filename: cryptography/hazmat/primitives/serialization/pkcs12.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/pbkdf2.py
+Filename: cryptography/hazmat/primitives/serialization/ssh.py
 Comment:

-Filename: cryptography/hazmat/primitives/kdf/scrypt.py
+Filename: cryptography/hazmat/primitives/twofactor/__init__.py
 Comment:

-Filename: cryptography/hazmat/primitives/ciphers/aead.py
+Filename: cryptography/hazmat/primitives/twofactor/totp.py
 Comment:

-Filename: cryptography/hazmat/primitives/ciphers/algorithms.py
+Filename: cryptography/hazmat/primitives/twofactor/utils.py
 Comment:

-Filename: cryptography/hazmat/primitives/ciphers/base.py
+Filename: cryptography/hazmat/primitives/twofactor/hotp.py
 Comment:

-Filename: cryptography/hazmat/primitives/ciphers/modes.py
+Filename: cryptography/hazmat/primitives/ciphers/base.py
 Comment:

 Filename: cryptography/hazmat/primitives/ciphers/__init__.py
 Comment:

-Filename: cryptography/hazmat/primitives/serialization/pkcs12.py
+Filename: cryptography/hazmat/primitives/ciphers/aead.py
 Comment:

-Filename: cryptography/hazmat/primitives/serialization/base.py
+Filename: cryptography/hazmat/primitives/ciphers/algorithms.py
 Comment:

-Filename: cryptography/hazmat/primitives/serialization/__init__.py
+Filename: cryptography/hazmat/primitives/ciphers/modes.py
 Comment:

-Filename: cryptography/hazmat/primitives/serialization/ssh.py
+Filename: cryptography/hazmat/primitives/asymmetric/x448.py
 Comment:

-Filename: cryptography/hazmat/primitives/twofactor/utils.py
+Filename: cryptography/hazmat/primitives/asymmetric/dh.py
 Comment:

-Filename: cryptography/hazmat/primitives/twofactor/hotp.py
+Filename: cryptography/hazmat/primitives/asymmetric/x25519.py
 Comment:

-Filename: cryptography/hazmat/primitives/twofactor/totp.py
+Filename: cryptography/hazmat/primitives/asymmetric/ed448.py
 Comment:

-Filename: cryptography/hazmat/primitives/twofactor/__init__.py
+Filename: cryptography/hazmat/primitives/asymmetric/__init__.py
 Comment:

 Filename: cryptography/hazmat/primitives/asymmetric/dsa.py
 Comment:

 Filename: cryptography/hazmat/primitives/asymmetric/rsa.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/ed448.py
+Filename: cryptography/hazmat/primitives/asymmetric/ed25519.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/x25519.py
+Filename: cryptography/hazmat/primitives/asymmetric/padding.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/ed25519.py
+Filename: cryptography/hazmat/primitives/asymmetric/utils.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/dh.py
+Filename: cryptography/hazmat/primitives/asymmetric/ec.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/utils.py
+Filename: cryptography/hazmat/primitives/kdf/__init__.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/padding.py
+Filename: cryptography/hazmat/primitives/kdf/x963kdf.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/__init__.py
+Filename: cryptography/hazmat/primitives/kdf/concatkdf.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/x448.py
+Filename: cryptography/hazmat/primitives/kdf/hkdf.py
 Comment:

-Filename: cryptography/hazmat/primitives/asymmetric/ec.py
+Filename: cryptography/hazmat/primitives/kdf/pbkdf2.py
 Comment:

-Filename: cryptography/x509/certificate_transparency.py
+Filename: cryptography/hazmat/primitives/kdf/scrypt.py
 Comment:

-Filename: cryptography/x509/ocsp.py
+Filename: cryptography/hazmat/primitives/kdf/kbkdf.py
+Comment:
+
+Filename: cryptography/x509/oid.py
 Comment:

 Filename: cryptography/x509/extensions.py
 Comment:

 Filename: cryptography/x509/base.py
 Comment:

+Filename: cryptography/x509/__init__.py
+Comment:
+
 Filename: cryptography/x509/name.py
 Comment:

-Filename: cryptography/x509/general_name.py
+Filename: cryptography/x509/certificate_transparency.py
 Comment:

-Filename: cryptography/x509/oid.py
+Filename: cryptography/x509/ocsp.py
 Comment:

-Filename: cryptography/x509/__init__.py
+Filename: cryptography/x509/general_name.py
 Comment:

 Zip file comment:

cryptography/about.py

@@ -1,23 +1,31 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 __all__ = [
-    "__title__", "__summary__", "__uri__", "__version__", "__author__",
-    "__email__", "__license__", "__copyright__",
+    "__title__",
+    "__summary__",
+    "__uri__",
+    "__version__",
+    "__author__",
+    "__email__",
+    "__license__",
+    "__copyright__",
 ]

 __title__ = "cryptography"
-__summary__ = ("cryptography is a package which provides cryptographic recipes"
-               " and primitives to Python developers.")
+__summary__ = (
+    "cryptography is a package which provides cryptographic recipes"
+    " and primitives to Python developers."
+)
 __uri__ = "https://github.com/pyca/cryptography"

-__version__ = "2.6.1"
+__version__ = "3.2.1"

 __author__ = "The cryptography developers"
 __email__ = "[email protected]"

 __license__ = "BSD or Apache License, Version 2.0"
-__copyright__ = "Copyright 2013-2017 {}".format(__author__)
+__copyright__ = "Copyright 2013-2020 {}".format(__author__)

cryptography/fernet.py

@@ -10,31 +10,30 @@
 import struct
 import time

 import six

 from cryptography import utils
 from cryptography.exceptions import InvalidSignature
-from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.primitives import hashes, padding
 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 from cryptography.hazmat.primitives.hmac import HMAC


 class InvalidToken(Exception):
     pass


 _MAX_CLOCK_SKEW = 60


 class Fernet(object):
     def __init__(self, key, backend=None):
-        if backend is None:
-            backend = default_backend()
+        backend = _get_backend(backend)

         key = base64.urlsafe_b64decode(key)
         if len(key) != 32:
             raise ValueError(
                 "Fernet key must be 32 url-safe base64-encoded bytes."
             )

@@ -43,15 +42,17 @@
         self._backend = backend

     @classmethod
     def generate_key(cls):
         return base64.urlsafe_b64encode(os.urandom(32))

     def encrypt(self, data):
-        current_time = int(time.time())
+        return self.encrypt_at_time(data, int(time.time()))
+
+    def encrypt_at_time(self, data, current_time):
         iv = os.urandom(16)
         return self._encrypt_from_parts(data, current_time, iv)

     def _encrypt_from_parts(self, data, current_time, iv):
         utils._check_bytes("data", data)

         padder = padding.PKCS7(algorithms.AES.block_size).padder()
@@ -68,15 +69,23 @@
         h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
         h.update(basic_parts)
         hmac = h.finalize()
         return base64.urlsafe_b64encode(basic_parts + hmac)

     def decrypt(self, token, ttl=None):
         timestamp, data = Fernet._get_unverified_token_data(token)
-        return self._decrypt_data(data, timestamp, ttl)
+        return self._decrypt_data(data, timestamp, ttl, int(time.time()))
+
+    def decrypt_at_time(self, token, ttl, current_time):
+        if ttl is None:
+            raise ValueError(
+                "decrypt_at_time() can only be used with a non-None ttl"
+            )
+        timestamp, data = Fernet._get_unverified_token_data(token)
+        return self._decrypt_data(data, timestamp, ttl, current_time)

     def extract_timestamp(self, token):
         timestamp, data = Fernet._get_unverified_token_data(token)
         # Verify the token was not tampered with.
         self._verify_signature(data)
         return timestamp

@@ -88,29 +97,28 @@
         except (TypeError, binascii.Error):
             raise InvalidToken

         if not data or six.indexbytes(data, 0) != 0x80:
             raise InvalidToken

         try:
-            timestamp, = struct.unpack(">Q", data[1:9])
+            (timestamp,) = struct.unpack(">Q", data[1:9])
         except struct.error:
             raise InvalidToken
         return timestamp, data

     def _verify_signature(self, data):
         h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
         h.update(data[:-32])
         try:
             h.verify(data[-32:])
         except InvalidSignature:
             raise InvalidToken

-    def _decrypt_data(self, data, timestamp, ttl):
-        current_time = int(time.time())
+    def _decrypt_data(self, data, timestamp, ttl, current_time):
         if ttl is not None:
             if timestamp + ttl < current_time:
                 raise InvalidToken

             if current_time + _MAX_CLOCK_SKEW < timestamp:
                 raise InvalidToken

@@ -142,21 +150,24 @@
         if not fernets:
             raise ValueError(
                 "MultiFernet requires at least one Fernet instance"
             )
         self._fernets = fernets

     def encrypt(self, msg):
-        return self._fernets[0].encrypt(msg)
+        return self.encrypt_at_time(msg, int(time.time()))
+
+    def encrypt_at_time(self, msg, current_time):
+        return self._fernets[0].encrypt_at_time(msg, current_time)

     def rotate(self, msg):
         timestamp, data = Fernet._get_unverified_token_data(msg)
         for f in self._fernets:
             try:
-                p = f._decrypt_data(data, timestamp, None)
+                p = f._decrypt_data(data, timestamp, None, None)
                 break
             except InvalidToken:
                 pass
         else:
             raise InvalidToken

         iv = os.urandom(16)
@@ -165,7 +176,15 @@
     def decrypt(self, msg, ttl=None):
         for f in self._fernets:
             try:
                 return f.decrypt(msg, ttl)
             except InvalidToken:
                 pass
         raise InvalidToken
+
+    def decrypt_at_time(self, msg, ttl, current_time):
+        for f in self._fernets:
+            try:
+                return f.decrypt_at_time(msg, ttl, current_time)
+            except InvalidToken:
+                pass
+        raise InvalidToken

cryptography/utils.py

@@ -17,16 +17,15 @@
     pass


 # Several APIs were deprecated with no specific end-of-life date because of the
 # ubiquity of their use. They should not be removed until we agree on when that
 # cycle ends.
 PersistentlyDeprecated2017 = CryptographyDeprecationWarning
-PersistentlyDeprecated2018 = CryptographyDeprecationWarning
-DeprecatedIn25 = CryptographyDeprecationWarning
+PersistentlyDeprecated2019 = CryptographyDeprecationWarning


 def _check_bytes(name, value):
     if not isinstance(value, bytes):
         raise TypeError("{} must be bytes".format(name))


@@ -42,44 +41,51 @@


 def register_interface(iface):
     def register_decorator(klass):
         verify_interface(iface, klass)
         iface.register(klass)
         return klass
+
     return register_decorator


 def register_interface_if(predicate, iface):
     def register_decorator(klass):
         if predicate:
             verify_interface(iface, klass)
             iface.register(klass)
         return klass
+
     return register_decorator


 if hasattr(int, "from_bytes"):
     int_from_bytes = int.from_bytes
 else:
+
     def int_from_bytes(data, byteorder, signed=False):
-        assert byteorder == 'big'
+        assert byteorder == "big"
         assert not signed

         return int(binascii.hexlify(data), 16)


 if hasattr(int, "to_bytes"):
+
     def int_to_bytes(integer, length=None):
         return integer.to_bytes(
-            length or (integer.bit_length() + 7) // 8 or 1, 'big'
+            length or (integer.bit_length() + 7) // 8 or 1, "big"
         )
+
+
 else:
+
     def int_to_bytes(integer, length=None):
-        hex_string = '%x' % integer
+        hex_string = "%x" % integer
         if length is None:
             n = len(hex_string)
         else:
             n = length * 2
         return binascii.unhexlify(hex_string.zfill(n + (n & 1)))


@@ -103,26 +109,18 @@
             # Can't properly verify these yet.
             continue
         sig = signature(getattr(iface, method))
         actual = signature(getattr(klass, method))
         if sig != actual:
             raise InterfaceNotImplemented(
                 "{}.{}'s signature differs from the expected. Expected: "
-                "{!r}. Received: {!r}".format(
-                    klass, method, sig, actual
-                )
+                "{!r}. Received: {!r}".format(klass, method, sig, actual)
             )


-# No longer needed as of 2.2, but retained because we have external consumers
-# who use it.
-def bit_length(x):
-    return x.bit_length()
-
-
 class _DeprecatedValue(object):
     def __init__(self, value, message, warning_class):
         self.value = value
         self.message = message
         self.warning_class = warning_class


@@ -165,8 +163,9 @@
     def inner(instance):
         cache = getattr(instance, cached_name, sentinel)
         if cache is not sentinel:
             return cache
         result = func(instance)
         setattr(instance, cached_name, result)
         return result
+
     return property(inner)

cryptography/init.py

@@ -1,16 +1,48 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

+import sys
+import warnings
+
 from cryptography.__about__ import (
-    __author__, __copyright__, __email__, __license__, __summary__, __title__,
-    __uri__, __version__
+    __author__,
+    __copyright__,
+    __email__,
+    __license__,
+    __summary__,
+    __title__,
+    __uri__,
+    __version__,
 )
+from cryptography.utils import CryptographyDeprecationWarning


 __all__ = [
-    "__title__", "__summary__", "__uri__", "__version__", "__author__",
-    "__email__", "__license__", "__copyright__",
+    "__title__",
+    "__summary__",
+    "__uri__",
+    "__version__",
+    "__author__",
+    "__email__",
+    "__license__",
+    "__copyright__",
 ]
+
+if sys.version_info[0] == 2:
+    warnings.warn(
+        "Python 2 is no longer supported by the Python core team. Support for "
+        "it is now deprecated in cryptography, and will be removed in a "
+        "future release.",
+        CryptographyDeprecationWarning,
+        stacklevel=2,
+    )
+if sys.version_info[:2] == (3, 5):
+    warnings.warn(
+        "Python 3.5 support will be dropped in the next release of "
+        "cryptography. Please upgrade your Python.",
+        CryptographyDeprecationWarning,
+        stacklevel=2,
+    )

cryptography/exceptions.py

@@ -15,14 +15,15 @@
     UNSUPPORTED_MGF = 4
     UNSUPPORTED_PUBLIC_KEY_ALGORITHM = 5
     UNSUPPORTED_ELLIPTIC_CURVE = 6
     UNSUPPORTED_SERIALIZATION = 7
     UNSUPPORTED_X509 = 8
     UNSUPPORTED_EXCHANGE_ALGORITHM = 9
     UNSUPPORTED_DIFFIE_HELLMAN = 10
+    UNSUPPORTED_MAC = 11


 class UnsupportedAlgorithm(Exception):
     def __init__(self, message, reason=None):
         super(UnsupportedAlgorithm, self).__init__(message)
         self._reason = reason

cryptography/hazmat/_oid.py

@@ -15,53 +15,63 @@
         intnodes = []

         # There must be at least 2 nodes, the first node must be 0..2, and
         # if less than 2, the second node cannot have a value outside the
         # range 0..39.  All nodes must be integers.
         for node in nodes:
             try:
-                intnodes.append(int(node, 0))
+                node_value = int(node, 10)
             except ValueError:
                 raise ValueError(
-                    "Malformed OID: %s (non-integer nodes)" % (
-                        self._dotted_string))
+                    "Malformed OID: %s (non-integer nodes)"
+                    % (self._dotted_string)
+                )
+            if node_value < 0:
+                raise ValueError(
+                    "Malformed OID: %s (negative-integer nodes)"
+                    % (self._dotted_string)
+                )
+            intnodes.append(node_value)

         if len(nodes) < 2:
             raise ValueError(
-                "Malformed OID: %s (insufficient number of nodes)" % (
-                    self._dotted_string))
+                "Malformed OID: %s (insufficient number of nodes)"
+                % (self._dotted_string)
+            )

         if intnodes[0] > 2:
             raise ValueError(
-                "Malformed OID: %s (first node outside valid range)" % (
-                    self._dotted_string))
+                "Malformed OID: %s (first node outside valid range)"
+                % (self._dotted_string)
+            )

         if intnodes[0] < 2 and intnodes[1] >= 40:
             raise ValueError(
-                "Malformed OID: %s (second node outside valid range)" % (
-                    self._dotted_string))
+                "Malformed OID: %s (second node outside valid range)"
+                % (self._dotted_string)
+            )

     def __eq__(self, other):
         if not isinstance(other, ObjectIdentifier):
             return NotImplemented

         return self.dotted_string == other.dotted_string

     def __ne__(self, other):
         return not self == other

     def __repr__(self):
         return "<ObjectIdentifier(oid={}, name={})>".format(
-            self.dotted_string,
-            self._name
+            self.dotted_string, self._name
         )

     def __hash__(self):
         return hash(self.dotted_string)

     @property
     def _name(self):
         # Lazy import to avoid an import cycle
         from cryptography.x509.oid import _OID_NAMES
+
         return _OID_NAMES.get(self, "Unknown OID")

     dotted_string = utils.read_only_property("_dotted_string")

cryptography/hazmat/backends/init.py

@@ -9,10 +9,18 @@


 def default_backend():
     global _default_backend

     if _default_backend is None:
         from cryptography.hazmat.backends.openssl.backend import backend
+
         _default_backend = backend

     return _default_backend
+
+
+def _get_backend(backend):
+    if backend is None:
+        return default_backend()
+    else:
+        return backend

cryptography/hazmat/backends/interfaces.py

@@ -53,45 +53,46 @@
         Return True if the hash algorithm is supported for HMAC by this
         backend.
         """

     @abc.abstractmethod
     def create_hmac_ctx(self, key, algorithm):
         """
-        Create a MACContext for calculating a message authentication code.
+        Create a context for calculating a message authentication code.
         """


 @six.add_metaclass(abc.ABCMeta)
 class CMACBackend(object):
     @abc.abstractmethod
     def cmac_algorithm_supported(self, algorithm):
         """
         Returns True if the block cipher is supported for CMAC by this backend
         """

     @abc.abstractmethod
     def create_cmac_ctx(self, algorithm):
         """
-        Create a MACContext for calculating a message authentication code.
+        Create a context for calculating a message authentication code.
         """


 @six.add_metaclass(abc.ABCMeta)
 class PBKDF2HMACBackend(object):
     @abc.abstractmethod
     def pbkdf2_hmac_supported(self, algorithm):
         """
         Return True if the hash algorithm is supported for PBKDF2 by this
         backend.
         """

     @abc.abstractmethod
-    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
-                           key_material):
+    def derive_pbkdf2_hmac(
+        self, algorithm, length, salt, iterations, key_material
+    ):
         """
         Return length bytes derived from provided PBKDF2 parameters.
         """


 @six.add_metaclass(abc.ABCMeta)
 class RSABackend(object):

cryptography/hazmat/backends/openssl/dsa.py

@@ -3,20 +3,23 @@
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.exceptions import InvalidSignature
 from cryptography.hazmat.backends.openssl.utils import (
-    _calculate_digest_and_algorithm, _check_not_prehashed,
-    _warn_sign_verify_deprecated
+    _calculate_digest_and_algorithm,
+    _check_not_prehashed,
+    _warn_sign_verify_deprecated,
 )
-from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives import hashes
 from cryptography.hazmat.primitives.asymmetric import (
-    AsymmetricSignatureContext, AsymmetricVerificationContext, dsa
+    AsymmetricSignatureContext,
+    AsymmetricVerificationContext,
+    dsa,
 )


 def _dsa_sig_sign(backend, private_key, data):
     sig_buf_len = backend._lib.DSA_size(private_key._dsa_cdata)
     sig_buf = backend._ffi.new("unsigned char[]", sig_buf_len)
     buflen = backend._ffi.new("unsigned int *")
@@ -25,15 +28,15 @@
     # must be an integer.
     res = backend._lib.DSA_sign(
         0, data, len(data), sig_buf, buflen, private_key._dsa_cdata
     )
     backend.openssl_assert(res == 1)
     backend.openssl_assert(buflen[0])

-    return backend._ffi.buffer(sig_buf)[:buflen[0]]
+    return backend._ffi.buffer(sig_buf)[: buflen[0]]


 def _dsa_sig_verify(backend, public_key, signature, data):
     # The first parameter passed to DSA_verify is unused by OpenSSL but
     # must be an integer.
     res = backend._lib.DSA_verify(
         0, data, len(data), signature, len(signature), public_key._dsa_cdata
@@ -94,15 +97,15 @@
         self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g)
         self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
         return dsa.DSAParameterNumbers(
             p=self._backend._bn_to_int(p[0]),
             q=self._backend._bn_to_int(q[0]),
-            g=self._backend._bn_to_int(g[0])
+            g=self._backend._bn_to_int(g[0]),
         )

     def generate_private_key(self):
         return self._backend.generate_dsa_private_key(self)


 @utils.register_interface(dsa.DSAPrivateKeyWithSerialization)
@@ -140,19 +143,19 @@
         self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL)
         return dsa.DSAPrivateNumbers(
             public_numbers=dsa.DSAPublicNumbers(
                 parameter_numbers=dsa.DSAParameterNumbers(
                     p=self._backend._bn_to_int(p[0]),
                     q=self._backend._bn_to_int(q[0]),
-                    g=self._backend._bn_to_int(g[0])
+                    g=self._backend._bn_to_int(g[0]),
                 ),
-                y=self._backend._bn_to_int(pub_key[0])
+                y=self._backend._bn_to_int(pub_key[0]),
             ),
-            x=self._backend._bn_to_int(priv_key[0])
+            x=self._backend._bn_to_int(priv_key[0]),
         )

     def public_key(self):
         dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata)
         self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL)
         dsa_cdata = self._backend._ffi.gc(
             dsa_cdata, self._backend._lib.DSA_free
@@ -179,16 +182,17 @@
         return _DSAParameters(self._backend, dsa_cdata)

     def private_bytes(self, encoding, format, encryption_algorithm):
         return self._backend._private_key_bytes(
             encoding,
             format,
             encryption_algorithm,
+            self,
             self._evp_pkey,
-            self._dsa_cdata
+            self._dsa_cdata,
         )

     def sign(self, data, algorithm):
         data, algorithm = _calculate_digest_and_algorithm(
             self._backend, data, algorithm
         )
         return _dsa_sig_sign(self._backend, self, data)
@@ -231,38 +235,29 @@
             self._dsa_cdata, pub_key, self._backend._ffi.NULL
         )
         self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
         return dsa.DSAPublicNumbers(
             parameter_numbers=dsa.DSAParameterNumbers(
                 p=self._backend._bn_to_int(p[0]),
                 q=self._backend._bn_to_int(q[0]),
-                g=self._backend._bn_to_int(g[0])
+                g=self._backend._bn_to_int(g[0]),
             ),
-            y=self._backend._bn_to_int(pub_key[0])
+            y=self._backend._bn_to_int(pub_key[0]),
         )

     def parameters(self):
         dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata)
         dsa_cdata = self._backend._ffi.gc(
             dsa_cdata, self._backend._lib.DSA_free
         )
         return _DSAParameters(self._backend, dsa_cdata)

     def public_bytes(self, encoding, format):
-        if format is serialization.PublicFormat.PKCS1:
-            raise ValueError(
-                "DSA public keys do not support PKCS1 serialization"
-            )
-
         return self._backend._public_key_bytes(
-            encoding,
-            format,
-            self,
-            self._evp_pkey,
-            None
+            encoding, format, self, self._evp_pkey, None
         )

     def verify(self, signature, data, algorithm):
         data, algorithm = _calculate_digest_and_algorithm(
             self._backend, data, algorithm
         )
         return _dsa_sig_verify(self._backend, self, signature, data)

cryptography/hazmat/backends/openssl/aead.py

@@ -9,16 +9,19 @@

 _ENCRYPT = 1
 _DECRYPT = 0


 def _aead_cipher_name(cipher):
     from cryptography.hazmat.primitives.ciphers.aead import (
-        AESCCM, AESGCM, ChaCha20Poly1305
+        AESCCM,
+        AESGCM,
+        ChaCha20Poly1305,
     )
+
     if isinstance(cipher, ChaCha20Poly1305):
         return b"chacha20-poly1305"
     elif isinstance(cipher, AESCCM):
         return "aes-{}-ccm".format(len(cipher._key) * 8).encode("ascii")
     else:
         assert isinstance(cipher, AESGCM)
         return "aes-{}-gcm".format(len(cipher._key) * 8).encode("ascii")
@@ -26,60 +29,60 @@

 def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
     evp_cipher = backend._lib.EVP_get_cipherbyname(cipher_name)
     backend.openssl_assert(evp_cipher != backend._ffi.NULL)
     ctx = backend._lib.EVP_CIPHER_CTX_new()
     ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free)
     res = backend._lib.EVP_CipherInit_ex(
-        ctx, evp_cipher,
+        ctx,
+        evp_cipher,
         backend._ffi.NULL,
         backend._ffi.NULL,
         backend._ffi.NULL,
-        int(operation == _ENCRYPT)
+        int(operation == _ENCRYPT),
     )
     backend.openssl_assert(res != 0)
     res = backend._lib.EVP_CIPHER_CTX_set_key_length(ctx, len(key))
     backend.openssl_assert(res != 0)
     res = backend._lib.EVP_CIPHER_CTX_ctrl(
-        ctx, backend._lib.EVP_CTRL_AEAD_SET_IVLEN, len(nonce),
-        backend._ffi.NULL
+        ctx,
+        backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
+        len(nonce),
+        backend._ffi.NULL,
     )
     backend.openssl_assert(res != 0)
     if operation == _DECRYPT:
         res = backend._lib.EVP_CIPHER_CTX_ctrl(
             ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
         )
         backend.openssl_assert(res != 0)
-    else:
+    elif cipher_name.endswith(b"-ccm"):
         res = backend._lib.EVP_CIPHER_CTX_ctrl(
             ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, tag_len, backend._ffi.NULL
         )
+        backend.openssl_assert(res != 0)

     nonce_ptr = backend._ffi.from_buffer(nonce)
     key_ptr = backend._ffi.from_buffer(key)
     res = backend._lib.EVP_CipherInit_ex(
         ctx,
         backend._ffi.NULL,
         backend._ffi.NULL,
         key_ptr,
         nonce_ptr,
-        int(operation == _ENCRYPT)
+        int(operation == _ENCRYPT),
     )
     backend.openssl_assert(res != 0)
     return ctx


 def _set_length(backend, ctx, data_len):
     intptr = backend._ffi.new("int *")
     res = backend._lib.EVP_CipherUpdate(
-        ctx,
-        backend._ffi.NULL,
-        intptr,
-        backend._ffi.NULL,
-        data_len
+        ctx, backend._ffi.NULL, intptr, backend._ffi.NULL, data_len
     )
     backend.openssl_assert(res != 0)


 def _process_aad(backend, ctx, associated_data):
     outlen = backend._ffi.new("int *")
     res = backend._lib.EVP_CipherUpdate(
@@ -94,14 +97,15 @@
     res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data))
     backend.openssl_assert(res != 0)
     return backend._ffi.buffer(buf, outlen[0])[:]


 def _encrypt(backend, cipher, nonce, data, associated_data, tag_length):
     from cryptography.hazmat.primitives.ciphers.aead import AESCCM
+
     cipher_name = _aead_cipher_name(cipher)
     ctx = _aead_setup(
         backend, cipher_name, cipher._key, nonce, None, tag_length, _ENCRYPT
     )
     # CCM requires us to pass the length of the data before processing anything
     # However calling this with any other AEAD results in an error
     if isinstance(cipher, AESCCM):
@@ -121,14 +125,15 @@
     tag = backend._ffi.buffer(tag_buf)[:]

     return processed_data + tag


 def _decrypt(backend, cipher, nonce, data, associated_data, tag_length):
     from cryptography.hazmat.primitives.ciphers.aead import AESCCM
+
     if len(data) < tag_length:
         raise InvalidTag
     tag = data[-tag_length:]
     data = data[:-tag_length]
     cipher_name = _aead_cipher_name(cipher)
     ctx = _aead_setup(
         backend, cipher_name, cipher._key, nonce, tag, tag_length, _DECRYPT

cryptography/hazmat/backends/openssl/rsa.py

@@ -1,32 +1,41 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

-import math
-
 from cryptography import utils
 from cryptography.exceptions import (
-    InvalidSignature, UnsupportedAlgorithm, _Reasons
+    InvalidSignature,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
 from cryptography.hazmat.backends.openssl.utils import (
-    _calculate_digest_and_algorithm, _check_not_prehashed,
-    _warn_sign_verify_deprecated
+    _calculate_digest_and_algorithm,
+    _check_not_prehashed,
+    _warn_sign_verify_deprecated,
 )
 from cryptography.hazmat.primitives import hashes
 from cryptography.hazmat.primitives.asymmetric import (
-    AsymmetricSignatureContext, AsymmetricVerificationContext, rsa
+    AsymmetricSignatureContext,
+    AsymmetricVerificationContext,
+    rsa,
 )
 from cryptography.hazmat.primitives.asymmetric.padding import (
-    AsymmetricPadding, MGF1, OAEP, PKCS1v15, PSS, calculate_max_pss_salt_length
+    AsymmetricPadding,
+    MGF1,
+    OAEP,
+    PKCS1v15,
+    PSS,
+    calculate_max_pss_salt_length,
 )
 from cryptography.hazmat.primitives.asymmetric.rsa import (
-    RSAPrivateKeyWithSerialization, RSAPublicKeyWithSerialization
+    RSAPrivateKeyWithSerialization,
+    RSAPublicKeyWithSerialization,
 )


 def _get_rsa_pss_salt_length(pss, key, hash_algorithm):
     salt = pss._salt_length

     if salt is MGF1.MAX_LENGTH or salt is PSS.MAX_LENGTH:
@@ -43,118 +52,90 @@
         padding_enum = backend._lib.RSA_PKCS1_PADDING
     elif isinstance(padding, OAEP):
         padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING

         if not isinstance(padding._mgf, MGF1):
             raise UnsupportedAlgorithm(
                 "Only MGF1 is supported by this backend.",
-                _Reasons.UNSUPPORTED_MGF
+                _Reasons.UNSUPPORTED_MGF,
             )

         if not backend.rsa_padding_supported(padding):
             raise UnsupportedAlgorithm(
                 "This combination of padding and hash algorithm is not "
                 "supported by this backend.",
-                _Reasons.UNSUPPORTED_PADDING
+                _Reasons.UNSUPPORTED_PADDING,
             )

     else:
         raise UnsupportedAlgorithm(
-            "{} is not supported by this backend.".format(
-                padding.name
-            ),
-            _Reasons.UNSUPPORTED_PADDING
+            "{} is not supported by this backend.".format(padding.name),
+            _Reasons.UNSUPPORTED_PADDING,
         )

     return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding)


 def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding):
     if isinstance(key, _RSAPublicKey):
         init = backend._lib.EVP_PKEY_encrypt_init
         crypt = backend._lib.EVP_PKEY_encrypt
     else:
         init = backend._lib.EVP_PKEY_decrypt_init
         crypt = backend._lib.EVP_PKEY_decrypt

-    pkey_ctx = backend._lib.EVP_PKEY_CTX_new(
-        key._evp_pkey, backend._ffi.NULL
-    )
+    pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
     backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
     pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
     res = init(pkey_ctx)
     backend.openssl_assert(res == 1)
-    res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(
-        pkey_ctx, padding_enum)
+    res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
     backend.openssl_assert(res > 0)
     buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
     backend.openssl_assert(buf_size > 0)
-    if (
-        isinstance(padding, OAEP) and
-        backend._lib.Cryptography_HAS_RSA_OAEP_MD
-    ):
+    if isinstance(padding, OAEP) and backend._lib.Cryptography_HAS_RSA_OAEP_MD:
         mgf1_md = backend._evp_md_non_null_from_algorithm(
-            padding._mgf._algorithm)
+            padding._mgf._algorithm
+        )
         res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
         backend.openssl_assert(res > 0)
         oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm)
         res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md)
         backend.openssl_assert(res > 0)

     if (
-        isinstance(padding, OAEP) and
-        padding._label is not None and
-        len(padding._label) > 0
+        isinstance(padding, OAEP)
+        and padding._label is not None
+        and len(padding._label) > 0
     ):
         # set0_rsa_oaep_label takes ownership of the char * so we need to
         # copy it into some new memory
         labelptr = backend._lib.OPENSSL_malloc(len(padding._label))
         backend.openssl_assert(labelptr != backend._ffi.NULL)
         backend._ffi.memmove(labelptr, padding._label, len(padding._label))
         res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label(
             pkey_ctx, labelptr, len(padding._label)
         )
         backend.openssl_assert(res == 1)

     outlen = backend._ffi.new("size_t *", buf_size)
     buf = backend._ffi.new("unsigned char[]", buf_size)
+    # Everything from this line onwards is written with the goal of being as
+    # constant-time as is practical given the constraints of Python and our
+    # API. See Bleichenbacher's '98 attack on RSA, and its many many variants.
+    # As such, you should not attempt to change this (particularly to "clean it
+    # up") without understanding why it was written this way (see
+    # Chesterton's Fence), and without measuring to verify you have not
+    # introduced observable time differences.
     res = crypt(pkey_ctx, buf, outlen, data, len(data))
+    resbuf = backend._ffi.buffer(buf)[: outlen[0]]
+    backend._lib.ERR_clear_error()
     if res <= 0:
-        _handle_rsa_enc_dec_error(backend, key)
-
-    return backend._ffi.buffer(buf)[:outlen[0]]
-
-
-def _handle_rsa_enc_dec_error(backend, key):
-    errors = backend._consume_errors()
-    backend.openssl_assert(errors)
-    backend.openssl_assert(errors[0].lib == backend._lib.ERR_LIB_RSA)
-    if isinstance(key, _RSAPublicKey):
-        backend.openssl_assert(
-            errors[0].reason == backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE
-        )
-        raise ValueError(
-            "Data too long for key size. Encrypt less data or use a "
-            "larger key size."
-        )
-    else:
-        decoding_errors = [
-            backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01,
-            backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02,
-            backend._lib.RSA_R_OAEP_DECODING_ERROR,
-            # Though this error looks similar to the
-            # RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE, this occurs on decrypts,
-            # rather than on encrypts
-            backend._lib.RSA_R_DATA_TOO_LARGE_FOR_MODULUS,
-        ]
-        if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR:
-            decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR)
-
-        backend.openssl_assert(errors[0].reason in decoding_errors)
-        raise ValueError("Decryption failed.")
+        raise ValueError("Encryption/decryption failed.")
+    return resbuf


 def _rsa_sig_determine_padding(backend, key, padding, algorithm):
     if not isinstance(padding, AsymmetricPadding):
         raise TypeError("Expected provider of AsymmetricPadding.")

     pkey_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
@@ -162,28 +143,30 @@

     if isinstance(padding, PKCS1v15):
         padding_enum = backend._lib.RSA_PKCS1_PADDING
     elif isinstance(padding, PSS):
         if not isinstance(padding._mgf, MGF1):
             raise UnsupportedAlgorithm(
                 "Only MGF1 is supported by this backend.",
-                _Reasons.UNSUPPORTED_MGF
+                _Reasons.UNSUPPORTED_MGF,
             )

         # Size of key in bytes - 2 is the maximum
         # PSS signature length (salt length is checked later)
         if pkey_size - algorithm.digest_size - 2 < 0:
-            raise ValueError("Digest too large for key size. Use a larger "
-                             "key or different digest.")
+            raise ValueError(
+                "Digest too large for key size. Use a larger "
+                "key or different digest."
+            )

         padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING
     else:
         raise UnsupportedAlgorithm(
             "{} is not supported by this backend.".format(padding.name),
-            _Reasons.UNSUPPORTED_PADDING
+            _Reasons.UNSUPPORTED_PADDING,
         )

     return padding_enum


 def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func):
     padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm)
@@ -196,73 +179,68 @@
     res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md)
     if res == 0:
         backend._consume_errors()
         raise UnsupportedAlgorithm(
             "{} is not supported by this backend for RSA signing.".format(
                 algorithm.name
             ),
-            _Reasons.UNSUPPORTED_HASH
+            _Reasons.UNSUPPORTED_HASH,
         )
     res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
     backend.openssl_assert(res > 0)
     if isinstance(padding, PSS):
         res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
             pkey_ctx, _get_rsa_pss_salt_length(padding, key, algorithm)
         )
         backend.openssl_assert(res > 0)

         mgf1_md = backend._evp_md_non_null_from_algorithm(
-            padding._mgf._algorithm)
+            padding._mgf._algorithm
+        )
         res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
         backend.openssl_assert(res > 0)

     return pkey_ctx


 def _rsa_sig_sign(backend, padding, algorithm, private_key, data):
     pkey_ctx = _rsa_sig_setup(
-        backend, padding, algorithm, private_key, data,
-        backend._lib.EVP_PKEY_sign_init
+        backend,
+        padding,
+        algorithm,
+        private_key,
+        data,
+        backend._lib.EVP_PKEY_sign_init,
     )
     buflen = backend._ffi.new("size_t *")
     res = backend._lib.EVP_PKEY_sign(
-        pkey_ctx,
-        backend._ffi.NULL,
-        buflen,
-        data,
-        len(data)
+        pkey_ctx, backend._ffi.NULL, buflen, data, len(data)
     )
     backend.openssl_assert(res == 1)
     buf = backend._ffi.new("unsigned char[]", buflen[0])
-    res = backend._lib.EVP_PKEY_sign(
-        pkey_ctx, buf, buflen, data, len(data))
+    res = backend._lib.EVP_PKEY_sign(pkey_ctx, buf, buflen, data, len(data))
     if res != 1:
-        errors = backend._consume_errors()
-        backend.openssl_assert(errors[0].lib == backend._lib.ERR_LIB_RSA)
-        if (
-            errors[0].reason ==
-            backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE
-        ):
-            reason = ("Salt length too long for key size. Try using "
-                      "MAX_LENGTH instead.")
-        else:
-            backend.openssl_assert(
-                errors[0].reason ==
-                backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
-            )
-            reason = "Digest too large for key size. Use a larger key."
-        raise ValueError(reason)
+        errors = backend._consume_errors_with_text()
+        raise ValueError(
+            "Digest or salt length too long for key size. Use a larger key "
+            "or shorter salt length if you are specifying a PSS salt",
+            errors,
+        )

     return backend._ffi.buffer(buf)[:]


 def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data):
     pkey_ctx = _rsa_sig_setup(
-        backend, padding, algorithm, public_key, data,
-        backend._lib.EVP_PKEY_verify_init
+        backend,
+        padding,
+        algorithm,
+        public_key,
+        data,
+        backend._lib.EVP_PKEY_verify_init,
     )
     res = backend._lib.EVP_PKEY_verify(
         pkey_ctx, signature, len(signature), data, len(data)
     )
     # The previous call can return negative numbers in the event of an
     # error. This is not a signature failure but we need to fail if it
     # occurs.
@@ -291,15 +269,15 @@

     def finalize(self):
         return _rsa_sig_sign(
             self._backend,
             self._padding,
             self._algorithm,
             self._private_key,
-            self._hash_ctx.finalize()
+            self._hash_ctx.finalize(),
         )


 @utils.register_interface(AsymmetricVerificationContext)
 class _RSAVerificationContext(object):
     def __init__(self, backend, public_key, signature, padding, algorithm):
         self._backend = backend
@@ -321,53 +299,63 @@
     def verify(self):
         return _rsa_sig_verify(
             self._backend,
             self._padding,
             self._algorithm,
             self._public_key,
             self._signature,
-            self._hash_ctx.finalize()
+            self._hash_ctx.finalize(),
         )


 @utils.register_interface(RSAPrivateKeyWithSerialization)
 class _RSAPrivateKey(object):
     def __init__(self, backend, rsa_cdata, evp_pkey):
+        res = backend._lib.RSA_check_key(rsa_cdata)
+        if res != 1:
+            errors = backend._consume_errors_with_text()
+            raise ValueError("Invalid private key", errors)
+
+        # Blinding is on by default in many versions of OpenSSL, but let's
+        # just be conservative here.
+        res = backend._lib.RSA_blinding_on(rsa_cdata, backend._ffi.NULL)
+        backend.openssl_assert(res == 1)
+
         self._backend = backend
         self._rsa_cdata = rsa_cdata
         self._evp_pkey = evp_pkey

         n = self._backend._ffi.new("BIGNUM **")
         self._backend._lib.RSA_get0_key(
-            self._rsa_cdata, n, self._backend._ffi.NULL,
-            self._backend._ffi.NULL
+            self._rsa_cdata,
+            n,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
         )
         self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
         self._key_size = self._backend._lib.BN_num_bits(n[0])

     key_size = utils.read_only_property("_key_size")

     def signer(self, padding, algorithm):
         _warn_sign_verify_deprecated()
         _check_not_prehashed(algorithm)
         return _RSASignatureContext(self._backend, self, padding, algorithm)

     def decrypt(self, ciphertext, padding):
-        key_size_bytes = int(math.ceil(self.key_size / 8.0))
+        key_size_bytes = (self.key_size + 7) // 8
         if key_size_bytes != len(ciphertext):
             raise ValueError("Ciphertext length must be equal to key size.")

         return _enc_dec_rsa(self._backend, self, ciphertext, padding)

     def public_key(self):
         ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata)
         self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
         ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free)
-        res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL)
-        self._backend.openssl_assert(res == 1)
         evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx)
         return _RSAPublicKey(self._backend, ctx, evp_pkey)

     def private_numbers(self):
         n = self._backend._ffi.new("BIGNUM **")
         e = self._backend._ffi.new("BIGNUM **")
         d = self._backend._ffi.new("BIGNUM **")
@@ -395,24 +383,25 @@
             d=self._backend._bn_to_int(d[0]),
             dmp1=self._backend._bn_to_int(dmp1[0]),
             dmq1=self._backend._bn_to_int(dmq1[0]),
             iqmp=self._backend._bn_to_int(iqmp[0]),
             public_numbers=rsa.RSAPublicNumbers(
                 e=self._backend._bn_to_int(e[0]),
                 n=self._backend._bn_to_int(n[0]),
-            )
+            ),
         )

     def private_bytes(self, encoding, format, encryption_algorithm):
         return self._backend._private_key_bytes(
             encoding,
             format,
             encryption_algorithm,
+            self,
             self._evp_pkey,
-            self._rsa_cdata
+            self._rsa_cdata,
         )

     def sign(self, data, padding, algorithm):
         data, algorithm = _calculate_digest_and_algorithm(
             self._backend, data, algorithm
         )
         return _rsa_sig_sign(self._backend, padding, algorithm, self, data)
@@ -423,16 +412,18 @@
     def __init__(self, backend, rsa_cdata, evp_pkey):
         self._backend = backend
         self._rsa_cdata = rsa_cdata
         self._evp_pkey = evp_pkey

         n = self._backend._ffi.new("BIGNUM **")
         self._backend._lib.RSA_get0_key(
-            self._rsa_cdata, n, self._backend._ffi.NULL,
-            self._backend._ffi.NULL
+            self._rsa_cdata,
+            n,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
         )
         self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
         self._key_size = self._backend._lib.BN_num_bits(n[0])

     key_size = utils.read_only_property("_key_size")

     def verifier(self, signature, padding, algorithm):
@@ -458,19 +449,15 @@
         return rsa.RSAPublicNumbers(
             e=self._backend._bn_to_int(e[0]),
             n=self._backend._bn_to_int(n[0]),
         )

     def public_bytes(self, encoding, format):
         return self._backend._public_key_bytes(
-            encoding,
-            format,
-            self,
-            self._evp_pkey,
-            self._rsa_cdata
+            encoding, format, self, self._evp_pkey, self._rsa_cdata
         )

     def verify(self, signature, data, padding, algorithm):
         data, algorithm = _calculate_digest_and_algorithm(
             self._backend, data, algorithm
         )
         return _rsa_sig_verify(

cryptography/hazmat/backends/openssl/decode_asn1.py

@@ -3,31 +3,28 @@
 # for complete details.

 from __future__ import absolute_import, division, print_function

 import datetime
 import ipaddress

-import asn1crypto.core
-
 import six

 from cryptography import x509
+from cryptography.hazmat._der import DERReader, INTEGER, NULL, SEQUENCE
 from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM
 from cryptography.x509.name import _ASN1_TYPE_TO_ENUM
 from cryptography.x509.oid import (
-    CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID,
+    CRLEntryExtensionOID,
+    CertificatePoliciesOID,
+    ExtensionOID,
     OCSPExtensionOID,
 )


-class _Integers(asn1crypto.core.SequenceOf):
-    _child_spec = asn1crypto.core.Integer
-
-
 def _obj2txt(backend, obj):
     # Set to 80 on the recommendation of
     # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values
     #
     # But OIDs longer than this occur in real life (e.g. Active
     # Directory makes some very long OIDs).  So we need to detect
     # and properly handle the case where the default buffer is not
@@ -65,15 +62,15 @@
     attributes = []
     prev_set_id = -1
     for x in range(count):
         entry = backend._lib.X509_NAME_get_entry(x509_name, x)
         attribute = _decode_x509_name_entry(backend, entry)
         set_id = backend._lib.Cryptography_X509_NAME_ENTRY_set(entry)
         if set_id != prev_set_id:
-            attributes.append(set([attribute]))
+            attributes.append({attribute})
         else:
             # is in the same RDN a previous entry
             attributes[-1].add(attribute)
         prev_set_id = set_id

     return x509.Name(x509.RelativeDistinguishedName(rdn) for rdn in attributes)

@@ -120,18 +117,18 @@
             # This is an IPv4 or IPv6 Network and not a single IP. This
             # type of data appears in Name Constraints. Unfortunately,
             # ipaddress doesn't support packed bytes + netmask. Additionally,
             # IPv6Network can only handle CIDR rather than the full 16 byte
             # netmask. To handle this we convert the netmask to integer, then
             # find the first 0 bit, which will be the prefix. If another 1
             # bit is present after that the netmask is invalid.
-            base = ipaddress.ip_address(data[:data_len // 2])
-            netmask = ipaddress.ip_address(data[data_len // 2:])
+            base = ipaddress.ip_address(data[: data_len // 2])
+            netmask = ipaddress.ip_address(data[data_len // 2 :])
             bits = bin(int(netmask))[2:]
-            prefix = bits.find('0')
+            prefix = bits.find("0")
             # If no 0 bits are found it is a /32 or /128
             if prefix == -1:
                 prefix = len(bits)

             if "1" in bits[prefix:]:
                 raise ValueError("Invalid netmask")

@@ -159,15 +156,15 @@
         return x509.OtherName(x509.ObjectIdentifier(type_id), value)
     else:
         # x400Address or ediPartyName
         raise x509.UnsupportedGeneralNameType(
             "{} is not a supported type".format(
                 x509._GENERAL_NAMES.get(gn.type, gn.type)
             ),
-            gn.type
+            gn.type,
         )


 def _decode_ocsp_no_check(backend, ext):
     return x509.OCSPNoCheck()


@@ -180,80 +177,87 @@
 def _decode_delta_crl_indicator(backend, ext):
     asn1_int = backend._ffi.cast("ASN1_INTEGER *", ext)
     asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
     return x509.DeltaCRLIndicator(_asn1_integer_to_int(backend, asn1_int))


 class _X509ExtensionParser(object):
-    def __init__(self, ext_count, get_ext, handlers):
+    def __init__(self, backend, ext_count, get_ext, handlers):
         self.ext_count = ext_count
         self.get_ext = get_ext
         self.handlers = handlers
+        self._backend = backend

-    def parse(self, backend, x509_obj):
+    def parse(self, x509_obj):
         extensions = []
         seen_oids = set()
-        for i in range(self.ext_count(backend, x509_obj)):
-            ext = self.get_ext(backend, x509_obj, i)
-            backend.openssl_assert(ext != backend._ffi.NULL)
-            crit = backend._lib.X509_EXTENSION_get_critical(ext)
+        for i in range(self.ext_count(x509_obj)):
+            ext = self.get_ext(x509_obj, i)
+            self._backend.openssl_assert(ext != self._backend._ffi.NULL)
+            crit = self._backend._lib.X509_EXTENSION_get_critical(ext)
             critical = crit == 1
             oid = x509.ObjectIdentifier(
-                _obj2txt(backend, backend._lib.X509_EXTENSION_get_object(ext))
+                _obj2txt(
+                    self._backend,
+                    self._backend._lib.X509_EXTENSION_get_object(ext),
+                )
             )
             if oid in seen_oids:
                 raise x509.DuplicateExtension(
                     "Duplicate {} extension found".format(oid), oid
                 )

             # These OIDs are only supported in OpenSSL 1.1.0+ but we want
             # to support them in all versions of OpenSSL so we decode them
             # ourselves.
             if oid == ExtensionOID.TLS_FEATURE:
-                data = backend._lib.X509_EXTENSION_get_data(ext)
-                parsed = _Integers.load(_asn1_string_to_bytes(backend, data))
+                # The extension contents are a SEQUENCE OF INTEGERs.
+                data = self._backend._lib.X509_EXTENSION_get_data(ext)
+                data_bytes = _asn1_string_to_bytes(self._backend, data)
+                features = DERReader(data_bytes).read_single_element(SEQUENCE)
+                parsed = []
+                while not features.is_empty():
+                    parsed.append(features.read_element(INTEGER).as_integer())
+                # Map the features to their enum value.
                 value = x509.TLSFeature(
-                    [_TLS_FEATURE_TYPE_TO_ENUM[x.native] for x in parsed]
+                    [_TLS_FEATURE_TYPE_TO_ENUM[x] for x in parsed]
                 )
                 extensions.append(x509.Extension(oid, critical, value))
                 seen_oids.add(oid)
                 continue
             elif oid == ExtensionOID.PRECERT_POISON:
-                data = backend._lib.X509_EXTENSION_get_data(ext)
-                parsed = asn1crypto.core.Null.load(
-                    _asn1_string_to_bytes(backend, data)
+                data = self._backend._lib.X509_EXTENSION_get_data(ext)
+                # The contents of the extension must be an ASN.1 NULL.
+                reader = DERReader(_asn1_string_to_bytes(self._backend, data))
+                reader.read_single_element(NULL).check_empty()
+                extensions.append(
+                    x509.Extension(oid, critical, x509.PrecertPoison())
                 )
-                assert parsed == asn1crypto.core.Null()
-                extensions.append(x509.Extension(
-                    oid, critical, x509.PrecertPoison()
-                ))
                 seen_oids.add(oid)
                 continue

             try:
                 handler = self.handlers[oid]
             except KeyError:
                 # Dump the DER payload into an UnrecognizedExtension object
-                data = backend._lib.X509_EXTENSION_get_data(ext)
-                backend.openssl_assert(data != backend._ffi.NULL)
-                der = backend._ffi.buffer(data.data, data.length)[:]
+                data = self._backend._lib.X509_EXTENSION_get_data(ext)
+                self._backend.openssl_assert(data != self._backend._ffi.NULL)
+                der = self._backend._ffi.buffer(data.data, data.length)[:]
                 unrecognized = x509.UnrecognizedExtension(oid, der)
-                extensions.append(
-                    x509.Extension(oid, critical, unrecognized)
-                )
+                extensions.append(x509.Extension(oid, critical, unrecognized))
             else:
-                ext_data = backend._lib.X509V3_EXT_d2i(ext)
-                if ext_data == backend._ffi.NULL:
-                    backend._consume_errors()
+                ext_data = self._backend._lib.X509V3_EXT_d2i(ext)
+                if ext_data == self._backend._ffi.NULL:
+                    self._backend._consume_errors()
                     raise ValueError(
                         "The {} extension is invalid and can't be "
                         "parsed".format(oid)
                     )

-                value = handler(backend, ext_data)
+                value = handler(self._backend, ext_data)
                 extensions.append(x509.Extension(oid, critical, value))

             seen_oids.add(oid)

         return x509.Extensions(extensions)


@@ -267,65 +271,53 @@
         qualifiers = None
         pi = backend._lib.sk_POLICYINFO_value(cp, i)
         oid = x509.ObjectIdentifier(_obj2txt(backend, pi.policyid))
         if pi.qualifiers != backend._ffi.NULL:
             qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers)
             qualifiers = []
             for j in range(qnum):
-                pqi = backend._lib.sk_POLICYQUALINFO_value(
-                    pi.qualifiers, j
-                )
-                pqualid = x509.ObjectIdentifier(
-                    _obj2txt(backend, pqi.pqualid)
-                )
+                pqi = backend._lib.sk_POLICYQUALINFO_value(pi.qualifiers, j)
+                pqualid = x509.ObjectIdentifier(_obj2txt(backend, pqi.pqualid))
                 if pqualid == CertificatePoliciesOID.CPS_QUALIFIER:
                     cpsuri = backend._ffi.buffer(
                         pqi.d.cpsuri.data, pqi.d.cpsuri.length
-                    )[:].decode('ascii')
+                    )[:].decode("ascii")
                     qualifiers.append(cpsuri)
                 else:
                     assert pqualid == CertificatePoliciesOID.CPS_USER_NOTICE
                     user_notice = _decode_user_notice(
                         backend, pqi.d.usernotice
                     )
                     qualifiers.append(user_notice)

-        certificate_policies.append(
-            x509.PolicyInformation(oid, qualifiers)
-        )
+        certificate_policies.append(x509.PolicyInformation(oid, qualifiers))

     return x509.CertificatePolicies(certificate_policies)


 def _decode_user_notice(backend, un):
     explicit_text = None
     notice_reference = None

     if un.exptext != backend._ffi.NULL:
         explicit_text = _asn1_string_to_utf8(backend, un.exptext)

     if un.noticeref != backend._ffi.NULL:
-        organization = _asn1_string_to_utf8(
-            backend, un.noticeref.organization
-        )
+        organization = _asn1_string_to_utf8(backend, un.noticeref.organization)

-        num = backend._lib.sk_ASN1_INTEGER_num(
-            un.noticeref.noticenos
-        )
+        num = backend._lib.sk_ASN1_INTEGER_num(un.noticeref.noticenos)
         notice_numbers = []
         for i in range(num):
             asn1_int = backend._lib.sk_ASN1_INTEGER_value(
                 un.noticeref.noticenos, i
             )
             notice_num = _asn1_integer_to_int(backend, asn1_int)
             notice_numbers.append(notice_num)

-        notice_reference = x509.NoticeReference(
-            organization, notice_numbers
-        )
+        notice_reference = x509.NoticeReference(organization, notice_numbers)

     return x509.UserNotice(notice_reference, explicit_text)


 def _decode_basic_constraints(backend, bc_st):
     basic_constraints = backend._ffi.cast("BASIC_CONSTRAINTS *", bc_st)
     basic_constraints = backend._ffi.gc(
@@ -360,43 +352,59 @@

     if akid.keyid != backend._ffi.NULL:
         key_identifier = backend._ffi.buffer(
             akid.keyid.data, akid.keyid.length
         )[:]

     if akid.issuer != backend._ffi.NULL:
-        authority_cert_issuer = _decode_general_names(
-            backend, akid.issuer
-        )
+        authority_cert_issuer = _decode_general_names(backend, akid.issuer)

     authority_cert_serial_number = _asn1_integer_to_int_or_none(
         backend, akid.serial
     )

     return x509.AuthorityKeyIdentifier(
         key_identifier, authority_cert_issuer, authority_cert_serial_number
     )


-def _decode_authority_information_access(backend, aia):
-    aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia)
-    aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free)
-    num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
+def _decode_information_access(backend, ia):
+    ia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", ia)
+    ia = backend._ffi.gc(
+        ia,
+        lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free(
+            x,
+            backend._ffi.addressof(
+                backend._lib._original_lib, "ACCESS_DESCRIPTION_free"
+            ),
+        ),
+    )
+    num = backend._lib.sk_ACCESS_DESCRIPTION_num(ia)
     access_descriptions = []
     for i in range(num):
-        ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
+        ad = backend._lib.sk_ACCESS_DESCRIPTION_value(ia, i)
         backend.openssl_assert(ad.method != backend._ffi.NULL)
         oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method))
         backend.openssl_assert(ad.location != backend._ffi.NULL)
         gn = _decode_general_name(backend, ad.location)
         access_descriptions.append(x509.AccessDescription(oid, gn))

+    return access_descriptions
+
+
+def _decode_authority_information_access(backend, aia):
+    access_descriptions = _decode_information_access(backend, aia)
     return x509.AuthorityInformationAccess(access_descriptions)


+def _decode_subject_information_access(backend, aia):
+    access_descriptions = _decode_information_access(backend, aia)
+    return x509.SubjectInformationAccess(access_descriptions)
+
+
 def _decode_key_usage(backend, bit_string):
     bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
     bit_string = backend._ffi.gc(bit_string, backend._lib.ASN1_BIT_STRING_free)
     get_bit = backend._lib.ASN1_BIT_STRING_get_bit
     digital_signature = get_bit(bit_string, 0) == 1
     content_commitment = get_bit(bit_string, 1) == 1
     key_encipherment = get_bit(bit_string, 2) == 1
@@ -411,15 +419,15 @@
         content_commitment,
         key_encipherment,
         data_encipherment,
         key_agreement,
         key_cert_sign,
         crl_sign,
         encipher_only,
-        decipher_only
+        decipher_only,
     )


 def _decode_general_names_extension(backend, gns):
     gns = backend._ffi.cast("GENERAL_NAMES *", gns)
     gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
     general_names = _decode_general_names(backend, gns)
@@ -479,16 +487,21 @@
     only_attr = idp.onlyattr == 255
     if idp.onlysomereasons != backend._ffi.NULL:
         only_some_reasons = _decode_reasons(backend, idp.onlysomereasons)
     else:
         only_some_reasons = None

     return x509.IssuingDistributionPoint(
-        full_name, relative_name, only_user, only_ca, only_some_reasons,
-        indirect_crl, only_attr
+        full_name,
+        relative_name,
+        only_user,
+        only_ca,
+        only_some_reasons,
+        indirect_crl,
+        only_attr,
     )


 def _decode_policy_constraints(backend, pc):
     pc = backend._ffi.cast("POLICY_CONSTRAINTS *", pc)
     pc = backend._ffi.gc(pc, backend._lib.POLICY_CONSTRAINTS_free)

@@ -601,21 +614,17 @@
     # DistributionPointName ::= CHOICE {
     #      fullName                [0]      GeneralNames,
     #      nameRelativeToCRLIssuer [1]      RelativeDistinguishedName }
     rns = distpoint.name.relativename
     rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns)
     attributes = set()
     for i in range(rnum):
-        rn = backend._lib.sk_X509_NAME_ENTRY_value(
-            rns, i
-        )
+        rn = backend._lib.sk_X509_NAME_ENTRY_value(rns, i)
         backend.openssl_assert(rn != backend._ffi.NULL)
-        attributes.add(
-            _decode_x509_name_entry(backend, rn)
-        )
+        attributes.add(_decode_x509_name_entry(backend, rn))

     relative_name = x509.RelativeDistinguishedName(attributes)

     return None, relative_name


 def _decode_crl_distribution_points(backend, cdps):
@@ -631,27 +640,38 @@
 def _decode_inhibit_any_policy(backend, asn1_int):
     asn1_int = backend._ffi.cast("ASN1_INTEGER *", asn1_int)
     asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
     skip_certs = _asn1_integer_to_int(backend, asn1_int)
     return x509.InhibitAnyPolicy(skip_certs)


-def _decode_precert_signed_certificate_timestamps(backend, asn1_scts):
+def _decode_scts(backend, asn1_scts):
     from cryptography.hazmat.backends.openssl.x509 import (
-        _SignedCertificateTimestamp
+        _SignedCertificateTimestamp,
     )
+
     asn1_scts = backend._ffi.cast("Cryptography_STACK_OF_SCT *", asn1_scts)
     asn1_scts = backend._ffi.gc(asn1_scts, backend._lib.SCT_LIST_free)

     scts = []
     for i in range(backend._lib.sk_SCT_num(asn1_scts)):
         sct = backend._lib.sk_SCT_value(asn1_scts, i)

         scts.append(_SignedCertificateTimestamp(backend, asn1_scts, sct))
-    return x509.PrecertificateSignedCertificateTimestamps(scts)
+    return scts
+
+
+def _decode_precert_signed_certificate_timestamps(backend, asn1_scts):
+    return x509.PrecertificateSignedCertificateTimestamps(
+        _decode_scts(backend, asn1_scts)
+    )
+
+
+def _decode_signed_certificate_timestamps(backend, asn1_scts):
+    return x509.SignedCertificateTimestamps(_decode_scts(backend, asn1_scts))


 #    CRLReason ::= ENUMERATED {
 #        unspecified             (0),
 #        keyCompromise           (1),
 #        cACompromise            (2),
 #        affiliationChanged      (3),
@@ -682,15 +702,15 @@
     x509.ReasonFlags.ca_compromise: 2,
     x509.ReasonFlags.affiliation_changed: 3,
     x509.ReasonFlags.superseded: 4,
     x509.ReasonFlags.cessation_of_operation: 5,
     x509.ReasonFlags.certificate_hold: 6,
     x509.ReasonFlags.remove_from_crl: 8,
     x509.ReasonFlags.privilege_withdrawn: 9,
-    x509.ReasonFlags.aa_compromise: 10
+    x509.ReasonFlags.aa_compromise: 10,
 }


 def _decode_crl_reason(backend, enum):
     enum = backend._ffi.cast("ASN1_ENUMERATED *", enum)
     enum = backend._ffi.gc(enum, backend._lib.ASN1_ENUMERATED_free)
     code = backend._lib.ASN1_ENUMERATED_get(enum)
@@ -698,17 +718,15 @@
     try:
         return x509.CRLReason(_CRL_ENTRY_REASON_CODE_TO_ENUM[code])
     except KeyError:
         raise ValueError("Unsupported reason code: {}".format(code))


 def _decode_invalidity_date(backend, inv_date):
-    generalized_time = backend._ffi.cast(
-        "ASN1_GENERALIZEDTIME *", inv_date
-    )
+    generalized_time = backend._ffi.cast("ASN1_GENERALIZEDTIME *", inv_date)
     generalized_time = backend._ffi.gc(
         generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free
     )
     return x509.InvalidityDate(
         _parse_asn1_generalized_time(backend, generalized_time)
     )

@@ -761,15 +779,15 @@
             "Unsupported ASN1 string type. Type: {}".format(asn1_string.type)
         )

     backend.openssl_assert(buf[0] != backend._ffi.NULL)
     buf = backend._ffi.gc(
         buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])
     )
-    return backend._ffi.buffer(buf[0], res)[:].decode('utf8')
+    return backend._ffi.buffer(buf[0], res)[:].decode("utf8")


 def _parse_asn1_time(backend, asn1_time):
     backend.openssl_assert(asn1_time != backend._ffi.NULL)
     generalized_time = backend._lib.ASN1_TIME_to_generalizedtime(
         asn1_time, backend._ffi.NULL
     )
@@ -795,38 +813,41 @@

 def _decode_nonce(backend, nonce):
     nonce = backend._ffi.cast("ASN1_OCTET_STRING *", nonce)
     nonce = backend._ffi.gc(nonce, backend._lib.ASN1_OCTET_STRING_free)
     return x509.OCSPNonce(_asn1_string_to_bytes(backend, nonce))


-_EXTENSION_HANDLERS_NO_SCT = {
+_EXTENSION_HANDLERS_BASE = {
     ExtensionOID.BASIC_CONSTRAINTS: _decode_basic_constraints,
     ExtensionOID.SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
     ExtensionOID.KEY_USAGE: _decode_key_usage,
     ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
     ExtensionOID.EXTENDED_KEY_USAGE: _decode_extended_key_usage,
     ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
     ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
         _decode_authority_information_access
     ),
+    ExtensionOID.SUBJECT_INFORMATION_ACCESS: (
+        _decode_subject_information_access
+    ),
     ExtensionOID.CERTIFICATE_POLICIES: _decode_certificate_policies,
     ExtensionOID.CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
     ExtensionOID.FRESHEST_CRL: _decode_freshest_crl,
     ExtensionOID.OCSP_NO_CHECK: _decode_ocsp_no_check,
     ExtensionOID.INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
     ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints,
     ExtensionOID.POLICY_CONSTRAINTS: _decode_policy_constraints,
 }
-_EXTENSION_HANDLERS = _EXTENSION_HANDLERS_NO_SCT.copy()
-_EXTENSION_HANDLERS[
-    ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
-] = _decode_precert_signed_certificate_timestamps
-
+_EXTENSION_HANDLERS_SCT = {
+    ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: (
+        _decode_precert_signed_certificate_timestamps
+    )
+}

 _REVOKED_EXTENSION_HANDLERS = {
     CRLEntryExtensionOID.CRL_REASON: _decode_crl_reason,
     CRLEntryExtensionOID.INVALIDITY_DATE: _decode_invalidity_date,
     CRLEntryExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer,
 }

@@ -835,58 +856,23 @@
     ExtensionOID.DELTA_CRL_INDICATOR: _decode_delta_crl_indicator,
     ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
     ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
         _decode_authority_information_access
     ),
     ExtensionOID.ISSUING_DISTRIBUTION_POINT: _decode_issuing_dist_point,
+    ExtensionOID.FRESHEST_CRL: _decode_freshest_crl,
 }

 _OCSP_REQ_EXTENSION_HANDLERS = {
     OCSPExtensionOID.NONCE: _decode_nonce,
 }

 _OCSP_BASICRESP_EXTENSION_HANDLERS = {
     OCSPExtensionOID.NONCE: _decode_nonce,
 }

-_CERTIFICATE_EXTENSION_PARSER_NO_SCT = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
-    get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
-    handlers=_EXTENSION_HANDLERS_NO_SCT
-)
-
-_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
-    get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
-    handlers=_EXTENSION_HANDLERS
-)
-
-_CSR_EXTENSION_PARSER = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x),
-    get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i),
-    handlers=_EXTENSION_HANDLERS
-)
-
-_REVOKED_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.X509_REVOKED_get_ext_count(x),
-    get_ext=lambda backend, x, i: backend._lib.X509_REVOKED_get_ext(x, i),
-    handlers=_REVOKED_EXTENSION_HANDLERS,
-)
-
-_CRL_EXTENSION_PARSER = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.X509_CRL_get_ext_count(x),
-    get_ext=lambda backend, x, i: backend._lib.X509_CRL_get_ext(x, i),
-    handlers=_CRL_EXTENSION_HANDLERS,
-)
-
-_OCSP_REQ_EXT_PARSER = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.OCSP_REQUEST_get_ext_count(x),
-    get_ext=lambda backend, x, i: backend._lib.OCSP_REQUEST_get_ext(x, i),
-    handlers=_OCSP_REQ_EXTENSION_HANDLERS,
-)
-
-_OCSP_BASICRESP_EXT_PARSER = _X509ExtensionParser(
-    ext_count=lambda backend, x: backend._lib.OCSP_BASICRESP_get_ext_count(x),
-    get_ext=lambda backend, x, i: backend._lib.OCSP_BASICRESP_get_ext(x, i),
-    handlers=_OCSP_BASICRESP_EXTENSION_HANDLERS,
-)
+_OCSP_SINGLERESP_EXTENSION_HANDLERS_SCT = {
+    ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: (
+        _decode_signed_certificate_timestamps
+    )
+}

cryptography/hazmat/backends/openssl/ed448.py

@@ -3,51 +3,43 @@
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import exceptions, utils
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric.ed448 import (
-    Ed448PrivateKey, Ed448PublicKey
+    Ed448PrivateKey,
+    Ed448PublicKey,
 )

 _ED448_KEY_SIZE = 57
 _ED448_SIG_SIZE = 114


 @utils.register_interface(Ed448PublicKey)
 class _Ed448PublicKey(object):
     def __init__(self, backend, evp_pkey):
         self._backend = backend
         self._evp_pkey = evp_pkey

     def public_bytes(self, encoding, format):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                encoding is not serialization.Encoding.Raw or
-                format is not serialization.PublicFormat.Raw
+                encoding is not serialization.Encoding.Raw
+                or format is not serialization.PublicFormat.Raw
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw"
                 )

             return self._raw_public_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PublicFormat.SubjectPublicKeyInfo
-        ):
-            raise ValueError(
-                "format must be SubjectPublicKeyInfo when encoding is PEM or "
-                "DER"
-            )
-
         return self._backend._public_key_bytes(
             encoding, format, self, self._evp_pkey, None
         )

     def _raw_public_bytes(self):
         buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE)
         buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE)
@@ -61,16 +53,19 @@
     def verify(self, signature, data):
         evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
         self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
         evp_md_ctx = self._backend._ffi.gc(
             evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
         )
         res = self._backend._lib.EVP_DigestVerifyInit(
-            evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL,
-            self._backend._ffi.NULL, self._evp_pkey
+            evp_md_ctx,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._evp_pkey,
         )
         self._backend.openssl_assert(res == 1)
         res = self._backend._lib.EVP_DigestVerify(
             evp_md_ctx, signature, len(signature), data, len(data)
         )
         if res != 1:
             self._backend._consume_errors()
@@ -97,54 +92,51 @@
     def sign(self, data):
         evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
         self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
         evp_md_ctx = self._backend._ffi.gc(
             evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
         )
         res = self._backend._lib.EVP_DigestSignInit(
-            evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL,
-            self._backend._ffi.NULL, self._evp_pkey
+            evp_md_ctx,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._evp_pkey,
         )
         self._backend.openssl_assert(res == 1)
         buf = self._backend._ffi.new("unsigned char[]", _ED448_SIG_SIZE)
         buflen = self._backend._ffi.new("size_t *", len(buf))
         res = self._backend._lib.EVP_DigestSign(
             evp_md_ctx, buf, buflen, data, len(data)
         )
         self._backend.openssl_assert(res == 1)
         self._backend.openssl_assert(buflen[0] == _ED448_SIG_SIZE)
         return self._backend._ffi.buffer(buf, buflen[0])[:]

     def private_bytes(self, encoding, format, encryption_algorithm):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                format is not serialization.PrivateFormat.Raw or
-                encoding is not serialization.Encoding.Raw or not
-                isinstance(encryption_algorithm, serialization.NoEncryption)
+                format is not serialization.PrivateFormat.Raw
+                or encoding is not serialization.Encoding.Raw
+                or not isinstance(
+                    encryption_algorithm, serialization.NoEncryption
+                )
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw "
-                    "and encryption_algorithm must be NoEncryption"
+                    "and encryption_algorithm must be NoEncryption()"
                 )

             return self._raw_private_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PrivateFormat.PKCS8
-        ):
-            raise ValueError(
-                "format must be PKCS8 when encoding is PEM or DER"
-            )
-
         return self._backend._private_key_bytes(
-            encoding, format, encryption_algorithm, self._evp_pkey, None
+            encoding, format, encryption_algorithm, self, self._evp_pkey, None
         )

     def _raw_private_bytes(self):
         buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE)
         buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE)
         res = self._backend._lib.EVP_PKEY_get_raw_private_key(
             self._evp_pkey, buf, buflen

cryptography/hazmat/backends/openssl/ocsp.py

@@ -5,24 +5,31 @@
 from __future__ import absolute_import, division, print_function

 import functools

 from cryptography import utils, x509
 from cryptography.exceptions import UnsupportedAlgorithm
 from cryptography.hazmat.backends.openssl.decode_asn1 import (
-    _CRL_ENTRY_REASON_CODE_TO_ENUM, _OCSP_BASICRESP_EXT_PARSER,
-    _OCSP_REQ_EXT_PARSER, _asn1_integer_to_int,
-    _asn1_string_to_bytes, _decode_x509_name, _obj2txt,
+    _CRL_ENTRY_REASON_CODE_TO_ENUM,
+    _asn1_integer_to_int,
+    _asn1_string_to_bytes,
+    _decode_x509_name,
+    _obj2txt,
     _parse_asn1_generalized_time,
 )
 from cryptography.hazmat.backends.openssl.x509 import _Certificate
 from cryptography.hazmat.primitives import serialization
 from cryptography.x509.ocsp import (
-    OCSPCertStatus, OCSPRequest, OCSPResponse, OCSPResponseStatus,
-    _CERT_STATUS_TO_ENUM, _OIDS_TO_HASH, _RESPONSE_STATUS_TO_ENUM,
+    OCSPCertStatus,
+    OCSPRequest,
+    OCSPResponse,
+    OCSPResponseStatus,
+    _CERT_STATUS_TO_ENUM,
+    _OIDS_TO_HASH,
+    _RESPONSE_STATUS_TO_ENUM,
 )


 def _requires_successful_response(func):
     @functools.wraps(func)
     def wrapper(self, *args):
         if self.response_status != OCSPResponseStatus.SUCCESSFUL:
@@ -35,49 +42,57 @@

     return wrapper


 def _issuer_key_hash(backend, cert_id):
     key_hash = backend._ffi.new("ASN1_OCTET_STRING **")
     res = backend._lib.OCSP_id_get0_info(
-        backend._ffi.NULL, backend._ffi.NULL,
-        key_hash, backend._ffi.NULL, cert_id
+        backend._ffi.NULL,
+        backend._ffi.NULL,
+        key_hash,
+        backend._ffi.NULL,
+        cert_id,
     )
     backend.openssl_assert(res == 1)
     backend.openssl_assert(key_hash[0] != backend._ffi.NULL)
     return _asn1_string_to_bytes(backend, key_hash[0])


 def _issuer_name_hash(backend, cert_id):
     name_hash = backend._ffi.new("ASN1_OCTET_STRING **")
     res = backend._lib.OCSP_id_get0_info(
-        name_hash, backend._ffi.NULL,
-        backend._ffi.NULL, backend._ffi.NULL, cert_id
+        name_hash,
+        backend._ffi.NULL,
+        backend._ffi.NULL,
+        backend._ffi.NULL,
+        cert_id,
     )
     backend.openssl_assert(res == 1)
     backend.openssl_assert(name_hash[0] != backend._ffi.NULL)
     return _asn1_string_to_bytes(backend, name_hash[0])


 def _serial_number(backend, cert_id):
     num = backend._ffi.new("ASN1_INTEGER **")
     res = backend._lib.OCSP_id_get0_info(
-        backend._ffi.NULL, backend._ffi.NULL,
-        backend._ffi.NULL, num, cert_id
+        backend._ffi.NULL, backend._ffi.NULL, backend._ffi.NULL, num, cert_id
     )
     backend.openssl_assert(res == 1)
     backend.openssl_assert(num[0] != backend._ffi.NULL)
     return _asn1_integer_to_int(backend, num[0])


 def _hash_algorithm(backend, cert_id):
     asn1obj = backend._ffi.new("ASN1_OBJECT **")
     res = backend._lib.OCSP_id_get0_info(
-        backend._ffi.NULL, asn1obj,
-        backend._ffi.NULL, backend._ffi.NULL, cert_id
+        backend._ffi.NULL,
+        asn1obj,
+        backend._ffi.NULL,
+        backend._ffi.NULL,
+        cert_id,
     )
     backend.openssl_assert(res == 1)
     backend.openssl_assert(asn1obj[0] != backend._ffi.NULL)
     oid = _obj2txt(backend, asn1obj[0])
     try:
         return _OIDS_TO_HASH[oid]
     except KeyError:
@@ -98,17 +113,21 @@
             basic = self._backend._lib.OCSP_response_get1_basic(
                 self._ocsp_response
             )
             self._backend.openssl_assert(basic != self._backend._ffi.NULL)
             self._basic = self._backend._ffi.gc(
                 basic, self._backend._lib.OCSP_BASICRESP_free
             )
-            self._backend.openssl_assert(
-                self._backend._lib.OCSP_resp_count(self._basic) == 1
-            )
+            num_resp = self._backend._lib.OCSP_resp_count(self._basic)
+            if num_resp != 1:
+                raise ValueError(
+                    "OCSP response contains more than one SINGLERESP structure"
+                    ", which this library does not support. "
+                    "{} found".format(num_resp)
+                )
             self._single = self._backend._lib.OCSP_resp_get0(self._basic, 0)
             self._backend.openssl_assert(
                 self._single != self._backend._ffi.NULL
             )
             self._cert_id = self._backend._lib.OCSP_SINGLERESP_get0_id(
                 self._single
             )
@@ -313,36 +332,39 @@
     @_requires_successful_response
     def serial_number(self):
         return _serial_number(self._backend, self._cert_id)

     @utils.cached_property
     @_requires_successful_response
     def extensions(self):
-        return _OCSP_BASICRESP_EXT_PARSER.parse(self._backend, self._basic)
+        return self._backend._ocsp_basicresp_ext_parser.parse(self._basic)
+
+    @utils.cached_property
+    @_requires_successful_response
+    def single_extensions(self):
+        return self._backend._ocsp_singleresp_ext_parser.parse(self._single)

     def public_bytes(self, encoding):
         if encoding is not serialization.Encoding.DER:
-            raise ValueError(
-                "The only allowed encoding value is Encoding.DER"
-            )
+            raise ValueError("The only allowed encoding value is Encoding.DER")

         bio = self._backend._create_mem_bio_gc()
         res = self._backend._lib.i2d_OCSP_RESPONSE_bio(
             bio, self._ocsp_response
         )
         self._backend.openssl_assert(res > 0)
         return self._backend._read_mem_bio(bio)


 @utils.register_interface(OCSPRequest)
 class _OCSPRequest(object):
     def __init__(self, backend, ocsp_request):
         if backend._lib.OCSP_request_onereq_count(ocsp_request) > 1:
             raise NotImplementedError(
-                'OCSP request contains more than one request'
+                "OCSP request contains more than one request"
             )
         self._backend = backend
         self._ocsp_request = ocsp_request
         self._request = self._backend._lib.OCSP_request_onereq_get0(
             self._ocsp_request, 0
         )
         self._backend.openssl_assert(self._request != self._backend._ffi.NULL)
@@ -363,19 +385,17 @@

     @property
     def hash_algorithm(self):
         return _hash_algorithm(self._backend, self._cert_id)

     @utils.cached_property
     def extensions(self):
-        return _OCSP_REQ_EXT_PARSER.parse(self._backend, self._ocsp_request)
+        return self._backend._ocsp_req_ext_parser.parse(self._ocsp_request)

     def public_bytes(self, encoding):
         if encoding is not serialization.Encoding.DER:
-            raise ValueError(
-                "The only allowed encoding value is Encoding.DER"
-            )
+            raise ValueError("The only allowed encoding value is Encoding.DER")

         bio = self._backend._create_mem_bio_gc()
         res = self._backend._lib.i2d_OCSP_REQUEST_bio(bio, self._ocsp_request)
         self._backend.openssl_assert(res > 0)
         return self._backend._read_mem_bio(bio)

cryptography/hazmat/backends/openssl/x25519.py

@@ -1,68 +1,46 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

-import warnings
-
 from cryptography import utils
 from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric.x25519 import (
-    X25519PrivateKey, X25519PublicKey
+    X25519PrivateKey,
+    X25519PublicKey,
 )


 _X25519_KEY_SIZE = 32


 @utils.register_interface(X25519PublicKey)
 class _X25519PublicKey(object):
     def __init__(self, backend, evp_pkey):
         self._backend = backend
         self._evp_pkey = evp_pkey

-    def public_bytes(self, encoding=None, format=None):
-        if encoding is None or format is None:
-            if encoding is not None or format is not None:
-                raise ValueError("Both encoding and format are required")
-            else:
-                warnings.warn(
-                    "public_bytes now requires encoding and format arguments. "
-                    "Support for calling without arguments will be removed in "
-                    "cryptography 2.7",
-                    utils.DeprecatedIn25,
-                )
-                encoding = serialization.Encoding.Raw
-                format = serialization.PublicFormat.Raw
+    def public_bytes(self, encoding, format):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                encoding is not serialization.Encoding.Raw or
-                format is not serialization.PublicFormat.Raw
+                encoding is not serialization.Encoding.Raw
+                or format is not serialization.PublicFormat.Raw
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw"
                 )

             return self._raw_public_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PublicFormat.SubjectPublicKeyInfo
-        ):
-            raise ValueError(
-                "format must be SubjectPublicKeyInfo when encoding is PEM or "
-                "DER"
-            )
-
         return self._backend._public_key_bytes(
             encoding, format, self, self._evp_pkey, None
         )

     def _raw_public_bytes(self):
         ucharpp = self._backend._ffi.new("unsigned char **")
         res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint(
@@ -95,55 +73,51 @@
         )
         return _X25519PublicKey(self._backend, evp_pkey)

     def exchange(self, peer_public_key):
         if not isinstance(peer_public_key, X25519PublicKey):
             raise TypeError("peer_public_key must be X25519PublicKey.")

-        return _evp_pkey_derive(
-            self._backend, self._evp_pkey, peer_public_key
-        )
+        return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)

     def private_bytes(self, encoding, format, encryption_algorithm):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                format is not serialization.PrivateFormat.Raw or
-                encoding is not serialization.Encoding.Raw or not
-                isinstance(encryption_algorithm, serialization.NoEncryption)
+                format is not serialization.PrivateFormat.Raw
+                or encoding is not serialization.Encoding.Raw
+                or not isinstance(
+                    encryption_algorithm, serialization.NoEncryption
+                )
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw "
-                    "and encryption_algorithm must be NoEncryption"
+                    "and encryption_algorithm must be NoEncryption()"
                 )

             return self._raw_private_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PrivateFormat.PKCS8
-        ):
-            raise ValueError(
-                "format must be PKCS8 when encoding is PEM or DER"
-            )
-
         return self._backend._private_key_bytes(
-            encoding, format, encryption_algorithm, self._evp_pkey, None
+            encoding, format, encryption_algorithm, self, self._evp_pkey, None
         )

     def _raw_private_bytes(self):
         # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can
         # switch this to EVP_PKEY_new_raw_private_key
         # The trick we use here is serializing to a PKCS8 key and just
         # using the last 32 bytes, which is the key itself.
         bio = self._backend._create_mem_bio_gc()
         res = self._backend._lib.i2d_PKCS8PrivateKey_bio(
-            bio, self._evp_pkey,
-            self._backend._ffi.NULL, self._backend._ffi.NULL,
-            0, self._backend._ffi.NULL, self._backend._ffi.NULL
+            bio,
+            self._evp_pkey,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            0,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
         )
         self._backend.openssl_assert(res == 1)
         pkcs8 = self._backend._read_mem_bio(bio)
         self._backend.openssl_assert(len(pkcs8) == 48)
         return pkcs8[-_X25519_KEY_SIZE:]

cryptography/hazmat/backends/openssl/x509.py

@@ -6,31 +6,44 @@

 import datetime
 import operator

 from cryptography import utils, x509
 from cryptography.exceptions import UnsupportedAlgorithm
 from cryptography.hazmat.backends.openssl.decode_asn1 import (
-    _CERTIFICATE_EXTENSION_PARSER, _CERTIFICATE_EXTENSION_PARSER_NO_SCT,
-    _CRL_EXTENSION_PARSER, _CSR_EXTENSION_PARSER,
-    _REVOKED_CERTIFICATE_EXTENSION_PARSER, _asn1_integer_to_int,
-    _asn1_string_to_bytes, _decode_x509_name, _obj2txt, _parse_asn1_time
+    _asn1_integer_to_int,
+    _asn1_string_to_bytes,
+    _decode_x509_name,
+    _obj2txt,
+    _parse_asn1_time,
 )
 from cryptography.hazmat.backends.openssl.encode_asn1 import (
-    _encode_asn1_int_gc
+    _encode_asn1_int_gc,
+    _txt2obj_gc,
 )
 from cryptography.hazmat.primitives import hashes, serialization
 from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.x509.name import _ASN1Type


 @utils.register_interface(x509.Certificate)
 class _Certificate(object):
-    def __init__(self, backend, x509):
+    def __init__(self, backend, x509_cert):
         self._backend = backend
-        self._x509 = x509
+        self._x509 = x509_cert
+
+        version = self._backend._lib.X509_get_version(self._x509)
+        if version == 0:
+            self._version = x509.Version.v1
+        elif version == 2:
+            self._version = x509.Version.v3
+        else:
+            raise x509.InvalidVersion(
+                "{} is not a valid X509 version".format(version), version
+            )

     def __repr__(self):
         return "<Certificate(subject={}, ...)>".format(self.subject)

     def __eq__(self, other):
         if not isinstance(other, x509.Certificate):
             return NotImplemented
@@ -40,30 +53,23 @@

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash(self.public_bytes(serialization.Encoding.DER))

+    def __deepcopy__(self, memo):
+        return self
+
     def fingerprint(self, algorithm):
         h = hashes.Hash(algorithm, self._backend)
         h.update(self.public_bytes(serialization.Encoding.DER))
         return h.finalize()

-    @property
-    def version(self):
-        version = self._backend._lib.X509_get_version(self._x509)
-        if version == 0:
-            return x509.Version.v1
-        elif version == 2:
-            return x509.Version.v3
-        else:
-            raise x509.InvalidVersion(
-                "{} is not a valid X509 version".format(version), version
-            )
+    version = utils.read_only_property("_version")

     @property
     def serial_number(self):
         asn1_int = self._backend._lib.X509_get_serialNumber(self._x509)
         self._backend.openssl_assert(asn1_int != self._backend._ffi.NULL)
         return _asn1_integer_to_int(self._backend, asn1_int)

@@ -76,20 +82,20 @@

         pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free)

         return self._backend._evp_pkey_to_public_key(pkey)

     @property
     def not_valid_before(self):
-        asn1_time = self._backend._lib.X509_get_notBefore(self._x509)
+        asn1_time = self._backend._lib.X509_getm_notBefore(self._x509)
         return _parse_asn1_time(self._backend, asn1_time)

     @property
     def not_valid_after(self):
-        asn1_time = self._backend._lib.X509_get_notAfter(self._x509)
+        asn1_time = self._backend._lib.X509_getm_notAfter(self._x509)
         return _parse_asn1_time(self._backend, asn1_time)

     @property
     def issuer(self):
         issuer = self._backend._lib.X509_get_issuer_name(self._x509)
         self._backend.openssl_assert(issuer != self._backend._ffi.NULL)
         return _decode_x509_name(self._backend, issuer)
@@ -118,22 +124,15 @@
         )
         self._backend.openssl_assert(alg[0] != self._backend._ffi.NULL)
         oid = _obj2txt(self._backend, alg[0].algorithm)
         return x509.ObjectIdentifier(oid)

     @utils.cached_property
     def extensions(self):
-        if self._backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
-            return _CERTIFICATE_EXTENSION_PARSER.parse(
-                self._backend, self._x509
-            )
-        else:
-            return _CERTIFICATE_EXTENSION_PARSER_NO_SCT.parse(
-                self._backend, self._x509
-            )
+        return self._backend._certificate_extension_parser.parse(self._x509)

     @property
     def signature(self):
         sig = self._backend._ffi.new("ASN1_BIT_STRING **")
         self._backend._lib.X509_get0_signature(
             sig, self._backend._ffi.NULL, self._x509
         )
@@ -187,21 +186,21 @@

     @property
     def revocation_date(self):
         return _parse_asn1_time(
             self._backend,
             self._backend._lib.X509_REVOKED_get0_revocationDate(
                 self._x509_revoked
-            )
+            ),
         )

     @utils.cached_property
     def extensions(self):
-        return _REVOKED_CERTIFICATE_EXTENSION_PARSER.parse(
-            self._backend, self._x509_revoked
+        return self._backend._revoked_cert_extension_parser.parse(
+            self._x509_revoked
         )


 @utils.register_interface(x509.CertificateRevocationList)
 class _CertificateRevocationList(object):
     def __init__(self, backend, x509_crl):
         self._backend = backend
@@ -216,17 +215,15 @@

     def __ne__(self, other):
         return not self == other

     def fingerprint(self, algorithm):
         h = hashes.Hash(algorithm, self._backend)
         bio = self._backend._create_mem_bio_gc()
-        res = self._backend._lib.i2d_X509_CRL_bio(
-            bio, self._x509_crl
-        )
+        res = self._backend._lib.i2d_X509_CRL_bio(bio, self._x509_crl)
         self._backend.openssl_assert(res == 1)
         der = self._backend._read_mem_bio(bio)
         h.update(der)
         return h.finalize()

     @utils.cached_property
     def _sorted_crl(self):
@@ -243,17 +240,15 @@
         asn1_int = _encode_asn1_int_gc(self._backend, serial_number)
         res = self._backend._lib.X509_CRL_get0_by_serial(
             self._sorted_crl, revoked, asn1_int
         )
         if res == 0:
             return None
         else:
-            self._backend.openssl_assert(
-                revoked[0] != self._backend._ffi.NULL
-            )
+            self._backend.openssl_assert(revoked[0] != self._backend._ffi.NULL)
             return _RevokedCertificate(
                 self._backend, self._sorted_crl, revoked[0]
             )

     @property
     def signature_hash_algorithm(self):
         oid = self.signature_algorithm_oid
@@ -352,21 +347,25 @@
         if revoked == self._backend._ffi.NULL:
             return 0
         else:
             return self._backend._lib.sk_X509_REVOKED_num(revoked)

     @utils.cached_property
     def extensions(self):
-        return _CRL_EXTENSION_PARSER.parse(self._backend, self._x509_crl)
+        return self._backend._crl_extension_parser.parse(self._x509_crl)

     def is_signature_valid(self, public_key):
-        if not isinstance(public_key, (dsa.DSAPublicKey, rsa.RSAPublicKey,
-                                       ec.EllipticCurvePublicKey)):
-            raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,'
-                            ' or EllipticCurvePublicKey.')
+        if not isinstance(
+            public_key,
+            (dsa.DSAPublicKey, rsa.RSAPublicKey, ec.EllipticCurvePublicKey),
+        ):
+            raise TypeError(
+                "Expecting one of DSAPublicKey, RSAPublicKey,"
+                " or EllipticCurvePublicKey."
+            )
         res = self._backend._lib.X509_CRL_verify(
             self._x509_crl, public_key._evp_pkey
         )

         if res != 1:
             self._backend._consume_errors()
             return False
@@ -428,20 +427,21 @@

     @utils.cached_property
     def extensions(self):
         x509_exts = self._backend._lib.X509_REQ_get_extensions(self._x509_req)
         x509_exts = self._backend._ffi.gc(
             x509_exts,
             lambda x: self._backend._lib.sk_X509_EXTENSION_pop_free(
-                x, self._backend._ffi.addressof(
+                x,
+                self._backend._ffi.addressof(
                     self._backend._lib._original_lib, "X509_EXTENSION_free"
-                )
-            )
+                ),
+            ),
         )
-        return _CSR_EXTENSION_PARSER.parse(self._backend, x509_exts)
+        return self._backend._csr_extension_parser.parse(x509_exts)

     def public_bytes(self, encoding):
         bio = self._backend._create_mem_bio_gc()
         if encoding is serialization.Encoding.PEM:
             res = self._backend._lib.PEM_write_bio_X509_REQ(
                 bio, self._x509_req
             )
@@ -481,14 +481,55 @@

         if res != 1:
             self._backend._consume_errors()
             return False

         return True

+    def get_attribute_for_oid(self, oid):
+        obj = _txt2obj_gc(self._backend, oid.dotted_string)
+        pos = self._backend._lib.X509_REQ_get_attr_by_OBJ(
+            self._x509_req, obj, -1
+        )
+        if pos == -1:
+            raise x509.AttributeNotFound(
+                "No {} attribute was found".format(oid), oid
+            )
+
+        attr = self._backend._lib.X509_REQ_get_attr(self._x509_req, pos)
+        self._backend.openssl_assert(attr != self._backend._ffi.NULL)
+        # We don't support multiple valued attributes for now.
+        self._backend.openssl_assert(
+            self._backend._lib.X509_ATTRIBUTE_count(attr) == 1
+        )
+        asn1_type = self._backend._lib.X509_ATTRIBUTE_get0_type(attr, 0)
+        self._backend.openssl_assert(asn1_type != self._backend._ffi.NULL)
+        # We need this to ensure that our C type cast is safe.
+        # Also this should always be a sane string type, but we'll see if
+        # that is true in the real world...
+        if asn1_type.type not in (
+            _ASN1Type.UTF8String.value,
+            _ASN1Type.PrintableString.value,
+            _ASN1Type.IA5String.value,
+        ):
+            raise ValueError(
+                "OID {} has a disallowed ASN.1 type: {}".format(
+                    oid, asn1_type.type
+                )
+            )
+
+        data = self._backend._lib.X509_ATTRIBUTE_get0_data(
+            attr, 0, asn1_type.type, self._backend._ffi.NULL
+        )
+        self._backend.openssl_assert(data != self._backend._ffi.NULL)
+        # This cast is safe iff we assert on the type above to ensure
+        # that it is always a type of ASN1_STRING
+        data = self._backend._ffi.cast("ASN1_STRING *", data)
+        return _asn1_string_to_bytes(self._backend, data)
+

 @utils.register_interface(
     x509.certificate_transparency.SignedCertificateTimestamp
 )
 class _SignedCertificateTimestamp(object):
     def __init__(self, backend, sct_list, sct):
         self._backend = backend
@@ -509,17 +550,17 @@
         assert log_id_length >= 0
         return self._backend._ffi.buffer(out[0], log_id_length)[:]

     @property
     def timestamp(self):
         timestamp = self._backend._lib.SCT_get_timestamp(self._sct)
         milliseconds = timestamp % 1000
-        return datetime.datetime.utcfromtimestamp(
-            timestamp // 1000
-        ).replace(microsecond=milliseconds * 1000)
+        return datetime.datetime.utcfromtimestamp(timestamp // 1000).replace(
+            microsecond=milliseconds * 1000
+        )

     @property
     def entry_type(self):
         entry_type = self._backend._lib.SCT_get_log_entry_type(self._sct)
         # We currently only support loading SCTs from the X.509 extension, so
         # we only have precerts.
         assert entry_type == self._backend._lib.CT_LOG_ENTRY_TYPE_PRECERT

cryptography/hazmat/backends/openssl/hashes.py

@@ -22,19 +22,21 @@
             ctx = self._backend._ffi.gc(
                 ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
             )
             evp_md = self._backend._evp_md_from_algorithm(algorithm)
             if evp_md == self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
                     "{} is not a supported hash on this backend.".format(
-                        algorithm.name),
-                    _Reasons.UNSUPPORTED_HASH
+                        algorithm.name
+                    ),
+                    _Reasons.UNSUPPORTED_HASH,
                 )
-            res = self._backend._lib.EVP_DigestInit_ex(ctx, evp_md,
-                                                       self._backend._ffi.NULL)
+            res = self._backend._lib.EVP_DigestInit_ex(
+                ctx, evp_md, self._backend._ffi.NULL
+            )
             self._backend.openssl_assert(res != 0)

         self._ctx = ctx

     algorithm = utils.read_only_property("_algorithm")

     def copy(self):
@@ -54,25 +56,27 @@
         self._backend.openssl_assert(res != 0)

     def finalize(self):
         if isinstance(self.algorithm, hashes.ExtendableOutputFunction):
             # extendable output functions use a different finalize
             return self._finalize_xof()
         else:
-            buf = self._backend._ffi.new("unsigned char[]",
-                                         self._backend._lib.EVP_MAX_MD_SIZE)
+            buf = self._backend._ffi.new(
+                "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE
+            )
             outlen = self._backend._ffi.new("unsigned int *")
             res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen)
             self._backend.openssl_assert(res != 0)
             self._backend.openssl_assert(
                 outlen[0] == self.algorithm.digest_size
             )
-            return self._backend._ffi.buffer(buf)[:outlen[0]]
+            return self._backend._ffi.buffer(buf)[: outlen[0]]

     def _finalize_xof(self):
-        buf = self._backend._ffi.new("unsigned char[]",
-                                     self.algorithm.digest_size)
+        buf = self._backend._ffi.new(
+            "unsigned char[]", self.algorithm.digest_size
+        )
         res = self._backend._lib.EVP_DigestFinalXOF(
             self._ctx, buf, self.algorithm.digest_size
         )
         self._backend.openssl_assert(res != 0)
-        return self._backend._ffi.buffer(buf)[:self.algorithm.digest_size]
+        return self._backend._ffi.buffer(buf)[: self.algorithm.digest_size]

cryptography/hazmat/backends/openssl/backend.py

@@ -1,97 +1,172 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

-import base64
 import collections
 import contextlib
 import itertools
+import warnings
 from contextlib import contextmanager

-import asn1crypto.core
-
 import six
 from six.moves import range

 from cryptography import utils, x509
 from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat._der import (
+    INTEGER,
+    NULL,
+    SEQUENCE,
+    encode_der,
+    encode_der_integer,
+)
 from cryptography.hazmat.backends.interfaces import (
-    CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, DSABackend,
-    EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
-    PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
+    CMACBackend,
+    CipherBackend,
+    DERSerializationBackend,
+    DHBackend,
+    DSABackend,
+    EllipticCurveBackend,
+    HMACBackend,
+    HashBackend,
+    PBKDF2HMACBackend,
+    PEMSerializationBackend,
+    RSABackend,
+    ScryptBackend,
+    X509Backend,
 )
 from cryptography.hazmat.backends.openssl import aead
 from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
 from cryptography.hazmat.backends.openssl.cmac import _CMACContext
 from cryptography.hazmat.backends.openssl.decode_asn1 import (
-    _CRL_ENTRY_REASON_ENUM_TO_CODE, _Integers
+    _CRL_ENTRY_REASON_ENUM_TO_CODE,
+    _CRL_EXTENSION_HANDLERS,
+    _EXTENSION_HANDLERS_BASE,
+    _EXTENSION_HANDLERS_SCT,
+    _OCSP_BASICRESP_EXTENSION_HANDLERS,
+    _OCSP_REQ_EXTENSION_HANDLERS,
+    _OCSP_SINGLERESP_EXTENSION_HANDLERS_SCT,
+    _REVOKED_EXTENSION_HANDLERS,
+    _X509ExtensionParser,
 )
 from cryptography.hazmat.backends.openssl.dh import (
-    _DHParameters, _DHPrivateKey, _DHPublicKey, _dh_params_dup
+    _DHParameters,
+    _DHPrivateKey,
+    _DHPublicKey,
+    _dh_params_dup,
 )
 from cryptography.hazmat.backends.openssl.dsa import (
-    _DSAParameters, _DSAPrivateKey, _DSAPublicKey
+    _DSAParameters,
+    _DSAPrivateKey,
+    _DSAPublicKey,
 )
 from cryptography.hazmat.backends.openssl.ec import (
-    _EllipticCurvePrivateKey, _EllipticCurvePublicKey
+    _EllipticCurvePrivateKey,
+    _EllipticCurvePublicKey,
 )
 from cryptography.hazmat.backends.openssl.ed25519 import (
-    _Ed25519PrivateKey, _Ed25519PublicKey
+    _Ed25519PrivateKey,
+    _Ed25519PublicKey,
 )
 from cryptography.hazmat.backends.openssl.ed448 import (
-    _ED448_KEY_SIZE, _Ed448PrivateKey, _Ed448PublicKey
+    _ED448_KEY_SIZE,
+    _Ed448PrivateKey,
+    _Ed448PublicKey,
 )
 from cryptography.hazmat.backends.openssl.encode_asn1 import (
     _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS,
-    _CRL_EXTENSION_ENCODE_HANDLERS, _EXTENSION_ENCODE_HANDLERS,
+    _CRL_EXTENSION_ENCODE_HANDLERS,
+    _EXTENSION_ENCODE_HANDLERS,
     _OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS,
     _OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS,
-    _encode_asn1_int_gc, _encode_asn1_str_gc, _encode_name_gc, _txt2obj_gc,
+    _encode_asn1_int_gc,
+    _encode_asn1_str_gc,
+    _encode_name_gc,
+    _txt2obj_gc,
 )
 from cryptography.hazmat.backends.openssl.hashes import _HashContext
 from cryptography.hazmat.backends.openssl.hmac import _HMACContext
 from cryptography.hazmat.backends.openssl.ocsp import (
-    _OCSPRequest, _OCSPResponse
+    _OCSPRequest,
+    _OCSPResponse,
+)
+from cryptography.hazmat.backends.openssl.poly1305 import (
+    _POLY1305_KEY_SIZE,
+    _Poly1305Context,
 )
 from cryptography.hazmat.backends.openssl.rsa import (
-    _RSAPrivateKey, _RSAPublicKey
+    _RSAPrivateKey,
+    _RSAPublicKey,
 )
 from cryptography.hazmat.backends.openssl.x25519 import (
-    _X25519PrivateKey, _X25519PublicKey
+    _X25519PrivateKey,
+    _X25519PublicKey,
 )
 from cryptography.hazmat.backends.openssl.x448 import (
-    _X448PrivateKey, _X448PublicKey
+    _X448PrivateKey,
+    _X448PublicKey,
 )
 from cryptography.hazmat.backends.openssl.x509 import (
-    _Certificate, _CertificateRevocationList,
-    _CertificateSigningRequest, _RevokedCertificate
+    _Certificate,
+    _CertificateRevocationList,
+    _CertificateSigningRequest,
+    _RevokedCertificate,
 )
 from cryptography.hazmat.bindings.openssl import binding
 from cryptography.hazmat.primitives import hashes, serialization
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa
+from cryptography.hazmat.primitives.asymmetric import (
+    dsa,
+    ec,
+    ed25519,
+    ed448,
+    rsa,
+)
 from cryptography.hazmat.primitives.asymmetric.padding import (
-    MGF1, OAEP, PKCS1v15, PSS
+    MGF1,
+    OAEP,
+    PKCS1v15,
+    PSS,
 )
 from cryptography.hazmat.primitives.ciphers.algorithms import (
-    AES, ARC4, Blowfish, CAST5, Camellia, ChaCha20, IDEA, SEED, TripleDES
+    AES,
+    ARC4,
+    Blowfish,
+    CAST5,
+    Camellia,
+    ChaCha20,
+    IDEA,
+    SEED,
+    TripleDES,
 )
 from cryptography.hazmat.primitives.ciphers.modes import (
-    CBC, CFB, CFB8, CTR, ECB, GCM, OFB, XTS
+    CBC,
+    CFB,
+    CFB8,
+    CTR,
+    ECB,
+    GCM,
+    OFB,
+    XTS,
 )
 from cryptography.hazmat.primitives.kdf import scrypt
-from cryptography.hazmat.primitives.serialization import ssh
+from cryptography.hazmat.primitives.serialization import pkcs7, ssh
 from cryptography.x509 import ocsp


 _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])


+# Not actually supported, just used as a marker for some serialization tests.
+class _RC2(object):
+    pass
+
+
 @utils.register_interface(CipherBackend)
 @utils.register_interface(CMACBackend)
 @utils.register_interface(DERSerializationBackend)
 @utils.register_interface(DHBackend)
 @utils.register_interface(DSABackend)
 @utils.register_interface(EllipticCurveBackend)
 @utils.register_interface(HashBackend)
@@ -103,39 +178,92 @@
 @utils.register_interface_if(
     binding.Binding().lib.Cryptography_HAS_SCRYPT, ScryptBackend
 )
 class Backend(object):
     """
     OpenSSL API binding interfaces.
     """
+
     name = "openssl"

+    # FIPS has opinions about acceptable algorithms and key sizes, but the
+    # disallowed algorithms are still present in OpenSSL. They just error if
+    # you try to use them. To avoid that we allowlist the algorithms in
+    # FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are.
+    _fips_aead = {
+        b"aes-128-ccm",
+        b"aes-192-ccm",
+        b"aes-256-ccm",
+        b"aes-128-gcm",
+        b"aes-192-gcm",
+        b"aes-256-gcm",
+    }
+    _fips_ciphers = (AES, TripleDES)
+    _fips_hashes = (
+        hashes.SHA1,
+        hashes.SHA224,
+        hashes.SHA256,
+        hashes.SHA384,
+        hashes.SHA512,
+        hashes.SHA512_224,
+        hashes.SHA512_256,
+        hashes.SHA3_224,
+        hashes.SHA3_256,
+        hashes.SHA3_384,
+        hashes.SHA3_512,
+        hashes.SHAKE128,
+        hashes.SHAKE256,
+    )
+    _fips_rsa_min_key_size = 2048
+    _fips_rsa_min_public_exponent = 65537
+    _fips_dsa_min_modulus = 1 << 2048
+    _fips_dh_min_key_size = 2048
+    _fips_dh_min_modulus = 1 << _fips_dh_min_key_size
+
     def __init__(self):
         self._binding = binding.Binding()
         self._ffi = self._binding.ffi
         self._lib = self._binding.lib
+        self._fips_enabled = self._is_fips_enabled()

         self._cipher_registry = {}
         self._register_default_ciphers()
-        self.activate_osrandom_engine()
+        self._register_x509_ext_parsers()
+        self._register_x509_encoders()
+        if self._fips_enabled and self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
+            warnings.warn(
+                "OpenSSL FIPS mode is enabled. Can't enable DRBG fork safety.",
+                UserWarning,
+            )
+        else:
+            self.activate_osrandom_engine()
         self._dh_types = [self._lib.EVP_PKEY_DH]
         if self._lib.Cryptography_HAS_EVP_PKEY_DHX:
             self._dh_types.append(self._lib.EVP_PKEY_DHX)

-    def openssl_assert(self, ok):
-        return binding._openssl_assert(self._lib, ok)
+    def openssl_assert(self, ok, errors=None):
+        return binding._openssl_assert(self._lib, ok, errors=errors)
+
+    def _is_fips_enabled(self):
+        fips_mode = getattr(self._lib, "FIPS_mode", lambda: 0)
+        mode = fips_mode()
+        if mode == 0:
+            # OpenSSL without FIPS pushes an error on the error stack
+            self._lib.ERR_clear_error()
+        return bool(mode)

     def activate_builtin_random(self):
-        if self._lib.Cryptography_HAS_ENGINE:
+        if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
             # Obtain a new structural reference.
             e = self._lib.ENGINE_get_default_RAND()
             if e != self._ffi.NULL:
                 self._lib.ENGINE_unregister_RAND(e)
-                # Reset the RNG to use the new engine.
-                self._lib.RAND_cleanup()
+                # Reset the RNG to use the built-in.
+                res = self._lib.RAND_set_rand_method(self._ffi.NULL)
+                self.openssl_assert(res == 1)
                 # decrement the structural reference from get_default_RAND
                 res = self._lib.ENGINE_finish(e)
                 self.openssl_assert(res == 1)

     @contextlib.contextmanager
     def _get_osurandom_engine(self):
         # Fetches an engine by id and returns it. This creates a structural
@@ -153,39 +281,40 @@
             res = self._lib.ENGINE_free(e)
             self.openssl_assert(res == 1)
             # Decrement the functional ref incremented by ENGINE_init.
             res = self._lib.ENGINE_finish(e)
             self.openssl_assert(res == 1)

     def activate_osrandom_engine(self):
-        if self._lib.Cryptography_HAS_ENGINE:
+        if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
             # Unregister and free the current engine.
             self.activate_builtin_random()
             with self._get_osurandom_engine() as e:
                 # Set the engine as the default RAND provider.
                 res = self._lib.ENGINE_set_default_RAND(e)
                 self.openssl_assert(res == 1)
-            # Reset the RNG to use the new engine.
-            self._lib.RAND_cleanup()
+            # Reset the RNG to use the engine
+            res = self._lib.RAND_set_rand_method(self._ffi.NULL)
+            self.openssl_assert(res == 1)

     def osrandom_engine_implementation(self):
         buf = self._ffi.new("char[]", 64)
         with self._get_osurandom_engine() as e:
-            res = self._lib.ENGINE_ctrl_cmd(e, b"get_implementation",
-                                            len(buf), buf,
-                                            self._ffi.NULL, 0)
+            res = self._lib.ENGINE_ctrl_cmd(
+                e, b"get_implementation", len(buf), buf, self._ffi.NULL, 0
+            )
             self.openssl_assert(res > 0)
-        return self._ffi.string(buf).decode('ascii')
+        return self._ffi.string(buf).decode("ascii")

     def openssl_version_text(self):
         """
         Friendly string name of the loaded OpenSSL library. This is not
         necessarily the same version as it was compiled against.

-        Example: OpenSSL 1.0.1e 11 Feb 2013
+        Example: OpenSSL 1.1.1d  10 Sep 2019
         """
         return self._ffi.string(
             self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION)
         ).decode("ascii")

     def openssl_version_number(self):
         return self._lib.OpenSSL_version_num()
@@ -206,136 +335,206 @@

     def _evp_md_non_null_from_algorithm(self, algorithm):
         evp_md = self._evp_md_from_algorithm(algorithm)
         self.openssl_assert(evp_md != self._ffi.NULL)
         return evp_md

     def hash_supported(self, algorithm):
+        if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
+            return False
+
         evp_md = self._evp_md_from_algorithm(algorithm)
         return evp_md != self._ffi.NULL

     def hmac_supported(self, algorithm):
         return self.hash_supported(algorithm)

     def create_hash_ctx(self, algorithm):
         return _HashContext(self, algorithm)

     def cipher_supported(self, cipher, mode):
+        if self._fips_enabled and not isinstance(cipher, self._fips_ciphers):
+            return False
         try:
             adapter = self._cipher_registry[type(cipher), type(mode)]
         except KeyError:
             return False
         evp_cipher = adapter(self, cipher, mode)
         return self._ffi.NULL != evp_cipher

     def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
         if (cipher_cls, mode_cls) in self._cipher_registry:
-            raise ValueError("Duplicate registration for: {} {}.".format(
-                cipher_cls, mode_cls)
+            raise ValueError(
+                "Duplicate registration for: {} {}.".format(
+                    cipher_cls, mode_cls
+                )
             )
         self._cipher_registry[cipher_cls, mode_cls] = adapter

     def _register_default_ciphers(self):
         for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]:
             self.register_cipher_adapter(
                 AES,
                 mode_cls,
-                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
             )
         for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
             self.register_cipher_adapter(
                 Camellia,
                 mode_cls,
-                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
             )
         for mode_cls in [CBC, CFB, CFB8, OFB]:
             self.register_cipher_adapter(
-                TripleDES,
-                mode_cls,
-                GetCipherByName("des-ede3-{mode.name}")
+                TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}")
             )
         self.register_cipher_adapter(
-            TripleDES,
-            ECB,
-            GetCipherByName("des-ede3")
+            TripleDES, ECB, GetCipherByName("des-ede3")
         )
         for mode_cls in [CBC, CFB, OFB, ECB]:
             self.register_cipher_adapter(
-                Blowfish,
-                mode_cls,
-                GetCipherByName("bf-{mode.name}")
+                Blowfish, mode_cls, GetCipherByName("bf-{mode.name}")
             )
         for mode_cls in [CBC, CFB, OFB, ECB]:
             self.register_cipher_adapter(
-                SEED,
-                mode_cls,
-                GetCipherByName("seed-{mode.name}")
+                SEED, mode_cls, GetCipherByName("seed-{mode.name}")
             )
         for cipher_cls, mode_cls in itertools.product(
             [CAST5, IDEA],
             [CBC, OFB, CFB, ECB],
         ):
             self.register_cipher_adapter(
                 cipher_cls,
                 mode_cls,
-                GetCipherByName("{cipher.name}-{mode.name}")
+                GetCipherByName("{cipher.name}-{mode.name}"),
             )
+        self.register_cipher_adapter(ARC4, type(None), GetCipherByName("rc4"))
+        # We don't actually support RC2, this is just used by some tests.
+        self.register_cipher_adapter(_RC2, type(None), GetCipherByName("rc2"))
         self.register_cipher_adapter(
-            ARC4,
-            type(None),
-            GetCipherByName("rc4")
-        )
-        self.register_cipher_adapter(
-            ChaCha20,
-            type(None),
-            GetCipherByName("chacha20")
+            ChaCha20, type(None), GetCipherByName("chacha20")
         )
         self.register_cipher_adapter(AES, XTS, _get_xts_cipher)

+    def _register_x509_ext_parsers(self):
+        ext_handlers = _EXTENSION_HANDLERS_BASE.copy()
+        # All revoked extensions are valid single response extensions, see:
+        # https://tools.ietf.org/html/rfc6960#section-4.4.5
+        singleresp_handlers = _REVOKED_EXTENSION_HANDLERS.copy()
+
+        if self._lib.Cryptography_HAS_SCT:
+            ext_handlers.update(_EXTENSION_HANDLERS_SCT)
+            singleresp_handlers.update(_OCSP_SINGLERESP_EXTENSION_HANDLERS_SCT)
+
+        self._certificate_extension_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.X509_get_ext_count,
+            get_ext=self._lib.X509_get_ext,
+            handlers=ext_handlers,
+        )
+        self._csr_extension_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.sk_X509_EXTENSION_num,
+            get_ext=self._lib.sk_X509_EXTENSION_value,
+            handlers=ext_handlers,
+        )
+        self._revoked_cert_extension_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.X509_REVOKED_get_ext_count,
+            get_ext=self._lib.X509_REVOKED_get_ext,
+            handlers=_REVOKED_EXTENSION_HANDLERS,
+        )
+        self._crl_extension_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.X509_CRL_get_ext_count,
+            get_ext=self._lib.X509_CRL_get_ext,
+            handlers=_CRL_EXTENSION_HANDLERS,
+        )
+        self._ocsp_req_ext_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.OCSP_REQUEST_get_ext_count,
+            get_ext=self._lib.OCSP_REQUEST_get_ext,
+            handlers=_OCSP_REQ_EXTENSION_HANDLERS,
+        )
+        self._ocsp_basicresp_ext_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.OCSP_BASICRESP_get_ext_count,
+            get_ext=self._lib.OCSP_BASICRESP_get_ext,
+            handlers=_OCSP_BASICRESP_EXTENSION_HANDLERS,
+        )
+        self._ocsp_singleresp_ext_parser = _X509ExtensionParser(
+            self,
+            ext_count=self._lib.OCSP_SINGLERESP_get_ext_count,
+            get_ext=self._lib.OCSP_SINGLERESP_get_ext,
+            handlers=singleresp_handlers,
+        )
+
+    def _register_x509_encoders(self):
+        self._extension_encode_handlers = _EXTENSION_ENCODE_HANDLERS.copy()
+        self._crl_extension_encode_handlers = (
+            _CRL_EXTENSION_ENCODE_HANDLERS.copy()
+        )
+        self._crl_entry_extension_encode_handlers = (
+            _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS.copy()
+        )
+        self._ocsp_request_extension_encode_handlers = (
+            _OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS.copy()
+        )
+        self._ocsp_basicresp_extension_encode_handlers = (
+            _OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS.copy()
+        )
+
     def create_symmetric_encryption_ctx(self, cipher, mode):
         return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

     def create_symmetric_decryption_ctx(self, cipher, mode):
         return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

     def pbkdf2_hmac_supported(self, algorithm):
         return self.hmac_supported(algorithm)

-    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
-                           key_material):
+    def derive_pbkdf2_hmac(
+        self, algorithm, length, salt, iterations, key_material
+    ):
         buf = self._ffi.new("unsigned char[]", length)
         evp_md = self._evp_md_non_null_from_algorithm(algorithm)
         key_material_ptr = self._ffi.from_buffer(key_material)
         res = self._lib.PKCS5_PBKDF2_HMAC(
             key_material_ptr,
             len(key_material),
             salt,
             len(salt),
             iterations,
             evp_md,
             length,
-            buf
+            buf,
         )
         self.openssl_assert(res == 1)
         return self._ffi.buffer(buf)[:]

     def _consume_errors(self):
         return binding._consume_errors(self._lib)

+    def _consume_errors_with_text(self):
+        return binding._consume_errors_with_text(self._lib)
+
     def _bn_to_int(self, bn):
         assert bn != self._ffi.NULL

         if not six.PY2:
             # Python 3 has constant time from_bytes, so use that.
             bn_num_bytes = self._lib.BN_num_bytes(bn)
             bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes)
             bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
             # A zero length means the BN has value 0
             self.openssl_assert(bin_len >= 0)
-            return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
+            val = int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
+            if self._lib.BN_is_negative(bn):
+                val = -val
+            return val
         else:
             # Under Python 2 the best we can do is hex()
             hex_cdata = self._lib.BN_bn2hex(bn)
             self.openssl_assert(hex_cdata != self._ffi.NULL)
             hex_str = self._ffi.string(hex_cdata)
             self._lib.OPENSSL_free(hex_cdata)
             return int(hex_str, 16)
@@ -386,27 +585,30 @@
         )
         self.openssl_assert(res == 1)
         evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)

         return _RSAPrivateKey(self, rsa_cdata, evp_pkey)

     def generate_rsa_parameters_supported(self, public_exponent, key_size):
-        return (public_exponent >= 3 and public_exponent & 1 != 0 and
-                key_size >= 512)
+        return (
+            public_exponent >= 3
+            and public_exponent & 1 != 0
+            and key_size >= 512
+        )

     def load_rsa_private_numbers(self, numbers):
         rsa._check_private_key_components(
             numbers.p,
             numbers.q,
             numbers.d,
             numbers.dmp1,
             numbers.dmq1,
             numbers.iqmp,
             numbers.public_numbers.e,
-            numbers.public_numbers.n
+            numbers.public_numbers.n,
         )
         rsa_cdata = self._lib.RSA_new()
         self.openssl_assert(rsa_cdata != self._ffi.NULL)
         rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
         p = self._int_to_bn(numbers.p)
         q = self._int_to_bn(numbers.q)
         d = self._int_to_bn(numbers.d)
@@ -417,16 +619,14 @@
         n = self._int_to_bn(numbers.public_numbers.n)
         res = self._lib.RSA_set0_factors(rsa_cdata, p, q)
         self.openssl_assert(res == 1)
         res = self._lib.RSA_set0_key(rsa_cdata, n, e, d)
         self.openssl_assert(res == 1)
         res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp)
         self.openssl_assert(res == 1)
-        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
-        self.openssl_assert(res == 1)
         evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)

         return _RSAPrivateKey(self, rsa_cdata, evp_pkey)

     def load_rsa_public_numbers(self, numbers):
         rsa._check_public_key_components(numbers.e, numbers.n)
         rsa_cdata = self._lib.RSA_new()
@@ -456,17 +656,15 @@
         """
         Return a _MemoryBIO namedtuple of (BIO, char*).

         The char* is the storage for the BIO and it must stay alive until the
         BIO is finished with.
         """
         data_ptr = self._ffi.from_buffer(data)
-        bio = self._lib.BIO_new_mem_buf(
-            data_ptr, len(data)
-        )
+        bio = self._lib.BIO_new_mem_buf(data_ptr, len(data))
         self.openssl_assert(bio != self._ffi.NULL)

         return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr)

     def _create_mem_bio_gc(self):
         """
         Creates an empty memory BIO.
@@ -574,53 +772,61 @@
             return _Ed448PublicKey(self, evp_pkey)
         else:
             raise UnsupportedAlgorithm("Unsupported key type.")

     def _oaep_hash_supported(self, algorithm):
         if self._lib.Cryptography_HAS_RSA_OAEP_MD:
             return isinstance(
-                algorithm, (
+                algorithm,
+                (
                     hashes.SHA1,
                     hashes.SHA224,
                     hashes.SHA256,
                     hashes.SHA384,
                     hashes.SHA512,
-                )
+                ),
             )
         else:
             return isinstance(algorithm, hashes.SHA1)

     def rsa_padding_supported(self, padding):
         if isinstance(padding, PKCS1v15):
             return True
         elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
             return self.hash_supported(padding._mgf._algorithm)
         elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
             return (
-                self._oaep_hash_supported(padding._mgf._algorithm) and
-                self._oaep_hash_supported(padding._algorithm) and
-                (
-                    (padding._label is None or len(padding._label) == 0) or
-                    self._lib.Cryptography_HAS_RSA_OAEP_LABEL == 1
+                self._oaep_hash_supported(padding._mgf._algorithm)
+                and self._oaep_hash_supported(padding._algorithm)
+                and (
+                    (padding._label is None or len(padding._label) == 0)
+                    or self._lib.Cryptography_HAS_RSA_OAEP_LABEL == 1
                 )
             )
         else:
             return False

     def generate_dsa_parameters(self, key_size):
-        if key_size not in (1024, 2048, 3072):
-            raise ValueError("Key size must be 1024 or 2048 or 3072 bits.")
+        if key_size not in (1024, 2048, 3072, 4096):
+            raise ValueError(
+                "Key size must be 1024, 2048, 3072, or 4096 bits."
+            )

         ctx = self._lib.DSA_new()
         self.openssl_assert(ctx != self._ffi.NULL)
         ctx = self._ffi.gc(ctx, self._lib.DSA_free)

         res = self._lib.DSA_generate_parameters_ex(
-            ctx, key_size, self._ffi.NULL, 0,
-            self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+            ctx,
+            key_size,
+            self._ffi.NULL,
+            0,
+            self._ffi.NULL,
+            self._ffi.NULL,
+            self._ffi.NULL,
         )

         self.openssl_assert(res == 1)

         return _DSAParameters(self, ctx)

     def generate_dsa_private_key(self, parameters):
@@ -708,28 +914,45 @@
         return self.cipher_supported(
             algorithm, CBC(b"\x00" * algorithm.block_size)
         )

     def create_cmac_ctx(self, algorithm):
         return _CMACContext(self, algorithm)

-    def create_x509_csr(self, builder, private_key, algorithm):
-        if not isinstance(algorithm, hashes.HashAlgorithm):
-            raise TypeError('Algorithm must be a registered hash algorithm.')
-
-        if (
-            isinstance(algorithm, hashes.MD5) and not
-            isinstance(private_key, rsa.RSAPrivateKey)
+    def _x509_check_signature_params(self, private_key, algorithm):
+        if isinstance(
+            private_key, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)
+        ):
+            if algorithm is not None:
+                raise ValueError(
+                    "algorithm must be None when signing via ed25519 or ed448"
+                )
+        elif not isinstance(
+            private_key,
+            (rsa.RSAPrivateKey, dsa.DSAPrivateKey, ec.EllipticCurvePrivateKey),
+        ):
+            raise TypeError(
+                "Key must be an rsa, dsa, ec, ed25519, or ed448 private key."
+            )
+        elif not isinstance(algorithm, hashes.HashAlgorithm):
+            raise TypeError("Algorithm must be a registered hash algorithm.")
+        elif isinstance(algorithm, hashes.MD5) and not isinstance(
+            private_key, rsa.RSAPrivateKey
         ):
             raise ValueError(
-                "MD5 is not a supported hash algorithm for EC/DSA CSRs"
+                "MD5 hash algorithm is only supported with RSA keys"
             )

+    def create_x509_csr(self, builder, private_key, algorithm):
+        if not isinstance(builder, x509.CertificateSigningRequestBuilder):
+            raise TypeError("Builder type mismatch.")
+        self._x509_check_signature_params(private_key, algorithm)
+
         # Resolve the signature algorithm.
-        evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+        evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)

         # Create an empty request.
         x509_req = self._lib.X509_REQ_new()
         self.openssl_assert(x509_req != self._ffi.NULL)
         x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free)

         # Set x509 version.
@@ -740,79 +963,72 @@
         res = self._lib.X509_REQ_set_subject_name(
             x509_req, _encode_name_gc(self, builder._subject_name)
         )
         self.openssl_assert(res == 1)

         # Set subject public key.
         public_key = private_key.public_key()
-        res = self._lib.X509_REQ_set_pubkey(
-            x509_req, public_key._evp_pkey
-        )
+        res = self._lib.X509_REQ_set_pubkey(x509_req, public_key._evp_pkey)
         self.openssl_assert(res == 1)

         # Add extensions.
         sk_extension = self._lib.sk_X509_EXTENSION_new_null()
         self.openssl_assert(sk_extension != self._ffi.NULL)
         sk_extension = self._ffi.gc(
             sk_extension,
             lambda x: self._lib.sk_X509_EXTENSION_pop_free(
-                x, self._ffi.addressof(
+                x,
+                self._ffi.addressof(
                     self._lib._original_lib, "X509_EXTENSION_free"
-                )
-            )
+                ),
+            ),
         )
         # Don't GC individual extensions because the memory is owned by
         # sk_extensions and will be freed along with it.
         self._create_x509_extensions(
             extensions=builder._extensions,
-            handlers=_EXTENSION_ENCODE_HANDLERS,
+            handlers=self._extension_encode_handlers,
             x509_obj=sk_extension,
             add_func=self._lib.sk_X509_EXTENSION_insert,
-            gc=False
+            gc=False,
         )
         res = self._lib.X509_REQ_add_extensions(x509_req, sk_extension)
         self.openssl_assert(res == 1)

-        # Sign the request using the requester's private key.
-        res = self._lib.X509_REQ_sign(
-            x509_req, private_key._evp_pkey, evp_md
-        )
-        if res == 0:
-            errors = self._consume_errors()
-            self.openssl_assert(
-                errors[0]._lib_reason_match(
-                    self._lib.ERR_LIB_RSA,
-                    self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
-                )
+        # Add attributes (all bytes encoded as ASN1 UTF8_STRING)
+        for attr_oid, attr_val in builder._attributes:
+            obj = _txt2obj_gc(self, attr_oid.dotted_string)
+            res = self._lib.X509_REQ_add1_attr_by_OBJ(
+                x509_req,
+                obj,
+                x509.name._ASN1Type.UTF8String.value,
+                attr_val,
+                len(attr_val),
             )
+            self.openssl_assert(res == 1)

-            raise ValueError("Digest too big for RSA key")
+        # Sign the request using the requester's private key.
+        res = self._lib.X509_REQ_sign(x509_req, private_key._evp_pkey, evp_md)
+        if res == 0:
+            errors = self._consume_errors_with_text()
+            raise ValueError("Signing failed", errors)

         return _CertificateSigningRequest(self, x509_req)

     def create_x509_certificate(self, builder, private_key, algorithm):
         if not isinstance(builder, x509.CertificateBuilder):
-            raise TypeError('Builder type mismatch.')
-        if not isinstance(algorithm, hashes.HashAlgorithm):
-            raise TypeError('Algorithm must be a registered hash algorithm.')
-
-        if (
-            isinstance(algorithm, hashes.MD5) and not
-            isinstance(private_key, rsa.RSAPrivateKey)
-        ):
-            raise ValueError(
-                "MD5 is not a supported hash algorithm for EC/DSA certificates"
-            )
+            raise TypeError("Builder type mismatch.")
+        self._x509_check_signature_params(private_key, algorithm)

         # Resolve the signature algorithm.
-        evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+        evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)

         # Create an empty certificate.
         x509_cert = self._lib.X509_new()
-        x509_cert = self._ffi.gc(x509_cert, backend._lib.X509_free)
+        x509_cert = self._ffi.gc(x509_cert, self._lib.X509_free)

         # Set the x509 version.
         res = self._lib.X509_set_version(x509_cert, builder._version.value)
         self.openssl_assert(res == 1)

         # Set the subject's name.
         res = self._lib.X509_set_subject_name(
@@ -829,87 +1045,79 @@
         # Set the certificate serial number.
         serial_number = _encode_asn1_int_gc(self, builder._serial_number)
         res = self._lib.X509_set_serialNumber(x509_cert, serial_number)
         self.openssl_assert(res == 1)

         # Set the "not before" time.
         self._set_asn1_time(
-            self._lib.X509_get_notBefore(x509_cert), builder._not_valid_before
+            self._lib.X509_getm_notBefore(x509_cert), builder._not_valid_before
         )

         # Set the "not after" time.
         self._set_asn1_time(
-            self._lib.X509_get_notAfter(x509_cert), builder._not_valid_after
+            self._lib.X509_getm_notAfter(x509_cert), builder._not_valid_after
         )

         # Add extensions.
         self._create_x509_extensions(
             extensions=builder._extensions,
-            handlers=_EXTENSION_ENCODE_HANDLERS,
+            handlers=self._extension_encode_handlers,
             x509_obj=x509_cert,
             add_func=self._lib.X509_add_ext,
-            gc=True
+            gc=True,
         )

         # Set the issuer name.
         res = self._lib.X509_set_issuer_name(
             x509_cert, _encode_name_gc(self, builder._issuer_name)
         )
         self.openssl_assert(res == 1)

         # Sign the certificate with the issuer's private key.
-        res = self._lib.X509_sign(
-            x509_cert, private_key._evp_pkey, evp_md
-        )
+        res = self._lib.X509_sign(x509_cert, private_key._evp_pkey, evp_md)
         if res == 0:
-            errors = self._consume_errors()
-            self.openssl_assert(
-                errors[0]._lib_reason_match(
-                    self._lib.ERR_LIB_RSA,
-                    self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
-                )
-            )
-            raise ValueError("Digest too big for RSA key")
+            errors = self._consume_errors_with_text()
+            raise ValueError("Signing failed", errors)

         return _Certificate(self, x509_cert)

+    def _evp_md_x509_null_if_eddsa(self, private_key, algorithm):
+        if isinstance(
+            private_key, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)
+        ):
+            # OpenSSL requires us to pass NULL for EVP_MD for ed25519/ed448
+            return self._ffi.NULL
+        else:
+            return self._evp_md_non_null_from_algorithm(algorithm)
+
     def _set_asn1_time(self, asn1_time, time):
         if time.year >= 2050:
-            asn1_str = time.strftime('%Y%m%d%H%M%SZ').encode('ascii')
+            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')
+            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()
         self.openssl_assert(asn1_time != self._ffi.NULL)
         asn1_time = self._ffi.gc(asn1_time, self._lib.ASN1_TIME_free)
         self._set_asn1_time(asn1_time, time)
         return asn1_time

     def create_x509_crl(self, builder, private_key, algorithm):
         if not isinstance(builder, x509.CertificateRevocationListBuilder):
-            raise TypeError('Builder type mismatch.')
-        if not isinstance(algorithm, hashes.HashAlgorithm):
-            raise TypeError('Algorithm must be a registered hash algorithm.')
-
-        if (
-            isinstance(algorithm, hashes.MD5) and not
-            isinstance(private_key, rsa.RSAPrivateKey)
-        ):
-            raise ValueError(
-                "MD5 is not a supported hash algorithm for EC/DSA CRLs"
-            )
+            raise TypeError("Builder type mismatch.")
+        self._x509_check_signature_params(private_key, algorithm)

-        evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+        evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)

         # Create an empty CRL.
         x509_crl = self._lib.X509_CRL_new()
-        x509_crl = self._ffi.gc(x509_crl, backend._lib.X509_CRL_free)
+        x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free)

         # Set the x509 CRL version. We only support v2 (integer value 1).
         res = self._lib.X509_CRL_set_version(x509_crl, 1)
         self.openssl_assert(res == 1)

         # Set the issuer name.
         res = self._lib.X509_CRL_set_issuer_name(
@@ -926,52 +1134,41 @@
         next_update = self._create_asn1_time(builder._next_update)
         res = self._lib.X509_CRL_set_nextUpdate(x509_crl, next_update)
         self.openssl_assert(res == 1)

         # Add extensions.
         self._create_x509_extensions(
             extensions=builder._extensions,
-            handlers=_CRL_EXTENSION_ENCODE_HANDLERS,
+            handlers=self._crl_extension_encode_handlers,
             x509_obj=x509_crl,
             add_func=self._lib.X509_CRL_add_ext,
-            gc=True
+            gc=True,
         )

         # add revoked certificates
         for revoked_cert in builder._revoked_certificates:
             # Duplicating because the X509_CRL takes ownership and will free
             # this memory when X509_CRL_free is called.
-            revoked = self._lib.Cryptography_X509_REVOKED_dup(
-                revoked_cert._x509_revoked
-            )
+            revoked = self._lib.X509_REVOKED_dup(revoked_cert._x509_revoked)
             self.openssl_assert(revoked != self._ffi.NULL)
             res = self._lib.X509_CRL_add0_revoked(x509_crl, revoked)
             self.openssl_assert(res == 1)

-        res = self._lib.X509_CRL_sign(
-            x509_crl, private_key._evp_pkey, evp_md
-        )
+        res = self._lib.X509_CRL_sign(x509_crl, private_key._evp_pkey, evp_md)
         if res == 0:
-            errors = self._consume_errors()
-            self.openssl_assert(
-                errors[0]._lib_reason_match(
-                    self._lib.ERR_LIB_RSA,
-                    self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
-                )
-            )
-            raise ValueError("Digest too big for RSA key")
+            errors = self._consume_errors_with_text()
+            raise ValueError("Signing failed", errors)

         return _CertificateRevocationList(self, x509_crl)

-    def _create_x509_extensions(self, extensions, handlers, x509_obj,
-                                add_func, gc):
+    def _create_x509_extensions(
+        self, extensions, handlers, x509_obj, add_func, gc
+    ):
         for i, extension in enumerate(extensions):
-            x509_extension = self._create_x509_extension(
-                handlers, extension
-            )
+            x509_extension = self._create_x509_extension(handlers, extension)
             self.openssl_assert(x509_extension != self._ffi.NULL)

             if gc:
                 x509_extension = self._ffi.gc(
                     x509_extension, self._lib.X509_EXTENSION_free
                 )
             res = add_func(x509_obj, x509_extension, i)
@@ -984,41 +1181,46 @@
         )

     def _create_x509_extension(self, handlers, extension):
         if isinstance(extension.value, x509.UnrecognizedExtension):
             value = _encode_asn1_str_gc(self, extension.value.value)
             return self._create_raw_x509_extension(extension, value)
         elif isinstance(extension.value, x509.TLSFeature):
-            asn1 = _Integers([x.value for x in extension.value]).dump()
+            asn1 = encode_der(
+                SEQUENCE,
+                *[
+                    encode_der(INTEGER, encode_der_integer(x.value))
+                    for x in extension.value
+                ]
+            )
             value = _encode_asn1_str_gc(self, asn1)
             return self._create_raw_x509_extension(extension, value)
         elif isinstance(extension.value, x509.PrecertPoison):
-            asn1 = asn1crypto.core.Null().dump()
-            value = _encode_asn1_str_gc(self, asn1)
+            value = _encode_asn1_str_gc(self, encode_der(NULL))
             return self._create_raw_x509_extension(extension, value)
         else:
             try:
                 encode = handlers[extension.oid]
             except KeyError:
                 raise NotImplementedError(
-                    'Extension not supported: {}'.format(extension.oid)
+                    "Extension not supported: {}".format(extension.oid)
                 )

             ext_struct = encode(self, extension.value)
             nid = self._lib.OBJ_txt2nid(
                 extension.oid.dotted_string.encode("ascii")
             )
-            backend.openssl_assert(nid != self._lib.NID_undef)
+            self.openssl_assert(nid != self._lib.NID_undef)
             return self._lib.X509V3_EXT_i2d(
                 nid, 1 if extension.critical else 0, ext_struct
             )

     def create_x509_revoked_certificate(self, builder):
         if not isinstance(builder, x509.RevokedCertificateBuilder):
-            raise TypeError('Builder type mismatch.')
+            raise TypeError("Builder type mismatch.")

         x509_revoked = self._lib.X509_REVOKED_new()
         self.openssl_assert(x509_revoked != self._ffi.NULL)
         x509_revoked = self._ffi.gc(x509_revoked, self._lib.X509_REVOKED_free)
         serial_number = _encode_asn1_int_gc(self, builder._serial_number)
         res = self._lib.X509_REVOKED_set_serialNumber(
             x509_revoked, serial_number
@@ -1026,18 +1228,18 @@
         self.openssl_assert(res == 1)
         rev_date = self._create_asn1_time(builder._revocation_date)
         res = self._lib.X509_REVOKED_set_revocationDate(x509_revoked, rev_date)
         self.openssl_assert(res == 1)
         # add CRL entry extensions
         self._create_x509_extensions(
             extensions=builder._extensions,
-            handlers=_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS,
+            handlers=self._crl_entry_extension_encode_handlers,
             x509_obj=x509_revoked,
             add_func=self._lib.X509_REVOKED_add_ext,
-            gc=True
+            gc=True,
         )
         return _RevokedCertificate(self, None, x509_revoked)

     def load_pem_private_key(self, data, password):
         return self._load_key(
             self._lib.PEM_read_bio_PrivateKey,
             self._evp_pkey_to_private_key,
@@ -1070,15 +1272,16 @@
             else:
                 self._handle_key_loading_error()

     def load_pem_parameters(self, data):
         mem_bio = self._bytes_to_bio(data)
         # only DH is supported currently
         dh_cdata = self._lib.PEM_read_bio_DHparams(
-            mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL)
+            mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+        )
         if dh_cdata != self._ffi.NULL:
             dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
             return _DHParameters(self, dh_cdata)
         else:
             self._handle_key_loading_error()

     def load_der_private_key(self, data, password):
@@ -1135,17 +1338,15 @@
                 evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)
                 return _RSAPublicKey(self, rsa_cdata, evp_pkey)
             else:
                 self._handle_key_loading_error()

     def load_der_parameters(self, data):
         mem_bio = self._bytes_to_bio(data)
-        dh_cdata = self._lib.d2i_DHparams_bio(
-            mem_bio.bio, self._ffi.NULL
-        )
+        dh_cdata = self._lib.d2i_DHparams_bio(mem_bio.bio, self._ffi.NULL)
         if dh_cdata != self._ffi.NULL:
             dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
             return _DHParameters(self, dh_cdata)
         elif self._lib.Cryptography_HAS_EVP_PKEY_DHX:
             # We check to see if the is dhx.
             self._consume_errors()
             res = self._lib.BIO_reset(mem_bio.bio)
@@ -1270,55 +1471,51 @@
             else:
                 self._handle_key_loading_error()

         evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)

         if password is not None and userdata.called == 0:
             raise TypeError(
-                "Password was given but private key is not encrypted.")
+                "Password was given but private key is not encrypted."
+            )

         assert (
-            (password is not None and userdata.called == 1) or
-            password is None
-        )
+            password is not None and userdata.called == 1
+        ) or password is None

         return convert_func(evp_pkey)

     def _handle_key_loading_error(self):
         errors = self._consume_errors()

         if not errors:
             raise ValueError("Could not deserialize key data.")

-        elif (
-            errors[0]._lib_reason_match(
-                self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
-            ) or errors[0]._lib_reason_match(
-                self._lib.ERR_LIB_PKCS12,
-                self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR
-            )
+        elif errors[0]._lib_reason_match(
+            self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
+        ) or errors[0]._lib_reason_match(
+            self._lib.ERR_LIB_PKCS12,
+            self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
         ):
             raise ValueError("Bad decrypt. Incorrect password?")

-        elif (
-            errors[0]._lib_reason_match(
-                self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
-            ) or errors[0]._lib_reason_match(
-                self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
-            )
+        elif errors[0]._lib_reason_match(
+            self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
+        ) or errors[0]._lib_reason_match(
+            self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
         ):
             raise UnsupportedAlgorithm(
                 "PEM data is encrypted with an unsupported cipher",
-                _Reasons.UNSUPPORTED_CIPHER
+                _Reasons.UNSUPPORTED_CIPHER,
             )

         elif any(
             error._lib_reason_match(
                 self._lib.ERR_LIB_EVP,
-                self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM
+                self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM,
             )
             for error in errors
         ):
             raise ValueError("Unsupported public key algorithm.")

         else:
             assert errors[0].lib in (
@@ -1333,22 +1530,15 @@
             curve_nid = self._elliptic_curve_to_nid(curve)
         except UnsupportedAlgorithm:
             curve_nid = self._lib.NID_undef

         group = self._lib.EC_GROUP_new_by_curve_name(curve_nid)

         if group == self._ffi.NULL:
-            errors = self._consume_errors()
-            self.openssl_assert(
-                curve_nid == self._lib.NID_undef or
-                errors[0]._lib_reason_match(
-                    self._lib.ERR_LIB_EC,
-                    self._lib.EC_R_UNKNOWN_GROUP
-                )
-            )
+            self._consume_errors()
             return False
         else:
             self.openssl_assert(curve_nid != self._lib.NID_undef)
             self._lib.EC_GROUP_free(group)
             return True

     def elliptic_curve_signature_algorithm_supported(
@@ -1373,39 +1563,41 @@

             evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

             return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
         else:
             raise UnsupportedAlgorithm(
                 "Backend object does not support {}.".format(curve.name),
-                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
             )

     def load_elliptic_curve_private_numbers(self, numbers):
         public = numbers.public_numbers

         ec_cdata = self._ec_key_new_by_curve(public.curve)

         private_value = self._ffi.gc(
             self._int_to_bn(numbers.private_value), self._lib.BN_clear_free
         )
         res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value)
         self.openssl_assert(res == 1)

         ec_cdata = self._ec_key_set_public_key_affine_coordinates(
-            ec_cdata, public.x, public.y)
+            ec_cdata, public.x, public.y
+        )

         evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

         return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)

     def load_elliptic_curve_public_numbers(self, numbers):
         ec_cdata = self._ec_key_new_by_curve(numbers.curve)
         ec_cdata = self._ec_key_set_public_key_affine_coordinates(
-            ec_cdata, numbers.x, numbers.y)
+            ec_cdata, numbers.x, numbers.y
+        )
         evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

         return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)

     def load_elliptic_curve_public_bytes(self, curve, point_bytes):
         ec_cdata = self._ec_key_new_by_curve(curve)
         group = self._lib.EC_KEY_get0_group(ec_cdata)
@@ -1435,16 +1627,17 @@
         self.openssl_assert(point != self._ffi.NULL)
         point = self._ffi.gc(point, self._lib.EC_POINT_free)

         value = self._int_to_bn(private_value)
         value = self._ffi.gc(value, self._lib.BN_clear_free)

         with self._tmp_bn_ctx() as bn_ctx:
-            res = self._lib.EC_POINT_mul(group, point, value, self._ffi.NULL,
-                                         self._ffi.NULL, bn_ctx)
+            res = self._lib.EC_POINT_mul(
+                group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx
+            )
             self.openssl_assert(res == 1)

             bn_x = self._lib.BN_CTX_get(bn_ctx)
             bn_y = self._lib.BN_CTX_get(bn_ctx)

             res = get_func(group, point, bn_x, bn_y, bn_ctx)
             self.openssl_assert(res == 1)
@@ -1458,16 +1651,27 @@

         evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

         return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)

     def _ec_key_new_by_curve(self, curve):
         curve_nid = self._elliptic_curve_to_nid(curve)
+        return self._ec_key_new_by_curve_nid(curve_nid)
+
+    def _ec_key_new_by_curve_nid(self, curve_nid):
         ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
         self.openssl_assert(ec_cdata != self._ffi.NULL)
+        # Setting the ASN.1 flag to OPENSSL_EC_NAMED_CURVE is
+        # only necessary on OpenSSL 1.0.2t/u. Once we drop support for 1.0.2
+        # we can remove this as it's done automatically when getting an EC_KEY
+        # from new_by_curve_name
+        # CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER
+        self._lib.EC_KEY_set_asn1_flag(
+            ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE
+        )
         return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)

     def load_der_ocsp_request(self, data):
         mem_bio = self._bytes_to_bio(data)
         request = self._lib.d2i_OCSP_REQUEST_bio(mem_bio.bio, self._ffi.NULL)
         if request == self._ffi.NULL:
             self._consume_errors()
@@ -1488,39 +1692,40 @@

     def create_ocsp_request(self, builder):
         ocsp_req = self._lib.OCSP_REQUEST_new()
         self.openssl_assert(ocsp_req != self._ffi.NULL)
         ocsp_req = self._ffi.gc(ocsp_req, self._lib.OCSP_REQUEST_free)
         cert, issuer, algorithm = builder._request
         evp_md = self._evp_md_non_null_from_algorithm(algorithm)
-        certid = self._lib.OCSP_cert_to_id(
-            evp_md, cert._x509, issuer._x509
-        )
+        certid = self._lib.OCSP_cert_to_id(evp_md, cert._x509, issuer._x509)
         self.openssl_assert(certid != self._ffi.NULL)
         onereq = self._lib.OCSP_request_add0_id(ocsp_req, certid)
         self.openssl_assert(onereq != self._ffi.NULL)
         self._create_x509_extensions(
             extensions=builder._extensions,
-            handlers=_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS,
+            handlers=self._ocsp_request_extension_encode_handlers,
             x509_obj=ocsp_req,
             add_func=self._lib.OCSP_REQUEST_add_ext,
             gc=True,
         )
         return _OCSPRequest(self, ocsp_req)

     def _create_ocsp_basic_response(self, builder, private_key, algorithm):
+        self._x509_check_signature_params(private_key, algorithm)
+
         basic = self._lib.OCSP_BASICRESP_new()
         self.openssl_assert(basic != self._ffi.NULL)
         basic = self._ffi.gc(basic, self._lib.OCSP_BASICRESP_free)
         evp_md = self._evp_md_non_null_from_algorithm(
             builder._response._algorithm
         )
         certid = self._lib.OCSP_cert_to_id(
-            evp_md, builder._response._cert._x509,
-            builder._response._issuer._x509
+            evp_md,
+            builder._response._cert._x509,
+            builder._response._issuer._x509,
         )
         self.openssl_assert(certid != self._ffi.NULL)
         certid = self._ffi.gc(certid, self._lib.OCSP_CERTID_free)
         if builder._response._revocation_reason is None:
             reason = -1
         else:
             reason = _CRL_ENTRY_REASON_ENUM_TO_CODE[
@@ -1544,55 +1749,58 @@
         res = self._lib.OCSP_basic_add1_status(
             basic,
             certid,
             builder._response._cert_status.value,
             reason,
             rev_time,
             this_update,
-            next_update
+            next_update,
         )
         self.openssl_assert(res != self._ffi.NULL)
         # okay, now sign the basic structure
-        evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+        evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)
         responder_cert, responder_encoding = builder._responder_id
         flags = self._lib.OCSP_NOCERTS
         if responder_encoding is ocsp.OCSPResponderEncoding.HASH:
             flags |= self._lib.OCSP_RESPID_KEY

         if builder._certs is not None:
             for cert in builder._certs:
                 res = self._lib.OCSP_basic_add1_cert(basic, cert._x509)
                 self.openssl_assert(res == 1)

         self._create_x509_extensions(
             extensions=builder._extensions,
-            handlers=_OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS,
+            handlers=self._ocsp_basicresp_extension_encode_handlers,
             x509_obj=basic,
             add_func=self._lib.OCSP_BASICRESP_add_ext,
             gc=True,
         )

         res = self._lib.OCSP_basic_sign(
-            basic, responder_cert._x509, private_key._evp_pkey,
-            evp_md, self._ffi.NULL, flags
+            basic,
+            responder_cert._x509,
+            private_key._evp_pkey,
+            evp_md,
+            self._ffi.NULL,
+            flags,
         )
         if res != 1:
-            errors = self._consume_errors()
-            self.openssl_assert(
-                errors[0]._lib_reason_match(
-                    self._lib.ERR_LIB_X509,
-                    self._lib.X509_R_KEY_VALUES_MISMATCH
-                )
+            errors = self._consume_errors_with_text()
+            raise ValueError(
+                "Error while signing. responder_cert must be signed "
+                "by private_key",
+                errors,
             )
-            raise ValueError("responder_cert must be signed by private_key")

         return basic

-    def create_ocsp_response(self, response_status, builder, private_key,
-                             algorithm):
+    def create_ocsp_response(
+        self, response_status, builder, private_key, algorithm
+    ):
         if response_status is ocsp.OCSPResponseStatus.SUCCESSFUL:
             basic = self._create_ocsp_basic_response(
                 builder, private_key, algorithm
             )
         else:
             basic = self._ffi.NULL

@@ -1600,42 +1808,38 @@
             response_status.value, basic
         )
         self.openssl_assert(ocsp_resp != self._ffi.NULL)
         ocsp_resp = self._ffi.gc(ocsp_resp, self._lib.OCSP_RESPONSE_free)
         return _OCSPResponse(self, ocsp_resp)

     def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
-        return (
-            self.elliptic_curve_supported(curve) and
-            isinstance(algorithm, ec.ECDH)
+        return self.elliptic_curve_supported(curve) and isinstance(
+            algorithm, ec.ECDH
         )

     def _ec_cdata_to_evp_pkey(self, ec_cdata):
         evp_pkey = self._create_evp_pkey_gc()
         res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata)
         self.openssl_assert(res == 1)
         return evp_pkey

     def _elliptic_curve_to_nid(self, curve):
         """
         Get the NID for a curve name.
         """

-        curve_aliases = {
-            "secp192r1": "prime192v1",
-            "secp256r1": "prime256v1"
-        }
+        curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"}

         curve_name = curve_aliases.get(curve.name, curve.name)

         curve_nid = self._lib.OBJ_sn2nid(curve_name.encode())
         if curve_nid == self._lib.NID_undef:
             raise UnsupportedAlgorithm(
                 "{} is not a supported elliptic curve".format(curve.name),
-                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
             )
         return curve_nid

     @contextmanager
     def _tmp_bn_ctx(self):
         bn_ctx = self._lib.BN_CTX_new()
         self.openssl_assert(bn_ctx != self._ffi.NULL)
@@ -1690,239 +1894,197 @@
         res = self._lib.EC_KEY_set_public_key_affine_coordinates(ctx, x, y)
         if res != 1:
             self._consume_errors()
             raise ValueError("Invalid EC key.")

         return ctx

-    def _private_key_bytes(self, encoding, format, encryption_algorithm,
-                           evp_pkey, cdata):
+    def _private_key_bytes(
+        self, encoding, format, encryption_algorithm, key, evp_pkey, cdata
+    ):
+        # validate argument types
+        if not isinstance(encoding, serialization.Encoding):
+            raise TypeError("encoding must be an item from the Encoding enum")
         if not isinstance(format, serialization.PrivateFormat):
             raise TypeError(
                 "format must be an item from the PrivateFormat enum"
             )
-
-        # X9.62 encoding is only valid for EC public keys
-        if encoding is serialization.Encoding.X962:
-            raise ValueError("X9.62 format is only valid for EC public keys")
-
-        # Raw format and encoding are only valid for X25519, Ed25519, X448, and
-        # Ed448 keys. We capture those cases before this method is called so if
-        # we see those enum values here it means the caller has passed them to
-        # a key that doesn't support raw type
-        if format is serialization.PrivateFormat.Raw:
-            raise ValueError("raw format is invalid with this key or encoding")
-
-        if encoding is serialization.Encoding.Raw:
-            raise ValueError("raw encoding is invalid with this key or format")
-
-        if not isinstance(encryption_algorithm,
-                          serialization.KeySerializationEncryption):
+        if not isinstance(
+            encryption_algorithm, serialization.KeySerializationEncryption
+        ):
             raise TypeError(
                 "Encryption algorithm must be a KeySerializationEncryption "
                 "instance"
             )

+        # validate password
         if isinstance(encryption_algorithm, serialization.NoEncryption):
             password = b""
-            passlen = 0
-            evp_cipher = self._ffi.NULL
-        elif isinstance(encryption_algorithm,
-                        serialization.BestAvailableEncryption):
-            # This is a curated value that we will update over time.
-            evp_cipher = self._lib.EVP_get_cipherbyname(
-                b"aes-256-cbc"
-            )
+        elif isinstance(
+            encryption_algorithm, serialization.BestAvailableEncryption
+        ):
             password = encryption_algorithm.password
-            passlen = len(password)
-            if passlen > 1023:
+            if len(password) > 1023:
                 raise ValueError(
                     "Passwords longer than 1023 bytes are not supported by "
                     "this backend"
                 )
         else:
             raise ValueError("Unsupported encryption type")

-        key_type = self._lib.EVP_PKEY_id(evp_pkey)
-        if encoding is serialization.Encoding.PEM:
-            if format is serialization.PrivateFormat.PKCS8:
+        # PKCS8 + PEM/DER
+        if format is serialization.PrivateFormat.PKCS8:
+            if encoding is serialization.Encoding.PEM:
                 write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey
-                key = evp_pkey
+            elif encoding is serialization.Encoding.DER:
+                write_bio = self._lib.i2d_PKCS8PrivateKey_bio
             else:
-                assert format is serialization.PrivateFormat.TraditionalOpenSSL
+                raise ValueError("Unsupported encoding for PKCS8")
+            return self._private_key_bytes_via_bio(
+                write_bio, evp_pkey, password
+            )
+
+        # TraditionalOpenSSL + PEM/DER
+        if format is serialization.PrivateFormat.TraditionalOpenSSL:
+            if self._fips_enabled and not isinstance(
+                encryption_algorithm, serialization.NoEncryption
+            ):
+                raise ValueError(
+                    "Encrypted traditional OpenSSL format is not "
+                    "supported in FIPS mode."
+                )
+            key_type = self._lib.EVP_PKEY_id(evp_pkey)
+
+            if encoding is serialization.Encoding.PEM:
                 if key_type == self._lib.EVP_PKEY_RSA:
                     write_bio = self._lib.PEM_write_bio_RSAPrivateKey
                 elif key_type == self._lib.EVP_PKEY_DSA:
                     write_bio = self._lib.PEM_write_bio_DSAPrivateKey
-                else:
-                    assert key_type == self._lib.EVP_PKEY_EC
+                elif key_type == self._lib.EVP_PKEY_EC:
                     write_bio = self._lib.PEM_write_bio_ECPrivateKey
+                else:
+                    raise ValueError(
+                        "Unsupported key type for TraditionalOpenSSL"
+                    )
+                return self._private_key_bytes_via_bio(
+                    write_bio, cdata, password
+                )

-                key = cdata
-        elif encoding is serialization.Encoding.DER:
-            if format is serialization.PrivateFormat.TraditionalOpenSSL:
-                if not isinstance(
-                    encryption_algorithm, serialization.NoEncryption
-                ):
+            if encoding is serialization.Encoding.DER:
+                if password:
                     raise ValueError(
                         "Encryption is not supported for DER encoded "
                         "traditional OpenSSL keys"
                     )
+                if key_type == self._lib.EVP_PKEY_RSA:
+                    write_bio = self._lib.i2d_RSAPrivateKey_bio
+                elif key_type == self._lib.EVP_PKEY_EC:
+                    write_bio = self._lib.i2d_ECPrivateKey_bio
+                elif key_type == self._lib.EVP_PKEY_DSA:
+                    write_bio = self._lib.i2d_DSAPrivateKey_bio
+                else:
+                    raise ValueError(
+                        "Unsupported key type for TraditionalOpenSSL"
+                    )
+                return self._bio_func_output(write_bio, cdata)

-                return self._private_key_bytes_traditional_der(key_type, cdata)
-            else:
-                assert format is serialization.PrivateFormat.PKCS8
-                write_bio = self._lib.i2d_PKCS8PrivateKey_bio
-                key = evp_pkey
+            raise ValueError("Unsupported encoding for TraditionalOpenSSL")
+
+        # OpenSSH + PEM
+        if format is serialization.PrivateFormat.OpenSSH:
+            if encoding is serialization.Encoding.PEM:
+                return ssh.serialize_ssh_private_key(key, password)
+
+            raise ValueError(
+                "OpenSSH private key format can only be used"
+                " with PEM encoding"
+            )
+
+        # Anything that key-specific code was supposed to handle earlier,
+        # like Raw.
+        raise ValueError("format is invalid with this key")
+
+    def _private_key_bytes_via_bio(self, write_bio, evp_pkey, password):
+        if not password:
+            evp_cipher = self._ffi.NULL
         else:
-            raise TypeError("encoding must be Encoding.PEM or Encoding.DER")
+            # This is a curated value that we will update over time.
+            evp_cipher = self._lib.EVP_get_cipherbyname(b"aes-256-cbc")

-        bio = self._create_mem_bio_gc()
-        res = write_bio(
-            bio,
-            key,
+        return self._bio_func_output(
+            write_bio,
+            evp_pkey,
             evp_cipher,
             password,
-            passlen,
+            len(password),
+            self._ffi.NULL,
             self._ffi.NULL,
-            self._ffi.NULL
         )
-        self.openssl_assert(res == 1)
-        return self._read_mem_bio(bio)
-
-    def _private_key_bytes_traditional_der(self, key_type, cdata):
-        if key_type == self._lib.EVP_PKEY_RSA:
-            write_bio = self._lib.i2d_RSAPrivateKey_bio
-        elif key_type == self._lib.EVP_PKEY_EC:
-            write_bio = self._lib.i2d_ECPrivateKey_bio
-        else:
-            self.openssl_assert(key_type == self._lib.EVP_PKEY_DSA)
-            write_bio = self._lib.i2d_DSAPrivateKey_bio

+    def _bio_func_output(self, write_bio, *args):
         bio = self._create_mem_bio_gc()
-        res = write_bio(bio, cdata)
+        res = write_bio(bio, *args)
         self.openssl_assert(res == 1)
         return self._read_mem_bio(bio)

     def _public_key_bytes(self, encoding, format, key, evp_pkey, cdata):
         if not isinstance(encoding, serialization.Encoding):
             raise TypeError("encoding must be an item from the Encoding enum")
+        if not isinstance(format, serialization.PublicFormat):
+            raise TypeError(
+                "format must be an item from the PublicFormat enum"
+            )

-        # Compressed/UncompressedPoint are only valid for EC keys and those
-        # cases are handled by the ECPublicKey public_bytes method before this
-        # method is called
-        if format in (serialization.PublicFormat.UncompressedPoint,
-                      serialization.PublicFormat.CompressedPoint):
-            raise ValueError("Point formats are not valid for this key type")
-
-        # Raw format and encoding are only valid for X25519, Ed25519, X448, and
-        # Ed448 keys. We capture those cases before this method is called so if
-        # we see those enum values here it means the caller has passed them to
-        # a key that doesn't support raw type
-        if format is serialization.PublicFormat.Raw:
-            raise ValueError("raw format is invalid with this key or encoding")
-
-        if encoding is serialization.Encoding.Raw:
-            raise ValueError("raw encoding is invalid with this key or format")
-
-        if (
-            format is serialization.PublicFormat.OpenSSH or
-            encoding is serialization.Encoding.OpenSSH
-        ):
-            if (
-                format is not serialization.PublicFormat.OpenSSH or
-                encoding is not serialization.Encoding.OpenSSH
-            ):
-                raise ValueError(
-                    "OpenSSH format must be used with OpenSSH encoding"
-                )
-            return self._openssh_public_key_bytes(key)
-        elif format is serialization.PublicFormat.SubjectPublicKeyInfo:
+        # SubjectPublicKeyInfo + PEM/DER
+        if format is serialization.PublicFormat.SubjectPublicKeyInfo:
             if encoding is serialization.Encoding.PEM:
                 write_bio = self._lib.PEM_write_bio_PUBKEY
-            else:
-                assert encoding is serialization.Encoding.DER
+            elif encoding is serialization.Encoding.DER:
                 write_bio = self._lib.i2d_PUBKEY_bio
+            else:
+                raise ValueError(
+                    "SubjectPublicKeyInfo works only with PEM or DER encoding"
+                )
+            return self._bio_func_output(write_bio, evp_pkey)

-            key = evp_pkey
-        elif format is serialization.PublicFormat.PKCS1:
+        # PKCS1 + PEM/DER
+        if format is serialization.PublicFormat.PKCS1:
             # Only RSA is supported here.
-            assert self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_RSA
+            key_type = self._lib.EVP_PKEY_id(evp_pkey)
+            if key_type != self._lib.EVP_PKEY_RSA:
+                raise ValueError("PKCS1 format is supported only for RSA keys")
+
             if encoding is serialization.Encoding.PEM:
                 write_bio = self._lib.PEM_write_bio_RSAPublicKey
-            else:
-                assert encoding is serialization.Encoding.DER
+            elif encoding is serialization.Encoding.DER:
                 write_bio = self._lib.i2d_RSAPublicKey_bio
+            else:
+                raise ValueError("PKCS1 works only with PEM or DER encoding")
+            return self._bio_func_output(write_bio, cdata)

-            key = cdata
-        else:
-            raise TypeError(
-                "format must be an item from the PublicFormat enum"
-            )
+        # OpenSSH + OpenSSH
+        if format is serialization.PublicFormat.OpenSSH:
+            if encoding is serialization.Encoding.OpenSSH:
+                return ssh.serialize_ssh_public_key(key)

-        bio = self._create_mem_bio_gc()
-        res = write_bio(bio, key)
-        self.openssl_assert(res == 1)
-        return self._read_mem_bio(bio)
-
-    def _openssh_public_key_bytes(self, key):
-        if isinstance(key, rsa.RSAPublicKey):
-            public_numbers = key.public_numbers()
-            return b"ssh-rsa " + base64.b64encode(
-                ssh._ssh_write_string(b"ssh-rsa") +
-                ssh._ssh_write_mpint(public_numbers.e) +
-                ssh._ssh_write_mpint(public_numbers.n)
-            )
-        elif isinstance(key, dsa.DSAPublicKey):
-            public_numbers = key.public_numbers()
-            parameter_numbers = public_numbers.parameter_numbers
-            return b"ssh-dss " + base64.b64encode(
-                ssh._ssh_write_string(b"ssh-dss") +
-                ssh._ssh_write_mpint(parameter_numbers.p) +
-                ssh._ssh_write_mpint(parameter_numbers.q) +
-                ssh._ssh_write_mpint(parameter_numbers.g) +
-                ssh._ssh_write_mpint(public_numbers.y)
+            raise ValueError(
+                "OpenSSH format must be used with OpenSSH encoding"
             )
-        else:
-            assert isinstance(key, ec.EllipticCurvePublicKey)
-            public_numbers = key.public_numbers()
-            try:
-                curve_name = {
-                    ec.SECP256R1: b"nistp256",
-                    ec.SECP384R1: b"nistp384",
-                    ec.SECP521R1: b"nistp521",
-                }[type(public_numbers.curve)]
-            except KeyError:
-                raise ValueError(
-                    "Only SECP256R1, SECP384R1, and SECP521R1 curves are "
-                    "supported by the SSH public key format"
-                )

-            point = key.public_bytes(
-                serialization.Encoding.X962,
-                serialization.PublicFormat.UncompressedPoint
-            )
-            return b"ecdsa-sha2-" + curve_name + b" " + base64.b64encode(
-                ssh._ssh_write_string(b"ecdsa-sha2-" + curve_name) +
-                ssh._ssh_write_string(curve_name) +
-                ssh._ssh_write_string(point)
-            )
+        # Anything that key-specific code was supposed to handle earlier,
+        # like Raw, CompressedPoint, UncompressedPoint
+        raise ValueError("format is invalid with this key")

     def _parameter_bytes(self, encoding, format, cdata):
         if encoding is serialization.Encoding.OpenSSH:
-            raise TypeError(
-                "OpenSSH encoding is not supported"
-            )
+            raise TypeError("OpenSSH encoding is not supported")

         # Only DH is supported here currently.
         q = self._ffi.new("BIGNUM **")
-        self._lib.DH_get0_pqg(cdata,
-                              self._ffi.NULL,
-                              q,
-                              self._ffi.NULL)
+        self._lib.DH_get0_pqg(cdata, self._ffi.NULL, q, self._ffi.NULL)
         if encoding is serialization.Encoding.PEM:
             if q[0] != self._ffi.NULL:
                 write_bio = self._lib.PEM_write_bio_DHxparams
             else:
                 write_bio = self._lib.PEM_write_bio_DHparams
         elif encoding is serialization.Encoding.DER:
             if q[0] != self._ffi.NULL:
@@ -1945,18 +2107,15 @@
             raise ValueError("DH generator must be 2 or 5")

         dh_param_cdata = self._lib.DH_new()
         self.openssl_assert(dh_param_cdata != self._ffi.NULL)
         dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free)

         res = self._lib.DH_generate_parameters_ex(
-            dh_param_cdata,
-            key_size,
-            generator,
-            self._ffi.NULL
+            dh_param_cdata, key_size, generator, self._ffi.NULL
         )
         self.openssl_assert(res == 1)

         return _DHParameters(self, dh_param_cdata)

     def _dh_cdata_to_evp_pkey(self, dh_cdata):
         evp_pkey = self._create_evp_pkey_gc()
@@ -1972,15 +2131,16 @@

         evp_pkey = self._dh_cdata_to_evp_pkey(dh_key_cdata)

         return _DHPrivateKey(self, dh_key_cdata, evp_pkey)

     def generate_dh_private_key_and_parameters(self, generator, key_size):
         return self.generate_dh_private_key(
-            self.generate_dh_parameters(generator, key_size))
+            self.generate_dh_parameters(generator, key_size)
+        )

     def load_dh_private_numbers(self, numbers):
         parameter_numbers = numbers.public_numbers.parameter_numbers

         dh_cdata = self._lib.DH_new()
         self.openssl_assert(dh_cdata != self._ffi.NULL)
         dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
@@ -2011,20 +2171,18 @@
         # We want to ignore that error because p % 24 == 23 is also fine.
         # Specifically, g is then a quadratic residue. Within the context of
         # Diffie-Hellman this means it can only generate half the possible
         # values. That sounds bad, but quadratic nonresidues leak a bit of
         # the key to the attacker in exchange for having the full key space
         # available. See: https://crypto.stackexchange.com/questions/12961
         if codes[0] != 0 and not (
-            parameter_numbers.g == 2 and
-            codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0
+            parameter_numbers.g == 2
+            and codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0
         ):
-            raise ValueError(
-                "DH private numbers did not pass safety checks."
-            )
+            raise ValueError("DH private numbers did not pass safety checks.")

         evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata)

         return _DHPrivateKey(self, dh_cdata, evp_pkey)

     def load_dh_public_numbers(self, numbers):
         dh_cdata = self._lib.DH_new()
@@ -2111,19 +2269,19 @@
         # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can
         # switch this to EVP_PKEY_new_raw_public_key
         if len(data) != 32:
             raise ValueError("An X25519 public key is 32 bytes long")

         evp_pkey = self._create_evp_pkey_gc()
         res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519)
-        backend.openssl_assert(res == 1)
+        self.openssl_assert(res == 1)
         res = self._lib.EVP_PKEY_set1_tls_encodedpoint(
             evp_pkey, data, len(data)
         )
-        backend.openssl_assert(res == 1)
+        self.openssl_assert(res == 1)
         return _X25519PublicKey(self, evp_pkey)

     def x25519_load_private_bytes(self, data):
         # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can
         # switch this to EVP_PKEY_new_raw_private_key and drop the
         # zeroed_bytearray garbage.
         # OpenSSL only has facilities for loading PKCS8 formatted private
@@ -2143,15 +2301,15 @@
             raise ValueError("An X25519 private key is 32 bytes long")

         pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 '
         with self._zeroed_bytearray(48) as ba:
             ba[0:16] = pkcs8_prefix
             ba[16:] = data
             bio = self._bytes_to_bio(ba)
-            evp_pkey = backend._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL)
+            evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL)

         self.openssl_assert(evp_pkey != self._ffi.NULL)
         evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
         self.openssl_assert(
             self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_X25519
         )
         return _X25519PrivateKey(self, evp_pkey)
@@ -2170,14 +2328,16 @@
         return evp_pkey

     def x25519_generate_key(self):
         evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X25519)
         return _X25519PrivateKey(self, evp_pkey)

     def x25519_supported(self):
+        if self._fips_enabled:
+            return False
         return self._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER

     def x448_load_public_bytes(self, data):
         if len(data) != 56:
             raise ValueError("An X448 public key is 56 bytes long")

         evp_pkey = self._lib.EVP_PKEY_new_raw_public_key(
@@ -2200,17 +2360,21 @@
         return _X448PrivateKey(self, evp_pkey)

     def x448_generate_key(self):
         evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X448)
         return _X448PrivateKey(self, evp_pkey)

     def x448_supported(self):
+        if self._fips_enabled:
+            return False
         return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111

     def ed25519_supported(self):
+        if self._fips_enabled:
+            return False
         return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B

     def ed25519_load_public_bytes(self, data):
         utils._check_bytes("data", data)

         if len(data) != ed25519._ED25519_KEY_SIZE:
             raise ValueError("An Ed25519 public key is 32 bytes long")
@@ -2238,14 +2402,16 @@
         return _Ed25519PrivateKey(self, evp_pkey)

     def ed25519_generate_key(self):
         evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED25519)
         return _Ed25519PrivateKey(self, evp_pkey)

     def ed448_supported(self):
+        if self._fips_enabled:
+            return False
         return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B

     def ed448_load_public_bytes(self, data):
         utils._check_bytes("data", data)
         if len(data) != _ED448_KEY_SIZE:
             raise ValueError("An Ed448 public key is 57 bytes long")

@@ -2275,46 +2441,42 @@
         evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED448)
         return _Ed448PrivateKey(self, evp_pkey)

     def derive_scrypt(self, key_material, salt, length, n, r, p):
         buf = self._ffi.new("unsigned char[]", length)
         key_material_ptr = self._ffi.from_buffer(key_material)
         res = self._lib.EVP_PBE_scrypt(
-            key_material_ptr, len(key_material), salt, len(salt), n, r, p,
-            scrypt._MEM_LIMIT, buf, length
+            key_material_ptr,
+            len(key_material),
+            salt,
+            len(salt),
+            n,
+            r,
+            p,
+            scrypt._MEM_LIMIT,
+            buf,
+            length,
         )
         if res != 1:
-            errors = self._consume_errors()
-            if not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111:
-                # This error is only added to the stack in 1.1.1+
-                self.openssl_assert(
-                    errors[0]._lib_reason_match(
-                        self._lib.ERR_LIB_EVP,
-                        self._lib.ERR_R_MALLOC_FAILURE
-                    ) or
-                    errors[0]._lib_reason_match(
-                        self._lib.ERR_LIB_EVP,
-                        self._lib.EVP_R_MEMORY_LIMIT_EXCEEDED
-                    )
-                )
-
+            errors = self._consume_errors_with_text()
             # memory required formula explained here:
             # https://blog.filippo.io/the-scrypt-parameters/
-            min_memory = 128 * n * r // (1024**2)
+            min_memory = 128 * n * r // (1024 ** 2)
             raise MemoryError(
                 "Not enough memory to derive key. These parameters require"
-                " {} MB of memory.".format(min_memory)
+                " {} MB of memory.".format(min_memory),
+                errors,
             )
         return self._ffi.buffer(buf)[:]

     def aead_cipher_supported(self, cipher):
         cipher_name = aead._aead_cipher_name(cipher)
-        return (
-            self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL
-        )
+        if self._fips_enabled and cipher_name not in self._fips_aead:
+            return False
+        return self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL

     @contextlib.contextmanager
     def _zeroed_bytearray(self, length):
         """
         This method creates a bytearray, which we copy data into (hopefully
         also from a mutable buffer that can be dynamically erased!), and then
         zero when we're done.
@@ -2391,20 +2553,224 @@
             cert = _Certificate(self, x509)

         if sk_x509_ptr[0] != self._ffi.NULL:
             sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free)
             num = self._lib.sk_X509_num(sk_x509_ptr[0])
             for i in range(num):
                 x509 = self._lib.sk_X509_value(sk_x509, i)
-                x509 = self._ffi.gc(x509, self._lib.X509_free)
                 self.openssl_assert(x509 != self._ffi.NULL)
+                x509 = self._ffi.gc(x509, self._lib.X509_free)
                 additional_certificates.append(_Certificate(self, x509))

         return (key, cert, additional_certificates)

+    def serialize_key_and_certificates_to_pkcs12(
+        self, name, key, cert, cas, encryption_algorithm
+    ):
+        password = None
+        if name is not None:
+            utils._check_bytes("name", name)
+
+        if isinstance(encryption_algorithm, serialization.NoEncryption):
+            nid_cert = -1
+            nid_key = -1
+            pkcs12_iter = 0
+            mac_iter = 0
+        elif isinstance(
+            encryption_algorithm, serialization.BestAvailableEncryption
+        ):
+            # PKCS12 encryption is hopeless trash and can never be fixed.
+            # This is the least terrible option.
+            nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
+            nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
+            # At least we can set this higher than OpenSSL's default
+            pkcs12_iter = 20000
+            # mac_iter chosen for compatibility reasons, see:
+            # https://www.openssl.org/docs/man1.1.1/man3/PKCS12_create.html
+            # Did we mention how lousy PKCS12 encryption is?
+            mac_iter = 1
+            password = encryption_algorithm.password
+        else:
+            raise ValueError("Unsupported key encryption type")
+
+        if cas is None or len(cas) == 0:
+            sk_x509 = self._ffi.NULL
+        else:
+            sk_x509 = self._lib.sk_X509_new_null()
+            sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free)
+
+            # reverse the list when building the stack so that they're encoded
+            # in the order they were originally provided. it is a mystery
+            for ca in reversed(cas):
+                res = self._lib.sk_X509_push(sk_x509, ca._x509)
+                backend.openssl_assert(res >= 1)
+
+        with self._zeroed_null_terminated_buf(password) as password_buf:
+            with self._zeroed_null_terminated_buf(name) as name_buf:
+                p12 = self._lib.PKCS12_create(
+                    password_buf,
+                    name_buf,
+                    key._evp_pkey if key else self._ffi.NULL,
+                    cert._x509 if cert else self._ffi.NULL,
+                    sk_x509,
+                    nid_key,
+                    nid_cert,
+                    pkcs12_iter,
+                    mac_iter,
+                    0,
+                )
+
+        self.openssl_assert(p12 != self._ffi.NULL)
+        p12 = self._ffi.gc(p12, self._lib.PKCS12_free)
+
+        bio = self._create_mem_bio_gc()
+        res = self._lib.i2d_PKCS12_bio(bio, p12)
+        self.openssl_assert(res > 0)
+        return self._read_mem_bio(bio)
+
+    def poly1305_supported(self):
+        if self._fips_enabled:
+            return False
+        return self._lib.Cryptography_HAS_POLY1305 == 1
+
+    def create_poly1305_ctx(self, key):
+        utils._check_byteslike("key", key)
+        if len(key) != _POLY1305_KEY_SIZE:
+            raise ValueError("A poly1305 key is 32 bytes long")
+
+        return _Poly1305Context(self, key)
+
+    def load_pem_pkcs7_certificates(self, data):
+        utils._check_bytes("data", data)
+        bio = self._bytes_to_bio(data)
+        p7 = self._lib.PEM_read_bio_PKCS7(
+            bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+        )
+        if p7 == self._ffi.NULL:
+            self._consume_errors()
+            raise ValueError("Unable to parse PKCS7 data")
+
+        p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
+        return self._load_pkcs7_certificates(p7)
+
+    def load_der_pkcs7_certificates(self, data):
+        utils._check_bytes("data", data)
+        bio = self._bytes_to_bio(data)
+        p7 = self._lib.d2i_PKCS7_bio(bio.bio, self._ffi.NULL)
+        if p7 == self._ffi.NULL:
+            self._consume_errors()
+            raise ValueError("Unable to parse PKCS7 data")
+
+        p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
+        return self._load_pkcs7_certificates(p7)
+
+    def _load_pkcs7_certificates(self, p7):
+        nid = self._lib.OBJ_obj2nid(p7.type)
+        self.openssl_assert(nid != self._lib.NID_undef)
+        if nid != self._lib.NID_pkcs7_signed:
+            raise UnsupportedAlgorithm(
+                "Only basic signed structures are currently supported. NID"
+                " for this data was {}".format(nid),
+                _Reasons.UNSUPPORTED_SERIALIZATION,
+            )
+
+        sk_x509 = p7.d.sign.cert
+        num = self._lib.sk_X509_num(sk_x509)
+        certs = []
+        for i in range(num):
+            x509 = self._lib.sk_X509_value(sk_x509, i)
+            self.openssl_assert(x509 != self._ffi.NULL)
+            res = self._lib.X509_up_ref(x509)
+            # When OpenSSL is less than 1.1.0 up_ref returns the current
+            # refcount. On 1.1.0+ it returns 1 for success.
+            self.openssl_assert(res >= 1)
+            x509 = self._ffi.gc(x509, self._lib.X509_free)
+            certs.append(_Certificate(self, x509))
+
+        return certs
+
+    def pkcs7_sign(self, builder, encoding, options):
+        bio = self._bytes_to_bio(builder._data)
+        init_flags = self._lib.PKCS7_PARTIAL
+        final_flags = 0
+
+        if len(builder._additional_certs) == 0:
+            certs = self._ffi.NULL
+        else:
+            certs = self._lib.sk_X509_new_null()
+            certs = self._ffi.gc(certs, self._lib.sk_X509_free)
+            for cert in builder._additional_certs:
+                res = self._lib.sk_X509_push(certs, cert._x509)
+                self.openssl_assert(res >= 1)
+
+        if pkcs7.PKCS7Options.DetachedSignature in options:
+            # Don't embed the data in the PKCS7 structure
+            init_flags |= self._lib.PKCS7_DETACHED
+            final_flags |= self._lib.PKCS7_DETACHED
+
+        # This just inits a structure for us. However, there
+        # are flags we need to set, joy.
+        p7 = self._lib.PKCS7_sign(
+            self._ffi.NULL,
+            self._ffi.NULL,
+            certs,
+            self._ffi.NULL,
+            init_flags,
+        )
+        self.openssl_assert(p7 != self._ffi.NULL)
+        p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
+        signer_flags = 0
+        # These flags are configurable on a per-signature basis
+        # but we've deliberately chosen to make the API only allow
+        # setting it across all signatures for now.
+        if pkcs7.PKCS7Options.NoCapabilities in options:
+            signer_flags |= self._lib.PKCS7_NOSMIMECAP
+        elif pkcs7.PKCS7Options.NoAttributes in options:
+            signer_flags |= self._lib.PKCS7_NOATTR
+
+        if pkcs7.PKCS7Options.NoCerts in options:
+            signer_flags |= self._lib.PKCS7_NOCERTS
+
+        for certificate, private_key, hash_algorithm in builder._signers:
+            md = self._evp_md_non_null_from_algorithm(hash_algorithm)
+            p7signerinfo = self._lib.PKCS7_sign_add_signer(
+                p7, certificate._x509, private_key._evp_pkey, md, signer_flags
+            )
+            self.openssl_assert(p7signerinfo != self._ffi.NULL)
+
+        for option in options:
+            # DetachedSignature, NoCapabilities, and NoAttributes are already
+            # handled so we just need to check these last two options.
+            if option is pkcs7.PKCS7Options.Text:
+                final_flags |= self._lib.PKCS7_TEXT
+            elif option is pkcs7.PKCS7Options.Binary:
+                final_flags |= self._lib.PKCS7_BINARY
+
+        bio_out = self._create_mem_bio_gc()
+        if encoding is serialization.Encoding.SMIME:
+            # This finalizes the structure
+            res = self._lib.SMIME_write_PKCS7(
+                bio_out, p7, bio.bio, final_flags
+            )
+        elif encoding is serialization.Encoding.PEM:
+            res = self._lib.PKCS7_final(p7, bio.bio, final_flags)
+            self.openssl_assert(res == 1)
+            res = self._lib.PEM_write_bio_PKCS7_stream(
+                bio_out, p7, bio.bio, final_flags
+            )
+        else:
+            assert encoding is serialization.Encoding.DER
+            # We need to call finalize here becauase i2d_PKCS7_bio does not
+            # finalize.
+            res = self._lib.PKCS7_final(p7, bio.bio, final_flags)
+            self.openssl_assert(res == 1)
+            res = self._lib.i2d_PKCS7_bio(bio_out, p7)
+        self.openssl_assert(res == 1)
+        return self._read_mem_bio(bio_out)
+

 class GetCipherByName(object):
     def __init__(self, fmt):
         self._fmt = fmt

     def __call__(self, backend, cipher, mode):
         cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()

cryptography/hazmat/backends/openssl/ed25519.py

@@ -3,48 +3,42 @@
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import exceptions, utils
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric.ed25519 import (
-    Ed25519PrivateKey, Ed25519PublicKey, _ED25519_KEY_SIZE, _ED25519_SIG_SIZE
+    Ed25519PrivateKey,
+    Ed25519PublicKey,
+    _ED25519_KEY_SIZE,
+    _ED25519_SIG_SIZE,
 )


 @utils.register_interface(Ed25519PublicKey)
 class _Ed25519PublicKey(object):
     def __init__(self, backend, evp_pkey):
         self._backend = backend
         self._evp_pkey = evp_pkey

     def public_bytes(self, encoding, format):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                encoding is not serialization.Encoding.Raw or
-                format is not serialization.PublicFormat.Raw
+                encoding is not serialization.Encoding.Raw
+                or format is not serialization.PublicFormat.Raw
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw"
                 )

             return self._raw_public_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PublicFormat.SubjectPublicKeyInfo
-        ):
-            raise ValueError(
-                "format must be SubjectPublicKeyInfo when encoding is PEM or "
-                "DER"
-            )
-
         return self._backend._public_key_bytes(
             encoding, format, self, self._evp_pkey, None
         )

     def _raw_public_bytes(self):
         buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
         buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
@@ -58,16 +52,19 @@
     def verify(self, signature, data):
         evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
         self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
         evp_md_ctx = self._backend._ffi.gc(
             evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
         )
         res = self._backend._lib.EVP_DigestVerifyInit(
-            evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL,
-            self._backend._ffi.NULL, self._evp_pkey
+            evp_md_ctx,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._evp_pkey,
         )
         self._backend.openssl_assert(res == 1)
         res = self._backend._lib.EVP_DigestVerify(
             evp_md_ctx, signature, len(signature), data, len(data)
         )
         if res != 1:
             self._backend._consume_errors()
@@ -94,54 +91,51 @@
     def sign(self, data):
         evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
         self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
         evp_md_ctx = self._backend._ffi.gc(
             evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
         )
         res = self._backend._lib.EVP_DigestSignInit(
-            evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL,
-            self._backend._ffi.NULL, self._evp_pkey
+            evp_md_ctx,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._evp_pkey,
         )
         self._backend.openssl_assert(res == 1)
         buf = self._backend._ffi.new("unsigned char[]", _ED25519_SIG_SIZE)
         buflen = self._backend._ffi.new("size_t *", len(buf))
         res = self._backend._lib.EVP_DigestSign(
             evp_md_ctx, buf, buflen, data, len(data)
         )
         self._backend.openssl_assert(res == 1)
         self._backend.openssl_assert(buflen[0] == _ED25519_SIG_SIZE)
         return self._backend._ffi.buffer(buf, buflen[0])[:]

     def private_bytes(self, encoding, format, encryption_algorithm):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                format is not serialization.PrivateFormat.Raw or
-                encoding is not serialization.Encoding.Raw or not
-                isinstance(encryption_algorithm, serialization.NoEncryption)
+                format is not serialization.PrivateFormat.Raw
+                or encoding is not serialization.Encoding.Raw
+                or not isinstance(
+                    encryption_algorithm, serialization.NoEncryption
+                )
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw "
-                    "and encryption_algorithm must be NoEncryption"
+                    "and encryption_algorithm must be NoEncryption()"
                 )

             return self._raw_private_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PrivateFormat.PKCS8
-        ):
-            raise ValueError(
-                "format must be PKCS8 when encoding is PEM or DER"
-            )
-
         return self._backend._private_key_bytes(
-            encoding, format, encryption_algorithm, self._evp_pkey, None
+            encoding, format, encryption_algorithm, self, self._evp_pkey, None
         )

     def _raw_private_bytes(self):
         buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
         buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
         res = self._backend._lib.EVP_PKEY_get_raw_private_key(
             self._evp_pkey, buf, buflen

cryptography/hazmat/backends/openssl/cmac.py

@@ -3,26 +3,29 @@
 # for complete details.

 from __future__ import absolute_import, division, print_function


 from cryptography import utils
 from cryptography.exceptions import (
-    InvalidSignature, UnsupportedAlgorithm, _Reasons
+    InvalidSignature,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
-from cryptography.hazmat.primitives import constant_time, mac
+from cryptography.hazmat.primitives import constant_time
 from cryptography.hazmat.primitives.ciphers.modes import CBC


[email protected]_interface(mac.MACContext)
 class _CMACContext(object):
     def __init__(self, backend, algorithm, ctx=None):
         if not backend.cmac_algorithm_supported(algorithm):
-            raise UnsupportedAlgorithm("This backend does not support CMAC.",
-                                       _Reasons.UNSUPPORTED_CIPHER)
+            raise UnsupportedAlgorithm(
+                "This backend does not support CMAC.",
+                _Reasons.UNSUPPORTED_CIPHER,
+            )

         self._backend = backend
         self._key = algorithm.key
         self._algorithm = algorithm
         self._output_length = algorithm.block_size // 8

         if ctx is None:
@@ -34,49 +37,46 @@
             ctx = self._backend._lib.CMAC_CTX_new()

             self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
             ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free)

             key_ptr = self._backend._ffi.from_buffer(self._key)
             res = self._backend._lib.CMAC_Init(
-                ctx, key_ptr, len(self._key),
-                evp_cipher, self._backend._ffi.NULL
+                ctx,
+                key_ptr,
+                len(self._key),
+                evp_cipher,
+                self._backend._ffi.NULL,
             )
             self._backend.openssl_assert(res == 1)

         self._ctx = ctx

     algorithm = utils.read_only_property("_algorithm")

     def update(self, data):
         res = self._backend._lib.CMAC_Update(self._ctx, data, len(data))
         self._backend.openssl_assert(res == 1)

     def finalize(self):
         buf = self._backend._ffi.new("unsigned char[]", self._output_length)
         length = self._backend._ffi.new("size_t *", self._output_length)
-        res = self._backend._lib.CMAC_Final(
-            self._ctx, buf, length
-        )
+        res = self._backend._lib.CMAC_Final(self._ctx, buf, length)
         self._backend.openssl_assert(res == 1)

         self._ctx = None

         return self._backend._ffi.buffer(buf)[:]

     def copy(self):
         copied_ctx = self._backend._lib.CMAC_CTX_new()
         copied_ctx = self._backend._ffi.gc(
             copied_ctx, self._backend._lib.CMAC_CTX_free
         )
-        res = self._backend._lib.CMAC_CTX_copy(
-            copied_ctx, self._ctx
-        )
+        res = self._backend._lib.CMAC_CTX_copy(copied_ctx, self._ctx)
         self._backend.openssl_assert(res == 1)
-        return _CMACContext(
-            self._backend, self._algorithm, ctx=copied_ctx
-        )
+        return _CMACContext(self._backend, self._algorithm, ctx=copied_ctx)

     def verify(self, signature):
         digest = self.finalize()
         if not constant_time.bytes_eq(digest, signature):
             raise InvalidSignature("Signature did not match digest.")

cryptography/hazmat/backends/openssl/dh.py

@@ -13,16 +13,16 @@
 def _dh_params_dup(dh_cdata, backend):
     lib = backend._lib
     ffi = backend._ffi

     param_cdata = lib.DHparams_dup(dh_cdata)
     backend.openssl_assert(param_cdata != ffi.NULL)
     param_cdata = ffi.gc(param_cdata, lib.DH_free)
-    if lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102:
-        # In OpenSSL versions < 1.0.2 or libressl DHparams_dup don't copy q
+    if lib.CRYPTOGRAPHY_IS_LIBRESSL:
+        # In libressl DHparams_dup don't copy q
         q = ffi.new("BIGNUM **")
         lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL)
         q_dup = lib.BN_dup(q[0])
         res = lib.DH_set0_pqg(param_cdata, ffi.NULL, q_dup, ffi.NULL)
         backend.openssl_assert(res == 1)

     return param_cdata
@@ -49,60 +49,43 @@
         if q[0] == self._backend._ffi.NULL:
             q_val = None
         else:
             q_val = self._backend._bn_to_int(q[0])
         return dh.DHParameterNumbers(
             p=self._backend._bn_to_int(p[0]),
             g=self._backend._bn_to_int(g[0]),
-            q=q_val
+            q=q_val,
         )

     def generate_private_key(self):
         return self._backend.generate_dh_private_key(self)

     def parameter_bytes(self, encoding, format):
         if format is not serialization.ParameterFormat.PKCS3:
-            raise ValueError(
-                "Only PKCS3 serialization is supported"
-            )
+            raise ValueError("Only PKCS3 serialization is supported")
         if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
             q = self._backend._ffi.new("BIGNUM **")
-            self._backend._lib.DH_get0_pqg(self._dh_cdata,
-                                           self._backend._ffi.NULL,
-                                           q,
-                                           self._backend._ffi.NULL)
+            self._backend._lib.DH_get0_pqg(
+                self._dh_cdata,
+                self._backend._ffi.NULL,
+                q,
+                self._backend._ffi.NULL,
+            )
             if q[0] != self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
                     "DH X9.42 serialization is not supported",
-                    _Reasons.UNSUPPORTED_SERIALIZATION)
+                    _Reasons.UNSUPPORTED_SERIALIZATION,
+                )

-        return self._backend._parameter_bytes(
-            encoding,
-            format,
-            self._dh_cdata
-        )
-
-
-def _handle_dh_compute_key_error(errors, backend):
-    lib = backend._lib
-
-    backend.openssl_assert(
-        errors[0]._lib_reason_match(
-            lib.ERR_LIB_DH, lib.DH_R_INVALID_PUBKEY
-        )
-    )
-
-    raise ValueError("Public key value is invalid for this exchange.")
+        return self._backend._parameter_bytes(encoding, format, self._dh_cdata)


 def _get_dh_num_bits(backend, dh_cdata):
     p = backend._ffi.new("BIGNUM **")
-    backend._lib.DH_get0_pqg(dh_cdata, p,
-                             backend._ffi.NULL,
-                             backend._ffi.NULL)
+    backend._lib.DH_get0_pqg(dh_cdata, p, backend._ffi.NULL, backend._ffi.NULL)
     backend.openssl_assert(p[0] != backend._ffi.NULL)
     return backend._lib.BN_num_bits(p[0])


 @utils.register_interface(dh.DHPrivateKeyWithSerialization)
 class _DHPrivateKey(object):
     def __init__(self, backend, dh_cdata, evp_pkey):
@@ -132,89 +115,97 @@
         self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL)
         return dh.DHPrivateNumbers(
             public_numbers=dh.DHPublicNumbers(
                 parameter_numbers=dh.DHParameterNumbers(
                     p=self._backend._bn_to_int(p[0]),
                     g=self._backend._bn_to_int(g[0]),
-                    q=q_val
+                    q=q_val,
                 ),
-                y=self._backend._bn_to_int(pub_key[0])
+                y=self._backend._bn_to_int(pub_key[0]),
             ),
-            x=self._backend._bn_to_int(priv_key[0])
+            x=self._backend._bn_to_int(priv_key[0]),
         )

     def exchange(self, peer_public_key):

         buf = self._backend._ffi.new("unsigned char[]", self._key_size_bytes)
         pub_key = self._backend._ffi.new("BIGNUM **")
-        self._backend._lib.DH_get0_key(peer_public_key._dh_cdata, pub_key,
-                                       self._backend._ffi.NULL)
+        self._backend._lib.DH_get0_key(
+            peer_public_key._dh_cdata, pub_key, self._backend._ffi.NULL
+        )
         self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
         res = self._backend._lib.DH_compute_key(
-            buf,
-            pub_key[0],
-            self._dh_cdata
+            buf, pub_key[0], self._dh_cdata
         )

         if res == -1:
-            errors = self._backend._consume_errors()
-            return _handle_dh_compute_key_error(errors, self._backend)
+            errors_with_text = self._backend._consume_errors_with_text()
+            raise ValueError(
+                "Error computing shared key. Public key is likely invalid "
+                "for this exchange.",
+                errors_with_text,
+            )
         else:
             self._backend.openssl_assert(res >= 1)

             key = self._backend._ffi.buffer(buf)[:res]
             pad = self._key_size_bytes - len(key)

             if pad > 0:
                 key = (b"\x00" * pad) + key

             return key

     def public_key(self):
         dh_cdata = _dh_params_dup(self._dh_cdata, self._backend)
         pub_key = self._backend._ffi.new("BIGNUM **")
-        self._backend._lib.DH_get0_key(self._dh_cdata,
-                                       pub_key, self._backend._ffi.NULL)
+        self._backend._lib.DH_get0_key(
+            self._dh_cdata, pub_key, self._backend._ffi.NULL
+        )
         self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
         pub_key_dup = self._backend._lib.BN_dup(pub_key[0])
         self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL)

-        res = self._backend._lib.DH_set0_key(dh_cdata,
-                                             pub_key_dup,
-                                             self._backend._ffi.NULL)
+        res = self._backend._lib.DH_set0_key(
+            dh_cdata, pub_key_dup, self._backend._ffi.NULL
+        )
         self._backend.openssl_assert(res == 1)
         evp_pkey = self._backend._dh_cdata_to_evp_pkey(dh_cdata)
         return _DHPublicKey(self._backend, dh_cdata, evp_pkey)

     def parameters(self):
         return _dh_cdata_to_parameters(self._dh_cdata, self._backend)

     def private_bytes(self, encoding, format, encryption_algorithm):
         if format is not serialization.PrivateFormat.PKCS8:
             raise ValueError(
                 "DH private keys support only PKCS8 serialization"
             )
         if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
             q = self._backend._ffi.new("BIGNUM **")
-            self._backend._lib.DH_get0_pqg(self._dh_cdata,
-                                           self._backend._ffi.NULL,
-                                           q,
-                                           self._backend._ffi.NULL)
+            self._backend._lib.DH_get0_pqg(
+                self._dh_cdata,
+                self._backend._ffi.NULL,
+                q,
+                self._backend._ffi.NULL,
+            )
             if q[0] != self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
                     "DH X9.42 serialization is not supported",
-                    _Reasons.UNSUPPORTED_SERIALIZATION)
+                    _Reasons.UNSUPPORTED_SERIALIZATION,
+                )

         return self._backend._private_key_bytes(
             encoding,
             format,
             encryption_algorithm,
+            self,
             self._evp_pkey,
-            self._dh_cdata
+            self._dh_cdata,
         )


 @utils.register_interface(dh.DHPublicKeyWithSerialization)
 class _DHPublicKey(object):
     def __init__(self, backend, dh_cdata, evp_pkey):
         self._backend = backend
@@ -234,47 +225,47 @@
         self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
         if q[0] == self._backend._ffi.NULL:
             q_val = None
         else:
             q_val = self._backend._bn_to_int(q[0])
         pub_key = self._backend._ffi.new("BIGNUM **")
-        self._backend._lib.DH_get0_key(self._dh_cdata,
-                                       pub_key, self._backend._ffi.NULL)
+        self._backend._lib.DH_get0_key(
+            self._dh_cdata, pub_key, self._backend._ffi.NULL
+        )
         self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
         return dh.DHPublicNumbers(
             parameter_numbers=dh.DHParameterNumbers(
                 p=self._backend._bn_to_int(p[0]),
                 g=self._backend._bn_to_int(g[0]),
-                q=q_val
+                q=q_val,
             ),
-            y=self._backend._bn_to_int(pub_key[0])
+            y=self._backend._bn_to_int(pub_key[0]),
         )

     def parameters(self):
         return _dh_cdata_to_parameters(self._dh_cdata, self._backend)

     def public_bytes(self, encoding, format):
         if format is not serialization.PublicFormat.SubjectPublicKeyInfo:
             raise ValueError(
                 "DH public keys support only "
                 "SubjectPublicKeyInfo serialization"
             )

         if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
             q = self._backend._ffi.new("BIGNUM **")
-            self._backend._lib.DH_get0_pqg(self._dh_cdata,
-                                           self._backend._ffi.NULL,
-                                           q,
-                                           self._backend._ffi.NULL)
+            self._backend._lib.DH_get0_pqg(
+                self._dh_cdata,
+                self._backend._ffi.NULL,
+                q,
+                self._backend._ffi.NULL,
+            )
             if q[0] != self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
                     "DH X9.42 serialization is not supported",
-                    _Reasons.UNSUPPORTED_SERIALIZATION)
+                    _Reasons.UNSUPPORTED_SERIALIZATION,
+                )

         return self._backend._public_key_bytes(
-            encoding,
-            format,
-            self,
-            self._evp_pkey,
-            None
+            encoding, format, self, self._evp_pkey, None
         )

cryptography/hazmat/backends/openssl/utils.py

@@ -13,28 +13,24 @@

 def _evp_pkey_derive(backend, evp_pkey, peer_public_key):
     ctx = backend._lib.EVP_PKEY_CTX_new(evp_pkey, backend._ffi.NULL)
     backend.openssl_assert(ctx != backend._ffi.NULL)
     ctx = backend._ffi.gc(ctx, backend._lib.EVP_PKEY_CTX_free)
     res = backend._lib.EVP_PKEY_derive_init(ctx)
     backend.openssl_assert(res == 1)
-    res = backend._lib.EVP_PKEY_derive_set_peer(
-        ctx, peer_public_key._evp_pkey
-    )
+    res = backend._lib.EVP_PKEY_derive_set_peer(ctx, peer_public_key._evp_pkey)
     backend.openssl_assert(res == 1)
     keylen = backend._ffi.new("size_t *")
     res = backend._lib.EVP_PKEY_derive(ctx, backend._ffi.NULL, keylen)
     backend.openssl_assert(res == 1)
     backend.openssl_assert(keylen[0] > 0)
     buf = backend._ffi.new("unsigned char[]", keylen[0])
     res = backend._lib.EVP_PKEY_derive(ctx, buf, keylen)
     if res != 1:
-        raise ValueError(
-            "Null shared key derived from public/private pair."
-        )
+        raise ValueError("Null shared key derived from public/private pair.")

     return backend._ffi.buffer(buf, keylen[0])[:]


 def _calculate_digest_and_algorithm(backend, data, algorithm):
     if not isinstance(algorithm, Prehashed):
         hash_ctx = hashes.Hash(algorithm, backend)
@@ -61,9 +57,9 @@


 def _warn_sign_verify_deprecated():
     warnings.warn(
         "signer and verifier have been deprecated. Please use sign "
         "and verify instead.",
         utils.PersistentlyDeprecated2017,
-        stacklevel=3
+        stacklevel=3,
     )

cryptography/hazmat/backends/openssl/x448.py

@@ -4,50 +4,42 @@

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric.x448 import (
-    X448PrivateKey, X448PublicKey
+    X448PrivateKey,
+    X448PublicKey,
 )

 _X448_KEY_SIZE = 56


 @utils.register_interface(X448PublicKey)
 class _X448PublicKey(object):
     def __init__(self, backend, evp_pkey):
         self._backend = backend
         self._evp_pkey = evp_pkey

     def public_bytes(self, encoding, format):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                encoding is not serialization.Encoding.Raw or
-                format is not serialization.PublicFormat.Raw
+                encoding is not serialization.Encoding.Raw
+                or format is not serialization.PublicFormat.Raw
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw"
                 )

             return self._raw_public_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PublicFormat.SubjectPublicKeyInfo
-        ):
-            raise ValueError(
-                "format must be SubjectPublicKeyInfo when encoding is PEM or "
-                "DER"
-            )
-
         return self._backend._public_key_bytes(
             encoding, format, self, self._evp_pkey, None
         )

     def _raw_public_bytes(self):
         buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
         buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
@@ -75,45 +67,37 @@
         self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE)
         return self._backend.x448_load_public_bytes(buf)

     def exchange(self, peer_public_key):
         if not isinstance(peer_public_key, X448PublicKey):
             raise TypeError("peer_public_key must be X448PublicKey.")

-        return _evp_pkey_derive(
-            self._backend, self._evp_pkey, peer_public_key
-        )
+        return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)

     def private_bytes(self, encoding, format, encryption_algorithm):
         if (
-            encoding is serialization.Encoding.Raw or
-            format is serialization.PublicFormat.Raw
+            encoding is serialization.Encoding.Raw
+            or format is serialization.PublicFormat.Raw
         ):
             if (
-                format is not serialization.PrivateFormat.Raw or
-                encoding is not serialization.Encoding.Raw or not
-                isinstance(encryption_algorithm, serialization.NoEncryption)
+                format is not serialization.PrivateFormat.Raw
+                or encoding is not serialization.Encoding.Raw
+                or not isinstance(
+                    encryption_algorithm, serialization.NoEncryption
+                )
             ):
                 raise ValueError(
                     "When using Raw both encoding and format must be Raw "
-                    "and encryption_algorithm must be NoEncryption"
+                    "and encryption_algorithm must be NoEncryption()"
                 )

             return self._raw_private_bytes()

-        if (
-            encoding in serialization._PEM_DER and
-            format is not serialization.PrivateFormat.PKCS8
-        ):
-            raise ValueError(
-                "format must be PKCS8 when encoding is PEM or DER"
-            )
-
         return self._backend._private_key_bytes(
-            encoding, format, encryption_algorithm, self._evp_pkey, None
+            encoding, format, encryption_algorithm, self, self._evp_pkey, None
         )

     def _raw_private_bytes(self):
         buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
         buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
         res = self._backend._lib.EVP_PKEY_get_raw_private_key(
             self._evp_pkey, buf, buflen

cryptography/hazmat/backends/openssl/encode_asn1.py

@@ -7,20 +7,23 @@
 import calendar
 import ipaddress

 import six

 from cryptography import utils, x509
 from cryptography.hazmat.backends.openssl.decode_asn1 import (
-    _CRL_ENTRY_REASON_ENUM_TO_CODE, _DISTPOINT_TYPE_FULLNAME,
-    _DISTPOINT_TYPE_RELATIVENAME
+    _CRL_ENTRY_REASON_ENUM_TO_CODE,
+    _DISTPOINT_TYPE_FULLNAME,
+    _DISTPOINT_TYPE_RELATIVENAME,
 )
 from cryptography.x509.name import _ASN1Type
 from cryptography.x509.oid import (
-    CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID,
+    CRLEntryExtensionOID,
+    ExtensionOID,
+    OCSPExtensionOID,
 )


 def _encode_asn1_int(backend, x):
     """
     Converts a python integer to an ASN1_INTEGER. The returned ASN1_INTEGER
     will not be garbage collected (to support adding them to structs that take
@@ -90,15 +93,16 @@
         for attribute in rdn:
             name_entry = _encode_name_entry(backend, attribute)
             # X509_NAME_add_entry dups the object so we need to gc this copy
             name_entry = backend._ffi.gc(
                 name_entry, backend._lib.X509_NAME_ENTRY_free
             )
             res = backend._lib.X509_NAME_add_entry(
-                subject, name_entry, -1, set_flag)
+                subject, name_entry, -1, set_flag
+            )
             backend.openssl_assert(res == 1)
             set_flag = -1
     return subject


 def _encode_name_gc(backend, attributes):
     subject = _encode_name(backend, attributes)
@@ -116,17 +120,19 @@
         res = backend._lib.sk_X509_NAME_ENTRY_push(stack, name_entry)
         backend.openssl_assert(res >= 1)
     return stack


 def _encode_name_entry(backend, attribute):
     if attribute._type is _ASN1Type.BMPString:
-        value = attribute.value.encode('utf_16_be')
+        value = attribute.value.encode("utf_16_be")
+    elif attribute._type is _ASN1Type.UniversalString:
+        value = attribute.value.encode("utf_32_be")
     else:
-        value = attribute.value.encode('utf8')
+        value = attribute.value.encode("utf8")

     obj = _txt2obj_gc(backend, attribute.oid.dotted_string)

     name_entry = backend._lib.X509_NAME_ENTRY_create_by_OBJ(
         backend._ffi.NULL, obj, attribute._type.value, value, len(value)
     )
     return name_entry
@@ -168,17 +174,16 @@
     backend.openssl_assert(res == 1)

     return asn1enum


 def _encode_invalidity_date(backend, invalidity_date):
     time = backend._lib.ASN1_GENERALIZEDTIME_set(
-        backend._ffi.NULL, calendar.timegm(
-            invalidity_date.invalidity_date.timetuple()
-        )
+        backend._ffi.NULL,
+        calendar.timegm(invalidity_date.invalidity_date.timetuple()),
     )
     backend.openssl_assert(time != backend._ffi.NULL)
     time = backend._ffi.gc(time, backend._lib.ASN1_GENERALIZEDTIME_free)

     return time


@@ -251,15 +256,15 @@


 def _txt2obj(backend, name):
     """
     Converts a Python string with an ASN.1 object ID in dotted form to a
     ASN1_OBJECT.
     """
-    name = name.encode('ascii')
+    name = name.encode("ascii")
     obj = backend._lib.OBJ_txt2obj(name, 1)
     backend.openssl_assert(obj != backend._ffi.NULL)
     return obj


 def _txt2obj_gc(backend, name):
     obj = _txt2obj(backend, name)
@@ -337,28 +342,35 @@
         constraints.pathlen = _encode_asn1_int(
             backend, basic_constraints.path_length
         )

     return constraints


-def _encode_authority_information_access(backend, authority_info_access):
+def _encode_information_access(backend, info_access):
     aia = backend._lib.sk_ACCESS_DESCRIPTION_new_null()
     backend.openssl_assert(aia != backend._ffi.NULL)
     aia = backend._ffi.gc(
-        aia, backend._lib.sk_ACCESS_DESCRIPTION_free
+        aia,
+        lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free(
+            x,
+            backend._ffi.addressof(
+                backend._lib._original_lib, "ACCESS_DESCRIPTION_free"
+            ),
+        ),
     )
-    for access_description in authority_info_access:
+    for access_description in info_access:
         ad = backend._lib.ACCESS_DESCRIPTION_new()
         method = _txt2obj(
             backend, access_description.access_method.dotted_string
         )
-        gn = _encode_general_name(backend, access_description.access_location)
+        _encode_general_name_preallocated(
+            backend, access_description.access_location, ad.location
+        )
         ad.method = method
-        ad.location = gn
         res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
         backend.openssl_assert(res >= 1)

     return aia


 def _encode_general_names(backend, names):
@@ -381,69 +393,68 @@


 def _encode_subject_key_identifier(backend, ski):
     return _encode_asn1_str_gc(backend, ski.digest)


 def _encode_general_name(backend, name):
+    gn = backend._lib.GENERAL_NAME_new()
+    _encode_general_name_preallocated(backend, name, gn)
+    return gn
+
+
+def _encode_general_name_preallocated(backend, name, gn):
     if isinstance(name, x509.DNSName):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         gn.type = backend._lib.GEN_DNS

         ia5 = backend._lib.ASN1_IA5STRING_new()
         backend.openssl_assert(ia5 != backend._ffi.NULL)
         # ia5strings are supposed to be ITU T.50 but to allow round-tripping
         # of broken certs that encode utf8 we'll encode utf8 here too.
         value = name.value.encode("utf8")

         res = backend._lib.ASN1_STRING_set(ia5, value, len(value))
         backend.openssl_assert(res == 1)
         gn.d.dNSName = ia5
     elif isinstance(name, x509.RegisteredID):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         gn.type = backend._lib.GEN_RID
         obj = backend._lib.OBJ_txt2obj(
-            name.value.dotted_string.encode('ascii'), 1
+            name.value.dotted_string.encode("ascii"), 1
         )
         backend.openssl_assert(obj != backend._ffi.NULL)
         gn.d.registeredID = obj
     elif isinstance(name, x509.DirectoryName):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         dir_name = _encode_name(backend, name.value)
         gn.type = backend._lib.GEN_DIRNAME
         gn.d.directoryName = dir_name
     elif isinstance(name, x509.IPAddress):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         if isinstance(name.value, ipaddress.IPv4Network):
-            packed = (
-                name.value.network_address.packed +
-                utils.int_to_bytes(((1 << 32) - name.value.num_addresses), 4)
+            packed = name.value.network_address.packed + utils.int_to_bytes(
+                ((1 << 32) - name.value.num_addresses), 4
             )
         elif isinstance(name.value, ipaddress.IPv6Network):
-            packed = (
-                name.value.network_address.packed +
-                utils.int_to_bytes((1 << 128) - name.value.num_addresses, 16)
+            packed = name.value.network_address.packed + utils.int_to_bytes(
+                (1 << 128) - name.value.num_addresses, 16
             )
         else:
             packed = name.value.packed
         ipaddr = _encode_asn1_str(backend, packed)
         gn.type = backend._lib.GEN_IPADD
         gn.d.iPAddress = ipaddr
     elif isinstance(name, x509.OtherName):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         other_name = backend._lib.OTHERNAME_new()
         backend.openssl_assert(other_name != backend._ffi.NULL)

         type_id = backend._lib.OBJ_txt2obj(
-            name.type_id.dotted_string.encode('ascii'), 1
+            name.type_id.dotted_string.encode("ascii"), 1
         )
         backend.openssl_assert(type_id != backend._ffi.NULL)
         data = backend._ffi.new("unsigned char[]", name.value)
         data_ptr_ptr = backend._ffi.new("unsigned char **")
         data_ptr_ptr[0] = data
         value = backend._lib.d2i_ASN1_TYPE(
             backend._ffi.NULL, data_ptr_ptr, len(name.value)
@@ -452,37 +463,31 @@
             backend._consume_errors()
             raise ValueError("Invalid ASN.1 data")
         other_name.type_id = type_id
         other_name.value = value
         gn.type = backend._lib.GEN_OTHERNAME
         gn.d.otherName = other_name
     elif isinstance(name, x509.RFC822Name):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         # ia5strings are supposed to be ITU T.50 but to allow round-tripping
         # of broken certs that encode utf8 we'll encode utf8 here too.
         data = name.value.encode("utf8")
         asn1_str = _encode_asn1_str(backend, data)
         gn.type = backend._lib.GEN_EMAIL
         gn.d.rfc822Name = asn1_str
     elif isinstance(name, x509.UniformResourceIdentifier):
-        gn = backend._lib.GENERAL_NAME_new()
         backend.openssl_assert(gn != backend._ffi.NULL)
         # ia5strings are supposed to be ITU T.50 but to allow round-tripping
         # of broken certs that encode utf8 we'll encode utf8 here too.
         data = name.value.encode("utf8")
         asn1_str = _encode_asn1_str(backend, data)
         gn.type = backend._lib.GEN_URI
         gn.d.uniformResourceIdentifier = asn1_str
     else:
-        raise ValueError(
-            "{} is an unknown GeneralName type".format(name)
-        )
-
-    return gn
+        raise ValueError("{} is an unknown GeneralName type".format(name))


 def _encode_extended_key_usage(backend, extended_key_usage):
     eku = backend._lib.sk_ASN1_OBJECT_new_null()
     eku = backend._ffi.gc(eku, backend._lib.sk_ASN1_OBJECT_free)
     for oid in extended_key_usage:
         obj = _txt2obj(backend, oid.dotted_string)
@@ -613,34 +618,32 @@
     ExtensionOID.SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier,
     ExtensionOID.KEY_USAGE: _encode_key_usage,
     ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _encode_alt_name,
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
     ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage,
     ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
     ExtensionOID.CERTIFICATE_POLICIES: _encode_certificate_policies,
-    ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
-        _encode_authority_information_access
-    ),
+    ExtensionOID.AUTHORITY_INFORMATION_ACCESS: _encode_information_access,
+    ExtensionOID.SUBJECT_INFORMATION_ACCESS: _encode_information_access,
     ExtensionOID.CRL_DISTRIBUTION_POINTS: _encode_cdps_freshest_crl,
     ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl,
     ExtensionOID.INHIBIT_ANY_POLICY: _encode_inhibit_any_policy,
     ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck,
     ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints,
     ExtensionOID.POLICY_CONSTRAINTS: _encode_policy_constraints,
 }

 _CRL_EXTENSION_ENCODE_HANDLERS = {
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
     ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
-    ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
-        _encode_authority_information_access
-    ),
+    ExtensionOID.AUTHORITY_INFORMATION_ACCESS: _encode_information_access,
     ExtensionOID.CRL_NUMBER: _encode_crl_number_delta_crl_indicator,
     ExtensionOID.DELTA_CRL_INDICATOR: _encode_crl_number_delta_crl_indicator,
     ExtensionOID.ISSUING_DISTRIBUTION_POINT: _encode_issuing_dist_point,
+    ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl,
 }

 _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
     CRLEntryExtensionOID.CERTIFICATE_ISSUER: _encode_alt_name,
     CRLEntryExtensionOID.CRL_REASON: _encode_crl_reason,
     CRLEntryExtensionOID.INVALIDITY_DATE: _encode_invalidity_date,
 }

cryptography/hazmat/backends/openssl/ec.py

@@ -2,50 +2,66 @@
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.exceptions import (
-    InvalidSignature, UnsupportedAlgorithm, _Reasons
+    InvalidSignature,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
 from cryptography.hazmat.backends.openssl.utils import (
-    _calculate_digest_and_algorithm, _check_not_prehashed,
-    _warn_sign_verify_deprecated
+    _calculate_digest_and_algorithm,
+    _check_not_prehashed,
+    _warn_sign_verify_deprecated,
 )
 from cryptography.hazmat.primitives import hashes, serialization
 from cryptography.hazmat.primitives.asymmetric import (
-    AsymmetricSignatureContext, AsymmetricVerificationContext, ec
+    AsymmetricSignatureContext,
+    AsymmetricVerificationContext,
+    ec,
 )


 def _check_signature_algorithm(signature_algorithm):
     if not isinstance(signature_algorithm, ec.ECDSA):
         raise UnsupportedAlgorithm(
             "Unsupported elliptic curve signature algorithm.",
-            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+        )


 def _ec_key_curve_sn(backend, ec_key):
     group = backend._lib.EC_KEY_get0_group(ec_key)
     backend.openssl_assert(group != backend._ffi.NULL)

     nid = backend._lib.EC_GROUP_get_curve_name(group)
     # The following check is to find EC keys with unnamed curves and raise
     # an error for now.
     if nid == backend._lib.NID_undef:
         raise NotImplementedError(
-            "ECDSA certificates with unnamed curves are unsupported "
-            "at this time"
+            "ECDSA keys with unnamed curves are unsupported " "at this time"
+        )
+
+    # This is like the above check, but it also catches the case where you
+    # explicitly encoded a curve with the same parameters as a named curve.
+    # Don't do that.
+    if (
+        backend._lib.CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER
+        and backend._lib.EC_GROUP_get_asn1_flag(group) == 0
+    ):
+        raise NotImplementedError(
+            "ECDSA keys with unnamed curves are unsupported " "at this time"
         )

     curve_name = backend._lib.OBJ_nid2sn(nid)
     backend.openssl_assert(curve_name != backend._ffi.NULL)

-    sn = backend._ffi.string(curve_name).decode('ascii')
+    sn = backend._ffi.string(curve_name).decode("ascii")
     return sn


 def _mark_asn1_named_ec_curve(backend, ec_cdata):
     """
     Set the named curve flag on the EC_KEY. This causes OpenSSL to
     serialize EC keys along with their curve OID which makes
@@ -59,29 +75,29 @@

 def _sn_to_elliptic_curve(backend, sn):
     try:
         return ec._CURVE_TYPES[sn]()
     except KeyError:
         raise UnsupportedAlgorithm(
             "{} is not a supported elliptic curve".format(sn),
-            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
         )


 def _ecdsa_sig_sign(backend, private_key, data):
     max_size = backend._lib.ECDSA_size(private_key._ec_key)
     backend.openssl_assert(max_size > 0)

     sigbuf = backend._ffi.new("unsigned char[]", max_size)
     siglen_ptr = backend._ffi.new("unsigned int[]", 1)
     res = backend._lib.ECDSA_sign(
         0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key
     )
     backend.openssl_assert(res == 1)
-    return backend._ffi.buffer(sigbuf)[:siglen_ptr[0]]
+    return backend._ffi.buffer(sigbuf)[: siglen_ptr[0]]


 def _ecdsa_sig_verify(backend, public_key, signature, data):
     res = backend._lib.ECDSA_verify(
         0, data, len(data), signature, len(signature), public_key._ec_key
     )
     if res != 1:
@@ -123,20 +139,20 @@
         )


 @utils.register_interface(ec.EllipticCurvePrivateKeyWithSerialization)
 class _EllipticCurvePrivateKey(object):
     def __init__(self, backend, ec_key_cdata, evp_pkey):
         self._backend = backend
-        _mark_asn1_named_ec_curve(backend, ec_key_cdata)
         self._ec_key = ec_key_cdata
         self._evp_pkey = evp_pkey

         sn = _ec_key_curve_sn(backend, ec_key_cdata)
         self._curve = _sn_to_elliptic_curve(backend, sn)
+        _mark_asn1_named_ec_curve(backend, ec_key_cdata)

     curve = utils.read_only_property("_curve")

     @property
     def key_size(self):
         return self.curve.key_size

@@ -152,15 +168,15 @@
         if not (
             self._backend.elliptic_curve_exchange_algorithm_supported(
                 algorithm, self.curve
             )
         ):
             raise UnsupportedAlgorithm(
                 "This backend does not support the ECDH algorithm.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )

         if peer_public_key.curve.name != self.curve.name:
             raise ValueError(
                 "peer_public_key and self are not on the same curve"
             )

@@ -179,20 +195,15 @@
         return self._backend._ffi.buffer(z_buf)[:z_len]

     def public_key(self):
         group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
         self._backend.openssl_assert(group != self._backend._ffi.NULL)

         curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group)
-
-        public_ec_key = self._backend._lib.EC_KEY_new_by_curve_name(curve_nid)
-        self._backend.openssl_assert(public_ec_key != self._backend._ffi.NULL)
-        public_ec_key = self._backend._ffi.gc(
-            public_ec_key, self._backend._lib.EC_KEY_free
-        )
+        public_ec_key = self._backend._ec_key_new_by_curve_nid(curve_nid)

         point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
         self._backend.openssl_assert(point != self._backend._ffi.NULL)

         res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point)
         self._backend.openssl_assert(res == 1)

@@ -201,44 +212,45 @@
         return _EllipticCurvePublicKey(self._backend, public_ec_key, evp_pkey)

     def private_numbers(self):
         bn = self._backend._lib.EC_KEY_get0_private_key(self._ec_key)
         private_value = self._backend._bn_to_int(bn)
         return ec.EllipticCurvePrivateNumbers(
             private_value=private_value,
-            public_numbers=self.public_key().public_numbers()
+            public_numbers=self.public_key().public_numbers(),
         )

     def private_bytes(self, encoding, format, encryption_algorithm):
         return self._backend._private_key_bytes(
             encoding,
             format,
             encryption_algorithm,
+            self,
             self._evp_pkey,
-            self._ec_key
+            self._ec_key,
         )

     def sign(self, data, signature_algorithm):
         _check_signature_algorithm(signature_algorithm)
         data, algorithm = _calculate_digest_and_algorithm(
             self._backend, data, signature_algorithm._algorithm
         )
         return _ecdsa_sig_sign(self._backend, self, data)


 @utils.register_interface(ec.EllipticCurvePublicKeyWithSerialization)
 class _EllipticCurvePublicKey(object):
     def __init__(self, backend, ec_key_cdata, evp_pkey):
         self._backend = backend
-        _mark_asn1_named_ec_curve(backend, ec_key_cdata)
         self._ec_key = ec_key_cdata
         self._evp_pkey = evp_pkey

         sn = _ec_key_curve_sn(backend, ec_key_cdata)
         self._curve = _sn_to_elliptic_curve(backend, sn)
+        _mark_asn1_named_ec_curve(backend, ec_key_cdata)

     curve = utils.read_only_property("_curve")

     @property
     def key_size(self):
         return self.curve.key_size

@@ -249,35 +261,31 @@
         _check_signature_algorithm(signature_algorithm)
         _check_not_prehashed(signature_algorithm.algorithm)
         return _ECDSAVerificationContext(
             self._backend, self, signature, signature_algorithm.algorithm
         )

     def public_numbers(self):
-        get_func, group = (
-            self._backend._ec_key_determine_group_get_func(self._ec_key)
+        get_func, group = self._backend._ec_key_determine_group_get_func(
+            self._ec_key
         )
         point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
         self._backend.openssl_assert(point != self._backend._ffi.NULL)

         with self._backend._tmp_bn_ctx() as bn_ctx:
             bn_x = self._backend._lib.BN_CTX_get(bn_ctx)
             bn_y = self._backend._lib.BN_CTX_get(bn_ctx)

             res = get_func(group, point, bn_x, bn_y, bn_ctx)
             self._backend.openssl_assert(res == 1)

             x = self._backend._bn_to_int(bn_x)
             y = self._backend._bn_to_int(bn_y)

-        return ec.EllipticCurvePublicNumbers(
-            x=x,
-            y=y,
-            curve=self._curve
-        )
+        return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve)

     def _encode_point(self, format):
         if format is serialization.PublicFormat.CompressedPoint:
             conversion = self._backend._lib.POINT_CONVERSION_COMPRESSED
         else:
             assert format is serialization.PublicFormat.UncompressedPoint
             conversion = self._backend._lib.POINT_CONVERSION_UNCOMPRESSED
@@ -296,44 +304,33 @@
                 group, point, conversion, buf, buflen, bn_ctx
             )
             self._backend.openssl_assert(buflen == res)

         return self._backend._ffi.buffer(buf)[:]

     def public_bytes(self, encoding, format):
-        if format is serialization.PublicFormat.PKCS1:
-            raise ValueError(
-                "EC public keys do not support PKCS1 serialization"
-            )

         if (
-            encoding is serialization.Encoding.X962 or
-            format is serialization.PublicFormat.CompressedPoint or
-            format is serialization.PublicFormat.UncompressedPoint
+            encoding is serialization.Encoding.X962
+            or format is serialization.PublicFormat.CompressedPoint
+            or format is serialization.PublicFormat.UncompressedPoint
         ):
-            if (
-                encoding is not serialization.Encoding.X962 or
-                format not in (
-                    serialization.PublicFormat.CompressedPoint,
-                    serialization.PublicFormat.UncompressedPoint
-                )
+            if encoding is not serialization.Encoding.X962 or format not in (
+                serialization.PublicFormat.CompressedPoint,
+                serialization.PublicFormat.UncompressedPoint,
             ):
                 raise ValueError(
                     "X962 encoding must be used with CompressedPoint or "
                     "UncompressedPoint format"
                 )

             return self._encode_point(format)
         else:
             return self._backend._public_key_bytes(
-                encoding,
-                format,
-                self,
-                self._evp_pkey,
-                None
+                encoding, format, self, self._evp_pkey, None
             )

     def verify(self, signature, data, signature_algorithm):
         _check_signature_algorithm(signature_algorithm)
         data, algorithm = _calculate_digest_and_algorithm(
             self._backend, data, signature_algorithm._algorithm
         )

cryptography/hazmat/backends/openssl/hmac.py

@@ -3,20 +3,21 @@
 # for complete details.

 from __future__ import absolute_import, division, print_function


 from cryptography import utils
 from cryptography.exceptions import (
-    InvalidSignature, UnsupportedAlgorithm, _Reasons
+    InvalidSignature,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
-from cryptography.hazmat.primitives import constant_time, hashes, mac
+from cryptography.hazmat.primitives import constant_time, hashes


[email protected]_interface(mac.MACContext)
 @utils.register_interface(hashes.HashContext)
 class _HMACContext(object):
     def __init__(self, backend, key, algorithm, ctx=None):
         self._algorithm = algorithm
         self._backend = backend

         if ctx is None:
@@ -25,16 +26,17 @@
             ctx = self._backend._ffi.gc(
                 ctx, self._backend._lib.Cryptography_HMAC_CTX_free
             )
             evp_md = self._backend._evp_md_from_algorithm(algorithm)
             if evp_md == self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
                     "{} is not a supported hash on this backend".format(
-                        algorithm.name),
-                    _Reasons.UNSUPPORTED_HASH
+                        algorithm.name
+                    ),
+                    _Reasons.UNSUPPORTED_HASH,
                 )
             key_ptr = self._backend._ffi.from_buffer(key)
             res = self._backend._lib.HMAC_Init_ex(
                 ctx, key_ptr, len(key), evp_md, self._backend._ffi.NULL
             )
             self._backend.openssl_assert(res != 0)

@@ -57,19 +59,20 @@

     def update(self, data):
         data_ptr = self._backend._ffi.from_buffer(data)
         res = self._backend._lib.HMAC_Update(self._ctx, data_ptr, len(data))
         self._backend.openssl_assert(res != 0)

     def finalize(self):
-        buf = self._backend._ffi.new("unsigned char[]",
-                                     self._backend._lib.EVP_MAX_MD_SIZE)
+        buf = self._backend._ffi.new(
+            "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE
+        )
         outlen = self._backend._ffi.new("unsigned int *")
         res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen)
         self._backend.openssl_assert(res != 0)
         self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size)
-        return self._backend._ffi.buffer(buf)[:outlen[0]]
+        return self._backend._ffi.buffer(buf)[: outlen[0]]

     def verify(self, signature):
         digest = self.finalize()
         if not constant_time.bytes_eq(digest, signature):
             raise InvalidSignature("Signature did not match digest.")

cryptography/hazmat/backends/openssl/ciphers.py

@@ -13,14 +13,15 @@
 @utils.register_interface(ciphers.CipherContext)
 @utils.register_interface(ciphers.AEADCipherContext)
 @utils.register_interface(ciphers.AEADEncryptionContext)
 @utils.register_interface(ciphers.AEADDecryptionContext)
 class _CipherContext(object):
     _ENCRYPT = 1
     _DECRYPT = 0
+    _MAX_CHUNK_SIZE = 2 ** 31 - 1

     def __init__(self, backend, cipher, mode, operation):
         self._backend = backend
         self._cipher = cipher
         self._mode = mode
         self._operation = operation
         self._tag = None
@@ -38,16 +39,17 @@
         registry = self._backend._cipher_registry
         try:
             adapter = registry[type(cipher), type(mode)]
         except KeyError:
             raise UnsupportedAlgorithm(
                 "cipher {} in {} mode is not supported "
                 "by this backend.".format(
-                    cipher.name, mode.name if mode else mode),
-                _Reasons.UNSUPPORTED_CIPHER
+                    cipher.name, mode.name if mode else mode
+                ),
+                _Reasons.UNSUPPORTED_CIPHER,
             )

         evp_cipher = adapter(self._backend, cipher, mode)
         if evp_cipher == self._backend._ffi.NULL:
             msg = "cipher {0.name} ".format(cipher)
             if mode is not None:
                 msg += "in {0.name} mode ".format(mode)
@@ -66,99 +68,99 @@
         elif isinstance(mode, modes.ModeWithNonce):
             iv_nonce = self._backend._ffi.from_buffer(mode.nonce)
         elif isinstance(cipher, modes.ModeWithNonce):
             iv_nonce = self._backend._ffi.from_buffer(cipher.nonce)
         else:
             iv_nonce = self._backend._ffi.NULL
         # begin init with cipher and operation type
-        res = self._backend._lib.EVP_CipherInit_ex(ctx, evp_cipher,
-                                                   self._backend._ffi.NULL,
-                                                   self._backend._ffi.NULL,
-                                                   self._backend._ffi.NULL,
-                                                   operation)
+        res = self._backend._lib.EVP_CipherInit_ex(
+            ctx,
+            evp_cipher,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            self._backend._ffi.NULL,
+            operation,
+        )
         self._backend.openssl_assert(res != 0)
         # set the key length to handle variable key ciphers
         res = self._backend._lib.EVP_CIPHER_CTX_set_key_length(
             ctx, len(cipher.key)
         )
         self._backend.openssl_assert(res != 0)
         if isinstance(mode, modes.GCM):
             res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
-                ctx, self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
-                len(iv_nonce), self._backend._ffi.NULL
+                ctx,
+                self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
+                len(iv_nonce),
+                self._backend._ffi.NULL,
             )
             self._backend.openssl_assert(res != 0)
             if mode.tag is not None:
                 res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
-                    ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
-                    len(mode.tag), mode.tag
+                    ctx,
+                    self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
+                    len(mode.tag),
+                    mode.tag,
                 )
                 self._backend.openssl_assert(res != 0)
                 self._tag = mode.tag
-            elif (
-                self._operation == self._DECRYPT and
-                self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
-                not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
-            ):
-                raise NotImplementedError(
-                    "delayed passing of GCM tag requires OpenSSL >= 1.0.2."
-                    " To use this feature please update OpenSSL"
-                )

         # pass key/iv
         res = self._backend._lib.EVP_CipherInit_ex(
             ctx,
             self._backend._ffi.NULL,
             self._backend._ffi.NULL,
             self._backend._ffi.from_buffer(cipher.key),
             iv_nonce,
-            operation
+            operation,
         )
         self._backend.openssl_assert(res != 0)
         # We purposely disable padding here as it's handled higher up in the
         # API.
         self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
         self._ctx = ctx

     def update(self, data):
         buf = bytearray(len(data) + self._block_size_bytes - 1)
         n = self.update_into(data, buf)
         return bytes(buf[:n])

     def update_into(self, data, buf):
-        if len(buf) < (len(data) + self._block_size_bytes - 1):
+        total_data_len = len(data)
+        if len(buf) < (total_data_len + self._block_size_bytes - 1):
             raise ValueError(
                 "buffer must be at least {} bytes for this "
                 "payload".format(len(data) + self._block_size_bytes - 1)
             )

-        buf = self._backend._ffi.cast(
-            "unsigned char *", self._backend._ffi.from_buffer(buf)
-        )
+        data_processed = 0
+        total_out = 0
         outlen = self._backend._ffi.new("int *")
-        res = self._backend._lib.EVP_CipherUpdate(
-            self._ctx, buf, outlen,
-            self._backend._ffi.from_buffer(data), len(data)
-        )
-        self._backend.openssl_assert(res != 0)
-        return outlen[0]
+        baseoutbuf = self._backend._ffi.from_buffer(buf)
+        baseinbuf = self._backend._ffi.from_buffer(data)

-    def finalize(self):
-        # OpenSSL 1.0.1 on Ubuntu 12.04 (and possibly other distributions)
-        # appears to have a bug where you must make at least one call to update
-        # even if you are only using authenticate_additional_data or the
-        # GCM tag will be wrong. An (empty) call to update resolves this
-        # and is harmless for all other versions of OpenSSL.
-        if isinstance(self._mode, modes.GCM):
-            self.update(b"")
+        while data_processed != total_data_len:
+            outbuf = baseoutbuf + total_out
+            inbuf = baseinbuf + data_processed
+            inlen = min(self._MAX_CHUNK_SIZE, total_data_len - data_processed)

+            res = self._backend._lib.EVP_CipherUpdate(
+                self._ctx, outbuf, outlen, inbuf, inlen
+            )
+            self._backend.openssl_assert(res != 0)
+            data_processed += inlen
+            total_out += outlen[0]
+
+        return total_out
+
+    def finalize(self):
         if (
-            self._operation == self._DECRYPT and
-            isinstance(self._mode, modes.ModeWithAuthenticationTag) and
-            self.tag is None
+            self._operation == self._DECRYPT
+            and isinstance(self._mode, modes.ModeWithAuthenticationTag)
+            and self.tag is None
         ):
             raise ValueError(
                 "Authentication tag must be provided when decrypting."
             )

         buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes)
         outlen = self._backend._ffi.new("int *")
@@ -168,62 +170,62 @@

             if not errors and isinstance(self._mode, modes.GCM):
                 raise InvalidTag

             self._backend.openssl_assert(
                 errors[0]._lib_reason_match(
                     self._backend._lib.ERR_LIB_EVP,
-                    self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
-                )
+                    self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
+                ),
+                errors=errors,
             )
             raise ValueError(
                 "The length of the provided data is not a multiple of "
                 "the block length."
             )

-        if (isinstance(self._mode, modes.GCM) and
-           self._operation == self._ENCRYPT):
+        if (
+            isinstance(self._mode, modes.GCM)
+            and self._operation == self._ENCRYPT
+        ):
             tag_buf = self._backend._ffi.new(
                 "unsigned char[]", self._block_size_bytes
             )
             res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
-                self._ctx, self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
-                self._block_size_bytes, tag_buf
+                self._ctx,
+                self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
+                self._block_size_bytes,
+                tag_buf,
             )
             self._backend.openssl_assert(res != 0)
             self._tag = self._backend._ffi.buffer(tag_buf)[:]

         res = self._backend._lib.EVP_CIPHER_CTX_cleanup(self._ctx)
         self._backend.openssl_assert(res == 1)
-        return self._backend._ffi.buffer(buf)[:outlen[0]]
+        return self._backend._ffi.buffer(buf)[: outlen[0]]

     def finalize_with_tag(self, tag):
-        if (
-            self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
-            not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
-        ):
-            raise NotImplementedError(
-                "finalize_with_tag requires OpenSSL >= 1.0.2. To use this "
-                "method please update OpenSSL"
-            )
         if len(tag) < self._mode._min_tag_length:
             raise ValueError(
                 "Authentication tag must be {} bytes or longer.".format(
-                    self._mode._min_tag_length)
+                    self._mode._min_tag_length
+                )
             )
         res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
-            self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
-            len(tag), tag
+            self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
         )
         self._backend.openssl_assert(res != 0)
         self._tag = tag
         return self.finalize()

     def authenticate_additional_data(self, data):
         outlen = self._backend._ffi.new("int *")
         res = self._backend._lib.EVP_CipherUpdate(
-            self._ctx, self._backend._ffi.NULL, outlen,
-            self._backend._ffi.from_buffer(data), len(data)
+            self._ctx,
+            self._backend._ffi.NULL,
+            outlen,
+            self._backend._ffi.from_buffer(data),
+            len(data),
         )
         self._backend.openssl_assert(res != 0)

     tag = utils.read_only_property("_tag")

cryptography/hazmat/bindings/openssl/binding.py

@@ -1,14 +1,15 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 import collections
+import os
 import threading
 import types
 import warnings

 import cryptography
 from cryptography import utils
 from cryptography.exceptions import InternalError
@@ -48,38 +49,49 @@
         err_reason = lib.ERR_GET_REASON(code)

         errors.append(_OpenSSLError(code, err_lib, err_func, err_reason))

     return errors


-def _openssl_assert(lib, ok):
-    if not ok:
-        errors = _consume_errors(lib)
-        errors_with_text = []
-        for err in errors:
-            buf = ffi.new("char[]", 256)
-            lib.ERR_error_string_n(err.code, buf, len(buf))
-            err_text_reason = ffi.string(buf)
-
-            errors_with_text.append(
-                _OpenSSLErrorWithText(
-                    err.code, err.lib, err.func, err.reason, err_text_reason
-                )
+def _errors_with_text(errors):
+    errors_with_text = []
+    for err in errors:
+        buf = ffi.new("char[]", 256)
+        lib.ERR_error_string_n(err.code, buf, len(buf))
+        err_text_reason = ffi.string(buf)
+
+        errors_with_text.append(
+            _OpenSSLErrorWithText(
+                err.code, err.lib, err.func, err.reason, err_text_reason
             )
+        )
+
+    return errors_with_text
+
+
+def _consume_errors_with_text(lib):
+    return _errors_with_text(_consume_errors(lib))
+
+
+def _openssl_assert(lib, ok, errors=None):
+    if not ok:
+        if errors is None:
+            errors = _consume_errors(lib)
+        errors_with_text = _errors_with_text(errors)

         raise InternalError(
             "Unknown OpenSSL error. This error is commonly encountered when "
             "another library is not cleaning up the OpenSSL error stack. If "
             "you are using cryptography with another library that uses "
             "OpenSSL try disabling it before reporting a bug. Otherwise "
             "please file an issue at https://github.com/pyca/cryptography/"
             "issues with information on how to reproduce "
             "this. ({0!r})".format(errors_with_text),
-            errors_with_text
+            errors_with_text,
         )


 def build_conditional_library(lib, conditional_names):
     conditional_lib = types.ModuleType("lib")
     conditional_lib._original_lib = lib
     excluded_names = set()
@@ -94,14 +106,15 @@
     return conditional_lib


 class Binding(object):
     """
     OpenSSL API wrapper.
     """
+
     lib = None
     ffi = ffi
     _lib_loaded = False
     _init_lock = threading.Lock()
     _lock_init_lock = threading.Lock()

     def __init__(self):
@@ -111,15 +124,15 @@
     def _register_osrandom_engine(cls):
         # Clear any errors extant in the queue before we start. In many
         # scenarios other things may be interacting with OpenSSL in the same
         # process space and it has proven untenable to assume that they will
         # reliably clear the error queue. Once we clear it here we will
         # error on any subsequent unexpected item in the stack.
         cls.lib.ERR_clear_error()
-        if cls.lib.Cryptography_HAS_ENGINE:
+        if cls.lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
             result = cls.lib.Cryptography_add_osrandom_engine()
             _openssl_assert(cls.lib, result in (1, 2))

     @classmethod
     def _ensure_ffi_initialized(cls):
         with cls._init_lock:
             if not cls._lib_loaded:
@@ -137,35 +150,47 @@
     def init_static_locks(cls):
         with cls._lock_init_lock:
             cls._ensure_ffi_initialized()
             # Use Python's implementation if available, importing _ssl triggers
             # the setup for this.
             __import__("_ssl")

-            if (not cls.lib.Cryptography_HAS_LOCKING_CALLBACKS or
-                    cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL):
+            if (
+                not cls.lib.Cryptography_HAS_LOCKING_CALLBACKS
+                or cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL
+            ):
                 return

             # If nothing else has setup a locking callback already, we set up
             # our own
             res = lib.Cryptography_setup_ssl_threads()
             _openssl_assert(cls.lib, res == 1)


 def _verify_openssl_version(lib):
     if (
-        lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
-        not lib.CRYPTOGRAPHY_IS_LIBRESSL
+        lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_110
+        and not lib.CRYPTOGRAPHY_IS_LIBRESSL
     ):
-        warnings.warn(
-            "OpenSSL version 1.0.1 is no longer supported by the OpenSSL "
-            "project, please upgrade. A future version of cryptography will "
-            "drop support for it.",
-            utils.CryptographyDeprecationWarning
-        )
+        if os.environ.get("CRYPTOGRAPHY_ALLOW_OPENSSL_102"):
+            warnings.warn(
+                "OpenSSL version 1.0.2 is no longer supported by the OpenSSL "
+                "project, please upgrade. The next version of cryptography "
+                "will completely remove support for it.",
+                utils.CryptographyDeprecationWarning,
+            )
+        else:
+            raise RuntimeError(
+                "You are linking against OpenSSL 1.0.2, which is no longer "
+                "supported by the OpenSSL project. To use this version of "
+                "cryptography you need to upgrade to a newer version of "
+                "OpenSSL. For this version only you can also set the "
+                "environment variable CRYPTOGRAPHY_ALLOW_OPENSSL_102 to "
+                "allow OpenSSL 1.0.2."
+            )


 def _verify_package_version(version):
     # Occasionally we run into situations where the version of the Python
     # package does not match the version of the shared object that is loaded.
     # This may occur in environments where multiple versions of cryptography
     # are installed and available in the python path. To avoid errors cropping

cryptography/hazmat/bindings/openssl/_conditional.py

@@ -9,32 +9,14 @@
     return [
         "EC_POINT_set_affine_coordinates_GF2m",
         "EC_POINT_get_affine_coordinates_GF2m",
         "EC_POINT_set_compressed_coordinates_GF2m",
     ]


-def cryptography_has_ec_1_0_2():
-    return [
-        "EC_curve_nid2nist",
-    ]
-
-
-def cryptography_has_set_ecdh_auto():
-    return [
-        "SSL_CTX_set_ecdh_auto",
-    ]
-
-
-def cryptography_has_rsa_r_pkcs_decoding_error():
-    return [
-        "RSA_R_PKCS_DECODING_ERROR"
-    ]
-
-
 def cryptography_has_rsa_oaep_md():
     return [
         "EVP_PKEY_CTX_set_rsa_oaep_md",
     ]


 def cryptography_has_rsa_oaep_label():
@@ -47,86 +29,30 @@
     return [
         "SSLv3_method",
         "SSLv3_client_method",
         "SSLv3_server_method",
     ]


-def cryptography_has_alpn():
-    return [
-        "SSL_CTX_set_alpn_protos",
-        "SSL_set_alpn_protos",
-        "SSL_CTX_set_alpn_select_cb",
-        "SSL_get0_alpn_selected",
-    ]
-
-
-def cryptography_has_compression():
-    return [
-        "SSL_get_current_compression",
-        "SSL_get_current_expansion",
-        "SSL_COMP_get_name",
-    ]
-
-
-def cryptography_has_get_server_tmp_key():
-    return [
-        "SSL_get_server_tmp_key",
-    ]
-
-
-def cryptography_has_102_verification_error_codes():
-    return [
-        'X509_V_ERR_SUITE_B_INVALID_VERSION',
-        'X509_V_ERR_SUITE_B_INVALID_ALGORITHM',
-        'X509_V_ERR_SUITE_B_INVALID_CURVE',
-        'X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM',
-        'X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED',
-        'X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256',
-        'X509_V_ERR_HOSTNAME_MISMATCH',
-        'X509_V_ERR_EMAIL_MISMATCH',
-        'X509_V_ERR_IP_ADDRESS_MISMATCH'
-    ]
-
-
-def cryptography_has_102_verification_params():
+def cryptography_has_102_verification():
     return [
+        "X509_V_ERR_SUITE_B_INVALID_VERSION",
+        "X509_V_ERR_SUITE_B_INVALID_ALGORITHM",
+        "X509_V_ERR_SUITE_B_INVALID_CURVE",
+        "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM",
+        "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED",
+        "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256",
         "X509_V_FLAG_SUITEB_128_LOS_ONLY",
         "X509_V_FLAG_SUITEB_192_LOS",
         "X509_V_FLAG_SUITEB_128_LOS",
-        "X509_VERIFY_PARAM_set1_host",
-        "X509_VERIFY_PARAM_set1_email",
-        "X509_VERIFY_PARAM_set1_ip",
-        "X509_VERIFY_PARAM_set1_ip_asc",
-        "X509_VERIFY_PARAM_set_hostflags",
-        "SSL_get0_param",
-        "X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT",
-        "X509_CHECK_FLAG_NO_WILDCARDS",
-        "X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS",
-        "X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS",
-        "X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS"
     ]


 def cryptography_has_110_verification_params():
-    return [
-        "X509_CHECK_FLAG_NEVER_CHECK_SUBJECT"
-    ]
-
-
-def cryptography_has_x509_v_flag_trusted_first():
-    return [
-        "X509_V_FLAG_TRUSTED_FIRST",
-    ]
-
-
-def cryptography_has_x509_v_flag_partial_chain():
-    return [
-        "X509_V_FLAG_PARTIAL_CHAIN",
-    ]
+    return ["X509_CHECK_FLAG_NEVER_CHECK_SUBJECT"]


 def cryptography_has_set_cert_cb():
     return [
         "SSL_CTX_set_cert_cb",
         "SSL_set_cert_cb",
     ]
@@ -156,26 +82,14 @@

 def cryptography_has_scrypt():
     return [
         "EVP_PBE_scrypt",
     ]


-def cryptography_has_generic_dtls_method():
-    return [
-        "DTLS_method",
-        "DTLS_server_method",
-        "DTLS_client_method",
-        "SSL_OP_NO_DTLSv1",
-        "SSL_OP_NO_DTLSv1_2",
-        "DTLS_set_link_mtu",
-        "DTLS_get_link_min_mtu",
-    ]
-
-
 def cryptography_has_evp_pkey_dhx():
     return [
         "EVP_PKEY_DHX",
     ]


 def cryptography_has_mem_functions():
@@ -236,14 +150,21 @@
 def cryptography_has_ed25519():
     return [
         "NID_ED25519",
         "EVP_PKEY_ED25519",
     ]


+def cryptography_has_poly1305():
+    return [
+        "NID_poly1305",
+        "EVP_PKEY_POLY1305",
+    ]
+
+
 def cryptography_has_oneshot_evp_digest_sign_verify():
     return [
         "EVP_DigestSign",
         "EVP_DigestVerify",
     ]


@@ -258,15 +179,15 @@
         "EVP_PKEY_get1_tls_encodedpoint",
         "EVP_PKEY_set1_tls_encodedpoint",
     ]


 def cryptography_has_fips():
     return [
-        "FIPS_set_mode",
+        "FIPS_mode_set",
         "FIPS_mode",
     ]


 def cryptography_has_ssl_sigalgs():
     return [
         "SSL_CTX_set1_sigalgs_list",
@@ -317,110 +238,108 @@
         "SSL_SESSION_get_max_early_data",
         "SSL_write_early_data",
         "SSL_read_early_data",
         "SSL_CTX_set_max_early_data",
     ]


+def cryptography_has_keylog():
+    return [
+        "SSL_CTX_set_keylog_callback",
+        "SSL_CTX_get_keylog_callback",
+    ]
+
+
 def cryptography_has_raw_key():
     return [
         "EVP_PKEY_new_raw_private_key",
         "EVP_PKEY_new_raw_public_key",
         "EVP_PKEY_get_raw_private_key",
         "EVP_PKEY_get_raw_public_key",
     ]


-def cryptography_has_evp_r_memory_limit_exceeded():
-    return [
-        "EVP_R_MEMORY_LIMIT_EXCEEDED",
-    ]
-
-
 def cryptography_has_engine():
     return [
         "ENGINE_by_id",
         "ENGINE_init",
         "ENGINE_finish",
         "ENGINE_get_default_RAND",
         "ENGINE_set_default_RAND",
         "ENGINE_unregister_RAND",
         "ENGINE_ctrl_cmd",
         "ENGINE_free",
         "ENGINE_get_name",
         "Cryptography_add_osrandom_engine",
+        "ENGINE_ctrl_cmd_string",
+        "ENGINE_load_builtin_engines",
+        "ENGINE_load_private_key",
+        "ENGINE_load_public_key",
+    ]
+
+
+def cryptography_has_verified_chain():
+    return [
+        "SSL_get0_verified_chain",
+    ]
+
+
+def cryptography_has_srtp():
+    return [
+        "SSL_CTX_set_tlsext_use_srtp",
+        "SSL_set_tlsext_use_srtp",
+        "SSL_get_selected_srtp_profile",
     ]


 # This is a mapping of
 # {condition: function-returning-names-dependent-on-that-condition} so we can
 # loop over them and delete unsupported names at runtime. It will be removed
 # when cffi supports #if in cdef. We use functions instead of just a dict of
 # lists so we can use coverage to measure which are used.
 CONDITIONAL_NAMES = {
     "Cryptography_HAS_EC2M": cryptography_has_ec2m,
-    "Cryptography_HAS_EC_1_0_2": cryptography_has_ec_1_0_2,
-    "Cryptography_HAS_SET_ECDH_AUTO": cryptography_has_set_ecdh_auto,
-    "Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR": (
-        cryptography_has_rsa_r_pkcs_decoding_error
-    ),
     "Cryptography_HAS_RSA_OAEP_MD": cryptography_has_rsa_oaep_md,
     "Cryptography_HAS_RSA_OAEP_LABEL": cryptography_has_rsa_oaep_label,
     "Cryptography_HAS_SSL3_METHOD": cryptography_has_ssl3_method,
-    "Cryptography_HAS_ALPN": cryptography_has_alpn,
-    "Cryptography_HAS_COMPRESSION": cryptography_has_compression,
-    "Cryptography_HAS_GET_SERVER_TMP_KEY": cryptography_has_get_server_tmp_key,
-    "Cryptography_HAS_102_VERIFICATION_ERROR_CODES": (
-        cryptography_has_102_verification_error_codes
-    ),
-    "Cryptography_HAS_102_VERIFICATION_PARAMS": (
-        cryptography_has_102_verification_params
-    ),
+    "Cryptography_HAS_102_VERIFICATION": cryptography_has_102_verification,
     "Cryptography_HAS_110_VERIFICATION_PARAMS": (
         cryptography_has_110_verification_params
     ),
-    "Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST": (
-        cryptography_has_x509_v_flag_trusted_first
-    ),
-    "Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN": (
-        cryptography_has_x509_v_flag_partial_chain
-    ),
     "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
     "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
     "Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
     "Cryptography_HAS_LOCKING_CALLBACKS": cryptography_has_locking_callbacks,
     "Cryptography_HAS_SCRYPT": cryptography_has_scrypt,
-    "Cryptography_HAS_GENERIC_DTLS_METHOD": (
-        cryptography_has_generic_dtls_method
-    ),
     "Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx,
     "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions,
     "Cryptography_HAS_SCT": cryptography_has_sct,
     "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": (
         cryptography_has_x509_store_ctx_get_issuer
     ),
     "Cryptography_HAS_X25519": cryptography_has_x25519,
     "Cryptography_HAS_X448": cryptography_has_x448,
     "Cryptography_HAS_ED448": cryptography_has_ed448,
     "Cryptography_HAS_ED25519": cryptography_has_ed25519,
+    "Cryptography_HAS_POLY1305": cryptography_has_poly1305,
     "Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY": (
         cryptography_has_oneshot_evp_digest_sign_verify
     ),
     "Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint": (
         cryptography_has_evp_pkey_get_set_tls_encodedpoint
     ),
     "Cryptography_HAS_FIPS": cryptography_has_fips,
     "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs,
     "Cryptography_HAS_PSK": cryptography_has_psk,
     "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext,
     "Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup,
     "Cryptography_HAS_CIPHER_DETAILS": cryptography_has_cipher_details,
     "Cryptography_HAS_TLSv1_3": cryptography_has_tlsv13,
+    "Cryptography_HAS_KEYLOG": cryptography_has_keylog,
     "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key,
     "Cryptography_HAS_EVP_DIGESTFINAL_XOF": (
         cryptography_has_evp_digestfinal_xof
     ),
-    "Cryptography_HAS_EVP_R_MEMORY_LIMIT_EXCEEDED": (
-        cryptography_has_evp_r_memory_limit_exceeded
-    ),
     "Cryptography_HAS_ENGINE": cryptography_has_engine,
+    "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain,
+    "Cryptography_HAS_SRTP": cryptography_has_srtp,
 }

cryptography/hazmat/primitives/hashes.py

@@ -6,16 +6,19 @@

 import abc

 import six

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HashBackend


 @six.add_metaclass(abc.ABCMeta)
 class HashAlgorithm(object):
     @abc.abstractproperty
     def name(self):
@@ -62,19 +65,20 @@
     """
     An interface for extendable output functions.
     """


 @utils.register_interface(HashContext)
 class Hash(object):
-    def __init__(self, algorithm, backend, ctx=None):
+    def __init__(self, algorithm, backend=None, ctx=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, HashBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HashBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if not isinstance(algorithm, HashAlgorithm):
             raise TypeError("Expected instance of hashes.HashAlgorithm.")
         self._algorithm = algorithm

         self._backend = backend

cryptography/hazmat/primitives/constant_time.py

@@ -1,35 +1,14 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 import hmac
-import warnings

-from cryptography import utils
-from cryptography.hazmat.bindings._constant_time import lib

+def bytes_eq(a, b):
+    if not isinstance(a, bytes) or not isinstance(b, bytes):
+        raise TypeError("a and b must be bytes.")

-if hasattr(hmac, "compare_digest"):
-    def bytes_eq(a, b):
-        if not isinstance(a, bytes) or not isinstance(b, bytes):
-            raise TypeError("a and b must be bytes.")
-
-        return hmac.compare_digest(a, b)
-
-else:
-    warnings.warn(
-        "Support for your Python version is deprecated. The next version of "
-        "cryptography will remove support. Please upgrade to a 2.7.x "
-        "release that supports hmac.compare_digest as soon as possible.",
-        utils.PersistentlyDeprecated2018,
-    )
-
-    def bytes_eq(a, b):
-        if not isinstance(a, bytes) or not isinstance(b, bytes):
-            raise TypeError("a and b must be bytes.")
-
-        return lib.Cryptography_constant_time_bytes_eq(
-            a, len(a), b, len(b)
-        ) == 1
+    return hmac.compare_digest(a, b)

cryptography/hazmat/primitives/cmac.py

@@ -2,33 +2,34 @@
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import CMACBackend
-from cryptography.hazmat.primitives import ciphers, mac
+from cryptography.hazmat.primitives import ciphers


[email protected]_interface(mac.MACContext)
 class CMAC(object):
-    def __init__(self, algorithm, backend, ctx=None):
+    def __init__(self, algorithm, backend=None, ctx=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, CMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement CMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if not isinstance(algorithm, ciphers.BlockCipherAlgorithm):
-            raise TypeError(
-                "Expected instance of BlockCipherAlgorithm."
-            )
+            raise TypeError("Expected instance of BlockCipherAlgorithm.")
         self._algorithm = algorithm

         self._backend = backend
         if ctx is None:
             self._ctx = self._backend.create_cmac_ctx(self._algorithm)
         else:
             self._ctx = ctx
@@ -55,11 +56,9 @@
         ctx, self._ctx = self._ctx, None
         ctx.verify(signature)

     def copy(self):
         if self._ctx is None:
             raise AlreadyFinalized("Context was already finalized.")
         return CMAC(
-            self._algorithm,
-            backend=self._backend,
-            ctx=self._ctx.copy()
+            self._algorithm, backend=self._backend, ctx=self._ctx.copy()
         )

cryptography/hazmat/primitives/padding.py

@@ -36,22 +36,22 @@
         raise ValueError("block_size must be a multiple of 8.")


 def _byte_padding_update(buffer_, data, block_size):
     if buffer_ is None:
         raise AlreadyFinalized("Context was already finalized.")

-    utils._check_bytes("data", data)
+    utils._check_byteslike("data", data)

-    buffer_ += data
+    buffer_ += bytes(data)

     finished_blocks = len(buffer_) // (block_size // 8)

-    result = buffer_[:finished_blocks * (block_size // 8)]
-    buffer_ = buffer_[finished_blocks * (block_size // 8):]
+    result = buffer_[: finished_blocks * (block_size // 8)]
+    buffer_ = buffer_[finished_blocks * (block_size // 8) :]

     return buffer_, result


 def _byte_padding_pad(buffer_, block_size, paddingfn):
     if buffer_ is None:
         raise AlreadyFinalized("Context was already finalized.")
@@ -60,22 +60,22 @@
     return buffer_ + paddingfn(pad_size)


 def _byte_unpadding_update(buffer_, data, block_size):
     if buffer_ is None:
         raise AlreadyFinalized("Context was already finalized.")

-    utils._check_bytes("data", data)
+    utils._check_byteslike("data", data)

-    buffer_ += data
+    buffer_ += bytes(data)

     finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0)

-    result = buffer_[:finished_blocks * (block_size // 8)]
-    buffer_ = buffer_[finished_blocks * (block_size // 8):]
+    result = buffer_[: finished_blocks * (block_size // 8)]
+    buffer_ = buffer_[finished_blocks * (block_size // 8) :]

     return buffer_, result


 def _byte_unpadding_check(buffer_, block_size, checkfn):
     if buffer_ is None:
         raise AlreadyFinalized("Context was already finalized.")
@@ -109,43 +109,46 @@
     def __init__(self, block_size):
         self.block_size = block_size
         # TODO: more copies than necessary, we should use zero-buffer (#193)
         self._buffer = b""

     def update(self, data):
         self._buffer, result = _byte_padding_update(
-            self._buffer, data, self.block_size)
+            self._buffer, data, self.block_size
+        )
         return result

     def _padding(self, size):
         return six.int2byte(size) * size

     def finalize(self):
         result = _byte_padding_pad(
-            self._buffer, self.block_size, self._padding)
+            self._buffer, self.block_size, self._padding
+        )
         self._buffer = None
         return result


 @utils.register_interface(PaddingContext)
 class _PKCS7UnpaddingContext(object):
     def __init__(self, block_size):
         self.block_size = block_size
         # TODO: more copies than necessary, we should use zero-buffer (#193)
         self._buffer = b""

     def update(self, data):
         self._buffer, result = _byte_unpadding_update(
-            self._buffer, data, self.block_size)
+            self._buffer, data, self.block_size
+        )
         return result

     def finalize(self):
         result = _byte_unpadding_check(
-            self._buffer, self.block_size,
-            lib.Cryptography_check_pkcs7_padding)
+            self._buffer, self.block_size, lib.Cryptography_check_pkcs7_padding
+        )
         self._buffer = None
         return result


 class ANSIX923(object):
     def __init__(self, block_size):
         _byte_padding_check(block_size)
@@ -163,38 +166,43 @@
     def __init__(self, block_size):
         self.block_size = block_size
         # TODO: more copies than necessary, we should use zero-buffer (#193)
         self._buffer = b""

     def update(self, data):
         self._buffer, result = _byte_padding_update(
-            self._buffer, data, self.block_size)
+            self._buffer, data, self.block_size
+        )
         return result

     def _padding(self, size):
         return six.int2byte(0) * (size - 1) + six.int2byte(size)

     def finalize(self):
         result = _byte_padding_pad(
-            self._buffer, self.block_size, self._padding)
+            self._buffer, self.block_size, self._padding
+        )
         self._buffer = None
         return result


 @utils.register_interface(PaddingContext)
 class _ANSIX923UnpaddingContext(object):
     def __init__(self, block_size):
         self.block_size = block_size
         # TODO: more copies than necessary, we should use zero-buffer (#193)
         self._buffer = b""

     def update(self, data):
         self._buffer, result = _byte_unpadding_update(
-            self._buffer, data, self.block_size)
+            self._buffer, data, self.block_size
+        )
         return result

     def finalize(self):
         result = _byte_unpadding_check(
-            self._buffer, self.block_size,
-            lib.Cryptography_check_ansix923_padding)
+            self._buffer,
+            self.block_size,
+            lib.Cryptography_check_ansix923_padding,
+        )
         self._buffer = None
         return result

cryptography/hazmat/primitives/hmac.py

@@ -2,28 +2,31 @@
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HMACBackend
-from cryptography.hazmat.primitives import hashes, mac
+from cryptography.hazmat.primitives import hashes


[email protected]_interface(mac.MACContext)
 @utils.register_interface(hashes.HashContext)
 class HMAC(object):
-    def __init__(self, key, algorithm, backend, ctx=None):
+    def __init__(self, key, algorithm, backend=None, ctx=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if not isinstance(algorithm, hashes.HashAlgorithm):
             raise TypeError("Expected instance of hashes.HashAlgorithm.")
         self._algorithm = algorithm

         self._backend = backend
@@ -44,15 +47,15 @@
     def copy(self):
         if self._ctx is None:
             raise AlreadyFinalized("Context was already finalized.")
         return HMAC(
             self._key,
             self.algorithm,
             backend=self._backend,
-            ctx=self._ctx.copy()
+            ctx=self._ctx.copy(),
         )

     def finalize(self):
         if self._ctx is None:
             raise AlreadyFinalized("Context was already finalized.")
         digest = self._ctx.finalize()
         self._ctx = None

cryptography/hazmat/primitives/keywrap.py

@@ -2,14 +2,15 @@
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 import struct

+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.primitives.ciphers import Cipher
 from cryptography.hazmat.primitives.ciphers.algorithms import AES
 from cryptography.hazmat.primitives.ciphers.modes import ECB
 from cryptography.hazmat.primitives.constant_time import bytes_eq


 def _wrap_core(wrapping_key, a, r, backend):
@@ -29,69 +30,75 @@
             r[i] = b[-8:]

     assert encryptor.finalize() == b""

     return a + b"".join(r)


-def aes_key_wrap(wrapping_key, key_to_wrap, backend):
+def aes_key_wrap(wrapping_key, key_to_wrap, backend=None):
+    backend = _get_backend(backend)
     if len(wrapping_key) not in [16, 24, 32]:
         raise ValueError("The wrapping key must be a valid AES key length")

     if len(key_to_wrap) < 16:
         raise ValueError("The key to wrap must be at least 16 bytes")

     if len(key_to_wrap) % 8 != 0:
         raise ValueError("The key to wrap must be a multiple of 8 bytes")

     a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
-    r = [key_to_wrap[i:i + 8] for i in range(0, len(key_to_wrap), 8)]
+    r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
     return _wrap_core(wrapping_key, a, r, backend)


 def _unwrap_core(wrapping_key, a, r, backend):
     # Implement RFC 3394 Key Unwrap - 2.2.2 (index method)
     decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
     n = len(r)
     for j in reversed(range(6)):
         for i in reversed(range(n)):
             # pack/unpack are safe as these are always 64-bit chunks
-            atr = struct.pack(
-                ">Q", struct.unpack(">Q", a)[0] ^ ((n * j) + i + 1)
-            ) + r[i]
+            atr = (
+                struct.pack(
+                    ">Q", struct.unpack(">Q", a)[0] ^ ((n * j) + i + 1)
+                )
+                + r[i]
+            )
             # every decryption operation is a discrete 16 byte chunk so
             # it is safe to reuse the decryptor for the entire operation
             b = decryptor.update(atr)
             a = b[:8]
             r[i] = b[-8:]

     assert decryptor.finalize() == b""
     return a, r


-def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend):
+def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend=None):
+    backend = _get_backend(backend)
     if len(wrapping_key) not in [16, 24, 32]:
         raise ValueError("The wrapping key must be a valid AES key length")

     aiv = b"\xA6\x59\x59\xA6" + struct.pack(">i", len(key_to_wrap))
     # pad the key to wrap if necessary
     pad = (8 - (len(key_to_wrap) % 8)) % 8
     key_to_wrap = key_to_wrap + b"\x00" * pad
     if len(key_to_wrap) == 8:
         # RFC 5649 - 4.1 - exactly 8 octets after padding
         encryptor = Cipher(AES(wrapping_key), ECB(), backend).encryptor()
         b = encryptor.update(aiv + key_to_wrap)
         assert encryptor.finalize() == b""
         return b
     else:
-        r = [key_to_wrap[i:i + 8] for i in range(0, len(key_to_wrap), 8)]
+        r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
         return _wrap_core(wrapping_key, aiv, r, backend)


-def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend):
+def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend=None):
+    backend = _get_backend(backend)
     if len(wrapped_key) < 16:
         raise InvalidUnwrap("Must be at least 16 bytes")

     if len(wrapping_key) not in [16, 24, 32]:
         raise ValueError("The wrapping key must be a valid AES key length")

     if len(wrapped_key) == 16:
@@ -99,53 +106,53 @@
         decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
         b = decryptor.update(wrapped_key)
         assert decryptor.finalize() == b""
         a = b[:8]
         data = b[8:]
         n = 1
     else:
-        r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
+        r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
         encrypted_aiv = r.pop(0)
         n = len(r)
         a, r = _unwrap_core(wrapping_key, encrypted_aiv, r, backend)
         data = b"".join(r)

     # 1) Check that MSB(32,A) = A65959A6.
     # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n.  If so, let
     #    MLI = LSB(32,A).
     # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of
     #    the output data are zero.
     (mli,) = struct.unpack(">I", a[4:])
     b = (8 * n) - mli
     if (
-        not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") or not
-        8 * (n - 1) < mli <= 8 * n or (
-            b != 0 and not bytes_eq(data[-b:], b"\x00" * b)
-        )
+        not bytes_eq(a[:4], b"\xa6\x59\x59\xa6")
+        or not 8 * (n - 1) < mli <= 8 * n
+        or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b))
     ):
         raise InvalidUnwrap()

     if b == 0:
         return data
     else:
         return data[:-b]


-def aes_key_unwrap(wrapping_key, wrapped_key, backend):
+def aes_key_unwrap(wrapping_key, wrapped_key, backend=None):
+    backend = _get_backend(backend)
     if len(wrapped_key) < 24:
         raise InvalidUnwrap("Must be at least 24 bytes")

     if len(wrapped_key) % 8 != 0:
         raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes")

     if len(wrapping_key) not in [16, 24, 32]:
         raise ValueError("The wrapping key must be a valid AES key length")

     aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
-    r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
+    r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
     a = r.pop(0)
     a, r = _unwrap_core(wrapping_key, a, r, backend)
     if not bytes_eq(a, aiv):
         raise InvalidUnwrap()

     return b"".join(r)

cryptography/hazmat/primitives/kdf/hkdf.py

@@ -4,28 +4,33 @@

 from __future__ import absolute_import, division, print_function

 import six

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HMACBackend
 from cryptography.hazmat.primitives import constant_time, hmac
 from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


 @utils.register_interface(KeyDerivationFunction)
 class HKDF(object):
-    def __init__(self, algorithm, length, salt, info, backend):
+    def __init__(self, algorithm, length, salt, info, backend=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         self._algorithm = algorithm

         if salt is None:
             salt = b"\x00" * self._algorithm.digest_size
         else:
@@ -49,32 +54,32 @@
     def verify(self, key_material, expected_key):
         if not constant_time.bytes_eq(self.derive(key_material), expected_key):
             raise InvalidKey


 @utils.register_interface(KeyDerivationFunction)
 class HKDFExpand(object):
-    def __init__(self, algorithm, length, info, backend):
+    def __init__(self, algorithm, length, info, backend=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         self._algorithm = algorithm

         self._backend = backend

         max_length = 255 * algorithm.digest_size

         if length > max_length:
             raise ValueError(
-                "Can not derive keys larger than {} octets.".format(
-                    max_length
-                ))
+                "Can not derive keys larger than {} octets.".format(max_length)
+            )

         self._length = length

         if info is None:
             info = b""
         else:
             utils._check_bytes("info", info)
@@ -91,15 +96,15 @@
             h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)
             h.update(output[-1])
             h.update(self._info)
             h.update(six.int2byte(counter))
             output.append(h.finalize())
             counter += 1

-        return b"".join(output)[:self._length]
+        return b"".join(output)[: self._length]

     def derive(self, key_material):
         utils._check_byteslike("key_material", key_material)
         if self._used:
             raise AlreadyFinalized

         self._used = True

cryptography/hazmat/primitives/kdf/kbkdf.py

@@ -6,16 +6,20 @@

 from enum import Enum

 from six.moves import range

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HMACBackend
 from cryptography.hazmat.primitives import constant_time, hashes, hmac
 from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


 class Mode(Enum):
     CounterMode = "ctr"
@@ -24,58 +28,71 @@
 class CounterLocation(Enum):
     BeforeFixed = "before_fixed"
     AfterFixed = "after_fixed"


 @utils.register_interface(KeyDerivationFunction)
 class KBKDFHMAC(object):
-    def __init__(self, algorithm, mode, length, rlen, llen,
-                 location, label, context, fixed, backend):
+    def __init__(
+        self,
+        algorithm,
+        mode,
+        length,
+        rlen,
+        llen,
+        location,
+        label,
+        context,
+        fixed,
+        backend=None,
+    ):
+        backend = _get_backend(backend)
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if not isinstance(algorithm, hashes.HashAlgorithm):
             raise UnsupportedAlgorithm(
                 "Algorithm supplied is not a supported hash algorithm.",
-                _Reasons.UNSUPPORTED_HASH
+                _Reasons.UNSUPPORTED_HASH,
             )

         if not backend.hmac_supported(algorithm):
             raise UnsupportedAlgorithm(
                 "Algorithm supplied is not a supported hmac algorithm.",
-                _Reasons.UNSUPPORTED_HASH
+                _Reasons.UNSUPPORTED_HASH,
             )

         if not isinstance(mode, Mode):
             raise TypeError("mode must be of type Mode")

         if not isinstance(location, CounterLocation):
             raise TypeError("location must be of type CounterLocation")

         if (label or context) and fixed:
-            raise ValueError("When supplying fixed data, "
-                             "label and context are ignored.")
+            raise ValueError(
+                "When supplying fixed data, " "label and context are ignored."
+            )

         if rlen is None or not self._valid_byte_length(rlen):
             raise ValueError("rlen must be between 1 and 4")

         if llen is None and fixed is None:
             raise ValueError("Please specify an llen")

         if llen is not None and not isinstance(llen, int):
             raise TypeError("llen must be an integer")

         if label is None:
-            label = b''
+            label = b""

         if context is None:
-            context = b''
+            context = b""

         utils._check_bytes("label", label)
         utils._check_bytes("context", context)
         self._algorithm = algorithm
         self._mode = mode
         self._length = length
         self._rlen = rlen
@@ -85,15 +102,15 @@
         self._context = context
         self._backend = backend
         self._used = False
         self._fixed_data = fixed

     def _valid_byte_length(self, value):
         if not isinstance(value, int):
-            raise TypeError('value must be of type int')
+            raise TypeError("value must be of type int")

         value_bin = utils.int_to_bytes(1, value)
         if not 1 <= len(value_bin) <= 4:
             return False
         return True

     def derive(self, key_material):
@@ -102,23 +119,23 @@

         utils._check_byteslike("key_material", key_material)
         self._used = True

         # inverse floor division (equivalent to ceiling)
         rounds = -(-self._length // self._algorithm.digest_size)

-        output = [b'']
+        output = [b""]

         # For counter mode, the number of iterations shall not be
         # larger than 2^r-1, where r <= 32 is the binary length of the counter
         # This ensures that the counter values used as an input to the
         # PRF will not repeat during a particular call to the KDF function.
         r_bin = utils.int_to_bytes(1, self._rlen)
         if rounds > pow(2, len(r_bin) * 8) - 1:
-            raise ValueError('There are too many iterations.')
+            raise ValueError("There are too many iterations.")

         for i in range(1, rounds + 1):
             h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)

             counter = utils.int_to_bytes(i, self._rlen)
             if self._location == CounterLocation.BeforeFixed:
                 h.update(counter)
@@ -126,15 +143,15 @@
             h.update(self._generate_fixed_input())

             if self._location == CounterLocation.AfterFixed:
                 h.update(counter)

             output.append(h.finalize())

-        return b''.join(output)[:self._length]
+        return b"".join(output)[: self._length]

     def _generate_fixed_input(self):
         if self._fixed_data and isinstance(self._fixed_data, bytes):
             return self._fixed_data

         l_val = utils.int_to_bytes(self._length * 8, self._llen)

cryptography/hazmat/primitives/kdf/x963kdf.py

@@ -4,44 +4,50 @@

 from __future__ import absolute_import, division, print_function

 import struct

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HashBackend
 from cryptography.hazmat.primitives import constant_time, hashes
 from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


 def _int_to_u32be(n):
-    return struct.pack('>I', n)
+    return struct.pack(">I", n)


 @utils.register_interface(KeyDerivationFunction)
 class X963KDF(object):
-    def __init__(self, algorithm, length, sharedinfo, backend):
+    def __init__(self, algorithm, length, sharedinfo, backend=None):
+        backend = _get_backend(backend)

         max_len = algorithm.digest_size * (2 ** 32 - 1)
         if length > max_len:
             raise ValueError(
-                "Can not derive keys larger than {} bits.".format(max_len))
+                "Can not derive keys larger than {} bits.".format(max_len)
+            )
         if sharedinfo is not None:
             utils._check_bytes("sharedinfo", sharedinfo)

         self._algorithm = algorithm
         self._length = length
         self._sharedinfo = sharedinfo

         if not isinstance(backend, HashBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HashBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )
         self._backend = backend
         self._used = False

     def derive(self, key_material):
         if self._used:
             raise AlreadyFinalized
@@ -57,12 +63,12 @@
             h.update(_int_to_u32be(counter))
             if self._sharedinfo is not None:
                 h.update(self._sharedinfo)
             output.append(h.finalize())
             outlen += len(output[-1])
             counter += 1

-        return b"".join(output)[:self._length]
+        return b"".join(output)[: self._length]

     def verify(self, key_material, expected_key):
         if not constant_time.bytes_eq(self.derive(key_material), expected_key):
             raise InvalidKey

cryptography/hazmat/primitives/kdf/concatkdf.py

@@ -4,92 +4,98 @@

 from __future__ import absolute_import, division, print_function

 import struct

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HMACBackend
 from cryptography.hazmat.backends.interfaces import HashBackend
 from cryptography.hazmat.primitives import constant_time, hashes, hmac
 from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


 def _int_to_u32be(n):
-    return struct.pack('>I', n)
+    return struct.pack(">I", n)


 def _common_args_checks(algorithm, length, otherinfo):
     max_length = algorithm.digest_size * (2 ** 32 - 1)
     if length > max_length:
         raise ValueError(
-            "Can not derive keys larger than {} bits.".format(
-                max_length
-            ))
+            "Can not derive keys larger than {} bits.".format(max_length)
+        )
     if otherinfo is not None:
         utils._check_bytes("otherinfo", otherinfo)


 def _concatkdf_derive(key_material, length, auxfn, otherinfo):
     utils._check_byteslike("key_material", key_material)
     output = [b""]
     outlen = 0
     counter = 1

-    while (length > outlen):
+    while length > outlen:
         h = auxfn()
         h.update(_int_to_u32be(counter))
         h.update(key_material)
         h.update(otherinfo)
         output.append(h.finalize())
         outlen += len(output[-1])
         counter += 1

     return b"".join(output)[:length]


 @utils.register_interface(KeyDerivationFunction)
 class ConcatKDFHash(object):
-    def __init__(self, algorithm, length, otherinfo, backend):
+    def __init__(self, algorithm, length, otherinfo, backend=None):
+        backend = _get_backend(backend)

         _common_args_checks(algorithm, length, otherinfo)
         self._algorithm = algorithm
         self._length = length
         self._otherinfo = otherinfo
         if self._otherinfo is None:
             self._otherinfo = b""

         if not isinstance(backend, HashBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HashBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )
         self._backend = backend
         self._used = False

     def _hash(self):
         return hashes.Hash(self._algorithm, self._backend)

     def derive(self, key_material):
         if self._used:
             raise AlreadyFinalized
         self._used = True
-        return _concatkdf_derive(key_material, self._length,
-                                 self._hash, self._otherinfo)
+        return _concatkdf_derive(
+            key_material, self._length, self._hash, self._otherinfo
+        )

     def verify(self, key_material, expected_key):
         if not constant_time.bytes_eq(self.derive(key_material), expected_key):
             raise InvalidKey


 @utils.register_interface(KeyDerivationFunction)
 class ConcatKDFHMAC(object):
-    def __init__(self, algorithm, length, salt, otherinfo, backend):
+    def __init__(self, algorithm, length, salt, otherinfo, backend=None):
+        backend = _get_backend(backend)

         _common_args_checks(algorithm, length, otherinfo)
         self._algorithm = algorithm
         self._length = length
         self._otherinfo = otherinfo
         if self._otherinfo is None:
             self._otherinfo = b""
@@ -100,25 +106,26 @@
             utils._check_bytes("salt", salt)

         self._salt = salt

         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )
         self._backend = backend
         self._used = False

     def _hmac(self):
         return hmac.HMAC(self._salt, self._algorithm, self._backend)

     def derive(self, key_material):
         if self._used:
             raise AlreadyFinalized
         self._used = True
-        return _concatkdf_derive(key_material, self._length,
-                                 self._hmac, self._otherinfo)
+        return _concatkdf_derive(
+            key_material, self._length, self._hmac, self._otherinfo
+        )

     def verify(self, key_material, expected_key):
         if not constant_time.bytes_eq(self.derive(key_material), expected_key):
             raise InvalidKey

cryptography/hazmat/primitives/kdf/pbkdf2.py

@@ -2,35 +2,41 @@
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import PBKDF2HMACBackend
 from cryptography.hazmat.primitives import constant_time
 from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


 @utils.register_interface(KeyDerivationFunction)
 class PBKDF2HMAC(object):
-    def __init__(self, algorithm, length, salt, iterations, backend):
+    def __init__(self, algorithm, length, salt, iterations, backend=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, PBKDF2HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement PBKDF2HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if not backend.pbkdf2_hmac_supported(algorithm):
             raise UnsupportedAlgorithm(
                 "{} is not supported for PBKDF2 by this backend.".format(
-                    algorithm.name),
-                _Reasons.UNSUPPORTED_HASH
+                    algorithm.name
+                ),
+                _Reasons.UNSUPPORTED_HASH,
             )
         self._used = False
         self._algorithm = algorithm
         self._length = length
         utils._check_bytes("salt", salt)
         self._salt = salt
         self._iterations = iterations
@@ -43,14 +49,14 @@

         utils._check_byteslike("key_material", key_material)
         return self._backend.derive_pbkdf2_hmac(
             self._algorithm,
             self._length,
             self._salt,
             self._iterations,
-            key_material
+            key_material,
         )

     def verify(self, key_material, expected_key):
         derived_key = self.derive(key_material)
         if not constant_time.bytes_eq(derived_key, expected_key):
             raise InvalidKey("Keys do not match.")

cryptography/hazmat/primitives/kdf/scrypt.py

@@ -4,33 +4,38 @@

 from __future__ import absolute_import, division, print_function

 import sys

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+    AlreadyFinalized,
+    InvalidKey,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import ScryptBackend
 from cryptography.hazmat.primitives import constant_time
 from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


 # This is used by the scrypt tests to skip tests that require more memory
 # than the MEM_LIMIT
 _MEM_LIMIT = sys.maxsize // 2


 @utils.register_interface(KeyDerivationFunction)
 class Scrypt(object):
-    def __init__(self, salt, length, n, r, p, backend):
+    def __init__(self, salt, length, n, r, p, backend=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, ScryptBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement ScryptBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         self._length = length
         utils._check_bytes("salt", salt)
         if n < 2 or (n & (n - 1)) != 0:
             raise ValueError("n must be greater than 1 and be a power of 2.")

cryptography/hazmat/primitives/ciphers/aead.py

@@ -14,15 +14,15 @@
 class ChaCha20Poly1305(object):
     _MAX_SIZE = 2 ** 32

     def __init__(self, key):
         if not backend.aead_cipher_supported(self):
             raise exceptions.UnsupportedAlgorithm(
                 "ChaCha20Poly1305 is not supported by this version of OpenSSL",
-                exceptions._Reasons.UNSUPPORTED_CIPHER
+                exceptions._Reasons.UNSUPPORTED_CIPHER,
             )
         utils._check_byteslike("key", key)

         if len(key) != 32:
             raise ValueError("ChaCha20Poly1305 key must be 32 bytes.")

         self._key = key
@@ -38,26 +38,22 @@
         if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
             # This is OverflowError to match what cffi would raise
             raise OverflowError(
                 "Data or associated data too long. Max 2**32 bytes"
             )

         self._check_params(nonce, data, associated_data)
-        return aead._encrypt(
-            backend, self, nonce, data, associated_data, 16
-        )
+        return aead._encrypt(backend, self, nonce, data, associated_data, 16)

     def decrypt(self, nonce, data, associated_data):
         if associated_data is None:
             associated_data = b""

         self._check_params(nonce, data, associated_data)
-        return aead._decrypt(
-            backend, self, nonce, data, associated_data, 16
-        )
+        return aead._decrypt(backend, self, nonce, data, associated_data, 16)

     def _check_params(self, nonce, data, associated_data):
         utils._check_byteslike("nonce", nonce)
         utils._check_bytes("data", data)
         utils._check_bytes("associated_data", associated_data)
         if len(nonce) != 12:
             raise ValueError("Nonce must be 12 bytes")
@@ -76,20 +72,14 @@
             raise TypeError("tag_length must be an integer")

         if tag_length not in (4, 6, 8, 10, 12, 14, 16):
             raise ValueError("Invalid tag_length")

         self._tag_length = tag_length

-        if not backend.aead_cipher_supported(self):
-            raise exceptions.UnsupportedAlgorithm(
-                "AESCCM is not supported by this version of OpenSSL",
-                exceptions._Reasons.UNSUPPORTED_CIPHER
-            )
-
     @classmethod
     def generate_key(cls, bit_length):
         if not isinstance(bit_length, int):
             raise TypeError("bit_length must be an integer")

         if bit_length not in (128, 192, 256):
             raise ValueError("bit_length must be 128, 192, or 256")
@@ -122,15 +112,15 @@
         )

     def _validate_lengths(self, nonce, data_len):
         # For information about computing this, see
         # https://tools.ietf.org/html/rfc3610#section-2.1
         l_val = 15 - len(nonce)
         if 2 ** (8 * l_val) < data_len:
-            raise ValueError("Nonce too long for data")
+            raise ValueError("Data too long for nonce")

     def _check_params(self, nonce, data, associated_data):
         utils._check_byteslike("nonce", nonce)
         utils._check_bytes("data", data)
         utils._check_bytes("associated_data", associated_data)
         if not 7 <= len(nonce) <= 13:
             raise ValueError("Nonce must be between 7 and 13 bytes")
@@ -163,26 +153,22 @@
         if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
             # This is OverflowError to match what cffi would raise
             raise OverflowError(
                 "Data or associated data too long. Max 2**32 bytes"
             )

         self._check_params(nonce, data, associated_data)
-        return aead._encrypt(
-            backend, self, nonce, data, associated_data, 16
-        )
+        return aead._encrypt(backend, self, nonce, data, associated_data, 16)

     def decrypt(self, nonce, data, associated_data):
         if associated_data is None:
             associated_data = b""

         self._check_params(nonce, data, associated_data)
-        return aead._decrypt(
-            backend, self, nonce, data, associated_data, 16
-        )
+        return aead._decrypt(backend, self, nonce, data, associated_data, 16)

     def _check_params(self, nonce, data, associated_data):
         utils._check_byteslike("nonce", nonce)
         utils._check_bytes("data", data)
         utils._check_bytes("associated_data", associated_data)
         if len(nonce) == 0:
             raise ValueError("Nonce must be at least 1 byte")

cryptography/hazmat/primitives/ciphers/algorithms.py

@@ -2,28 +2,31 @@
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography import utils
 from cryptography.hazmat.primitives.ciphers import (
-    BlockCipherAlgorithm, CipherAlgorithm
+    BlockCipherAlgorithm,
+    CipherAlgorithm,
 )
 from cryptography.hazmat.primitives.ciphers.modes import ModeWithNonce


 def _verify_key_size(algorithm, key):
     # Verify that the key is instance of bytes
     utils._check_byteslike("key", key)

     # Verify that the key size matches the expected key size
     if len(key) * 8 not in algorithm.key_sizes:
-        raise ValueError("Invalid key size ({}) for {}.".format(
-            len(key) * 8, algorithm.name
-        ))
+        raise ValueError(
+            "Invalid key size ({}) for {}.".format(
+                len(key) * 8, algorithm.name
+            )
+        )
     return key


 @utils.register_interface(BlockCipherAlgorithm)
 @utils.register_interface(CipherAlgorithm)
 class AES(object):
     name = "AES"

cryptography/hazmat/primitives/ciphers/base.py

@@ -6,17 +6,21 @@

 import abc

 import six

 from cryptography import utils
 from cryptography.exceptions import (
-    AlreadyFinalized, AlreadyUpdated, NotYetFinalized, UnsupportedAlgorithm,
-    _Reasons
+    AlreadyFinalized,
+    AlreadyUpdated,
+    NotYetFinalized,
+    UnsupportedAlgorithm,
+    _Reasons,
 )
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import CipherBackend
 from cryptography.hazmat.primitives.ciphers import modes


 @six.add_metaclass(abc.ABCMeta)
 class CipherAlgorithm(object):
     @abc.abstractproperty
@@ -90,19 +94,20 @@
         """
         Returns tag bytes. This is only available after encryption is
         finalized.
         """


 class Cipher(object):
-    def __init__(self, algorithm, mode, backend):
+    def __init__(self, algorithm, mode, backend=None):
+        backend = _get_backend(backend)
         if not isinstance(backend, CipherBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement CipherBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if not isinstance(algorithm, CipherAlgorithm):
             raise TypeError("Expected interface of CipherAlgorithm.")

         if mode is not None:
             mode.validate_for_algorithm(algorithm)
@@ -226,10 +231,11 @@


 @utils.register_interface(AEADEncryptionContext)
 class _AEADEncryptionContext(_AEADCipherContext):
     @property
     def tag(self):
         if self._ctx is not None:
-            raise NotYetFinalized("You must finalize encryption before "
-                                  "getting the tag.")
+            raise NotYetFinalized(
+                "You must finalize encryption before " "getting the tag."
+            )
         return self._tag

cryptography/hazmat/primitives/ciphers/modes.py

@@ -68,17 +68,19 @@
         raise ValueError(
             "Only 128, 192, and 256 bit keys are allowed for this AES mode"
         )


 def _check_iv_length(self, algorithm):
     if len(self.initialization_vector) * 8 != algorithm.block_size:
-        raise ValueError("Invalid IV size ({}) for {}.".format(
-            len(self.initialization_vector), self.name
-        ))
+        raise ValueError(
+            "Invalid IV size ({}) for {}.".format(
+                len(self.initialization_vector), self.name
+            )
+        )


 def _check_iv_and_key_length(self, algorithm):
     _check_aes_key_length(self, algorithm)
     _check_iv_length(self, algorithm)


@@ -174,17 +176,19 @@
         self._nonce = nonce

     nonce = utils.read_only_property("_nonce")

     def validate_for_algorithm(self, algorithm):
         _check_aes_key_length(self, algorithm)
         if len(self.nonce) * 8 != algorithm.block_size:
-            raise ValueError("Invalid nonce size ({}) for {}.".format(
-                len(self.nonce), self.name
-            ))
+            raise ValueError(
+                "Invalid nonce size ({}) for {}.".format(
+                    len(self.nonce), self.name
+                )
+            )


 @utils.register_interface(Mode)
 @utils.register_interface(ModeWithInitializationVector)
 @utils.register_interface(ModeWithAuthenticationTag)
 class GCM(object):
     name = "GCM"
@@ -202,15 +206,16 @@
         if tag is not None:
             utils._check_bytes("tag", tag)
             if min_tag_length < 4:
                 raise ValueError("min_tag_length must be >= 4")
             if len(tag) < min_tag_length:
                 raise ValueError(
                     "Authentication tag must be {} bytes or longer.".format(
-                        min_tag_length)
+                        min_tag_length
+                    )
                 )
         self._tag = tag
         self._min_tag_length = min_tag_length

     tag = utils.read_only_property("_tag")
     initialization_vector = utils.read_only_property("_initialization_vector")

cryptography/hazmat/primitives/ciphers/init.py

@@ -1,16 +1,21 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography.hazmat.primitives.ciphers.base import (
-    AEADCipherContext, AEADDecryptionContext, AEADEncryptionContext,
-    BlockCipherAlgorithm, Cipher, CipherAlgorithm, CipherContext
+    AEADCipherContext,
+    AEADDecryptionContext,
+    AEADEncryptionContext,
+    BlockCipherAlgorithm,
+    Cipher,
+    CipherAlgorithm,
+    CipherContext,
 )


 __all__ = [
     "Cipher",
     "CipherAlgorithm",
     "BlockCipherAlgorithm",

cryptography/hazmat/primitives/serialization/pkcs12.py

@@ -1,9 +1,50 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

+from cryptography import x509
+from cryptography.hazmat.backends import _get_backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa

-def load_key_and_certificates(data, password, backend):
+
+def load_key_and_certificates(data, password, backend=None):
+    backend = _get_backend(backend)
     return backend.load_key_and_certificates_from_pkcs12(data, password)
+
+
+def serialize_key_and_certificates(name, key, cert, cas, encryption_algorithm):
+    if key is not None and not isinstance(
+        key,
+        (
+            rsa.RSAPrivateKeyWithSerialization,
+            dsa.DSAPrivateKeyWithSerialization,
+            ec.EllipticCurvePrivateKeyWithSerialization,
+        ),
+    ):
+        raise TypeError("Key must be RSA, DSA, or EllipticCurve private key.")
+    if cert is not None and not isinstance(cert, x509.Certificate):
+        raise TypeError("cert must be a certificate")
+
+    if cas is not None:
+        cas = list(cas)
+        if not all(isinstance(val, x509.Certificate) for val in cas):
+            raise TypeError("all values in cas must be certificates")
+
+    if not isinstance(
+        encryption_algorithm, serialization.KeySerializationEncryption
+    ):
+        raise TypeError(
+            "Key encryption algorithm must be a "
+            "KeySerializationEncryption instance"
+        )
+
+    if key is None and cert is None and not cas:
+        raise ValueError("You must supply at least one of key, cert, or cas")
+
+    backend = _get_backend(None)
+    return backend.serialize_key_and_certificates_to_pkcs12(
+        name, key, cert, cas, encryption_algorithm
+    )

cryptography/hazmat/primitives/serialization/base.py

@@ -6,52 +6,61 @@

 import abc
 from enum import Enum

 import six

 from cryptography import utils
+from cryptography.hazmat.backends import _get_backend


-def load_pem_private_key(data, password, backend):
+def load_pem_private_key(data, password, backend=None):
+    backend = _get_backend(backend)
     return backend.load_pem_private_key(data, password)


-def load_pem_public_key(data, backend):
+def load_pem_public_key(data, backend=None):
+    backend = _get_backend(backend)
     return backend.load_pem_public_key(data)


-def load_pem_parameters(data, backend):
+def load_pem_parameters(data, backend=None):
+    backend = _get_backend(backend)
     return backend.load_pem_parameters(data)


-def load_der_private_key(data, password, backend):
+def load_der_private_key(data, password, backend=None):
+    backend = _get_backend(backend)
     return backend.load_der_private_key(data, password)


-def load_der_public_key(data, backend):
+def load_der_public_key(data, backend=None):
+    backend = _get_backend(backend)
     return backend.load_der_public_key(data)


-def load_der_parameters(data, backend):
+def load_der_parameters(data, backend=None):
+    backend = _get_backend(backend)
     return backend.load_der_parameters(data)


 class Encoding(Enum):
     PEM = "PEM"
     DER = "DER"
     OpenSSH = "OpenSSH"
     Raw = "Raw"
     X962 = "ANSI X9.62"
+    SMIME = "S/MIME"


 class PrivateFormat(Enum):
     PKCS8 = "PKCS8"
     TraditionalOpenSSL = "TraditionalOpenSSL"
     Raw = "Raw"
+    OpenSSH = "OpenSSH"


 class PublicFormat(Enum):
     SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
     PKCS1 = "Raw PKCS#1"
     OpenSSH = "OpenSSH"
     Raw = "Raw"

cryptography/hazmat/primitives/serialization/init.py

@@ -1,26 +1,44 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 from cryptography.hazmat.primitives.serialization.base import (
-    BestAvailableEncryption, Encoding, KeySerializationEncryption,
-    NoEncryption, ParameterFormat, PrivateFormat, PublicFormat,
-    load_der_parameters, load_der_private_key, load_der_public_key,
-    load_pem_parameters, load_pem_private_key, load_pem_public_key,
+    BestAvailableEncryption,
+    Encoding,
+    KeySerializationEncryption,
+    NoEncryption,
+    ParameterFormat,
+    PrivateFormat,
+    PublicFormat,
+    load_der_parameters,
+    load_der_private_key,
+    load_der_public_key,
+    load_pem_parameters,
+    load_pem_private_key,
+    load_pem_public_key,
 )
 from cryptography.hazmat.primitives.serialization.ssh import (
-    load_ssh_public_key
+    load_ssh_private_key,
+    load_ssh_public_key,
 )


-_PEM_DER = (Encoding.PEM, Encoding.DER)
-
 __all__ = [
-    "load_der_parameters", "load_der_private_key", "load_der_public_key",
-    "load_pem_parameters", "load_pem_private_key", "load_pem_public_key",
-    "load_ssh_public_key", "Encoding", "PrivateFormat", "PublicFormat",
-    "ParameterFormat", "KeySerializationEncryption", "BestAvailableEncryption",
+    "load_der_parameters",
+    "load_der_private_key",
+    "load_der_public_key",
+    "load_pem_parameters",
+    "load_pem_private_key",
+    "load_pem_public_key",
+    "load_ssh_private_key",
+    "load_ssh_public_key",
+    "Encoding",
+    "PrivateFormat",
+    "PublicFormat",
+    "ParameterFormat",
+    "KeySerializationEncryption",
+    "BestAvailableEncryption",
     "NoEncryption",
 ]

cryptography/hazmat/primitives/serialization/ssh.py

@@ -1,153 +1,683 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

-import base64
+import binascii
+import os
+import re
 import struct

 import six

 from cryptography import utils
 from cryptography.exceptions import UnsupportedAlgorithm
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa
-
-
-def load_ssh_public_key(data, backend):
-    key_parts = data.split(b' ', 2)
-
-    if len(key_parts) < 2:
-        raise ValueError(
-            'Key is not in the proper format or contains extra data.')
-
-    key_type = key_parts[0]
-
-    if key_type == b'ssh-rsa':
-        loader = _load_ssh_rsa_public_key
-    elif key_type == b'ssh-dss':
-        loader = _load_ssh_dss_public_key
-    elif key_type in [
-        b'ecdsa-sha2-nistp256', b'ecdsa-sha2-nistp384', b'ecdsa-sha2-nistp521',
-    ]:
-        loader = _load_ssh_ecdsa_public_key
-    elif key_type == b'ssh-ed25519':
-        loader = _load_ssh_ed25519_public_key
-    else:
-        raise UnsupportedAlgorithm('Key type is not supported.')
-
-    key_body = key_parts[1]
-
-    try:
-        decoded_data = base64.b64decode(key_body)
-    except TypeError:
-        raise ValueError('Key is not in the proper format.')
-
-    inner_key_type, rest = _ssh_read_next_string(decoded_data)
-
-    if inner_key_type != key_type:
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.primitives.serialization import (
+    Encoding,
+    NoEncryption,
+    PrivateFormat,
+    PublicFormat,
+)
+
+try:
+    from bcrypt import kdf as _bcrypt_kdf
+
+    _bcrypt_supported = True
+except ImportError:
+    _bcrypt_supported = False
+
+    def _bcrypt_kdf(*args, **kwargs):
+        raise UnsupportedAlgorithm("Need bcrypt module")
+
+
+try:
+    from base64 import encodebytes as _base64_encode
+except ImportError:
+    from base64 import encodestring as _base64_encode
+
+_SSH_ED25519 = b"ssh-ed25519"
+_SSH_RSA = b"ssh-rsa"
+_SSH_DSA = b"ssh-dss"
+_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256"
+_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384"
+_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
+_CERT_SUFFIX = b"[email protected]"
+
+_SSH_PUBKEY_RC = re.compile(br"\A(\S+)[ \t]+(\S+)")
+_SK_MAGIC = b"openssh-key-v1\0"
+_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
+_SK_END = b"-----END OPENSSH PRIVATE KEY-----"
+_BCRYPT = b"bcrypt"
+_NONE = b"none"
+_DEFAULT_CIPHER = b"aes256-ctr"
+_DEFAULT_ROUNDS = 16
+_MAX_PASSWORD = 72
+
+# re is only way to work on bytes-like data
+_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL)
+
+# padding for max blocksize
+_PADDING = memoryview(bytearray(range(1, 1 + 16)))
+
+# ciphers that are actually used in key wrapping
+_SSH_CIPHERS = {
+    b"aes256-ctr": (algorithms.AES, 32, modes.CTR, 16),
+    b"aes256-cbc": (algorithms.AES, 32, modes.CBC, 16),
+}
+
+# map local curve name to key type
+_ECDSA_KEY_TYPE = {
+    "secp256r1": _ECDSA_NISTP256,
+    "secp384r1": _ECDSA_NISTP384,
+    "secp521r1": _ECDSA_NISTP521,
+}
+
+_U32 = struct.Struct(b">I")
+_U64 = struct.Struct(b">Q")
+
+
+def _ecdsa_key_type(public_key):
+    """Return SSH key_type and curve_name for private key."""
+    curve = public_key.curve
+    if curve.name not in _ECDSA_KEY_TYPE:
         raise ValueError(
-            'Key header and key body contain different key type values.'
+            "Unsupported curve for ssh private key: %r" % curve.name
         )
-
-    return loader(key_type, rest, backend)
+    return _ECDSA_KEY_TYPE[curve.name]


-def _load_ssh_rsa_public_key(key_type, decoded_data, backend):
-    e, rest = _ssh_read_next_mpint(decoded_data)
-    n, rest = _ssh_read_next_mpint(rest)
+def _ssh_pem_encode(data, prefix=_SK_START + b"\n", suffix=_SK_END + b"\n"):
+    return b"".join([prefix, _base64_encode(data), suffix])

-    if rest:
-        raise ValueError('Key body contains extra bytes.')

-    return rsa.RSAPublicNumbers(e, n).public_key(backend)
+def _check_block_size(data, block_len):
+    """Require data to be full blocks"""
+    if not data or len(data) % block_len != 0:
+        raise ValueError("Corrupt data: missing padding")


-def _load_ssh_dss_public_key(key_type, decoded_data, backend):
-    p, rest = _ssh_read_next_mpint(decoded_data)
-    q, rest = _ssh_read_next_mpint(rest)
-    g, rest = _ssh_read_next_mpint(rest)
-    y, rest = _ssh_read_next_mpint(rest)
+def _check_empty(data):
+    """All data should have been parsed."""
+    if data:
+        raise ValueError("Corrupt data: unparsed data")

-    if rest:
-        raise ValueError('Key body contains extra bytes.')

-    parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
-    public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+def _init_cipher(ciphername, password, salt, rounds, backend):
+    """Generate key + iv and return cipher."""
+    if not password:
+        raise ValueError("Key is password-protected.")

-    return public_numbers.public_key(backend)
+    algo, key_len, mode, iv_len = _SSH_CIPHERS[ciphername]
+    seed = _bcrypt_kdf(password, salt, key_len + iv_len, rounds, True)
+    return Cipher(algo(seed[:key_len]), mode(seed[key_len:]), backend)


-def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend):
-    curve_name, rest = _ssh_read_next_string(decoded_data)
-    data, rest = _ssh_read_next_string(rest)
-
-    if expected_key_type != b"ecdsa-sha2-" + curve_name:
-        raise ValueError(
-            'Key header and key body contain different key type values.'
-        )
+def _get_u32(data):
+    """Uint32"""
+    if len(data) < 4:
+        raise ValueError("Invalid data")
+    return _U32.unpack(data[:4])[0], data[4:]

-    if rest:
-        raise ValueError('Key body contains extra bytes.')

-    curve = {
-        b"nistp256": ec.SECP256R1,
-        b"nistp384": ec.SECP384R1,
-        b"nistp521": ec.SECP521R1,
-    }[curve_name]()
+def _get_u64(data):
+    """Uint64"""
+    if len(data) < 8:
+        raise ValueError("Invalid data")
+    return _U64.unpack(data[:8])[0], data[8:]
+
+
+def _get_sshstr(data):
+    """Bytes with u32 length prefix"""
+    n, data = _get_u32(data)
+    if n > len(data):
+        raise ValueError("Invalid data")
+    return data[:n], data[n:]
+
+
+def _get_mpint(data):
+    """Big integer."""
+    val, data = _get_sshstr(data)
+    if val and six.indexbytes(val, 0) > 0x7F:
+        raise ValueError("Invalid data")
+    return utils.int_from_bytes(val, "big"), data
+
+
+def _to_mpint(val):
+    """Storage format for signed bigint."""
+    if val < 0:
+        raise ValueError("negative mpint not allowed")
+    if not val:
+        return b""
+    nbytes = (val.bit_length() + 8) // 8
+    return utils.int_to_bytes(val, nbytes)
+
+
+class _FragList(object):
+    """Build recursive structure without data copy."""
+
+    def __init__(self, init=None):
+        self.flist = []
+        if init:
+            self.flist.extend(init)
+
+    def put_raw(self, val):
+        """Add plain bytes"""
+        self.flist.append(val)
+
+    def put_u32(self, val):
+        """Big-endian uint32"""
+        self.flist.append(_U32.pack(val))
+
+    def put_sshstr(self, val):
+        """Bytes prefixed with u32 length"""
+        if isinstance(val, (bytes, memoryview, bytearray)):
+            self.put_u32(len(val))
+            self.flist.append(val)
+        else:
+            self.put_u32(val.size())
+            self.flist.extend(val.flist)
+
+    def put_mpint(self, val):
+        """Big-endian bigint prefixed with u32 length"""
+        self.put_sshstr(_to_mpint(val))
+
+    def size(self):
+        """Current number of bytes"""
+        return sum(map(len, self.flist))
+
+    def render(self, dstbuf, pos=0):
+        """Write into bytearray"""
+        for frag in self.flist:
+            flen = len(frag)
+            start, pos = pos, pos + flen
+            dstbuf[start:pos] = frag
+        return pos
+
+    def tobytes(self):
+        """Return as bytes"""
+        buf = memoryview(bytearray(self.size()))
+        self.render(buf)
+        return buf.tobytes()
+
+
+class _SSHFormatRSA(object):
+    """Format for RSA keys.
+
+    Public:
+        mpint e, n
+    Private:
+        mpint n, e, d, iqmp, p, q
+    """

-    if six.indexbytes(data, 0) != 4:
-        raise NotImplementedError(
-            "Compressed elliptic curve points are not supported"
+    def get_public(self, data):
+        """RSA public fields"""
+        e, data = _get_mpint(data)
+        n, data = _get_mpint(data)
+        return (e, n), data
+
+    def load_public(self, key_type, data, backend):
+        """Make RSA public key from data."""
+        (e, n), data = self.get_public(data)
+        public_numbers = rsa.RSAPublicNumbers(e, n)
+        public_key = public_numbers.public_key(backend)
+        return public_key, data
+
+    def load_private(self, data, pubfields, backend):
+        """Make RSA private key from data."""
+        n, data = _get_mpint(data)
+        e, data = _get_mpint(data)
+        d, data = _get_mpint(data)
+        iqmp, data = _get_mpint(data)
+        p, data = _get_mpint(data)
+        q, data = _get_mpint(data)
+
+        if (e, n) != pubfields:
+            raise ValueError("Corrupt data: rsa field mismatch")
+        dmp1 = rsa.rsa_crt_dmp1(d, p)
+        dmq1 = rsa.rsa_crt_dmq1(d, q)
+        public_numbers = rsa.RSAPublicNumbers(e, n)
+        private_numbers = rsa.RSAPrivateNumbers(
+            p, q, d, dmp1, dmq1, iqmp, public_numbers
         )
+        private_key = private_numbers.private_key(backend)
+        return private_key, data

-    return ec.EllipticCurvePublicKey.from_encoded_point(curve, data)
-
-
-def _load_ssh_ed25519_public_key(expected_key_type, decoded_data, backend):
-    data, rest = _ssh_read_next_string(decoded_data)
+    def encode_public(self, public_key, f_pub):
+        """Write RSA public key"""
+        pubn = public_key.public_numbers()
+        f_pub.put_mpint(pubn.e)
+        f_pub.put_mpint(pubn.n)
+
+    def encode_private(self, private_key, f_priv):
+        """Write RSA private key"""
+        private_numbers = private_key.private_numbers()
+        public_numbers = private_numbers.public_numbers
+
+        f_priv.put_mpint(public_numbers.n)
+        f_priv.put_mpint(public_numbers.e)
+
+        f_priv.put_mpint(private_numbers.d)
+        f_priv.put_mpint(private_numbers.iqmp)
+        f_priv.put_mpint(private_numbers.p)
+        f_priv.put_mpint(private_numbers.q)
+
+
+class _SSHFormatDSA(object):
+    """Format for DSA keys.
+
+    Public:
+        mpint p, q, g, y
+    Private:
+        mpint p, q, g, y, x
+    """

-    if rest:
-        raise ValueError('Key body contains extra bytes.')
+    def get_public(self, data):
+        """DSA public fields"""
+        p, data = _get_mpint(data)
+        q, data = _get_mpint(data)
+        g, data = _get_mpint(data)
+        y, data = _get_mpint(data)
+        return (p, q, g, y), data
+
+    def load_public(self, key_type, data, backend):
+        """Make DSA public key from data."""
+        (p, q, g, y), data = self.get_public(data)
+        parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
+        public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+        self._validate(public_numbers)
+        public_key = public_numbers.public_key(backend)
+        return public_key, data
+
+    def load_private(self, data, pubfields, backend):
+        """Make DSA private key from data."""
+        (p, q, g, y), data = self.get_public(data)
+        x, data = _get_mpint(data)
+
+        if (p, q, g, y) != pubfields:
+            raise ValueError("Corrupt data: dsa field mismatch")
+        parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
+        public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+        self._validate(public_numbers)
+        private_numbers = dsa.DSAPrivateNumbers(x, public_numbers)
+        private_key = private_numbers.private_key(backend)
+        return private_key, data
+
+    def encode_public(self, public_key, f_pub):
+        """Write DSA public key"""
+        public_numbers = public_key.public_numbers()
+        parameter_numbers = public_numbers.parameter_numbers
+        self._validate(public_numbers)
+
+        f_pub.put_mpint(parameter_numbers.p)
+        f_pub.put_mpint(parameter_numbers.q)
+        f_pub.put_mpint(parameter_numbers.g)
+        f_pub.put_mpint(public_numbers.y)
+
+    def encode_private(self, private_key, f_priv):
+        """Write DSA private key"""
+        self.encode_public(private_key.public_key(), f_priv)
+        f_priv.put_mpint(private_key.private_numbers().x)
+
+    def _validate(self, public_numbers):
+        parameter_numbers = public_numbers.parameter_numbers
+        if parameter_numbers.p.bit_length() != 1024:
+            raise ValueError("SSH supports only 1024 bit DSA keys")
+
+
+class _SSHFormatECDSA(object):
+    """Format for ECDSA keys.
+
+    Public:
+        str curve
+        bytes point
+    Private:
+        str curve
+        bytes point
+        mpint secret
+    """

-    return ed25519.Ed25519PublicKey.from_public_bytes(data)
+    def __init__(self, ssh_curve_name, curve):
+        self.ssh_curve_name = ssh_curve_name
+        self.curve = curve
+
+    def get_public(self, data):
+        """ECDSA public fields"""
+        curve, data = _get_sshstr(data)
+        point, data = _get_sshstr(data)
+        if curve != self.ssh_curve_name:
+            raise ValueError("Curve name mismatch")
+        if six.indexbytes(point, 0) != 4:
+            raise NotImplementedError("Need uncompressed point")
+        return (curve, point), data
+
+    def load_public(self, key_type, data, backend):
+        """Make ECDSA public key from data."""
+        (curve_name, point), data = self.get_public(data)
+        public_key = ec.EllipticCurvePublicKey.from_encoded_point(
+            self.curve, point.tobytes()
+        )
+        return public_key, data

+    def load_private(self, data, pubfields, backend):
+        """Make ECDSA private key from data."""
+        (curve_name, point), data = self.get_public(data)
+        secret, data = _get_mpint(data)
+
+        if (curve_name, point) != pubfields:
+            raise ValueError("Corrupt data: ecdsa field mismatch")
+        private_key = ec.derive_private_key(secret, self.curve, backend)
+        return private_key, data
+
+    def encode_public(self, public_key, f_pub):
+        """Write ECDSA public key"""
+        point = public_key.public_bytes(
+            Encoding.X962, PublicFormat.UncompressedPoint
+        )
+        f_pub.put_sshstr(self.ssh_curve_name)
+        f_pub.put_sshstr(point)

-def _ssh_read_next_string(data):
+    def encode_private(self, private_key, f_priv):
+        """Write ECDSA private key"""
+        public_key = private_key.public_key()
+        private_numbers = private_key.private_numbers()
+
+        self.encode_public(public_key, f_priv)
+        f_priv.put_mpint(private_numbers.private_value)
+
+
+class _SSHFormatEd25519(object):
+    """Format for Ed25519 keys.
+
+    Public:
+        bytes point
+    Private:
+        bytes point
+        bytes secret_and_point
     """
-    Retrieves the next RFC 4251 string value from the data.

-    While the RFC calls these strings, in Python they are bytes objects.
-    """
-    if len(data) < 4:
-        raise ValueError("Key is not in the proper format")
+    def get_public(self, data):
+        """Ed25519 public fields"""
+        point, data = _get_sshstr(data)
+        return (point,), data
+
+    def load_public(self, key_type, data, backend):
+        """Make Ed25519 public key from data."""
+        (point,), data = self.get_public(data)
+        public_key = ed25519.Ed25519PublicKey.from_public_bytes(
+            point.tobytes()
+        )
+        return public_key, data

-    str_len, = struct.unpack('>I', data[:4])
-    if len(data) < str_len + 4:
-        raise ValueError("Key is not in the proper format")
+    def load_private(self, data, pubfields, backend):
+        """Make Ed25519 private key from data."""
+        (point,), data = self.get_public(data)
+        keypair, data = _get_sshstr(data)
+
+        secret = keypair[:32]
+        point2 = keypair[32:]
+        if point != point2 or (point,) != pubfields:
+            raise ValueError("Corrupt data: ed25519 field mismatch")
+        private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret)
+        return private_key, data
+
+    def encode_public(self, public_key, f_pub):
+        """Write Ed25519 public key"""
+        raw_public_key = public_key.public_bytes(
+            Encoding.Raw, PublicFormat.Raw
+        )
+        f_pub.put_sshstr(raw_public_key)

-    return data[4:4 + str_len], data[4 + str_len:]
+    def encode_private(self, private_key, f_priv):
+        """Write Ed25519 private key"""
+        public_key = private_key.public_key()
+        raw_private_key = private_key.private_bytes(
+            Encoding.Raw, PrivateFormat.Raw, NoEncryption()
+        )
+        raw_public_key = public_key.public_bytes(
+            Encoding.Raw, PublicFormat.Raw
+        )
+        f_keypair = _FragList([raw_private_key, raw_public_key])

+        self.encode_public(public_key, f_priv)
+        f_priv.put_sshstr(f_keypair)

-def _ssh_read_next_mpint(data):
-    """
-    Reads the next mpint from the data.

-    Currently, all mpints are interpreted as unsigned.
-    """
-    mpint_data, rest = _ssh_read_next_string(data)
+_KEY_FORMATS = {
+    _SSH_RSA: _SSHFormatRSA(),
+    _SSH_DSA: _SSHFormatDSA(),
+    _SSH_ED25519: _SSHFormatEd25519(),
+    _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
+    _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
+    _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
+}
+
+
+def _lookup_kformat(key_type):
+    """Return valid format or throw error"""
+    if not isinstance(key_type, bytes):
+        key_type = memoryview(key_type).tobytes()
+    if key_type in _KEY_FORMATS:
+        return _KEY_FORMATS[key_type]
+    raise UnsupportedAlgorithm("Unsupported key type: %r" % key_type)
+
+
+def load_ssh_private_key(data, password, backend=None):
+    """Load private key from OpenSSH custom encoding."""
+    utils._check_byteslike("data", data)
+    backend = _get_backend(backend)
+    if password is not None:
+        utils._check_bytes("password", password)
+
+    m = _PEM_RC.search(data)
+    if not m:
+        raise ValueError("Not OpenSSH private key format")
+    p1 = m.start(1)
+    p2 = m.end(1)
+    data = binascii.a2b_base64(memoryview(data)[p1:p2])
+    if not data.startswith(_SK_MAGIC):
+        raise ValueError("Not OpenSSH private key format")
+    data = memoryview(data)[len(_SK_MAGIC) :]
+
+    # parse header
+    ciphername, data = _get_sshstr(data)
+    kdfname, data = _get_sshstr(data)
+    kdfoptions, data = _get_sshstr(data)
+    nkeys, data = _get_u32(data)
+    if nkeys != 1:
+        raise ValueError("Only one key supported")
+
+    # load public key data
+    pubdata, data = _get_sshstr(data)
+    pub_key_type, pubdata = _get_sshstr(pubdata)
+    kformat = _lookup_kformat(pub_key_type)
+    pubfields, pubdata = kformat.get_public(pubdata)
+    _check_empty(pubdata)
+
+    # load secret data
+    edata, data = _get_sshstr(data)
+    _check_empty(data)
+
+    if (ciphername, kdfname) != (_NONE, _NONE):
+        ciphername = ciphername.tobytes()
+        if ciphername not in _SSH_CIPHERS:
+            raise UnsupportedAlgorithm("Unsupported cipher: %r" % ciphername)
+        if kdfname != _BCRYPT:
+            raise UnsupportedAlgorithm("Unsupported KDF: %r" % kdfname)
+        blklen = _SSH_CIPHERS[ciphername][3]
+        _check_block_size(edata, blklen)
+        salt, kbuf = _get_sshstr(kdfoptions)
+        rounds, kbuf = _get_u32(kbuf)
+        _check_empty(kbuf)
+        ciph = _init_cipher(
+            ciphername, password, salt.tobytes(), rounds, backend
+        )
+        edata = memoryview(ciph.decryptor().update(edata))
+    else:
+        blklen = 8
+        _check_block_size(edata, blklen)
+    ck1, edata = _get_u32(edata)
+    ck2, edata = _get_u32(edata)
+    if ck1 != ck2:
+        raise ValueError("Corrupt data: broken checksum")
+
+    # load per-key struct
+    key_type, edata = _get_sshstr(edata)
+    if key_type != pub_key_type:
+        raise ValueError("Corrupt data: key type mismatch")
+    private_key, edata = kformat.load_private(edata, pubfields, backend)
+    comment, edata = _get_sshstr(edata)
+
+    # yes, SSH does padding check *after* all other parsing is done.
+    # need to follow as it writes zero-byte padding too.
+    if edata != _PADDING[: len(edata)]:
+        raise ValueError("Corrupt data: invalid padding")
+
+    return private_key
+
+
+def serialize_ssh_private_key(private_key, password=None):
+    """Serialize private key with OpenSSH custom encoding."""
+    if password is not None:
+        utils._check_bytes("password", password)
+    if password and len(password) > _MAX_PASSWORD:
+        raise ValueError(
+            "Passwords longer than 72 bytes are not supported by "
+            "OpenSSH private key format"
+        )

-    return (
-        utils.int_from_bytes(mpint_data, byteorder='big', signed=False), rest
-    )
+    if isinstance(private_key, ec.EllipticCurvePrivateKey):
+        key_type = _ecdsa_key_type(private_key.public_key())
+    elif isinstance(private_key, rsa.RSAPrivateKey):
+        key_type = _SSH_RSA
+    elif isinstance(private_key, dsa.DSAPrivateKey):
+        key_type = _SSH_DSA
+    elif isinstance(private_key, ed25519.Ed25519PrivateKey):
+        key_type = _SSH_ED25519
+    else:
+        raise ValueError("Unsupported key type")
+    kformat = _lookup_kformat(key_type)

+    # setup parameters
+    f_kdfoptions = _FragList()
+    if password:
+        ciphername = _DEFAULT_CIPHER
+        blklen = _SSH_CIPHERS[ciphername][3]
+        kdfname = _BCRYPT
+        rounds = _DEFAULT_ROUNDS
+        salt = os.urandom(16)
+        f_kdfoptions.put_sshstr(salt)
+        f_kdfoptions.put_u32(rounds)
+        backend = _get_backend(None)
+        ciph = _init_cipher(ciphername, password, salt, rounds, backend)
+    else:
+        ciphername = kdfname = _NONE
+        blklen = 8
+        ciph = None
+    nkeys = 1
+    checkval = os.urandom(4)
+    comment = b""
+
+    # encode public and private parts together
+    f_public_key = _FragList()
+    f_public_key.put_sshstr(key_type)
+    kformat.encode_public(private_key.public_key(), f_public_key)
+
+    f_secrets = _FragList([checkval, checkval])
+    f_secrets.put_sshstr(key_type)
+    kformat.encode_private(private_key, f_secrets)
+    f_secrets.put_sshstr(comment)
+    f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)])
+
+    # top-level structure
+    f_main = _FragList()
+    f_main.put_raw(_SK_MAGIC)
+    f_main.put_sshstr(ciphername)
+    f_main.put_sshstr(kdfname)
+    f_main.put_sshstr(f_kdfoptions)
+    f_main.put_u32(nkeys)
+    f_main.put_sshstr(f_public_key)
+    f_main.put_sshstr(f_secrets)
+
+    # copy result info bytearray
+    slen = f_secrets.size()
+    mlen = f_main.size()
+    buf = memoryview(bytearray(mlen + blklen))
+    f_main.render(buf)
+    ofs = mlen - slen
+
+    # encrypt in-place
+    if ciph is not None:
+        ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
+
+    txt = _ssh_pem_encode(buf[:mlen])
+    buf[ofs:mlen] = bytearray(slen)
+    return txt
+
+
+def load_ssh_public_key(data, backend=None):
+    """Load public key from OpenSSH one-line format."""
+    backend = _get_backend(backend)
+    utils._check_byteslike("data", data)
+
+    m = _SSH_PUBKEY_RC.match(data)
+    if not m:
+        raise ValueError("Invalid line format")
+    key_type = orig_key_type = m.group(1)
+    key_body = m.group(2)
+    with_cert = False
+    if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]:
+        with_cert = True
+        key_type = key_type[: -len(_CERT_SUFFIX)]
+    kformat = _lookup_kformat(key_type)

-def _ssh_write_string(data):
-    return struct.pack(">I", len(data)) + data
+    try:
+        data = memoryview(binascii.a2b_base64(key_body))
+    except (TypeError, binascii.Error):
+        raise ValueError("Invalid key format")
+
+    inner_key_type, data = _get_sshstr(data)
+    if inner_key_type != orig_key_type:
+        raise ValueError("Invalid key format")
+    if with_cert:
+        nonce, data = _get_sshstr(data)
+    public_key, data = kformat.load_public(key_type, data, backend)
+    if with_cert:
+        serial, data = _get_u64(data)
+        cctype, data = _get_u32(data)
+        key_id, data = _get_sshstr(data)
+        principals, data = _get_sshstr(data)
+        valid_after, data = _get_u64(data)
+        valid_before, data = _get_u64(data)
+        crit_options, data = _get_sshstr(data)
+        extensions, data = _get_sshstr(data)
+        reserved, data = _get_sshstr(data)
+        sig_key, data = _get_sshstr(data)
+        signature, data = _get_sshstr(data)
+    _check_empty(data)
+    return public_key
+
+
+def serialize_ssh_public_key(public_key):
+    """One-line public key format for OpenSSH"""
+    if isinstance(public_key, ec.EllipticCurvePublicKey):
+        key_type = _ecdsa_key_type(public_key)
+    elif isinstance(public_key, rsa.RSAPublicKey):
+        key_type = _SSH_RSA
+    elif isinstance(public_key, dsa.DSAPublicKey):
+        key_type = _SSH_DSA
+    elif isinstance(public_key, ed25519.Ed25519PublicKey):
+        key_type = _SSH_ED25519
+    else:
+        raise ValueError("Unsupported key type")
+    kformat = _lookup_kformat(key_type)

+    f_pub = _FragList()
+    f_pub.put_sshstr(key_type)
+    kformat.encode_public(public_key, f_pub)

-def _ssh_write_mpint(value):
-    data = utils.int_to_bytes(value)
-    if six.indexbytes(data, 0) & 0x80:
-        data = b"\x00" + data
-    return _ssh_write_string(data)
+    pub = binascii.b2a_base64(f_pub.tobytes()).strip()
+    return b"".join([key_type, b" ", pub])

cryptography/hazmat/primitives/twofactor/utils.py

@@ -19,12 +19,15 @@
     if issuer is not None:
         parameters.append(("issuer", issuer))

     parameters.extend(extra_parameters)

     uriparts = {
         "type": type_name,
-        "label": ("%s:%s" % (quote(issuer), quote(account_name)) if issuer
-                  else quote(account_name)),
+        "label": (
+            "%s:%s" % (quote(issuer), quote(account_name))
+            if issuer
+            else quote(account_name)
+        ),
         "parameters": urlencode(parameters),
     }
     return "otpauth://{type}/{label}?{parameters}".format(**uriparts)

cryptography/hazmat/primitives/twofactor/hotp.py

@@ -4,31 +4,32 @@

 from __future__ import absolute_import, division, print_function

 import struct

 import six

-from cryptography.exceptions import (
-    UnsupportedAlgorithm, _Reasons
-)
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HMACBackend
 from cryptography.hazmat.primitives import constant_time, hmac
 from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
 from cryptography.hazmat.primitives.twofactor import InvalidToken
 from cryptography.hazmat.primitives.twofactor.utils import _generate_uri


 class HOTP(object):
-    def __init__(self, key, length, algorithm, backend,
-                 enforce_key_length=True):
+    def __init__(
+        self, key, length, algorithm, backend=None, enforce_key_length=True
+    ):
+        backend = _get_backend(backend)
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         if len(key) < 16 and enforce_key_length is True:
             raise ValueError("Key length has to be at least 128 bits.")

         if not isinstance(length, six.integer_types):
             raise TypeError("Length parameter must be an integer type.")
@@ -55,14 +56,14 @@

     def _dynamic_truncate(self, counter):
         ctx = hmac.HMAC(self._key, self._algorithm, self._backend)
         ctx.update(struct.pack(">Q", counter))
         hmac_value = ctx.finalize()

         offset = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
-        p = hmac_value[offset:offset + 4]
-        return struct.unpack(">I", p)[0] & 0x7fffffff
+        p = hmac_value[offset : offset + 4]
+        return struct.unpack(">I", p)[0] & 0x7FFFFFFF

     def get_provisioning_uri(self, account_name, counter, issuer):
-        return _generate_uri(self, "hotp", account_name, issuer, [
-            ("counter", int(counter)),
-        ])
+        return _generate_uri(
+            self, "hotp", account_name, issuer, [("counter", int(counter))]
+        )

cryptography/hazmat/primitives/twofactor/totp.py

@@ -1,40 +1,51 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

-from cryptography.exceptions import (
-    UnsupportedAlgorithm, _Reasons
-)
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import HMACBackend
 from cryptography.hazmat.primitives import constant_time
 from cryptography.hazmat.primitives.twofactor import InvalidToken
 from cryptography.hazmat.primitives.twofactor.hotp import HOTP
 from cryptography.hazmat.primitives.twofactor.utils import _generate_uri


 class TOTP(object):
-    def __init__(self, key, length, algorithm, time_step, backend,
-                 enforce_key_length=True):
+    def __init__(
+        self,
+        key,
+        length,
+        algorithm,
+        time_step,
+        backend=None,
+        enforce_key_length=True,
+    ):
+        backend = _get_backend(backend)
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
                 "Backend object does not implement HMACBackend.",
-                _Reasons.BACKEND_MISSING_INTERFACE
+                _Reasons.BACKEND_MISSING_INTERFACE,
             )

         self._time_step = time_step
         self._hotp = HOTP(key, length, algorithm, backend, enforce_key_length)

     def generate(self, time):
         counter = int(time / self._time_step)
         return self._hotp.generate(counter)

     def verify(self, totp, time):
         if not constant_time.bytes_eq(self.generate(time), totp):
             raise InvalidToken("Supplied TOTP value does not match.")

     def get_provisioning_uri(self, account_name, issuer):
-        return _generate_uri(self._hotp, "totp", account_name, issuer, [
-            ("period", int(self._time_step)),
-        ])
+        return _generate_uri(
+            self._hotp,
+            "totp",
+            account_name,
+            issuer,
+            [("period", int(self._time_step))],
+        )

cryptography/hazmat/primitives/asymmetric/dsa.py

@@ -5,14 +5,15 @@
 from __future__ import absolute_import, division, print_function

 import abc

 import six

 from cryptography import utils
+from cryptography.hazmat.backends import _get_backend


 @six.add_metaclass(abc.ABCMeta)
 class DSAParameters(object):
     @abc.abstractmethod
     def generate_private_key(self):
         """
@@ -115,25 +116,29 @@
         Verifies the signature of the data.
         """


 DSAPublicKeyWithSerialization = DSAPublicKey


-def generate_parameters(key_size, backend):
+def generate_parameters(key_size, backend=None):
+    backend = _get_backend(backend)
     return backend.generate_dsa_parameters(key_size)


-def generate_private_key(key_size, backend):
+def generate_private_key(key_size, backend=None):
+    backend = _get_backend(backend)
     return backend.generate_dsa_private_key_and_parameters(key_size)


 def _check_dsa_parameters(parameters):
-    if parameters.p.bit_length() not in [1024, 2048, 3072]:
-        raise ValueError("p must be exactly 1024, 2048, or 3072 bits long")
+    if parameters.p.bit_length() not in [1024, 2048, 3072, 4096]:
+        raise ValueError(
+            "p must be exactly 1024, 2048, 3072, or 4096 bits long"
+        )
     if parameters.q.bit_length() not in [160, 224, 256]:
         raise ValueError("q must be exactly 160, 224, or 256 bits long")

     if not (1 < parameters.g < parameters.p):
         raise ValueError("g, p don't satisfy 1 < g < p.")


@@ -146,47 +151,47 @@
     if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p):
         raise ValueError("y must be equal to (g ** x % p).")


 class DSAParameterNumbers(object):
     def __init__(self, p, q, g):
         if (
-            not isinstance(p, six.integer_types) or
-            not isinstance(q, six.integer_types) or
-            not isinstance(g, six.integer_types)
+            not isinstance(p, six.integer_types)
+            or not isinstance(q, six.integer_types)
+            or not isinstance(g, six.integer_types)
         ):
             raise TypeError(
                 "DSAParameterNumbers p, q, and g arguments must be integers."
             )

         self._p = p
         self._q = q
         self._g = g

     p = utils.read_only_property("_p")
     q = utils.read_only_property("_q")
     g = utils.read_only_property("_g")

-    def parameters(self, backend):
+    def parameters(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_dsa_parameter_numbers(self)

     def __eq__(self, other):
         if not isinstance(other, DSAParameterNumbers):
             return NotImplemented

         return self.p == other.p and self.q == other.q and self.g == other.g

     def __ne__(self, other):
         return not self == other

     def __repr__(self):
         return (
-            "<DSAParameterNumbers(p={self.p}, q={self.q}, g={self.g})>".format(
-                self=self
-            )
+            "<DSAParameterNumbers(p={self.p}, q={self.q}, "
+            "g={self.g})>".format(self=self)
         )


 class DSAPublicNumbers(object):
     def __init__(self, y, parameter_numbers):
         if not isinstance(y, six.integer_types):
             raise TypeError("DSAPublicNumbers y argument must be an integer.")
@@ -198,24 +203,25 @@

         self._y = y
         self._parameter_numbers = parameter_numbers

     y = utils.read_only_property("_y")
     parameter_numbers = utils.read_only_property("_parameter_numbers")

-    def public_key(self, backend):
+    def public_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_dsa_public_numbers(self)

     def __eq__(self, other):
         if not isinstance(other, DSAPublicNumbers):
             return NotImplemented

         return (
-            self.y == other.y and
-            self.parameter_numbers == other.parameter_numbers
+            self.y == other.y
+            and self.parameter_numbers == other.parameter_numbers
         )

     def __ne__(self, other):
         return not self == other

     def __repr__(self):
         return (
@@ -235,15 +241,16 @@
             )
         self._public_numbers = public_numbers
         self._x = x

     x = utils.read_only_property("_x")
     public_numbers = utils.read_only_property("_public_numbers")

-    def private_key(self, backend):
+    def private_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_dsa_private_numbers(self)

     def __eq__(self, other):
         if not isinstance(other, DSAPrivateNumbers):
             return NotImplemented

         return (

cryptography/hazmat/primitives/asymmetric/rsa.py

@@ -1,24 +1,26 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 import abc
+
 try:
     # Only available in math in 3.5+
     from math import gcd
 except ImportError:
     from fractions import gcd

 import six

 from cryptography import utils
 from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends import _get_backend
 from cryptography.hazmat.backends.interfaces import RSABackend


 @six.add_metaclass(abc.ABCMeta)
 class RSAPrivateKey(object):
     @abc.abstractmethod
     def signer(self, padding, algorithm):
@@ -104,38 +106,40 @@
         Verifies the signature of the data.
         """


 RSAPublicKeyWithSerialization = RSAPublicKey


-def generate_private_key(public_exponent, key_size, backend):
+def generate_private_key(public_exponent, key_size, backend=None):
+    backend = _get_backend(backend)
     if not isinstance(backend, RSABackend):
         raise UnsupportedAlgorithm(
             "Backend object does not implement RSABackend.",
-            _Reasons.BACKEND_MISSING_INTERFACE
+            _Reasons.BACKEND_MISSING_INTERFACE,
         )

     _verify_rsa_parameters(public_exponent, key_size)
     return backend.generate_rsa_private_key(public_exponent, key_size)


 def _verify_rsa_parameters(public_exponent, key_size):
-    if public_exponent < 3:
-        raise ValueError("public_exponent must be >= 3.")
-
-    if public_exponent & 1 == 0:
-        raise ValueError("public_exponent must be odd.")
+    if public_exponent not in (3, 65537):
+        raise ValueError(
+            "public_exponent must be either 3 (for legacy compatibility) or "
+            "65537. Almost everyone should choose 65537 here!"
+        )

     if key_size < 512:
         raise ValueError("key_size must be at least 512-bits.")


-def _check_private_key_components(p, q, private_exponent, dmp1, dmq1, iqmp,
-                                  public_exponent, modulus):
+def _check_private_key_components(
+    p, q, private_exponent, dmp1, dmq1, iqmp, public_exponent, modulus
+):
     if modulus < 3:
         raise ValueError("modulus must be >= 3.")

     if p >= modulus:
         raise ValueError("p must be < modulus.")

     if q >= modulus:
@@ -180,20 +184,20 @@
         raise ValueError("e must be odd.")


 def _modinv(e, m):
     """
     Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1
     """
-    x1, y1, x2, y2 = 1, 0, 0, 1
+    x1, x2 = 1, 0
     a, b = e, m
     while b > 0:
         q, r = divmod(a, b)
-        xn, yn = x1 - q * x2, y1 - q * y2
-        a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn
+        xn = x1 - q * x2
+        a, b, x1, x2 = b, r, x2, xn
     return x1 % m


 def rsa_crt_iqmp(p, q):
     """
     Compute the CRT (q ** -1) % p value from RSA primes p and q.
     """
@@ -262,23 +266,22 @@
     q, r = divmod(n, p)
     assert r == 0
     p, q = sorted((p, q), reverse=True)
     return (p, q)


 class RSAPrivateNumbers(object):
-    def __init__(self, p, q, d, dmp1, dmq1, iqmp,
-                 public_numbers):
+    def __init__(self, p, q, d, dmp1, dmq1, iqmp, public_numbers):
         if (
-            not isinstance(p, six.integer_types) or
-            not isinstance(q, six.integer_types) or
-            not isinstance(d, six.integer_types) or
-            not isinstance(dmp1, six.integer_types) or
-            not isinstance(dmq1, six.integer_types) or
-            not isinstance(iqmp, six.integer_types)
+            not isinstance(p, six.integer_types)
+            or not isinstance(q, six.integer_types)
+            or not isinstance(d, six.integer_types)
+            or not isinstance(dmp1, six.integer_types)
+            or not isinstance(dmq1, six.integer_types)
+            or not isinstance(iqmp, six.integer_types)
         ):
             raise TypeError(
                 "RSAPrivateNumbers p, q, d, dmp1, dmq1, iqmp arguments must"
                 " all be an integers."
             )

         if not isinstance(public_numbers, RSAPublicNumbers):
@@ -299,61 +302,64 @@
     q = utils.read_only_property("_q")
     d = utils.read_only_property("_d")
     dmp1 = utils.read_only_property("_dmp1")
     dmq1 = utils.read_only_property("_dmq1")
     iqmp = utils.read_only_property("_iqmp")
     public_numbers = utils.read_only_property("_public_numbers")

-    def private_key(self, backend):
+    def private_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_rsa_private_numbers(self)

     def __eq__(self, other):
         if not isinstance(other, RSAPrivateNumbers):
             return NotImplemented

         return (
-            self.p == other.p and
-            self.q == other.q and
-            self.d == other.d and
-            self.dmp1 == other.dmp1 and
-            self.dmq1 == other.dmq1 and
-            self.iqmp == other.iqmp and
-            self.public_numbers == other.public_numbers
+            self.p == other.p
+            and self.q == other.q
+            and self.d == other.d
+            and self.dmp1 == other.dmp1
+            and self.dmq1 == other.dmq1
+            and self.iqmp == other.iqmp
+            and self.public_numbers == other.public_numbers
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
-        return hash((
-            self.p,
-            self.q,
-            self.d,
-            self.dmp1,
-            self.dmq1,
-            self.iqmp,
-            self.public_numbers,
-        ))
+        return hash(
+            (
+                self.p,
+                self.q,
+                self.d,
+                self.dmp1,
+                self.dmq1,
+                self.iqmp,
+                self.public_numbers,
+            )
+        )


 class RSAPublicNumbers(object):
     def __init__(self, e, n):
-        if (
-            not isinstance(e, six.integer_types) or
-            not isinstance(n, six.integer_types)
+        if not isinstance(e, six.integer_types) or not isinstance(
+            n, six.integer_types
         ):
             raise TypeError("RSAPublicNumbers arguments must be integers.")

         self._e = e
         self._n = n

     e = utils.read_only_property("_e")
     n = utils.read_only_property("_n")

-    def public_key(self, backend):
+    def public_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_rsa_public_numbers(self)

     def __repr__(self):
         return "<RSAPublicNumbers(e={0.e}, n={0.n})>".format(self)

     def __eq__(self, other):
         if not isinstance(other, RSAPublicNumbers):

cryptography/hazmat/primitives/asymmetric/ed448.py

@@ -12,18 +12,19 @@


 @six.add_metaclass(abc.ABCMeta)
 class Ed448PublicKey(object):
     @classmethod
     def from_public_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.ed448_supported():
             raise UnsupportedAlgorithm(
                 "ed448 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
             )

         return backend.ed448_load_public_bytes(data)

     @abc.abstractmethod
     def public_bytes(self, encoding, format):
         """
@@ -38,28 +39,30 @@


 @six.add_metaclass(abc.ABCMeta)
 class Ed448PrivateKey(object):
     @classmethod
     def generate(cls):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.ed448_supported():
             raise UnsupportedAlgorithm(
                 "ed448 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
             )
         return backend.ed448_generate_key()

     @classmethod
     def from_private_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.ed448_supported():
             raise UnsupportedAlgorithm(
                 "ed448 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
             )

         return backend.ed448_load_private_bytes(data)

     @abc.abstractmethod
     def public_key(self):
         """

cryptography/hazmat/primitives/asymmetric/x25519.py

@@ -12,48 +12,51 @@


 @six.add_metaclass(abc.ABCMeta)
 class X25519PublicKey(object):
     @classmethod
     def from_public_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.x25519_supported():
             raise UnsupportedAlgorithm(
                 "X25519 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )

         return backend.x25519_load_public_bytes(data)

     @abc.abstractmethod
-    def public_bytes(self, encoding=None, format=None):
+    def public_bytes(self, encoding, format):
         """
         The serialized bytes of the public key.
         """


 @six.add_metaclass(abc.ABCMeta)
 class X25519PrivateKey(object):
     @classmethod
     def generate(cls):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.x25519_supported():
             raise UnsupportedAlgorithm(
                 "X25519 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )
         return backend.x25519_generate_key()

     @classmethod
     def from_private_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.x25519_supported():
             raise UnsupportedAlgorithm(
                 "X25519 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )

         return backend.x25519_load_private_bytes(data)

     @abc.abstractmethod
     def public_key(self):
         """

cryptography/hazmat/primitives/asymmetric/ed25519.py

@@ -16,18 +16,19 @@


 @six.add_metaclass(abc.ABCMeta)
 class Ed25519PublicKey(object):
     @classmethod
     def from_public_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.ed25519_supported():
             raise UnsupportedAlgorithm(
                 "ed25519 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
             )

         return backend.ed25519_load_public_bytes(data)

     @abc.abstractmethod
     def public_bytes(self, encoding, format):
         """
@@ -42,29 +43,31 @@


 @six.add_metaclass(abc.ABCMeta)
 class Ed25519PrivateKey(object):
     @classmethod
     def generate(cls):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.ed25519_supported():
             raise UnsupportedAlgorithm(
                 "ed25519 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
             )

         return backend.ed25519_generate_key()

     @classmethod
     def from_private_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.ed25519_supported():
             raise UnsupportedAlgorithm(
                 "ed25519 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
             )

         return backend.ed25519_load_private_bytes(data)

     @abc.abstractmethod
     def public_key(self):
         """

cryptography/hazmat/primitives/asymmetric/dh.py

@@ -5,87 +5,92 @@
 from __future__ import absolute_import, division, print_function

 import abc

 import six

 from cryptography import utils
+from cryptography.hazmat.backends import _get_backend


-def generate_parameters(generator, key_size, backend):
+def generate_parameters(generator, key_size, backend=None):
+    backend = _get_backend(backend)
     return backend.generate_dh_parameters(generator, key_size)


 class DHPrivateNumbers(object):
     def __init__(self, x, public_numbers):
         if not isinstance(x, six.integer_types):
             raise TypeError("x must be an integer.")

         if not isinstance(public_numbers, DHPublicNumbers):
-            raise TypeError("public_numbers must be an instance of "
-                            "DHPublicNumbers.")
+            raise TypeError(
+                "public_numbers must be an instance of " "DHPublicNumbers."
+            )

         self._x = x
         self._public_numbers = public_numbers

     def __eq__(self, other):
         if not isinstance(other, DHPrivateNumbers):
             return NotImplemented

         return (
-            self._x == other._x and
-            self._public_numbers == other._public_numbers
+            self._x == other._x
+            and self._public_numbers == other._public_numbers
         )

     def __ne__(self, other):
         return not self == other

-    def private_key(self, backend):
+    def private_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_dh_private_numbers(self)

     public_numbers = utils.read_only_property("_public_numbers")
     x = utils.read_only_property("_x")


 class DHPublicNumbers(object):
     def __init__(self, y, parameter_numbers):
         if not isinstance(y, six.integer_types):
             raise TypeError("y must be an integer.")

         if not isinstance(parameter_numbers, DHParameterNumbers):
             raise TypeError(
-                "parameters must be an instance of DHParameterNumbers.")
+                "parameters must be an instance of DHParameterNumbers."
+            )

         self._y = y
         self._parameter_numbers = parameter_numbers

     def __eq__(self, other):
         if not isinstance(other, DHPublicNumbers):
             return NotImplemented

         return (
-            self._y == other._y and
-            self._parameter_numbers == other._parameter_numbers
+            self._y == other._y
+            and self._parameter_numbers == other._parameter_numbers
         )

     def __ne__(self, other):
         return not self == other

-    def public_key(self, backend):
+    def public_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_dh_public_numbers(self)

     y = utils.read_only_property("_y")
     parameter_numbers = utils.read_only_property("_parameter_numbers")


 class DHParameterNumbers(object):
     def __init__(self, p, g, q=None):
-        if (
-            not isinstance(p, six.integer_types) or
-            not isinstance(g, six.integer_types)
+        if not isinstance(p, six.integer_types) or not isinstance(
+            g, six.integer_types
         ):
             raise TypeError("p and g must be integers")
         if q is not None and not isinstance(q, six.integer_types):
             raise TypeError("q must be integer or None")

         if g < 2:
             raise ValueError("DH generator must be 2 or greater")
@@ -95,23 +100,22 @@
         self._q = q

     def __eq__(self, other):
         if not isinstance(other, DHParameterNumbers):
             return NotImplemented

         return (
-            self._p == other._p and
-            self._g == other._g and
-            self._q == other._q
+            self._p == other._p and self._g == other._g and self._q == other._q
         )

     def __ne__(self, other):
         return not self == other

-    def parameters(self, backend):
+    def parameters(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_dh_parameter_numbers(self)

     p = utils.read_only_property("_p")
     g = utils.read_only_property("_g")
     q = utils.read_only_property("_q")

cryptography/hazmat/primitives/asymmetric/utils.py

@@ -1,34 +1,37 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

-from asn1crypto.algos import DSASignature
-
-import six
-
 from cryptography import utils
+from cryptography.hazmat._der import (
+    DERReader,
+    INTEGER,
+    SEQUENCE,
+    encode_der,
+    encode_der_integer,
+)
 from cryptography.hazmat.primitives import hashes


 def decode_dss_signature(signature):
-    data = DSASignature.load(signature, strict=True).native
-    return data['r'], data['s']
+    with DERReader(signature).read_single_element(SEQUENCE) as seq:
+        r = seq.read_element(INTEGER).as_integer()
+        s = seq.read_element(INTEGER).as_integer()
+        return r, s


 def encode_dss_signature(r, s):
-    if (
-        not isinstance(r, six.integer_types) or
-        not isinstance(s, six.integer_types)
-    ):
-        raise ValueError("Both r and s must be integers")
-
-    return DSASignature({'r': r, 's': s}).dump()
+    return encode_der(
+        SEQUENCE,
+        encode_der(INTEGER, encode_der_integer(r)),
+        encode_der(INTEGER, encode_der_integer(s)),
+    )


 class Prehashed(object):
     def __init__(self, algorithm):
         if not isinstance(algorithm, hashes.HashAlgorithm):
             raise TypeError("Expected instance of HashAlgorithm.")

cryptography/hazmat/primitives/asymmetric/padding.py

@@ -1,15 +1,14 @@
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.

 from __future__ import absolute_import, division, print_function

 import abc
-import math

 import six

 from cryptography import utils
 from cryptography.hazmat.primitives import hashes
 from cryptography.hazmat.primitives.asymmetric import rsa

@@ -32,16 +31,18 @@
 class PSS(object):
     MAX_LENGTH = object()
     name = "EMSA-PSS"

     def __init__(self, mgf, salt_length):
         self._mgf = mgf

-        if (not isinstance(salt_length, six.integer_types) and
-                salt_length is not self.MAX_LENGTH):
+        if (
+            not isinstance(salt_length, six.integer_types)
+            and salt_length is not self.MAX_LENGTH
+        ):
             raise TypeError("salt_length must be an integer.")

         if salt_length is not self.MAX_LENGTH and salt_length < 0:
             raise ValueError("salt_length must be zero or greater.")

         self._salt_length = salt_length

@@ -69,11 +70,11 @@
         self._algorithm = algorithm


 def calculate_max_pss_salt_length(key, hash_algorithm):
     if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
         raise TypeError("key must be an RSA public or private key")
     # bit length - 1 per RFC 3447
-    emlen = int(math.ceil((key.key_size - 1) / 8.0))
+    emlen = (key.key_size + 6) // 8
     salt_length = emlen - hash_algorithm.digest_size - 2
     assert salt_length >= 0
     return salt_length

cryptography/hazmat/primitives/asymmetric/x448.py

@@ -12,18 +12,19 @@


 @six.add_metaclass(abc.ABCMeta)
 class X448PublicKey(object):
     @classmethod
     def from_public_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.x448_supported():
             raise UnsupportedAlgorithm(
                 "X448 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )

         return backend.x448_load_public_bytes(data)

     @abc.abstractmethod
     def public_bytes(self, encoding, format):
         """
@@ -32,28 +33,30 @@


 @six.add_metaclass(abc.ABCMeta)
 class X448PrivateKey(object):
     @classmethod
     def generate(cls):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.x448_supported():
             raise UnsupportedAlgorithm(
                 "X448 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )
         return backend.x448_generate_key()

     @classmethod
     def from_private_bytes(cls, data):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not backend.x448_supported():
             raise UnsupportedAlgorithm(
                 "X448 is not supported by this version of OpenSSL.",
-                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
             )

         return backend.x448_load_private_bytes(data)

     @abc.abstractmethod
     def public_key(self):
         """

cryptography/hazmat/primitives/asymmetric/ec.py

@@ -7,14 +7,15 @@
 import abc
 import warnings

 import six

 from cryptography import utils
 from cryptography.hazmat._oid import ObjectIdentifier
+from cryptography.hazmat.backends import _get_backend


 class EllipticCurveOID(object):
     SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1")
     SECP224R1 = ObjectIdentifier("1.3.132.0.33")
     SECP256K1 = ObjectIdentifier("1.3.132.0.10")
     SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7")
@@ -162,14 +163,15 @@
         if len(data) == 0:
             raise ValueError("data must not be an empty byte string")

         if six.indexbytes(data, 0) not in [0x02, 0x03, 0x04]:
             raise ValueError("Unsupported elliptic curve point type")

         from cryptography.hazmat.backends.openssl.backend import backend
+
         return backend.load_elliptic_curve_public_bytes(curve, data)


 EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey


 @utils.register_interface(EllipticCurve)
@@ -285,137 +287,136 @@
     name = "brainpoolP512r1"
     key_size = 512


 _CURVE_TYPES = {
     "prime192v1": SECP192R1,
     "prime256v1": SECP256R1,
-
     "secp192r1": SECP192R1,
     "secp224r1": SECP224R1,
     "secp256r1": SECP256R1,
     "secp384r1": SECP384R1,
     "secp521r1": SECP521R1,
     "secp256k1": SECP256K1,
-
     "sect163k1": SECT163K1,
     "sect233k1": SECT233K1,
     "sect283k1": SECT283K1,
     "sect409k1": SECT409K1,
     "sect571k1": SECT571K1,
-
     "sect163r2": SECT163R2,
     "sect233r1": SECT233R1,
     "sect283r1": SECT283R1,
     "sect409r1": SECT409R1,
     "sect571r1": SECT571R1,
-
     "brainpoolP256r1": BrainpoolP256R1,
     "brainpoolP384r1": BrainpoolP384R1,
     "brainpoolP512r1": BrainpoolP512R1,
 }


 @utils.register_interface(EllipticCurveSignatureAlgorithm)
 class ECDSA(object):
     def __init__(self, algorithm):
         self._algorithm = algorithm

     algorithm = utils.read_only_property("_algorithm")


-def generate_private_key(curve, backend):
+def generate_private_key(curve, backend=None):
+    backend = _get_backend(backend)
     return backend.generate_elliptic_curve_private_key(curve)


-def derive_private_key(private_value, curve, backend):
+def derive_private_key(private_value, curve, backend=None):
+    backend = _get_backend(backend)
     if not isinstance(private_value, six.integer_types):
         raise TypeError("private_value must be an integer type.")

     if private_value <= 0:
         raise ValueError("private_value must be a positive integer.")

     if not isinstance(curve, EllipticCurve):
         raise TypeError("curve must provide the EllipticCurve interface.")

     return backend.derive_elliptic_curve_private_key(private_value, curve)


 class EllipticCurvePublicNumbers(object):
     def __init__(self, x, y, curve):
-        if (
-            not isinstance(x, six.integer_types) or
-            not isinstance(y, six.integer_types)
+        if not isinstance(x, six.integer_types) or not isinstance(
+            y, six.integer_types
         ):
             raise TypeError("x and y must be integers.")

         if not isinstance(curve, EllipticCurve):
             raise TypeError("curve must provide the EllipticCurve interface.")

         self._y = y
         self._x = x
         self._curve = curve

-    def public_key(self, backend):
+    def public_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_elliptic_curve_public_numbers(self)

     def encode_point(self):
         warnings.warn(
             "encode_point has been deprecated on EllipticCurvePublicNumbers"
             " and will be removed in a future version. Please use "
             "EllipticCurvePublicKey.public_bytes to obtain both "
             "compressed and uncompressed point encoding.",
-            utils.DeprecatedIn25,
+            utils.PersistentlyDeprecated2019,
             stacklevel=2,
         )
         # key_size is in bits. Convert to bytes and round up
         byte_length = (self.curve.key_size + 7) // 8
         return (
-            b'\x04' + utils.int_to_bytes(self.x, byte_length) +
-            utils.int_to_bytes(self.y, byte_length)
+            b"\x04"
+            + utils.int_to_bytes(self.x, byte_length)
+            + utils.int_to_bytes(self.y, byte_length)
         )

     @classmethod
     def from_encoded_point(cls, curve, data):
         if not isinstance(curve, EllipticCurve):
             raise TypeError("curve must be an EllipticCurve instance")

         warnings.warn(
             "Support for unsafe construction of public numbers from "
             "encoded data will be removed in a future version. "
             "Please use EllipticCurvePublicKey.from_encoded_point",
-            utils.DeprecatedIn25,
+            utils.PersistentlyDeprecated2019,
             stacklevel=2,
         )

-        if data.startswith(b'\x04'):
+        if data.startswith(b"\x04"):
             # key_size is in bits. Convert to bytes and round up
             byte_length = (curve.key_size + 7) // 8
             if len(data) == 2 * byte_length + 1:
-                x = utils.int_from_bytes(data[1:byte_length + 1], 'big')
-                y = utils.int_from_bytes(data[byte_length + 1:], 'big')
+                x = utils.int_from_bytes(data[1 : byte_length + 1], "big")
+                y = utils.int_from_bytes(data[byte_length + 1 :], "big")
                 return cls(x, y, curve)
             else:
-                raise ValueError('Invalid elliptic curve point data length')
+                raise ValueError("Invalid elliptic curve point data length")
         else:
-            raise ValueError('Unsupported elliptic curve point type')
+            raise ValueError("Unsupported elliptic curve point type")

     curve = utils.read_only_property("_curve")
     x = utils.read_only_property("_x")
     y = utils.read_only_property("_y")

     def __eq__(self, other):
         if not isinstance(other, EllipticCurvePublicNumbers):
             return NotImplemented

         return (
-            self.x == other.x and
-            self.y == other.y and
-            self.curve.name == other.curve.name and
-            self.curve.key_size == other.curve.key_size
+            self.x == other.x
+            and self.y == other.y
+            and self.curve.name == other.curve.name
+            and self.curve.key_size == other.curve.key_size
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash((self.x, self.y, self.curve.name, self.curve.key_size))
@@ -437,27 +438,28 @@
                 "public_numbers must be an EllipticCurvePublicNumbers "
                 "instance."
             )

         self._private_value = private_value
         self._public_numbers = public_numbers

-    def private_key(self, backend):
+    def private_key(self, backend=None):
+        backend = _get_backend(backend)
         return backend.load_elliptic_curve_private_numbers(self)

     private_value = utils.read_only_property("_private_value")
     public_numbers = utils.read_only_property("_public_numbers")

     def __eq__(self, other):
         if not isinstance(other, EllipticCurvePrivateNumbers):
             return NotImplemented

         return (
-            self.private_value == other.private_value and
-            self.public_numbers == other.public_numbers
+            self.private_value == other.private_value
+            and self.public_numbers == other.public_numbers
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash((self.private_value, self.public_numbers))

cryptography/x509/ocsp.py

@@ -9,15 +9,17 @@
 from enum import Enum

 import six

 from cryptography import x509
 from cryptography.hazmat.primitives import hashes
 from cryptography.x509.base import (
-    _EARLIEST_UTC_TIME, _convert_to_naive_utc_time, _reject_duplicate_extension
+    _EARLIEST_UTC_TIME,
+    _convert_to_naive_utc_time,
+    _reject_duplicate_extension,
 )


 _OIDS_TO_HASH = {
     "1.3.14.3.2.26": hashes.SHA1(),
     "2.16.840.1.101.3.4.2.4": hashes.SHA224(),
     "2.16.840.1.101.3.4.2.1": hashes.SHA256(),
@@ -36,18 +38,21 @@
     MALFORMED_REQUEST = 1
     INTERNAL_ERROR = 2
     TRY_LATER = 3
     SIG_REQUIRED = 5
     UNAUTHORIZED = 6


-_RESPONSE_STATUS_TO_ENUM = dict((x.value, x) for x in OCSPResponseStatus)
+_RESPONSE_STATUS_TO_ENUM = {x.value: x for x in OCSPResponseStatus}
 _ALLOWED_HASHES = (
-    hashes.SHA1, hashes.SHA224, hashes.SHA256,
-    hashes.SHA384, hashes.SHA512
+    hashes.SHA1,
+    hashes.SHA224,
+    hashes.SHA256,
+    hashes.SHA384,
+    hashes.SHA512,
 )


 def _verify_algorithm(algorithm):
     if not isinstance(algorithm, _ALLOWED_HASHES):
         raise ValueError(
             "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"
@@ -56,40 +61,41 @@

 class OCSPCertStatus(Enum):
     GOOD = 0
     REVOKED = 1
     UNKNOWN = 2


-_CERT_STATUS_TO_ENUM = dict((x.value, x) for x in OCSPCertStatus)
+_CERT_STATUS_TO_ENUM = {x.value: x for x in OCSPCertStatus}


 def load_der_ocsp_request(data):
     from cryptography.hazmat.backends.openssl.backend import backend
+
     return backend.load_der_ocsp_request(data)


 def load_der_ocsp_response(data):
     from cryptography.hazmat.backends.openssl.backend import backend
+
     return backend.load_der_ocsp_response(data)


 class OCSPRequestBuilder(object):
     def __init__(self, request=None, extensions=[]):
         self._request = request
         self._extensions = extensions

     def add_certificate(self, cert, issuer, algorithm):
         if self._request is not None:
             raise ValueError("Only one certificate can be added to a request")

         _verify_algorithm(algorithm)
-        if (
-            not isinstance(cert, x509.Certificate) or
-            not isinstance(issuer, x509.Certificate)
+        if not isinstance(cert, x509.Certificate) or not isinstance(
+            issuer, x509.Certificate
         ):
             raise TypeError("cert and issuer must be a Certificate")

         return OCSPRequestBuilder((cert, issuer, algorithm), self._extensions)

     def add_extension(self, extension, critical):
         if not isinstance(extension, x509.ExtensionType):
@@ -100,35 +106,43 @@

         return OCSPRequestBuilder(
             self._request, self._extensions + [extension]
         )

     def build(self):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if self._request is None:
             raise ValueError("You must add a certificate before building")

         return backend.create_ocsp_request(self)


 class _SingleResponse(object):
-    def __init__(self, cert, issuer, algorithm, cert_status, this_update,
-                 next_update, revocation_time, revocation_reason):
-        if (
-            not isinstance(cert, x509.Certificate) or
-            not isinstance(issuer, x509.Certificate)
+    def __init__(
+        self,
+        cert,
+        issuer,
+        algorithm,
+        cert_status,
+        this_update,
+        next_update,
+        revocation_time,
+        revocation_reason,
+    ):
+        if not isinstance(cert, x509.Certificate) or not isinstance(
+            issuer, x509.Certificate
         ):
             raise TypeError("cert and issuer must be a Certificate")

         _verify_algorithm(algorithm)
         if not isinstance(this_update, datetime.datetime):
             raise TypeError("this_update must be a datetime object")
-        if (
-            next_update is not None and
-            not isinstance(next_update, datetime.datetime)
+        if next_update is not None and not isinstance(
+            next_update, datetime.datetime
         ):
             raise TypeError("next_update must be a datetime object or None")

         self._cert = cert
         self._issuer = issuer
         self._algorithm = algorithm
         self._this_update = this_update
@@ -151,110 +165,134 @@
                 )
         else:
             if not isinstance(revocation_time, datetime.datetime):
                 raise TypeError("revocation_time must be a datetime object")

             revocation_time = _convert_to_naive_utc_time(revocation_time)
             if revocation_time < _EARLIEST_UTC_TIME:
-                raise ValueError('The revocation_time must be on or after'
-                                 ' 1950 January 1.')
+                raise ValueError(
+                    "The revocation_time must be on or after"
+                    " 1950 January 1."
+                )

-            if (
-                revocation_reason is not None and
-                not isinstance(revocation_reason, x509.ReasonFlags)
+            if revocation_reason is not None and not isinstance(
+                revocation_reason, x509.ReasonFlags
             ):
                 raise TypeError(
                     "revocation_reason must be an item from the ReasonFlags "
                     "enum or None"
                 )

         self._cert_status = cert_status
         self._revocation_time = revocation_time
         self._revocation_reason = revocation_reason


 class OCSPResponseBuilder(object):
-    def __init__(self, response=None, responder_id=None, certs=None,
-                 extensions=[]):
+    def __init__(
+        self, response=None, responder_id=None, certs=None, extensions=[]
+    ):
         self._response = response
         self._responder_id = responder_id
         self._certs = certs
         self._extensions = extensions

-    def add_response(self, cert, issuer, algorithm, cert_status, this_update,
-                     next_update, revocation_time, revocation_reason):
+    def add_response(
+        self,
+        cert,
+        issuer,
+        algorithm,
+        cert_status,
+        this_update,
+        next_update,
+        revocation_time,
+        revocation_reason,
+    ):
         if self._response is not None:
             raise ValueError("Only one response per OCSPResponse.")

         singleresp = _SingleResponse(
-            cert, issuer, algorithm, cert_status, this_update, next_update,
-            revocation_time, revocation_reason
+            cert,
+            issuer,
+            algorithm,
+            cert_status,
+            this_update,
+            next_update,
+            revocation_time,
+            revocation_reason,
         )
         return OCSPResponseBuilder(
-            singleresp, self._responder_id,
-            self._certs, self._extensions,
+            singleresp,
+            self._responder_id,
+            self._certs,
+            self._extensions,
         )

     def responder_id(self, encoding, responder_cert):
         if self._responder_id is not None:
             raise ValueError("responder_id can only be set once")
         if not isinstance(responder_cert, x509.Certificate):
             raise TypeError("responder_cert must be a Certificate")
         if not isinstance(encoding, OCSPResponderEncoding):
             raise TypeError(
                 "encoding must be an element from OCSPResponderEncoding"
             )

         return OCSPResponseBuilder(
-            self._response, (responder_cert, encoding),
-            self._certs, self._extensions,
+            self._response,
+            (responder_cert, encoding),
+            self._certs,
+            self._extensions,
         )

     def certificates(self, certs):
         if self._certs is not None:
             raise ValueError("certificates may only be set once")
         certs = list(certs)
         if len(certs) == 0:
             raise ValueError("certs must not be an empty list")
         if not all(isinstance(x, x509.Certificate) for x in certs):
             raise TypeError("certs must be a list of Certificates")
         return OCSPResponseBuilder(
-            self._response, self._responder_id,
-            certs, self._extensions,
+            self._response,
+            self._responder_id,
+            certs,
+            self._extensions,
         )

     def add_extension(self, extension, critical):
         if not isinstance(extension, x509.ExtensionType):
             raise TypeError("extension must be an ExtensionType")

         extension = x509.Extension(extension.oid, critical, extension)
         _reject_duplicate_extension(extension, self._extensions)

         return OCSPResponseBuilder(
-            self._response, self._responder_id,
-            self._certs, self._extensions + [extension],
+            self._response,
+            self._responder_id,
+            self._certs,
+            self._extensions + [extension],
         )

     def sign(self, private_key, algorithm):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if self._response is None:
             raise ValueError("You must add a response before signing")
         if self._responder_id is None:
             raise ValueError("You must add a responder_id before signing")

-        if not isinstance(algorithm, hashes.HashAlgorithm):
-            raise TypeError("Algorithm must be a registered hash algorithm.")
-
         return backend.create_ocsp_response(
             OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm
         )

     @classmethod
     def build_unsuccessful(cls, response_status):
         from cryptography.hazmat.backends.openssl.backend import backend
+
         if not isinstance(response_status, OCSPResponseStatus):
             raise TypeError(
                 "response_status must be an item from OCSPResponseStatus"
             )
         if response_status is OCSPResponseStatus.SUCCESSFUL:
             raise ValueError("response_status cannot be SUCCESSFUL")

@@ -282,14 +320,15 @@
         """

     @abc.abstractproperty
     def serial_number(self):
         """
         The serial number of the cert whose status is being checked
         """
+
     @abc.abstractmethod
     def public_bytes(self, encoding):
         """
         Serializes the request to DER
         """

     @abc.abstractproperty
@@ -416,7 +455,13 @@
         """

     @abc.abstractproperty
     def extensions(self):
         """
         The list of response extensions. Not single response extensions.
         """
+
+    @abc.abstractproperty
+    def single_extensions(self):
+        """
+        The list of single response extensions. Not response extensions.
+        """

cryptography/x509/extensions.py

@@ -6,55 +6,92 @@

 import abc
 import datetime
 import hashlib
 import ipaddress
 from enum import Enum

-from asn1crypto.keys import PublicKeyInfo
-
 import six

 from cryptography import utils
+from cryptography.hazmat._der import (
+    BIT_STRING,
+    DERReader,
+    OBJECT_IDENTIFIER,
+    SEQUENCE,
+)
 from cryptography.hazmat.primitives import constant_time, serialization
 from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
 from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
 from cryptography.x509.certificate_transparency import (
-    SignedCertificateTimestamp
+    SignedCertificateTimestamp,
 )
 from cryptography.x509.general_name import GeneralName, IPAddress, OtherName
 from cryptography.x509.name import RelativeDistinguishedName
 from cryptography.x509.oid import (
-    CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID, ObjectIdentifier,
+    CRLEntryExtensionOID,
+    ExtensionOID,
+    OCSPExtensionOID,
+    ObjectIdentifier,
 )


 def _key_identifier_from_public_key(public_key):
     if isinstance(public_key, RSAPublicKey):
         data = public_key.public_bytes(
             serialization.Encoding.DER,
             serialization.PublicFormat.PKCS1,
         )
     elif isinstance(public_key, EllipticCurvePublicKey):
         data = public_key.public_bytes(
             serialization.Encoding.X962,
-            serialization.PublicFormat.UncompressedPoint
+            serialization.PublicFormat.UncompressedPoint,
         )
     else:
         # This is a very slow way to do this.
         serialized = public_key.public_bytes(
             serialization.Encoding.DER,
-            serialization.PublicFormat.SubjectPublicKeyInfo
+            serialization.PublicFormat.SubjectPublicKeyInfo,
         )

-        data = bytes(PublicKeyInfo.load(serialized)['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)
+
+        # Double-check the algorithm structure.
+        with algorithm:
+            algorithm.read_element(OBJECT_IDENTIFIER)
+            if not algorithm.is_empty():
+                # Skip the optional parameters field.
+                algorithm.read_any_element()
+
+        # BIT STRING contents begin with the number of padding bytes added. It
+        # must be zero for SubjectPublicKeyInfo structures.
+        if public_key.read_byte() != 0:
+            raise ValueError("Invalid public key encoding")
+
+        data = public_key.data

     return hashlib.sha1(data).digest()


+def _make_sequence_methods(field_name):
+    def len_method(self):
+        return len(getattr(self, field_name))
+
+    def iter_method(self):
+        return iter(getattr(self, field_name))
+
+    def getitem_method(self, idx):
+        return getattr(self, field_name)[idx]
+
+    return len_method, iter_method, getitem_method
+
+
 class DuplicateExtension(Exception):
     def __init__(self, msg, oid):
         super(DuplicateExtension, self).__init__(msg)
         self.oid = oid


 class ExtensionNotFound(Exception):
@@ -95,27 +132,18 @@
             if isinstance(ext.value, extclass):
                 return ext

         raise ExtensionNotFound(
             "No {} extension was found".format(extclass), extclass.oid
         )

-    def __iter__(self):
-        return iter(self._extensions)
-
-    def __len__(self):
-        return len(self._extensions)
-
-    def __getitem__(self, idx):
-        return self._extensions[idx]
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions")

     def __repr__(self):
-        return (
-            "<Extensions({})>".format(self._extensions)
-        )
+        return "<Extensions({})>".format(self._extensions)


 @utils.register_interface(ExtensionType)
 class CRLNumber(object):
     oid = ExtensionOID.CRL_NUMBER

     def __init__(self, crl_number):
@@ -142,16 +170,20 @@
     crl_number = utils.read_only_property("_crl_number")


 @utils.register_interface(ExtensionType)
 class AuthorityKeyIdentifier(object):
     oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER

-    def __init__(self, key_identifier, authority_cert_issuer,
-                 authority_cert_serial_number):
+    def __init__(
+        self,
+        key_identifier,
+        authority_cert_issuer,
+        authority_cert_serial_number,
+    ):
         if (authority_cert_issuer is None) != (
             authority_cert_serial_number is None
         ):
             raise ValueError(
                 "authority_cert_issuer and authority_cert_serial_number "
                 "must both be present or both None"
             )
@@ -165,37 +197,35 @@
                     "authority_cert_issuer must be a list of GeneralName "
                     "objects"
                 )

         if authority_cert_serial_number is not None and not isinstance(
             authority_cert_serial_number, six.integer_types
         ):
-            raise TypeError(
-                "authority_cert_serial_number must be an integer"
-            )
+            raise TypeError("authority_cert_serial_number must be an integer")

         self._key_identifier = key_identifier
         self._authority_cert_issuer = authority_cert_issuer
         self._authority_cert_serial_number = authority_cert_serial_number

     @classmethod
     def from_issuer_public_key(cls, public_key):
         digest = _key_identifier_from_public_key(public_key)
         return cls(
             key_identifier=digest,
             authority_cert_issuer=None,
-            authority_cert_serial_number=None
+            authority_cert_serial_number=None,
         )

     @classmethod
     def from_issuer_subject_key_identifier(cls, ski):
         return cls(
-            key_identifier=ski.value.digest,
+            key_identifier=ski.digest,
             authority_cert_issuer=None,
-            authority_cert_serial_number=None
+            authority_cert_serial_number=None,
         )

     def __repr__(self):
         return (
             "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, "
             "authority_cert_issuer={0.authority_cert_issuer}, "
             "authority_cert_serial_number={0.authority_cert_serial_number}"
@@ -203,31 +233,31 @@
         )

     def __eq__(self, other):
         if not isinstance(other, AuthorityKeyIdentifier):
             return NotImplemented

         return (
-            self.key_identifier == other.key_identifier and
-            self.authority_cert_issuer == other.authority_cert_issuer and
-            self.authority_cert_serial_number ==
-            other.authority_cert_serial_number
+            self.key_identifier == other.key_identifier
+            and self.authority_cert_issuer == other.authority_cert_issuer
+            and self.authority_cert_serial_number
+            == other.authority_cert_serial_number
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         if self.authority_cert_issuer is None:
             aci = None
         else:
             aci = tuple(self.authority_cert_issuer)
-        return hash((
-            self.key_identifier, aci, self.authority_cert_serial_number
-        ))
+        return hash(
+            (self.key_identifier, aci, self.authority_cert_serial_number)
+        )

     key_identifier = utils.read_only_property("_key_identifier")
     authority_cert_issuer = utils.read_only_property("_authority_cert_issuer")
     authority_cert_serial_number = utils.read_only_property(
         "_authority_cert_serial_number"
     )

@@ -271,34 +301,59 @@
             raise TypeError(
                 "Every item in the descriptions list must be an "
                 "AccessDescription"
             )

         self._descriptions = descriptions

-    def __iter__(self):
-        return iter(self._descriptions)
-
-    def __len__(self):
-        return len(self._descriptions)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")

     def __repr__(self):
         return "<AuthorityInformationAccess({})>".format(self._descriptions)

     def __eq__(self, other):
         if not isinstance(other, AuthorityInformationAccess):
             return NotImplemented

         return self._descriptions == other._descriptions

     def __ne__(self, other):
         return not self == other

-    def __getitem__(self, idx):
-        return self._descriptions[idx]
+    def __hash__(self):
+        return hash(tuple(self._descriptions))
+
+
[email protected]_interface(ExtensionType)
+class SubjectInformationAccess(object):
+    oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS
+
+    def __init__(self, descriptions):
+        descriptions = list(descriptions)
+        if not all(isinstance(x, AccessDescription) for x in descriptions):
+            raise TypeError(
+                "Every item in the descriptions list must be an "
+                "AccessDescription"
+            )
+
+        self._descriptions = descriptions
+
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
+
+    def __repr__(self):
+        return "<SubjectInformationAccess({})>".format(self._descriptions)
+
+    def __eq__(self, other):
+        if not isinstance(other, SubjectInformationAccess):
+            return NotImplemented
+
+        return self._descriptions == other._descriptions
+
+    def __ne__(self, other):
+        return not self == other

     def __hash__(self):
         return hash(tuple(self._descriptions))


 class AccessDescription(object):
     def __init__(self, access_method, access_location):
@@ -318,16 +373,16 @@
         )

     def __eq__(self, other):
         if not isinstance(other, AccessDescription):
             return NotImplemented

         return (
-            self.access_method == other.access_method and
-            self.access_location == other.access_location
+            self.access_method == other.access_method
+            and self.access_location == other.access_location
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash((self.access_method, self.access_location))
@@ -343,31 +398,31 @@
     def __init__(self, ca, path_length):
         if not isinstance(ca, bool):
             raise TypeError("ca must be a boolean value")

         if path_length is not None and not ca:
             raise ValueError("path_length must be None when ca is False")

-        if (
-            path_length is not None and
-            (not isinstance(path_length, six.integer_types) or path_length < 0)
+        if path_length is not None and (
+            not isinstance(path_length, six.integer_types) or path_length < 0
         ):
             raise TypeError(
                 "path_length must be a non-negative integer or None"
             )

         self._ca = ca
         self._path_length = path_length

     ca = utils.read_only_property("_ca")
     path_length = utils.read_only_property("_path_length")

     def __repr__(self):
-        return ("<BasicConstraints(ca={0.ca}, "
-                "path_length={0.path_length})>").format(self)
+        return (
+            "<BasicConstraints(ca={0.ca}, " "path_length={0.path_length})>"
+        ).format(self)

     def __eq__(self, other):
         if not isinstance(other, BasicConstraints):
             return NotImplemented

         return self.ca == other.ca and self.path_length == other.path_length

@@ -418,35 +473,30 @@
             raise TypeError(
                 "distribution_points must be a list of DistributionPoint "
                 "objects"
             )

         self._distribution_points = distribution_points

-    def __iter__(self):
-        return iter(self._distribution_points)
-
-    def __len__(self):
-        return len(self._distribution_points)
+    __len__, __iter__, __getitem__ = _make_sequence_methods(
+        "_distribution_points"
+    )

     def __repr__(self):
         return "<CRLDistributionPoints({})>".format(self._distribution_points)

     def __eq__(self, other):
         if not isinstance(other, CRLDistributionPoints):
             return NotImplemented

         return self._distribution_points == other._distribution_points

     def __ne__(self, other):
         return not self == other

-    def __getitem__(self, idx):
-        return self._distribution_points[idx]
-
     def __hash__(self):
         return hash(tuple(self._distribution_points))


 @utils.register_interface(ExtensionType)
 class FreshestCRL(object):
     oid = ExtensionOID.FRESHEST_CRL
@@ -459,35 +509,30 @@
             raise TypeError(
                 "distribution_points must be a list of DistributionPoint "
                 "objects"
             )

         self._distribution_points = distribution_points

-    def __iter__(self):
-        return iter(self._distribution_points)
-
-    def __len__(self):
-        return len(self._distribution_points)
+    __len__, __iter__, __getitem__ = _make_sequence_methods(
+        "_distribution_points"
+    )

     def __repr__(self):
         return "<FreshestCRL({})>".format(self._distribution_points)

     def __eq__(self, other):
         if not isinstance(other, FreshestCRL):
             return NotImplemented

         return self._distribution_points == other._distribution_points

     def __ne__(self, other):
         return not self == other

-    def __getitem__(self, idx):
-        return self._distribution_points[idx]
-
     def __hash__(self):
         return hash(tuple(self._distribution_points))


 class DistributionPoint(object):
     def __init__(self, full_name, relative_name, reasons, crl_issuer):
         if full_name and relative_name:
@@ -512,22 +557,23 @@
         if crl_issuer:
             crl_issuer = list(crl_issuer)
             if not all(isinstance(x, GeneralName) for x in crl_issuer):
                 raise TypeError(
                     "crl_issuer must be None or a list of general names"
                 )

-        if reasons and (not isinstance(reasons, frozenset) or not all(
-            isinstance(x, ReasonFlags) for x in reasons
-        )):
+        if reasons and (
+            not isinstance(reasons, frozenset)
+            or not all(isinstance(x, ReasonFlags) for x in reasons)
+        ):
             raise TypeError("reasons must be None or frozenset of ReasonFlags")

         if reasons and (
-            ReasonFlags.unspecified in reasons or
-            ReasonFlags.remove_from_crl in reasons
+            ReasonFlags.unspecified in reasons
+            or ReasonFlags.remove_from_crl in reasons
         ):
             raise ValueError(
                 "unspecified and remove_from_crl are not valid reasons in a "
                 "DistributionPoint"
             )

         if reasons and not crl_issuer and not (full_name or relative_name):
@@ -540,27 +586,27 @@
         self._relative_name = relative_name
         self._reasons = reasons
         self._crl_issuer = crl_issuer

     def __repr__(self):
         return (
             "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela"
-            "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_issuer})>"
-            .format(self)
+            "tive_name}, reasons={0.reasons}, "
+            "crl_issuer={0.crl_issuer})>".format(self)
         )

     def __eq__(self, other):
         if not isinstance(other, DistributionPoint):
             return NotImplemented

         return (
-            self.full_name == other.full_name and
-            self.relative_name == other.relative_name and
-            self.reasons == other.reasons and
-            self.crl_issuer == other.crl_issuer
+            self.full_name == other.full_name
+            and self.relative_name == other.relative_name
+            and self.reasons == other.reasons
+            and self.crl_issuer == other.crl_issuer
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         if self.full_name is not None:
@@ -631,16 +677,16 @@
         )

     def __eq__(self, other):
         if not isinstance(other, PolicyConstraints):
             return NotImplemented

         return (
-            self.require_explicit_policy == other.require_explicit_policy and
-            self.inhibit_policy_mapping == other.inhibit_policy_mapping
+            self.require_explicit_policy == other.require_explicit_policy
+            and self.inhibit_policy_mapping == other.inhibit_policy_mapping
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash(
@@ -665,51 +711,44 @@
             raise TypeError(
                 "Every item in the policies list must be a "
                 "PolicyInformation"
             )

         self._policies = policies

-    def __iter__(self):
-        return iter(self._policies)
-
-    def __len__(self):
-        return len(self._policies)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_policies")

     def __repr__(self):
         return "<CertificatePolicies({})>".format(self._policies)

     def __eq__(self, other):
         if not isinstance(other, CertificatePolicies):
             return NotImplemented

         return self._policies == other._policies

     def __ne__(self, other):
         return not self == other

-    def __getitem__(self, idx):
-        return self._policies[idx]
-
     def __hash__(self):
         return hash(tuple(self._policies))


 class PolicyInformation(object):
     def __init__(self, policy_identifier, policy_qualifiers):
         if not isinstance(policy_identifier, ObjectIdentifier):
             raise TypeError("policy_identifier must be an ObjectIdentifier")

         self._policy_identifier = policy_identifier

         if policy_qualifiers:
             policy_qualifiers = list(policy_qualifiers)
             if not all(
-                    isinstance(x, (six.text_type, UserNotice))
-                    for x in policy_qualifiers
+                isinstance(x, (six.text_type, UserNotice))
+                for x in policy_qualifiers
             ):
                 raise TypeError(
                     "policy_qualifiers must be a list of strings and/or "
                     "UserNotice objects or None"
                 )

         self._policy_qualifiers = policy_qualifiers
@@ -721,16 +760,16 @@
         )

     def __eq__(self, other):
         if not isinstance(other, PolicyInformation):
             return NotImplemented

         return (
-            self.policy_identifier == other.policy_identifier and
-            self.policy_qualifiers == other.policy_qualifiers
+            self.policy_identifier == other.policy_identifier
+            and self.policy_qualifiers == other.policy_qualifiers
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         if self.policy_qualifiers is not None:
@@ -763,16 +802,16 @@
         )

     def __eq__(self, other):
         if not isinstance(other, UserNotice):
             return NotImplemented

         return (
-            self.notice_reference == other.notice_reference and
-            self.explicit_text == other.explicit_text
+            self.notice_reference == other.notice_reference
+            and self.explicit_text == other.explicit_text
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash((self.notice_reference, self.explicit_text))
@@ -782,33 +821,31 @@


 class NoticeReference(object):
     def __init__(self, organization, notice_numbers):
         self._organization = organization
         notice_numbers = list(notice_numbers)
         if not all(isinstance(x, int) for x in notice_numbers):
-            raise TypeError(
-                "notice_numbers must be a list of integers"
-            )
+            raise TypeError("notice_numbers must be a list of integers")

         self._notice_numbers = notice_numbers

     def __repr__(self):
         return (
             "<NoticeReference(organization={0.organization!r}, notice_numbers="
             "{0.notice_numbers})>".format(self)
         )

     def __eq__(self, other):
         if not isinstance(other, NoticeReference):
             return NotImplemented

         return (
-            self.organization == other.organization and
-            self.notice_numbers == other.notice_numbers
+            self.organization == other.organization
+            and self.notice_numbers == other.notice_numbers
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash((self.organization, tuple(self.notice_numbers)))
@@ -826,19 +863,15 @@
         if not all(isinstance(x, ObjectIdentifier) for x in usages):
             raise TypeError(
                 "Every item in the usages list must be an ObjectIdentifier"
             )

         self._usages = usages

-    def __iter__(self):
-        return iter(self._usages)
-
-    def __len__(self):
-        return len(self._usages)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_usages")

     def __repr__(self):
         return "<ExtendedKeyUsage({})>".format(self._usages)

     def __eq__(self, other):
         if not isinstance(other, ExtendedKeyUsage):
             return NotImplemented
@@ -852,55 +885,78 @@
         return hash(tuple(self._usages))


 @utils.register_interface(ExtensionType)
 class OCSPNoCheck(object):
     oid = ExtensionOID.OCSP_NO_CHECK

+    def __eq__(self, other):
+        if not isinstance(other, OCSPNoCheck):
+            return NotImplemented
+
+        return True
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __hash__(self):
+        return hash(OCSPNoCheck)
+
+    def __repr__(self):
+        return "<OCSPNoCheck()>"
+

 @utils.register_interface(ExtensionType)
 class PrecertPoison(object):
     oid = ExtensionOID.PRECERT_POISON

+    def __eq__(self, other):
+        if not isinstance(other, PrecertPoison):
+            return NotImplemented
+
+        return True
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __hash__(self):
+        return hash(PrecertPoison)
+
+    def __repr__(self):
+        return "<PrecertPoison()>"
+

 @utils.register_interface(ExtensionType)
 class TLSFeature(object):
     oid = ExtensionOID.TLS_FEATURE

     def __init__(self, features):
         features = list(features)
         if (
-            not all(isinstance(x, TLSFeatureType) for x in features) or
-            len(features) == 0
+            not all(isinstance(x, TLSFeatureType) for x in features)
+            or len(features) == 0
         ):
             raise TypeError(
                 "features must be a list of elements from the TLSFeatureType "
                 "enum"
             )

         self._features = features

-    def __iter__(self):
-        return iter(self._features)
-
-    def __len__(self):
-        return len(self._features)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_features")

     def __repr__(self):
         return "<TLSFeature(features={0._features})>".format(self)

     def __eq__(self, other):
         if not isinstance(other, TLSFeature):
             return NotImplemented

         return self._features == other._features

-    def __getitem__(self, idx):
-        return self._features[idx]
-
     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash(tuple(self._features))


@@ -911,15 +967,15 @@
     status_request = 5
     # status_request_v2 is defined in RFC 6961 and allows multiple OCSP
     # responses to be provided. It is not currently in use by clients or
     # servers.
     status_request_v2 = 17


-_TLS_FEATURE_TYPE_TO_ENUM = dict((x.value, x) for x in TLSFeatureType)
+_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType}


 @utils.register_interface(ExtensionType)
 class InhibitAnyPolicy(object):
     oid = ExtensionOID.INHIBIT_ANY_POLICY

     def __init__(self, skip_certs):
@@ -949,17 +1005,26 @@
     skip_certs = utils.read_only_property("_skip_certs")


 @utils.register_interface(ExtensionType)
 class KeyUsage(object):
     oid = ExtensionOID.KEY_USAGE

-    def __init__(self, digital_signature, content_commitment, key_encipherment,
-                 data_encipherment, key_agreement, key_cert_sign, crl_sign,
-                 encipher_only, decipher_only):
+    def __init__(
+        self,
+        digital_signature,
+        content_commitment,
+        key_encipherment,
+        data_encipherment,
+        key_agreement,
+        key_cert_sign,
+        crl_sign,
+        encipher_only,
+        decipher_only,
+    ):
         if not key_agreement and (encipher_only or decipher_only):
             raise ValueError(
                 "encipher_only and decipher_only can only be true when "
                 "key_agreement is true"
             )

         self._digital_signature = digital_signature
@@ -999,77 +1064,83 @@
             return self._decipher_only

     def __repr__(self):
         try:
             encipher_only = self.encipher_only
             decipher_only = self.decipher_only
         except ValueError:
-            encipher_only = None
-            decipher_only = None
+            # Users found None confusing because even though encipher/decipher
+            # have no meaning unless key_agreement is true, to construct an
+            # instance of the class you still need to pass False.
+            encipher_only = False
+            decipher_only = False

-        return ("<KeyUsage(digital_signature={0.digital_signature}, "
-                "content_commitment={0.content_commitment}, "
-                "key_encipherment={0.key_encipherment}, "
-                "data_encipherment={0.data_encipherment}, "
-                "key_agreement={0.key_agreement}, "
-                "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, "
-                "encipher_only={1}, decipher_only={2})>").format(
-                    self, encipher_only, decipher_only)
+        return (
+            "<KeyUsage(digital_signature={0.digital_signature}, "
+            "content_commitment={0.content_commitment}, "
+            "key_encipherment={0.key_encipherment}, "
+            "data_encipherment={0.data_encipherment}, "
+            "key_agreement={0.key_agreement}, "
+            "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, "
+            "encipher_only={1}, decipher_only={2})>"
+        ).format(self, encipher_only, decipher_only)

     def __eq__(self, other):
         if not isinstance(other, KeyUsage):
             return NotImplemented

         return (
-            self.digital_signature == other.digital_signature and
-            self.content_commitment == other.content_commitment and
-            self.key_encipherment == other.key_encipherment and
-            self.data_encipherment == other.data_encipherment and
-            self.key_agreement == other.key_agreement and
-            self.key_cert_sign == other.key_cert_sign and
-            self.crl_sign == other.crl_sign and
-            self._encipher_only == other._encipher_only and
-            self._decipher_only == other._decipher_only
+            self.digital_signature == other.digital_signature
+            and self.content_commitment == other.content_commitment
+            and self.key_encipherment == other.key_encipherment
+            and self.data_encipherment == other.data_encipherment
+            and self.key_agreement == other.key_agreement
+            and self.key_cert_sign == other.key_cert_sign
+            and self.crl_sign == other.crl_sign
+            and self._encipher_only == other._encipher_only
+            and self._decipher_only == other._decipher_only
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
-        return hash((
-            self.digital_signature, self.content_commitment,
-            self.key_encipherment, self.data_encipherment,
-            self.key_agreement, self.key_cert_sign,
-            self.crl_sign, self._encipher_only,
-            self._decipher_only
-        ))
+        return hash(
+            (
+                self.digital_signature,
+                self.content_commitment,
+                self.key_encipherment,
+                self.data_encipherment,
+                self.key_agreement,
+                self.key_cert_sign,
+                self.crl_sign,
+                self._encipher_only,
+                self._decipher_only,
+            )
+        )


 @utils.register_interface(ExtensionType)
 class NameConstraints(object):
     oid = ExtensionOID.NAME_CONSTRAINTS

     def __init__(self, permitted_subtrees, excluded_subtrees):
         if permitted_subtrees is not None:
             permitted_subtrees = list(permitted_subtrees)
-            if not all(
-                isinstance(x, GeneralName) for x in permitted_subtrees
-            ):
+            if not all(isinstance(x, GeneralName) for x in permitted_subtrees):
                 raise TypeError(
                     "permitted_subtrees must be a list of GeneralName objects "
                     "or None"
                 )

             self._validate_ip_name(permitted_subtrees)

         if excluded_subtrees is not None:
             excluded_subtrees = list(excluded_subtrees)
-            if not all(
-                isinstance(x, GeneralName) for x in excluded_subtrees
-            ):
+            if not all(isinstance(x, GeneralName) for x in excluded_subtrees):
                 raise TypeError(
                     "excluded_subtrees must be a list of GeneralName objects "
                     "or None"
                 )

             self._validate_ip_name(excluded_subtrees)

@@ -1083,25 +1154,29 @@
         self._excluded_subtrees = excluded_subtrees

     def __eq__(self, other):
         if not isinstance(other, NameConstraints):
             return NotImplemented

         return (
-            self.excluded_subtrees == other.excluded_subtrees and
-            self.permitted_subtrees == other.permitted_subtrees
+            self.excluded_subtrees == other.excluded_subtrees
+            and self.permitted_subtrees == other.permitted_subtrees
         )

     def __ne__(self, other):
         return not self == other

     def _validate_ip_name(self, tree):
-        if any(isinstance(name, IPAddress) and not isinstance(
-            name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)
-        ) for name in tree):
+        if any(
+            isinstance(name, IPAddress)
+            and not isinstance(
+                name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)
+            )
+            for name in tree
+        ):
             raise TypeError(
                 "IPAddress name constraints must be an IPv4Network or"
                 " IPv6Network object"
             )

     def __repr__(self):
         return (
@@ -1141,25 +1216,27 @@
         self._value = value

     oid = utils.read_only_property("_oid")
     critical = utils.read_only_property("_critical")
     value = utils.read_only_property("_value")

     def __repr__(self):
-        return ("<Extension(oid={0.oid}, critical={0.critical}, "
-                "value={0.value})>").format(self)
+        return (
+            "<Extension(oid={0.oid}, critical={0.critical}, "
+            "value={0.value})>"
+        ).format(self)

     def __eq__(self, other):
         if not isinstance(other, Extension):
             return NotImplemented

         return (
-            self.oid == other.oid and
-            self.critical == other.critical and
-            self.value == other.value
+            self.oid == other.oid
+            and self.critical == other.critical
+            and self.value == other.value
         )

     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash((self.oid, self.critical, self.value))
@@ -1172,19 +1249,15 @@
             raise TypeError(
                 "Every item in the general_names list must be an "
                 "object conforming to the GeneralName interface"
             )

         self._general_names = general_names

-    def __iter__(self):
-        return iter(self._general_names)
-
-    def __len__(self):
-        return len(self._general_names)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

     def get_values_for_type(self, type):
         # Return the value of each GeneralName, except for OtherName instances
         # which we return directly because it has two important properties not
         # just one value.
         objs = (i for i in self if isinstance(i, type))
         if type != OtherName:
@@ -1199,68 +1272,54 @@
             return NotImplemented

         return self._general_names == other._general_names

     def __ne__(self, other):
         return not self == other

-    def __getitem__(self, idx):
-        return self._general_names[idx]
-
     def __hash__(self):
         return hash(tuple(self._general_names))


 @utils.register_interface(ExtensionType)
 class SubjectAlternativeName(object):
     oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME

     def __init__(self, general_names):
         self._general_names = GeneralNames(general_names)

-    def __iter__(self):
-        return iter(self._general_names)
-
-    def __len__(self):
-        return len(self._general_names)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

     def get_values_for_type(self, type):
         return self._general_names.get_values_for_type(type)

     def __repr__(self):
         return "<SubjectAlternativeName({})>".format(self._general_names)

     def __eq__(self, other):
         if not isinstance(other, SubjectAlternativeName):
             return NotImplemented

         return self._general_names == other._general_names

-    def __getitem__(self, idx):
-        return self._general_names[idx]
-
     def __ne__(self, other):
         return not self == other

     def __hash__(self):
         return hash(self._general_names)


 @utils.register_interface(ExtensionType)
 class IssuerAlternativeName(object):
     oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME

     def __init__(self, general_names):
         self._general_names = GeneralNames(general_names)

-    def __iter__(self):
-        return iter(self._general_names)
-
-    def __len__(self):
-        return len(self._general_names)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

     def get_values_for_type(self, type):
         return self._general_names.get_values_for_type(type)

     def __repr__(self):
         return "<IssuerAlternativeName({})>".format(self._general_names)

@@ -1269,33 +1328,26 @@
             return NotImplemented

         return self._general_names == other._general_names

     def __ne__(self, other):
         return not self == other

-    def __getitem__(self, idx):
-        return self._general_names[idx]
-
     def __hash__(self):
         return hash(self._general_names)


 @utils.register_interface(ExtensionType)
 class CertificateIssuer(object):
     oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER

     def __init__(self, general_names):
         self._general_names = GeneralNames(general_names)

-    def __iter__(self):
-        return iter(self._general_names)
-
-    def __len__(self):
-        return len(self._general_names)
+    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

     def get_values_for_type(self, type):
         return self._general_names.get_values_for_type(type)

     def __repr__(self):
         return "<CertificateIssuer({})>".format(self._general_names)

@@ -1304,17 +1356,14 @@
             return Not
Clone this wiki locally