From 1742975367e457ee030e582b88bd870eaa788dfe Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 5 Sep 2022 20:25:19 +0800 Subject: [PATCH] support setting more PKCS12 serialization encryption options (#7560) * support setting more PKCS12 serialization encryption options This is limited support, but makes it possible to set two different PBES choices as well as set KDF rounds and MAC algorithm * Apply suggestions from code review Co-authored-by: Alex Gaynor * review feedback redux * Update docs/hazmat/primitives/asymmetric/serialization.rst Co-authored-by: Alex Gaynor Co-authored-by: Alex Gaynor --- .../primitives/asymmetric/serialization.rst | 127 +++++++++++++-- .../hazmat/backends/openssl/backend.py | 76 ++++++++- .../hazmat/primitives/_serialization.py | 70 +++++++- .../hazmat/primitives/serialization/pkcs12.py | 9 ++ tests/hazmat/primitives/test_pkcs12.py | 152 ++++++++++++++++++ tests/hazmat/primitives/test_serialization.py | 30 ++++ 6 files changed, 444 insertions(+), 20 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 3361ce6f3bcf..db3271b90d3c 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -491,12 +491,20 @@ file suffix. .. versionadded:: 3.0 + .. note:: + With OpenSSL 3.0.0+ the defaults for encryption when serializing PKCS12 + have changed and some versions of Windows and macOS will not be able to + read the new format. Maximum compatibility can be achieved by using + ``SHA1`` for MAC algorithm and + :attr:`~cryptography.hazmat.primitives.serialization.pkcs12.PBES.PBESv1SHA1And3KeyTripleDESCBC` + for encryption algorithm as seen in the example below. However, users + should avoid this unless required for compatibility. + .. warning:: - PKCS12 encryption is not secure and should not be used as a security - mechanism. Wrap a PKCS12 blob in a more secure envelope if you need - to store or send it safely. Encryption is provided for compatibility - reasons only. + PKCS12 encryption is typically not secure and should not be used as a + security mechanism. Wrap a PKCS12 blob in a more secure envelope if you + need to store or send it safely. Serialize a PKCS12 blob. @@ -535,11 +543,41 @@ file suffix. :param encryption_algorithm: The encryption algorithm that should be used for the key and certificate. An instance of an object conforming to the :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption` - interface. PKCS12 encryption is **very weak** and should not be used - as a security boundary. + interface. PKCS12 encryption is typically **very weak** and should not + be used as a security boundary. :return bytes: Serialized PKCS12. + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives.serialization import BestAvailableEncryption, load_pem_private_key, pkcs12 + >>> cert = x509.load_pem_x509_certificate(ca_cert) + >>> key = load_pem_private_key(ca_key, None) + >>> p12 = pkcs12.serialize_key_and_certificates( + ... b"friendlyname", key, cert, None, BestAvailableEncryption(b"password") + ... ) + + This example uses an ``encryption_builder()`` to create a PKCS12 with more + compatible, but substantially less secure, encryption. + + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.primitives.serialization import PrivateFormat, load_pem_private_key, pkcs12 + >>> encryption = ( + ... PrivateFormat.PKCS12.encryption_builder(). + ... kdf_rounds(50000). + ... key_cert_algorithm(pkcs12.PBES.PBESv1SHA1And3KeyTripleDESCBC). + ... hmac_hash(hashes.SHA1()).build(b"my password") + ... ) + >>> cert = x509.load_pem_x509_certificate(ca_cert) + >>> key = load_pem_private_key(ca_key, None) + >>> p12 = pkcs12.serialize_key_and_certificates( + ... b"friendlyname", key, None, None, encryption + ... ) + .. class:: PKCS12Certificate .. versionadded:: 36.0 @@ -579,6 +617,24 @@ file suffix. A list of :class:`~cryptography.hazmat.primitives.serialization.pkcs12.PKCS12Certificate` instances. +.. class:: PBES + + .. versionadded:: 38.0.0 + + An enumeration of password-based encryption schemes used in PKCS12. These + values are used with + :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryptionBuilder`. + + .. attribute:: PBESv1SHA1And3KeyTripleDESCBC + + PBESv1 using SHA1 as the KDF PRF and 3-key triple DES-CBC as the cipher. + + .. attribute:: PBESv2SHA256AndAES256CBC + + PBESv2 using SHA256 as the KDF PRF and AES256-CBC as the cipher. This + is only supported on OpenSSL 3.0.0 or newer. + + PKCS7 ~~~~~ @@ -841,16 +897,40 @@ Serialization Formats ... -----END OPENSSH PRIVATE KEY----- + .. attribute:: PKCS12 + + .. versionadded:: 38.0.0 + + The PKCS#12 format is a binary format used to store private keys and + certificates. This attribute is used in conjunction with + ``encryption_builder()`` to allow control of the encryption algorithm + and parameters. + + .. doctest:: + + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.primitives.serialization import PrivateFormat, pkcs12 + >>> encryption = ( + ... PrivateFormat.PKCS12.encryption_builder(). + ... kdf_rounds(50000). + ... key_cert_algorithm(pkcs12.PBES.PBESv2SHA256AndAES256CBC). + ... hmac_hash(hashes.SHA256()).build(b"my password") + ... ) + >>> p12 = pkcs12.serialize_key_and_certificates( + ... b"friendlyname", key, None, None, encryption + ... ) + .. method:: encryption_builder() .. versionadded:: 38.0.0 Returns a builder for configuring how values are encrypted with this - format. + format. You must call this method on an element of the enumeration. + For example, ``PrivateFormat.OpenSSH.encryption_builder()``. For most use cases, :class:`BestAvailableEncryption` is preferred. - :returns KeySerializationEncryptionBuilder: A new builder. + :returns: A new instance of :class:`KeySerializationEncryptionBuilder` .. doctest:: @@ -1022,7 +1102,8 @@ Serialization Encryption Types Encrypt using the best available encryption for a given key. This is a curated encryption choice and the algorithm may change over - time. + time. The encryption algorithm may vary based on which version of OpenSSL + the library is compiled against. :param bytes password: The password to use for encryption. @@ -1033,8 +1114,11 @@ Serialization Encryption Types .. class:: KeySerializationEncryptionBuilder - A builder that can be used to configure how key data is encrypted. To - create one, call :meth:`PrivateFormat.encryption_builder`. + .. versionadded:: 38.0.0 + + A builder that can be used to configure how data is encrypted. To + create one, call :meth:`PrivateFormat.encryption_builder`. Different + serialization types will support different options on this builder. .. method:: kdf_rounds(rounds) @@ -1042,7 +1126,21 @@ Serialization Encryption Types meaning of the number of rounds varies on the KDF being used. :param int rounds: Number of rounds. - :returns KeySerializationEncryptionBuilder: A new builder. + + .. method:: key_cert_algorithm(algorithm) + + Set the encryption algorithm to use when encrypting the key and + certificate in a PKCS12 structure. + + :param algorithm: A value from the :class:`~cryptography.hazmat.primitives.serialization.pkcs12.PBES` + enumeration. + + .. method:: hmac_hash(algorithm) + + Set the hash algorithm to use within the MAC for a PKCS12 structure. + + :param algorithm: An instance of a + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` .. method:: build(password) @@ -1050,8 +1148,9 @@ Serialization Encryption Types :class:`KeySerializationEncryption` with a given password. :param bytes password: The password. - :returns KeySerializationEncryption: A key key serialization - encryption that can be passed to ``private_bytes`` methods. + :returns: A :class:`KeySerializationEncryption` encryption object + that can be passed to methods like ``private_bytes`` or + :func:`~cryptography.hazmat.primitives.serialization.pkcs12.serialize_key_and_certificates`. .. _`a bug in Firefox`: https://bugzilla.mozilla.org/show_bug.cgi?id=773111 .. _`PKCS3`: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 180083fa9403..7cc7f97bb52b 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -116,6 +116,7 @@ from cryptography.hazmat.primitives.kdf import scrypt from cryptography.hazmat.primitives.serialization import pkcs7, ssh from cryptography.hazmat.primitives.serialization.pkcs12 import ( + PBES, PKCS12Certificate, PKCS12KeyAndCertificates, _ALLOWED_PKCS12_TYPES, @@ -2263,20 +2264,75 @@ def serialize_key_and_certificates_to_pkcs12( nid_key = -1 pkcs12_iter = 0 mac_iter = 0 + mac_alg = self._ffi.NULL 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 + # OpenSSL 3 supports PBESv2, but Libre and Boring do not, so + # we use PBESv1 with 3DES on the older paths. + if self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: + nid_cert = self._lib.NID_aes_256_cbc + nid_key = self._lib.NID_aes_256_cbc + else: + 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 + # MAC algorithm can only be set on OpenSSL 3.0.0+ + mac_alg = self._ffi.NULL password = encryption_algorithm.password + elif ( + isinstance( + encryption_algorithm, serialization._KeySerializationEncryption + ) + and encryption_algorithm._format + is serialization.PrivateFormat.PKCS12 + ): + # Default to OpenSSL's defaults. Behavior will vary based on the + # version of OpenSSL cryptography is compiled against. + nid_cert = 0 + nid_key = 0 + # Use the default iters we use in best available + pkcs12_iter = 20000 + # See the Best Available comment for why this is 1 + mac_iter = 1 + password = encryption_algorithm.password + keycertalg = encryption_algorithm._key_cert_algorithm + if keycertalg is PBES.PBESv1SHA1And3KeyTripleDESCBC: + nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC + nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC + elif keycertalg is PBES.PBESv2SHA256AndAES256CBC: + if not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: + raise UnsupportedAlgorithm( + "PBESv2 is not supported by this version of OpenSSL" + ) + nid_cert = self._lib.NID_aes_256_cbc + nid_key = self._lib.NID_aes_256_cbc + else: + assert keycertalg is None + # We use OpenSSL's defaults + + if encryption_algorithm._hmac_hash is not None: + if not self._lib.Cryptography_HAS_PKCS12_SET_MAC: + raise UnsupportedAlgorithm( + "Setting MAC algorithm is not supported by this " + "version of OpenSSL." + ) + mac_alg = self._evp_md_non_null_from_algorithm( + encryption_algorithm._hmac_hash + ) + self.openssl_assert(mac_alg != self._ffi.NULL) + else: + mac_alg = self._ffi.NULL + + if encryption_algorithm._kdf_rounds is not None: + pkcs12_iter = encryption_algorithm._kdf_rounds + else: raise ValueError("Unsupported key encryption type") @@ -2326,6 +2382,20 @@ def serialize_key_and_certificates_to_pkcs12( 0, ) + if ( + self._lib.Cryptography_HAS_PKCS12_SET_MAC + and mac_alg != self._ffi.NULL + ): + self._lib.PKCS12_set_mac( + p12, + password_buf, + -1, + self._ffi.NULL, + 0, + mac_iter, + mac_alg, + ) + self.openssl_assert(p12 != self._ffi.NULL) p12 = self._ffi.gc(p12, self._lib.PKCS12_free) diff --git a/src/cryptography/hazmat/primitives/_serialization.py b/src/cryptography/hazmat/primitives/_serialization.py index 73a7b5a49295..fddb4c85eded 100644 --- a/src/cryptography/hazmat/primitives/_serialization.py +++ b/src/cryptography/hazmat/primitives/_serialization.py @@ -6,11 +6,17 @@ import typing from cryptography import utils +from cryptography.hazmat.primitives.hashes import HashAlgorithm # This exists to break an import cycle. These classes are normally accessible # from the serialization module. +class PBES(utils.Enum): + PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" + PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" + + class Encoding(utils.Enum): PEM = "PEM" DER = "DER" @@ -25,11 +31,13 @@ class PrivateFormat(utils.Enum): TraditionalOpenSSL = "TraditionalOpenSSL" Raw = "Raw" OpenSSH = "OpenSSH" + PKCS12 = "PKCS12" def encryption_builder(self) -> "KeySerializationEncryptionBuilder": - if self is not PrivateFormat.OpenSSH: + if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12): raise ValueError( "encryption_builder only supported with PrivateFormat.OpenSSH" + " and PrivateFormat.PKCS12" ) return KeySerializationEncryptionBuilder(self) @@ -69,16 +77,64 @@ def __init__( format: PrivateFormat, *, _kdf_rounds: typing.Optional[int] = None, + _hmac_hash: typing.Optional[HashAlgorithm] = None, + _key_cert_algorithm: typing.Optional[PBES] = None, ) -> None: self._format = format self._kdf_rounds = _kdf_rounds + self._hmac_hash = _hmac_hash + self._key_cert_algorithm = _key_cert_algorithm def kdf_rounds(self, rounds: int) -> "KeySerializationEncryptionBuilder": if self._kdf_rounds is not None: raise ValueError("kdf_rounds already set") + + if not isinstance(rounds, int): + raise TypeError("kdf_rounds must be an integer") + + if rounds < 1: + raise ValueError("kdf_rounds must be a positive integer") + + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def hmac_hash( + self, algorithm: HashAlgorithm + ) -> "KeySerializationEncryptionBuilder": + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "hmac_hash only supported with PrivateFormat.PKCS12" + ) + + if self._hmac_hash is not None: + raise ValueError("hmac_hash already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=algorithm, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def key_cert_algorithm( + self, algorithm: PBES + ) -> "KeySerializationEncryptionBuilder": + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "key_cert_algorithm only supported with " + "PrivateFormat.PKCS12" + ) + if self._key_cert_algorithm is not None: + raise ValueError("key_cert_algorithm already set") return KeySerializationEncryptionBuilder( - self._format, _kdf_rounds=rounds + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=algorithm, ) def build(self, password: bytes) -> KeySerializationEncryption: @@ -86,7 +142,11 @@ def build(self, password: bytes) -> KeySerializationEncryption: raise ValueError("Password must be 1 or more bytes.") return _KeySerializationEncryption( - self._format, password, kdf_rounds=self._kdf_rounds + self._format, + password, + kdf_rounds=self._kdf_rounds, + hmac_hash=self._hmac_hash, + key_cert_algorithm=self._key_cert_algorithm, ) @@ -97,8 +157,12 @@ def __init__( password: bytes, *, kdf_rounds: typing.Optional[int], + hmac_hash: typing.Optional[HashAlgorithm], + key_cert_algorithm: typing.Optional[PBES], ): self._format = format self.password = password self._kdf_rounds = kdf_rounds + self._hmac_hash = hmac_hash + self._key_cert_algorithm = key_cert_algorithm diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs12.py b/src/cryptography/hazmat/primitives/serialization/pkcs12.py index 791befd28347..662ea75af748 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs12.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs12.py @@ -6,6 +6,7 @@ from cryptography import x509 from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives._serialization import PBES as PBES from cryptography.hazmat.primitives.asymmetric import ( dsa, ec, @@ -17,6 +18,14 @@ PRIVATE_KEY_TYPES, ) +__all__ = [ + "PBES", + "PKCS12Certificate", + "PKCS12KeyAndCertificates", + "load_key_and_certificates", + "load_pkcs12", + "serialize_key_and_certificates", +] _ALLOWED_PKCS12_TYPES = typing.Union[ rsa.RSAPrivateKey, diff --git a/tests/hazmat/primitives/test_pkcs12.py b/tests/hazmat/primitives/test_pkcs12.py index ddb0c648d73b..c9ef57e66bd2 100644 --- a/tests/hazmat/primitives/test_pkcs12.py +++ b/tests/hazmat/primitives/test_pkcs12.py @@ -9,6 +9,7 @@ import pytest from cryptography import x509 +from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.backends.openssl.backend import _RC2 from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( @@ -24,6 +25,7 @@ load_pem_private_key, ) from cryptography.hazmat.primitives.serialization.pkcs12 import ( + PBES, PKCS12Certificate, PKCS12KeyAndCertificates, load_key_and_certificates, @@ -530,6 +532,156 @@ def test_generate_unsupported_encryption_type(self, backend): ) assert str(exc.value) == "Unsupported key encryption type" + @pytest.mark.parametrize( + ("enc_alg", "enc_alg_der"), + [ + ( + PBES.PBESv2SHA256AndAES256CBC, + [ + b"\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x05\x0d", # PBESv2 + b"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x01\x2a", # AES + ], + ), + ( + PBES.PBESv1SHA1And3KeyTripleDESCBC, + [b"\x06\x0a\x2a\x86\x48\x86\xf7\x0d\x01\x0c\x01\x03"], + ), + ( + None, + [], + ), + ], + ) + @pytest.mark.parametrize( + ("mac_alg", "mac_alg_der"), + [ + (hashes.SHA1(), b"\x06\x05\x2b\x0e\x03\x02\x1a"), + (hashes.SHA256(), b"\x06\t`\x86H\x01e\x03\x04\x02\x01"), + (None, None), + ], + ) + @pytest.mark.parametrize( + ("iters", "iter_der"), + [ + (420, b"\x02\x02\x01\xa4"), + (22222, b"\x02\x02\x56\xce"), + (None, None), + ], + ) + def test_key_serialization_encryption( + self, + backend, + enc_alg, + enc_alg_der, + mac_alg, + mac_alg_der, + iters, + iter_der, + ): + if ( + enc_alg is PBES.PBESv2SHA256AndAES256CBC + ) and not backend._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: + pytest.skip("PBESv2 is not supported on OpenSSL < 3.0") + + if ( + mac_alg is not None + and not backend._lib.Cryptography_HAS_PKCS12_SET_MAC + ): + pytest.skip("PKCS12_set_mac is not supported (boring)") + + builder = serialization.PrivateFormat.PKCS12.encryption_builder() + if enc_alg is not None: + builder = builder.key_cert_algorithm(enc_alg) + if mac_alg is not None: + builder = builder.hmac_hash(mac_alg) + if iters is not None: + builder = builder.kdf_rounds(iters) + + encryption = builder.build(b"password") + key = ec.generate_private_key(ec.SECP256R1()) + cacert, cakey = _load_ca(backend) + now = datetime.utcnow() + cert = ( + x509.CertificateBuilder() + .subject_name(cacert.subject) + .issuer_name(cacert.subject) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(now) + .not_valid_after(now) + .sign(cakey, hashes.SHA256()) + ) + assert isinstance(cert, x509.Certificate) + p12 = serialize_key_and_certificates( + b"name", key, cert, [cacert], encryption + ) + # We want to know if we've serialized something that has the parameters + # we expect, so we match on specific byte strings of OIDs & DER values. + for der in enc_alg_der: + assert der in p12 + if mac_alg_der is not None: + assert mac_alg_der in p12 + if iter_der is not None: + assert iter_der in p12 + parsed_key, parsed_cert, parsed_more_certs = load_key_and_certificates( + p12, b"password", backend + ) + assert parsed_cert == cert + assert isinstance(parsed_key, ec.EllipticCurvePrivateKey) + assert parsed_key.public_key().public_bytes( + Encoding.PEM, PublicFormat.SubjectPublicKeyInfo + ) == key.public_key().public_bytes( + Encoding.PEM, PublicFormat.SubjectPublicKeyInfo + ) + assert parsed_more_certs == [cacert] + + @pytest.mark.supported( + only_if=lambda backend: ( + not backend._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER + ), + skip_message="Requires OpenSSL < 3.0.0 (or Libre/Boring)", + ) + @pytest.mark.parametrize( + ("algorithm"), + [ + serialization.PrivateFormat.PKCS12.encryption_builder() + .key_cert_algorithm(PBES.PBESv2SHA256AndAES256CBC) + .build(b"password"), + ], + ) + def test_key_serialization_encryption_unsupported( + self, algorithm, backend + ): + cacert, cakey = _load_ca(backend) + with pytest.raises(UnsupportedAlgorithm): + serialize_key_and_certificates( + b"name", cakey, cacert, [], algorithm + ) + + @pytest.mark.supported( + only_if=lambda backend: ( + not backend._lib.Cryptography_HAS_PKCS12_SET_MAC + ), + skip_message="Requires OpenSSL without PKCS12_set_mac (boring only)", + ) + @pytest.mark.parametrize( + "algorithm", + [ + serialization.PrivateFormat.PKCS12.encryption_builder() + .key_cert_algorithm(PBES.PBESv1SHA1And3KeyTripleDESCBC) + .hmac_hash(hashes.SHA256()) + .build(b"password"), + ], + ) + def test_key_serialization_encryption_set_mac_unsupported( + self, algorithm, backend + ): + cacert, cakey = _load_ca(backend) + with pytest.raises(UnsupportedAlgorithm): + serialize_key_and_certificates( + b"name", cakey, cacert, [], algorithm + ) + @pytest.mark.skip_fips( reason="PKCS12 unsupported in FIPS mode. So much bad crypto in it." diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 35f0e83f99fd..3a08d55709b7 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -19,6 +19,7 @@ x25519, x448, ) +from cryptography.hazmat.primitives.hashes import SHA1 from cryptography.hazmat.primitives.serialization import ( BestAvailableEncryption, Encoding, @@ -36,6 +37,7 @@ load_ssh_public_key, ssh, ) +from cryptography.hazmat.primitives.serialization.pkcs12 import PBES from .fixtures_rsa import RSA_KEY_2048 @@ -2445,9 +2447,37 @@ def test_duplicate_kdf_rounds(self): with pytest.raises(ValueError): b.kdf_rounds(12) + def test_invalid_kdf_rounds(self): + b = PrivateFormat.OpenSSH.encryption_builder() + with pytest.raises(ValueError): + b.kdf_rounds(0) + with pytest.raises(ValueError): + b.kdf_rounds(-1) + with pytest.raises(TypeError): + b.kdf_rounds("string") # type: ignore[arg-type] + def test_invalid_password(self): b = PrivateFormat.OpenSSH.encryption_builder() with pytest.raises(ValueError): b.build(12) # type: ignore[arg-type] with pytest.raises(ValueError): b.build(b"") + + def test_unsupported_type_for_methods(self): + b = PrivateFormat.OpenSSH.encryption_builder() + with pytest.raises(TypeError): + b.key_cert_algorithm(PBES.PBESv1SHA1And3KeyTripleDESCBC) + with pytest.raises(TypeError): + b.hmac_hash(SHA1()) + + def test_duplicate_hmac_hash(self): + b = PrivateFormat.PKCS12.encryption_builder().hmac_hash(SHA1()) + with pytest.raises(ValueError): + b.hmac_hash(SHA1()) + + def test_duplicate_key_cert_algorithm(self): + b = PrivateFormat.PKCS12.encryption_builder().key_cert_algorithm( + PBES.PBESv1SHA1And3KeyTripleDESCBC + ) + with pytest.raises(ValueError): + b.key_cert_algorithm(PBES.PBESv1SHA1And3KeyTripleDESCBC)