From 88a08f1df2553caba69b00729da4a83b1f6cce17 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 17:08:14 +0000 Subject: [PATCH 1/5] Move FFDH to decrepit Diffie-Hellman over finite fields was deprecated in 50.0.0 with the intent to remove it, but some protocols mandate it with no alternative to negotiate into (see #15603). Move it into cryptography.hazmat.decrepit.asymmetric.dh, alongside the parameter loaders, which are FFDH-only. The names in cryptography.hazmat.primitives.asymmetric.dh, load_pem_parameters/load_der_parameters in serialization, and loading FFDH keys with the generic key loading APIs still warn, but the warning now says FFDH has moved to decrepit and will only be available there starting in 53.0.0. The deprecated DH shim now takes every value from the decrepit module directly: previously the *WithSerialization aliases wrapped the deprecation marker of the base name rather than the class, because each utils.deprecated call replaces the module global it names. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01TmnZQpY2o4HeBNFN5tfqYE --- CHANGELOG.rst | 8 + docs/hazmat/decrepit/dh.rst | 425 ++++++++++++++++++ docs/hazmat/decrepit/index.rst | 1 + docs/hazmat/primitives/asymmetric/dh.rst | 380 +--------------- docs/hazmat/primitives/asymmetric/index.rst | 4 +- .../primitives/asymmetric/serialization.rst | 54 +-- .../hazmat/bindings/_rust/openssl/dh.pyi | 2 +- .../hazmat/decrepit/asymmetric/__init__.py | 5 + .../hazmat/decrepit/asymmetric/dh.py | 158 +++++++ .../hazmat/primitives/asymmetric/dh.py | 211 ++------- .../hazmat/primitives/asymmetric/types.py | 11 +- .../primitives/serialization/__init__.py | 19 +- src/rust/src/backend/dh.rs | 4 +- src/rust/src/types.rs | 2 - tests/hazmat/primitives/fixtures_dh.py | 45 +- tests/hazmat/primitives/test_dh.py | 126 +++++- tests/hazmat/primitives/test_serialization.py | 18 +- tests/x509/test_x509.py | 5 +- 18 files changed, 816 insertions(+), 662 deletions(-) create mode 100644 docs/hazmat/decrepit/dh.rst create mode 100644 src/cryptography/hazmat/decrepit/asymmetric/__init__.py create mode 100644 src/cryptography/hazmat/decrepit/asymmetric/dh.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 025e6762ea55..c8a3fb1c9c8c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,14 @@ Changelog the key loading APIs (including from X.509 certificates and certificate signing requests). Users should migrate to a more modern signature algorithm. +* Moved Diffie-Hellman key exchange over finite fields (FFDH), which was + deprecated in 50.0.0, into :doc:`/hazmat/decrepit/index` as + :mod:`cryptography.hazmat.decrepit.asymmetric.dh`. The types in + ``cryptography.hazmat.primitives.asymmetric.dh`` remain deprecated, as do + ``load_pem_parameters`` and ``load_der_parameters`` in + ``cryptography.hazmat.primitives.serialization`` and loading FFDH keys with + the key loading APIs. Starting in 53.0.0, FFDH will only be available from + the decrepit module. .. _v50-0-1: diff --git a/docs/hazmat/decrepit/dh.rst b/docs/hazmat/decrepit/dh.rst new file mode 100644 index 000000000000..189d9174a717 --- /dev/null +++ b/docs/hazmat/decrepit/dh.rst @@ -0,0 +1,425 @@ +.. hazmat:: + +Decrepit Diffie-Hellman key exchange +==================================== + +.. module:: cryptography.hazmat.decrepit.asymmetric.dh + +.. testsetup:: + + import base64 + + parameters_pem_data = b""" + -----BEGIN DH PARAMETERS----- + MIGHAoGBALsrWt44U1ojqTy88o0wfjysBE51V6Vtarjm2+5BslQK/RtlndHde3gx + +ccNs+InANszcuJFI8AHt4743kGRzy5XSlul4q4dDJENOHoyqYxueFuFVJELEwLQ + XrX/McKw+hS6GPVQnw6tZhgGo9apdNdYgeLQeQded8Bum8jqzP3rAgEC + -----END DH PARAMETERS----- + """.strip() + + parameters_der_data = base64.b64decode( + b"MIGHAoGBALsrWt44U1ojqTy88o0wfjysBE51V6Vtarjm2+5BslQK/RtlndHde3gx+ccNs+In" + b"ANsz\ncuJFI8AHt4743kGRzy5XSlul4q4dDJENOHoyqYxueFuFVJELEwLQXrX/McKw+hS6GP" + b"VQnw6tZhgG\no9apdNdYgeLQeQded8Bum8jqzP3rAgEC" + ) + +This module contains Diffie-Hellman key exchange over finite fields (FFDH). +FFDH should not be used unless necessary for backwards compatibility or +interoperability with legacy systems. Its use is **strongly discouraged**; +use :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` or +:class:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey` +instead where possible. + +`Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties +to jointly agree on a shared secret using an insecure channel. + + +Exchange Algorithm +~~~~~~~~~~~~~~~~~~ + +For most applications the ``shared_key`` should be passed to a key +derivation function. This allows mixing of additional information into the +key, derivation of multiple keys, and destroys any structure that may be +present. + +.. warning:: + + This example does not give `forward secrecy`_ and is only provided as a + demonstration of the basic Diffie-Hellman construction. For real world + applications always use the ephemeral form described after this example. + +.. code-block:: pycon + + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.decrepit.asymmetric import dh + >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF + >>> # Generate some parameters. These can be reused. + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) + >>> # Generate a private key for use in the exchange. + >>> server_private_key = parameters.generate_private_key() + >>> # In a real handshake the peer is a remote client. For this + >>> # example we'll generate another local private key though. Note that in + >>> # a DH handshake both peers must agree on a common set of parameters. + >>> peer_private_key = parameters.generate_private_key() + >>> shared_key = server_private_key.exchange(peer_private_key.public_key()) + >>> # Perform key derivation. + >>> derived_key = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(shared_key) + >>> # And now we can demonstrate that the handshake performed in the + >>> # opposite direction gives the same final value + >>> same_shared_key = peer_private_key.exchange( + ... server_private_key.public_key() + ... ) + >>> same_derived_key = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(same_shared_key) + >>> derived_key == same_derived_key + +DHE (or EDH), the ephemeral form of this exchange, is **strongly +preferred** over simple DH and provides `forward secrecy`_ when used. You must +generate a new private key using :func:`~DHParameters.generate_private_key` for +each :meth:`~DHPrivateKey.exchange` when performing an DHE key exchange. An +example of the ephemeral form: + +.. code-block:: pycon + + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.decrepit.asymmetric import dh + >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF + >>> # Generate some parameters. These can be reused. + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) + >>> # Generate a private key for use in the exchange. + >>> private_key = parameters.generate_private_key() + >>> # In a real handshake the peer_public_key will be received from the + >>> # other party. For this example we'll generate another private key and + >>> # get a public key from that. Note that in a DH handshake both peers + >>> # must agree on a common set of parameters. + >>> peer_public_key = parameters.generate_private_key().public_key() + >>> shared_key = private_key.exchange(peer_public_key) + >>> # Perform key derivation. + >>> derived_key = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(shared_key) + >>> # For the next handshake we MUST generate another private key, but + >>> # we can reuse the parameters. + >>> private_key_2 = parameters.generate_private_key() + >>> peer_public_key_2 = parameters.generate_private_key().public_key() + >>> shared_key_2 = private_key_2.exchange(peer_public_key_2) + >>> derived_key_2 = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(shared_key_2) + +To assemble a :class:`~DHParameters` and a :class:`~DHPublicKey` from +primitive integers, you must first create the +:class:`~DHParameterNumbers` and :class:`~DHPublicNumbers` objects. For +example, if **p**, **g**, and **y** are :class:`int` objects received from a +peer:: + + pn = dh.DHParameterNumbers(p, g) + parameters = pn.parameters() + peer_public_numbers = dh.DHPublicNumbers(y, pn) + peer_public_key = peer_public_numbers.public_key() + + +Group parameters +~~~~~~~~~~~~~~~~ + +.. function:: generate_parameters(generator, key_size) + + .. versionadded:: 51.0.0 + + Generate a new DH parameter group. + + :param generator: The :class:`int` to use as a generator. Must be + 2 or 5. + + :param key_size: The bit length of the prime modulus to generate. + + :returns: DH parameters as a new instance of + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters`. + + :raises ValueError: If ``key_size`` is not at least 512. + + +.. class:: DHParameters + + .. versionadded:: 51.0.0 + + .. method:: generate_private_key() + + Generate a DH private key. This method can be used to generate many + new private keys from a single set of parameters. + + :return: An instance of + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`. + + .. method:: parameter_numbers() + + Return the numbers that make up this set of parameters. + + :return: A :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameterNumbers`. + + .. method:: parameter_bytes(encoding, format) + + Allows serialization of the parameters to bytes. Encoding ( + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and + format ( + :attr:`~cryptography.hazmat.primitives.serialization.ParameterFormat.PKCS3`) + are chosen to define the exact serialization. + + :param encoding: A value from the + :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. + + :param format: A value from the + :class:`~cryptography.hazmat.primitives.serialization.ParameterFormat` + enum. At the moment only ``PKCS3`` is supported. + + :return bytes: Serialized parameters. + +Parameter serialization +~~~~~~~~~~~~~~~~~~~~~~~ + +.. function:: load_pem_parameters(data) + + .. versionadded:: 51.0.0 + + Deserialize parameters from PEM encoded data. + + .. doctest:: + + >>> from cryptography.hazmat.decrepit.asymmetric import dh + >>> parameters = dh.load_pem_parameters(parameters_pem_data) + >>> isinstance(parameters, dh.DHParameters) + True + + :param bytes data: The PEM encoded parameters data. + + :returns: An instance of :class:`DHParameters`. + + :raises ValueError: If the PEM data's structure could not be decoded + successfully. + + :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized + parameters type is not supported by the OpenSSL version + ``cryptography`` is using. + +.. function:: load_der_parameters(data) + + .. versionadded:: 51.0.0 + + Deserialize parameters from DER encoded data. + + .. doctest:: + + >>> from cryptography.hazmat.decrepit.asymmetric import dh + >>> parameters = dh.load_der_parameters(parameters_der_data) + >>> isinstance(parameters, dh.DHParameters) + True + + :param bytes data: The DER encoded parameters data. + + :returns: An instance of :class:`DHParameters`. + + :raises ValueError: If the DER data's structure could not be decoded + successfully. + + :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized + parameters type is not supported by the OpenSSL version + ``cryptography`` is using. + + +Key interfaces +~~~~~~~~~~~~~~ + +.. class:: DHPrivateKey + + .. versionadded:: 51.0.0 + + .. attribute:: key_size + + The bit length of the prime modulus. + + .. method:: public_key() + + Return the public key associated with this private key. + + :return: A :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey`. + + .. method:: parameters() + + Return the parameters associated with this private key. + + :return: A :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters`. + + .. method:: exchange(peer_public_key) + + :param DHPublicKey peer_public_key: The public key for + the peer. + + :return bytes: The agreed key. The bytes are ordered in 'big' endian. + + .. method:: private_numbers() + + Return the numbers that make up this private key. + + :return: A :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateNumbers`. + + .. method:: private_bytes(encoding, format, encryption_algorithm) + + Allows serialization of the key to bytes. Encoding ( + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`), + format ( + :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`) + and encryption algorithm (such as + :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption` + or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`) + are chosen to define the exact serialization. + + :param encoding: A value from the + :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. + + :param format: A value from the + :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat` + enum. + + :param encryption_algorithm: An instance of an object conforming to the + :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption` + interface. + + :return bytes: Serialized key. + + +.. class:: DHPublicKey + + .. versionadded:: 51.0.0 + + .. attribute:: key_size + + The bit length of the prime modulus. + + .. method:: parameters() + + Return the parameters associated with this private key. + + :return: A :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters`. + + .. method:: public_numbers() + + Return the numbers that make up this public key. + + :return: A :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicNumbers`. + + .. method:: public_bytes(encoding, format) + + Allows serialization of the key to bytes. Encoding ( + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and + format ( + :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`) + are chosen to define the exact serialization. + + :param encoding: A value from the + :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. + + :param format: A value from the + :class:`~cryptography.hazmat.primitives.serialization.PublicFormat` enum. + + :return bytes: Serialized key. + +Numbers +~~~~~~~ + +.. class:: DHParameterNumbers(p, g, q=None) + + .. versionadded:: 51.0.0 + + The collection of integers that define a Diffie-Hellman group. + + .. attribute:: p + + :type: int + + The prime modulus value. + + .. attribute:: g + + :type: int + + The generator value. Must be 2 or greater. + + .. attribute:: q + + :type: int + + p subgroup order value. + + .. method:: parameters() + + :returns: A new instance of :class:`DHParameters`. + + :raises ValueError: If the parameters are invalid. + +.. class:: DHPrivateNumbers(x, public_numbers) + + .. versionadded:: 51.0.0 + + The collection of integers that make up a Diffie-Hellman private key. + + .. attribute:: public_numbers + + :type: :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicNumbers` + + The :class:`DHPublicNumbers` which makes up the DH public + key associated with this DH private key. + + .. attribute:: x + + :type: int + + The private value. + + .. method:: private_key() + + :returns: A new instance of :class:`DHPrivateKey`. + + +.. class:: DHPublicNumbers(y, parameter_numbers) + + .. versionadded:: 51.0.0 + + The collection of integers that make up a Diffie-Hellman public key. + + .. attribute:: parameter_numbers + + :type: :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameterNumbers` + + The parameters for this DH group. + + .. attribute:: y + + :type: int + + The public value. + + .. method:: public_key() + + :returns: A new instance of :class:`DHPublicKey`. + + +.. _`Diffie-Hellman key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange +.. _`forward secrecy`: https://en.wikipedia.org/wiki/Forward_secrecy diff --git a/docs/hazmat/decrepit/index.rst b/docs/hazmat/decrepit/index.rst index a925cc55abf5..61a49195cbaf 100644 --- a/docs/hazmat/decrepit/index.rst +++ b/docs/hazmat/decrepit/index.rst @@ -13,3 +13,4 @@ their use is **strongly discouraged**. ciphers modes + dh diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index 7e53848acf8c..15f53cbb15b5 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -5,372 +5,20 @@ Diffie-Hellman key exchange .. currentmodule:: cryptography.hazmat.primitives.asymmetric.dh -.. deprecated:: 50.0.0 - Diffie-Hellman over finite fields (FFDH) is deprecated and support will - be removed in a future release. Users should migrate to a more modern - key exchange algorithm. - -.. note:: - For security and performance reasons we suggest using - :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` instead of DH - where possible. - - -`Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties -to jointly agree on a shared secret using an insecure channel. - - -Exchange Algorithm -~~~~~~~~~~~~~~~~~~ - -For most applications the ``shared_key`` should be passed to a key -derivation function. This allows mixing of additional information into the -key, derivation of multiple keys, and destroys any structure that may be -present. - .. warning:: - This example does not give `forward secrecy`_ and is only provided as a - demonstration of the basic Diffie-Hellman construction. For real world - applications always use the ephemeral form described after this example. - -.. code-block:: pycon - - >>> from cryptography.hazmat.primitives import hashes - >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF - >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048) - >>> # Generate a private key for use in the exchange. - >>> server_private_key = parameters.generate_private_key() - >>> # In a real handshake the peer is a remote client. For this - >>> # example we'll generate another local private key though. Note that in - >>> # a DH handshake both peers must agree on a common set of parameters. - >>> peer_private_key = parameters.generate_private_key() - >>> shared_key = server_private_key.exchange(peer_private_key.public_key()) - >>> # Perform key derivation. - >>> derived_key = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(shared_key) - >>> # And now we can demonstrate that the handshake performed in the - >>> # opposite direction gives the same final value - >>> same_shared_key = peer_private_key.exchange( - ... server_private_key.public_key() - ... ) - >>> same_derived_key = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(same_shared_key) - >>> derived_key == same_derived_key - -DHE (or EDH), the ephemeral form of this exchange, is **strongly -preferred** over simple DH and provides `forward secrecy`_ when used. You must -generate a new private key using :func:`~DHParameters.generate_private_key` for -each :meth:`~DHPrivateKey.exchange` when performing an DHE key exchange. An -example of the ephemeral form: - -.. code-block:: pycon - - >>> from cryptography.hazmat.primitives import hashes - >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF - >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048) - >>> # Generate a private key for use in the exchange. - >>> private_key = parameters.generate_private_key() - >>> # In a real handshake the peer_public_key will be received from the - >>> # other party. For this example we'll generate another private key and - >>> # get a public key from that. Note that in a DH handshake both peers - >>> # must agree on a common set of parameters. - >>> peer_public_key = parameters.generate_private_key().public_key() - >>> shared_key = private_key.exchange(peer_public_key) - >>> # Perform key derivation. - >>> derived_key = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(shared_key) - >>> # For the next handshake we MUST generate another private key, but - >>> # we can reuse the parameters. - >>> private_key_2 = parameters.generate_private_key() - >>> peer_public_key_2 = parameters.generate_private_key().public_key() - >>> shared_key_2 = private_key_2.exchange(peer_public_key_2) - >>> derived_key_2 = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(shared_key_2) - -To assemble a :class:`~DHParameters` and a :class:`~DHPublicKey` from -primitive integers, you must first create the -:class:`~DHParameterNumbers` and :class:`~DHPublicNumbers` objects. For -example, if **p**, **g**, and **y** are :class:`int` objects received from a -peer:: - - pn = dh.DHParameterNumbers(p, g) - parameters = pn.parameters() - peer_public_numbers = dh.DHPublicNumbers(y, pn) - peer_public_key = peer_public_numbers.public_key() - - -Group parameters -~~~~~~~~~~~~~~~~ - -.. function:: generate_parameters(generator, key_size) - - .. versionadded:: 1.7 - - Generate a new DH parameter group. - - :param generator: The :class:`int` to use as a generator. Must be - 2 or 5. - - :param key_size: The bit length of the prime modulus to generate. - - :returns: DH parameters as a new instance of - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. - - :raises ValueError: If ``key_size`` is not at least 512. - - -.. class:: DHParameters - - .. versionadded:: 1.7 - - - .. method:: generate_private_key() - - Generate a DH private key. This method can be used to generate many - new private keys from a single set of parameters. - - :return: An instance of - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`. - - .. method:: parameter_numbers() - - Return the numbers that make up this set of parameters. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`. - - .. method:: parameter_bytes(encoding, format) - - .. versionadded:: 2.0 - - Allows serialization of the parameters to bytes. Encoding ( - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and - format ( - :attr:`~cryptography.hazmat.primitives.serialization.ParameterFormat.PKCS3`) - are chosen to define the exact serialization. - - :param encoding: A value from the - :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. - - :param format: A value from the - :class:`~cryptography.hazmat.primitives.serialization.ParameterFormat` - enum. At the moment only ``PKCS3`` is supported. - - :return bytes: Serialized parameters. - -Key interfaces -~~~~~~~~~~~~~~ - -.. class:: DHPrivateKey - - .. versionadded:: 1.7 - - .. attribute:: key_size - - The bit length of the prime modulus. - - .. method:: public_key() - - Return the public key associated with this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`. - - .. method:: parameters() - - Return the parameters associated with this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. - - .. method:: exchange(peer_public_key) - - .. versionadded:: 1.7 - - :param DHPublicKey peer_public_key: The public key for - the peer. - - :return bytes: The agreed key. The bytes are ordered in 'big' endian. - - .. method:: private_numbers() - - Return the numbers that make up this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateNumbers`. - - .. method:: private_bytes(encoding, format, encryption_algorithm) - - .. versionadded:: 1.8 - - Allows serialization of the key to bytes. Encoding ( - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`), - format ( - :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`) - and encryption algorithm (such as - :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption` - or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`) - are chosen to define the exact serialization. - - :param encoding: A value from the - :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. - - :param format: A value from the - :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat` - enum. - - :param encryption_algorithm: An instance of an object conforming to the - :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption` - interface. - - :return bytes: Serialized key. - - -.. class:: DHPublicKey - - .. versionadded:: 1.7 - - .. attribute:: key_size - - The bit length of the prime modulus. - - .. method:: parameters() - - Return the parameters associated with this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. - - .. method:: public_numbers() - - Return the numbers that make up this public key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers`. - - .. method:: public_bytes(encoding, format) - - .. versionadded:: 1.8 - - Allows serialization of the key to bytes. Encoding ( - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and - format ( - :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`) - are chosen to define the exact serialization. - - :param encoding: A value from the - :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. - - :param format: A value from the - :class:`~cryptography.hazmat.primitives.serialization.PublicFormat` enum. - - :return bytes: Serialized key. - -Numbers -~~~~~~~ - -.. class:: DHParameterNumbers(p, g, q=None) - - .. versionadded:: 0.8 - - The collection of integers that define a Diffie-Hellman group. - - .. attribute:: p - - :type: int - - The prime modulus value. - - .. attribute:: g - - :type: int - - The generator value. Must be 2 or greater. - - .. attribute:: q - - .. versionadded:: 1.8 - - :type: int - - p subgroup order value. - - .. method:: parameters() - - .. versionadded:: 1.7 - - :returns: A new instance of :class:`DHParameters`. - - :raises ValueError: If the parameters are invalid. - -.. class:: DHPrivateNumbers(x, public_numbers) - - .. versionadded:: 0.8 - - The collection of integers that make up a Diffie-Hellman private key. - - .. attribute:: public_numbers - - :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers` - - The :class:`DHPublicNumbers` which makes up the DH public - key associated with this DH private key. - - .. attribute:: x - - :type: int - - The private value. - - .. method:: private_key() - - .. versionadded:: 1.7 - - :returns: A new instance of :class:`DHPrivateKey`. - - -.. class:: DHPublicNumbers(y, parameter_numbers) - - .. versionadded:: 0.8 - - The collection of integers that make up a Diffie-Hellman public key. - - .. attribute:: parameter_numbers - - :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers` - - The parameters for this DH group. - - .. attribute:: y - - :type: int - - The public value. - - .. method:: public_key() - - .. versionadded:: 1.7 - - :returns: A new instance of :class:`DHPublicKey`. - + Diffie-Hellman over finite fields (FFDH) has been deprecated and moved to + the :doc:`/hazmat/decrepit/index` module. If you need to continue using it + then update your code to use + :mod:`cryptography.hazmat.decrepit.asymmetric.dh`. Starting in 53.0.0 it + will only be available from that module. Users should migrate to a more + modern key exchange algorithm such as + :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` or + :class:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey` + where possible. -.. _`Diffie-Hellman key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange -.. _`forward secrecy`: https://en.wikipedia.org/wiki/Forward_secrecy +The classes and functions previously documented here, ``generate_parameters``, +``DHParameters``, ``DHPrivateKey``, ``DHPublicKey``, ``DHParameterNumbers``, +``DHPrivateNumbers``, and ``DHPublicNumbers``, are documented in +:doc:`/hazmat/decrepit/dh`. Accessing any of them through this module emits a +``CryptographyDeprecationWarning``. diff --git a/docs/hazmat/primitives/asymmetric/index.rst b/docs/hazmat/primitives/asymmetric/index.rst index caf5116180a7..eae30f86fd03 100644 --- a/docs/hazmat/primitives/asymmetric/index.rst +++ b/docs/hazmat/primitives/asymmetric/index.rst @@ -53,7 +53,7 @@ union type aliases can be used instead to reference a multitude of key types. .. versionadded:: 40.0.0 Type alias: A union of all public key types supported: - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`, @@ -67,7 +67,7 @@ union type aliases can be used instead to reference a multitude of key types. .. versionadded:: 40.0.0 Type alias: A union of all private key types supported: - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey`, diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 26764314059d..50725a9fb105 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -163,7 +163,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`, - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`, or :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` depending on the contents of ``data``. @@ -202,7 +202,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`, - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey`, or :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` depending on the contents of ``data``. @@ -216,25 +216,18 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END .. function:: load_pem_parameters(data) .. versionadded:: 2.0 - .. deprecated:: 50.0.0 - Diffie-Hellman over finite fields (FFDH) is deprecated and support - will be removed in a future release. + .. deprecated:: 51.0.0 + This function has been moved to + :func:`cryptography.hazmat.decrepit.asymmetric.dh.load_pem_parameters` + and will be removed from this module in 53.0.0. Deserialize parameters from PEM encoded data to one of the supported asymmetric parameters types. - .. doctest:: - - >>> from cryptography.hazmat.primitives.serialization import load_pem_parameters - >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> parameters = load_pem_parameters(parameters_pem_data) - >>> isinstance(parameters, dh.DHParameters) - True - :param bytes data: The PEM encoded parameters data. :returns: Currently only - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters` + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters` supported. :raises ValueError: If the PEM data's structure could not be decoded @@ -285,7 +278,7 @@ the rest. :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`, - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`, or :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` depending on the contents of ``data``. @@ -325,7 +318,7 @@ the rest. :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`, - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey`, or :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` depending on the contents of ``data``. @@ -348,9 +341,10 @@ the rest. .. versionadded:: 2.0 - .. deprecated:: 50.0.0 - Diffie-Hellman over finite fields (FFDH) is deprecated and support - will be removed in a future release. + .. deprecated:: 51.0.0 + This function has been moved to + :func:`cryptography.hazmat.decrepit.asymmetric.dh.load_der_parameters` + and will be removed from this module in 53.0.0. Deserialize parameters from DER encoded data to one of the supported asymmetric parameters types. @@ -358,7 +352,7 @@ the rest. :param bytes data: The DER encoded parameters data. :returns: Currently only - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters` + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters` supported. :raises ValueError: If the DER data's structure could not be decoded @@ -367,14 +361,6 @@ the rest. :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key type is not supported by the OpenSSL version ``cryptography`` is using. - .. doctest:: - - >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> from cryptography.hazmat.primitives.serialization import load_der_parameters - >>> parameters = load_der_parameters(parameters_der_data) - >>> isinstance(parameters, dh.DHParameters) - True - OpenSSH Public Key ~~~~~~~~~~~~~~~~~~ @@ -1681,7 +1667,7 @@ Serialization Formats :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` , :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` - , :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey` + , :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey` and :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`. @@ -1791,7 +1777,7 @@ Serialization Formats :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` , :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` - , :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey` + , :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey` , and :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`. @@ -1855,7 +1841,7 @@ Serialization Formats An enumeration for parameters formats. Used with the ``parameter_bytes`` method available on - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters`. .. attribute:: PKCS3 @@ -1872,13 +1858,13 @@ Serialization Encodings :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` , :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` - , :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`, + , :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`, and :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey` as well as ``public_bytes`` on :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`, - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`, + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`, and :class:`~cryptography.hazmat.primitives.asymmetric.x448.X448PublicKey`. @@ -1936,7 +1922,7 @@ Serialization Encryption Types :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` , :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` - , :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey` + , :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey` and :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`. All other classes in this section represent the available choices for diff --git a/src/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/src/cryptography/hazmat/bindings/_rust/openssl/dh.pyi index 08733d745c3d..6d03617c034a 100644 --- a/src/cryptography/hazmat/bindings/_rust/openssl/dh.pyi +++ b/src/cryptography/hazmat/bindings/_rust/openssl/dh.pyi @@ -4,7 +4,7 @@ import typing -from cryptography.hazmat.primitives.asymmetric import dh +from cryptography.hazmat.decrepit.asymmetric import dh MIN_MODULUS_SIZE: int diff --git a/src/cryptography/hazmat/decrepit/asymmetric/__init__.py b/src/cryptography/hazmat/decrepit/asymmetric/__init__.py new file mode 100644 index 000000000000..41d731863aa2 --- /dev/null +++ b/src/cryptography/hazmat/decrepit/asymmetric/__init__.py @@ -0,0 +1,5 @@ +# 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 annotations diff --git a/src/cryptography/hazmat/decrepit/asymmetric/dh.py b/src/cryptography/hazmat/decrepit/asymmetric/dh.py new file mode 100644 index 000000000000..3da4ee96f6eb --- /dev/null +++ b/src/cryptography/hazmat/decrepit/asymmetric/dh.py @@ -0,0 +1,158 @@ +# 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 annotations + +import abc + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + +generate_parameters = rust_openssl.dh.generate_parameters + +load_pem_parameters = rust_openssl.dh.from_pem_parameters +load_der_parameters = rust_openssl.dh.from_der_parameters + +DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers +DHPublicNumbers = rust_openssl.dh.DHPublicNumbers +DHParameterNumbers = rust_openssl.dh.DHParameterNumbers + + +class DHParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> DHPrivateKey: + """ + Generates and returns a DHPrivateKey. + """ + + @abc.abstractmethod + def parameter_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.ParameterFormat, + ) -> bytes: + """ + Returns the parameters serialized as bytes. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> DHParameterNumbers: + """ + Returns a DHParameterNumbers. + """ + + +DHParameters.register(rust_openssl.dh.DHParameters) + + +class DHPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> DHPublicNumbers: + """ + Returns a DHPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> DHPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DHPublicKey: + """ + Returns a deep copy. + """ + + +DHPublicKey.register(rust_openssl.dh.DHPublicKey) + + +class DHPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> DHPublicKey: + """ + The DHPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: DHPublicKey) -> bytes: + """ + Given peer's DHPublicKey, carry out the key exchange and + return shared key as bytes. + """ + + @abc.abstractmethod + def private_numbers(self) -> DHPrivateNumbers: + """ + Returns a DHPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __copy__(self) -> DHPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DHPrivateKey: + """ + Returns a deep copy. + """ + + +DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py index 814fdd3f8311..9c3f10f1335a 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dh.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -4,250 +4,95 @@ from __future__ import annotations -import abc - from cryptography import utils -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization +from cryptography.hazmat.decrepit.asymmetric import dh as _decrepit_dh _FFDH_DEPRECATION_MSG = ( - "Diffie-Hellman over finite fields (FFDH) is deprecated and support " - "will be removed in a future release. Use a more modern key exchange " - "algorithm." + "Diffie-Hellman over finite fields (FFDH) is deprecated and has been " + "moved to cryptography.hazmat.decrepit.asymmetric.dh. Starting in " + "53.0.0 it will only be available from that module." ) -generate_parameters = rust_openssl.dh.generate_parameters - - -DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers -DHPublicNumbers = rust_openssl.dh.DHPublicNumbers -DHParameterNumbers = rust_openssl.dh.DHParameterNumbers - - -class DHParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> DHPrivateKey: - """ - Generates and returns a DHPrivateKey. - """ - - @abc.abstractmethod - def parameter_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.ParameterFormat, - ) -> bytes: - """ - Returns the parameters serialized as bytes. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> DHParameterNumbers: - """ - Returns a DHParameterNumbers. - """ - - -DHParametersWithSerialization = DHParameters -DHParameters.register(rust_openssl.dh.DHParameters) - - -class DHPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> DHPublicNumbers: - """ - Returns a DHPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> DHPublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> DHPublicKey: - """ - Returns a deep copy. - """ - - -DHPublicKeyWithSerialization = DHPublicKey -DHPublicKey.register(rust_openssl.dh.DHPublicKey) - - -class DHPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> DHPublicKey: - """ - The DHPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this private key. - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: DHPublicKey) -> bytes: - """ - Given peer's DHPublicKey, carry out the key exchange and - return shared key as bytes. - """ - - @abc.abstractmethod - def private_numbers(self) -> DHPrivateNumbers: - """ - Returns a DHPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __copy__(self) -> DHPrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> DHPrivateKey: - """ - Returns a deep copy. - """ - - -DHPrivateKeyWithSerialization = DHPrivateKey -DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) - -# Aliases that do not emit the deprecation warning on attribute access, for -# internal use (e.g. the unions in -# cryptography.hazmat.primitives.asymmetric.types, which are evaluated at -# import time). -_DHPublicKey = DHPublicKey -_DHPrivateKey = DHPrivateKey - +# Every name is taken from the decrepit module directly (rather than from a +# module-level alias) because each ``utils.deprecated`` call replaces the +# module attribute it names, so a later call reading that attribute would +# wrap the deprecation marker instead of the underlying object. utils.deprecated( - generate_parameters, + _decrepit_dh.generate_parameters, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="generate_parameters", ) utils.deprecated( - DHPrivateNumbers, + _decrepit_dh.DHPrivateNumbers, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHPrivateNumbers", ) utils.deprecated( - DHPublicNumbers, + _decrepit_dh.DHPublicNumbers, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHPublicNumbers", ) utils.deprecated( - DHParameterNumbers, + _decrepit_dh.DHParameterNumbers, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHParameterNumbers", ) utils.deprecated( - DHParameters, + _decrepit_dh.DHParameters, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHParameters", ) utils.deprecated( - DHParameters, + _decrepit_dh.DHParameters, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHParametersWithSerialization", ) utils.deprecated( - DHPublicKey, + _decrepit_dh.DHPublicKey, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHPublicKey", ) utils.deprecated( - DHPublicKey, + _decrepit_dh.DHPublicKey, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHPublicKeyWithSerialization", ) utils.deprecated( - DHPrivateKey, + _decrepit_dh.DHPrivateKey, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHPrivateKey", ) utils.deprecated( - DHPrivateKey, + _decrepit_dh.DHPrivateKey, __name__, _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + utils.DeprecatedIn51, name="DHPrivateKeyWithSerialization", ) diff --git a/src/cryptography/hazmat/primitives/asymmetric/types.py b/src/cryptography/hazmat/primitives/asymmetric/types.py index ba20353eb46e..831c867a3995 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/types.py +++ b/src/cryptography/hazmat/primitives/asymmetric/types.py @@ -6,8 +6,8 @@ import typing +from cryptography.hazmat.decrepit.asymmetric import dh from cryptography.hazmat.primitives.asymmetric import ( - dh, dsa, ec, ed448, @@ -19,10 +19,11 @@ x25519, ) -# Every asymmetric key type. These use the private DH and DSA aliases so that -# importing this module doesn't trigger the FFDH or DSA deprecation warnings. +# Every asymmetric key type. FFDH types come from the decrepit module and DSA +# uses its private aliases so that importing this module doesn't trigger the +# FFDH or DSA deprecation warnings. PublicKeyTypes = typing.Union[ - dh._DHPublicKey, + dh.DHPublicKey, dsa._DSAPublicKey, rsa.RSAPublicKey, ec.EllipticCurvePublicKey, @@ -38,7 +39,7 @@ ] # Every asymmetric key type PrivateKeyTypes = typing.Union[ - dh._DHPrivateKey, + dh.DHPrivateKey, ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey, mldsa.MLDSA44PrivateKey, diff --git a/src/cryptography/hazmat/primitives/serialization/__init__.py b/src/cryptography/hazmat/primitives/serialization/__init__.py index 40beb2196ad7..3bb12d436c61 100644 --- a/src/cryptography/hazmat/primitives/serialization/__init__.py +++ b/src/cryptography/hazmat/primitives/serialization/__init__.py @@ -15,7 +15,6 @@ PublicFormat, _KeySerializationEncryption, ) -from cryptography.hazmat.primitives.asymmetric.dh import _FFDH_DEPRECATION_MSG from cryptography.hazmat.primitives.serialization.base import ( load_der_parameters, load_der_private_key, @@ -66,20 +65,26 @@ "ssh_key_fingerprint", ] -# These can only load FFDH parameters, so the functions themselves are -# deprecated alongside the rest of FFDH. +# These can only load FFDH parameters, so they have moved to the decrepit +# module alongside the rest of FFDH. utils.deprecated( load_pem_parameters, __name__, - _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + "load_pem_parameters has been moved to " + "cryptography.hazmat.decrepit.asymmetric.dh.load_pem_parameters and " + "will be removed from " + "cryptography.hazmat.primitives.serialization in 53.0.0.", + utils.DeprecatedIn51, name="load_pem_parameters", ) utils.deprecated( load_der_parameters, __name__, - _FFDH_DEPRECATION_MSG, - utils.DeprecatedIn50, + "load_der_parameters has been moved to " + "cryptography.hazmat.decrepit.asymmetric.dh.load_der_parameters and " + "will be removed from " + "cryptography.hazmat.primitives.serialization in 53.0.0.", + utils.DeprecatedIn51, name="load_der_parameters", ) diff --git a/src/rust/src/backend/dh.rs b/src/rust/src/backend/dh.rs index 7e5001dfd68e..db47150ef5c3 100644 --- a/src/rust/src/backend/dh.rs +++ b/src/rust/src/backend/dh.rs @@ -11,8 +11,8 @@ use crate::error::{CryptographyError, CryptographyResult}; use crate::{types, x509}; fn warn_ffdh_deprecated(py: pyo3::Python<'_>) -> pyo3::PyResult<()> { - let warning_cls = types::DEPRECATED_IN_50.get(py)?; - let message = c"Diffie-Hellman over finite fields (FFDH) is deprecated and support will be removed in a future release. Use a more modern key exchange algorithm."; + let warning_cls = types::DEPRECATED_IN_51.get(py)?; + let message = c"Diffie-Hellman over finite fields (FFDH) is deprecated and has been moved to cryptography.hazmat.decrepit.asymmetric.dh. Starting in 53.0.0 it will only be available from that module."; pyo3::PyErr::warn(py, &warning_cls, message, 1) } diff --git a/src/rust/src/types.rs b/src/rust/src/types.rs index d0fff8be732f..8ea0be91851e 100644 --- a/src/rust/src/types.rs +++ b/src/rust/src/types.rs @@ -47,8 +47,6 @@ pub static DEPRECATED_IN_42: LazyPyImport = LazyPyImport::new("cryptography.utils", &["DeprecatedIn42"]); pub static DEPRECATED_IN_43: LazyPyImport = LazyPyImport::new("cryptography.utils", &["DeprecatedIn43"]); -pub static DEPRECATED_IN_50: LazyPyImport = - LazyPyImport::new("cryptography.utils", &["DeprecatedIn50"]); pub static DEPRECATED_IN_51: LazyPyImport = LazyPyImport::new("cryptography.utils", &["DeprecatedIn51"]); diff --git a/tests/hazmat/primitives/fixtures_dh.py b/tests/hazmat/primitives/fixtures_dh.py index 28fee175fc5c..9df2e1f8c208 100644 --- a/tests/hazmat/primitives/fixtures_dh.py +++ b/tests/hazmat/primitives/fixtures_dh.py @@ -3,30 +3,23 @@ # for complete details. -import warnings +from cryptography.hazmat.decrepit.asymmetric import dh -from cryptography import utils -from cryptography.hazmat.primitives.asymmetric import dh - -# This module is imported at test collection time; suppress the FFDH -# deprecation warning so it doesn't appear in the warning summary. -with warnings.catch_warnings(): - warnings.simplefilter("ignore", utils.DeprecatedIn50) - FFDH3072_P = dh.DHParameterNumbers( - p=int( - "ffffffffffffffffadf85458a2bb4a9aafdc5620273d3cf1d8b9c583ce2d3695a9e" - "13641146433fbcc939dce249b3ef97d2fe363630c75d8f681b202aec4617ad3df1e" - "d5d5fd65612433f51f5f066ed0856365553ded1af3b557135e7f57c935984f0c70e" - "0e68b77e2a689daf3efe8721df158a136ade73530acca4f483a797abc0ab182b324" - "fb61d108a94bb2c8e3fbb96adab760d7f4681d4f42a3de394df4ae56ede76372bb1" - "90b07a7c8ee0a6d709e02fce1cdf7e2ecc03404cd28342f619172fe9ce98583ff8e" - "4f1232eef28183c3fe3b1b4c6fad733bb5fcbc2ec22005c58ef1837d1683b2c6f34" - "a26c1b2effa886b4238611fcfdcde355b3b6519035bbc34f4def99c023861b46fc9" - "d6e6c9077ad91d2691f7f7ee598cb0fac186d91caefe130985139270b4130c93bc4" - "37944f4fd4452e2d74dd364f2e21e71f54bff5cae82ab9c9df69ee86d2bc522363a" - "0dabc521979b0deada1dbf9a42d5c4484e0abcd06bfa53ddef3c1b20ee3fd59d7c2" - "5e41d2b66c62e37ffffffffffffffff", - 16, - ), - g=2, - ) +FFDH3072_P = dh.DHParameterNumbers( + p=int( + "ffffffffffffffffadf85458a2bb4a9aafdc5620273d3cf1d8b9c583ce2d3695a9e" + "13641146433fbcc939dce249b3ef97d2fe363630c75d8f681b202aec4617ad3df1e" + "d5d5fd65612433f51f5f066ed0856365553ded1af3b557135e7f57c935984f0c70e" + "0e68b77e2a689daf3efe8721df158a136ade73530acca4f483a797abc0ab182b324" + "fb61d108a94bb2c8e3fbb96adab760d7f4681d4f42a3de394df4ae56ede76372bb1" + "90b07a7c8ee0a6d709e02fce1cdf7e2ecc03404cd28342f619172fe9ce98583ff8e" + "4f1232eef28183c3fe3b1b4c6fad733bb5fcbc2ec22005c58ef1837d1683b2c6f34" + "a26c1b2effa886b4238611fcfdcde355b3b6519035bbc34f4def99c023861b46fc9" + "d6e6c9077ad91d2691f7f7ee598cb0fac186d91caefe130985139270b4130c93bc4" + "37944f4fd4452e2d74dd364f2e21e71f54bff5cae82ab9c9df69ee86d2bc522363a" + "0dabc521979b0deada1dbf9a42d5c4484e0abcd06bfa53ddef3c1b20ee3fd59d7c2" + "5e41d2b66c62e37ffffffffffffffff", + 16, + ), + g=2, +) diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index a9505688ecdf..e7739bbe31c8 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -13,16 +13,17 @@ from cryptography import utils from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.decrepit.asymmetric import dh from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import dh +from cryptography.hazmat.primitives.asymmetric import dh as deprecated_dh from ...doubles import DummyKeySerializationEncryption from ...utils import load_nist_vectors, load_vectors_from_file from .fixtures_dh import FFDH3072_P -# Accessing any attribute of the dh module and loading any DH key or -# parameters emits the FFDH deprecation warning. Ignore it module-wide -# rather than wrapping every call site. +# Loading a DH key with the generic key loading APIs emits the FFDH +# deprecation warning. Ignore it module-wide rather than wrapping every call +# site; the deprecation itself is tested in TestFFDHDeprecation. pytestmark = pytest.mark.filterwarnings( "ignore:Diffie-Hellman over finite fields" ":cryptography.utils.CryptographyDeprecationWarning" @@ -924,11 +925,9 @@ def test_parameter_bytes(self, encoding): encoding, serialization.ParameterFormat.PKCS3 ) if encoding is serialization.Encoding.PEM: - with pytest.warns(utils.DeprecatedIn50): - loaded_key = serialization.load_pem_parameters(serialized) + loaded_key = dh.load_pem_parameters(serialized) else: - with pytest.warns(utils.DeprecatedIn50): - loaded_key = serialization.load_der_parameters(serialized) + loaded_key = dh.load_der_parameters(serialized) loaded_param_num = loaded_key.parameter_numbers() assert loaded_param_num == parameters.parameter_numbers() @@ -965,15 +964,9 @@ def test_parameter_bytes_match( param_path, lambda pemfile: pemfile.read(), mode="rb" ) if encoding is serialization.Encoding.PEM: - with pytest.warns(utils.DeprecatedIn50): - parameters = serialization.load_pem_parameters( - param_bytes, backend - ) + parameters = dh.load_pem_parameters(param_bytes, backend) else: - with pytest.warns(utils.DeprecatedIn50): - parameters = serialization.load_der_parameters( - param_bytes, backend - ) + parameters = dh.load_der_parameters(param_bytes, backend) serialized = parameters.parameter_bytes( encoding, serialization.ParameterFormat.PKCS3, @@ -1011,11 +1004,9 @@ def test_public_bytes_values(self, param_path, encoding, vec_path): ) vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] if encoding is serialization.Encoding.PEM: - with pytest.warns(utils.DeprecatedIn50): - parameters = serialization.load_pem_parameters(key_bytes) + parameters = dh.load_pem_parameters(key_bytes) else: - with pytest.warns(utils.DeprecatedIn50): - parameters = serialization.load_der_parameters(key_bytes) + parameters = dh.load_der_parameters(key_bytes) parameter_numbers = parameters.parameter_numbers() assert parameter_numbers.g == int(vec["g"], 16) assert parameter_numbers.p == int(vec["p"], 16) @@ -1033,8 +1024,7 @@ def test_load_pkcs3_with_private_value_length(self): lambda pemfile: pemfile.read(), mode="rb", ) - with pytest.warns(utils.DeprecatedIn50): - parameters = serialization.load_pem_parameters(param_bytes) + parameters = dh.load_pem_parameters(param_bytes) parameter_numbers = parameters.parameter_numbers() assert parameter_numbers.g == 2 assert parameter_numbers.q is None @@ -1091,3 +1081,95 @@ def test_parameter_bytes_openssh_unsupported(self): serialization.Encoding.OpenSSH, serialization.ParameterFormat.PKCS3, ) + + +class TestFFDHDeprecation: + @pytest.mark.parametrize( + "name", + [ + "generate_parameters", + "DHParameterNumbers", + "DHPublicNumbers", + "DHPrivateNumbers", + "DHParameters", + "DHParametersWithSerialization", + "DHPublicKey", + "DHPublicKeyWithSerialization", + "DHPrivateKey", + "DHPrivateKeyWithSerialization", + ], + ) + def test_primitives_module_deprecated(self, name): + with pytest.warns( + utils.DeprecatedIn51, + match="cryptography.hazmat.decrepit.asymmetric.dh", + ): + value = getattr(deprecated_dh, name) + # The deprecated names are the same objects as the decrepit ones, so + # isinstance checks against either module path continue to work. + assert value is getattr(dh, name.replace("WithSerialization", "")) + + @pytest.mark.parametrize( + ("name", "encoding"), + [ + ("load_pem_parameters", serialization.Encoding.PEM), + ("load_der_parameters", serialization.Encoding.DER), + ], + ) + def test_serialization_parameter_loaders_deprecated(self, name, encoding): + parameters = FFDH3072_P.parameters() + serialized = parameters.parameter_bytes( + encoding, serialization.ParameterFormat.PKCS3 + ) + with pytest.warns( + utils.DeprecatedIn51, + match=f"cryptography.hazmat.decrepit.asymmetric.dh.{name}", + ): + loader = getattr(serialization, name) + loaded = loader(serialized) + assert loaded.parameter_numbers() == parameters.parameter_numbers() + + @pytest.mark.filterwarnings( + "error::cryptography.utils.CryptographyDeprecationWarning" + ) + def test_decrepit_module_does_not_warn(self): + parameters = dh.generate_parameters(generator=2, key_size=512) + private_key = parameters.generate_private_key() + public_key = private_key.public_key() + assert isinstance(parameters, dh.DHParameters) + assert isinstance(private_key, dh.DHPrivateKey) + assert isinstance(public_key, dh.DHPublicKey) + numbers = public_key.public_numbers() + assert isinstance(numbers, dh.DHPublicNumbers) + assert numbers.public_key() == public_key + serialized = parameters.parameter_bytes( + serialization.Encoding.PEM, serialization.ParameterFormat.PKCS3 + ) + loaded = dh.load_pem_parameters(serialized) + assert loaded.parameter_numbers() == parameters.parameter_numbers() + + def test_loading_dh_keys_deprecated(self): + key = FFDH3072_P.parameters().generate_private_key() + private_bytes = key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, + serialization.NoEncryption(), + ) + public_bytes = key.public_key().public_bytes( + serialization.Encoding.PEM, + serialization.PublicFormat.SubjectPublicKeyInfo, + ) + with pytest.warns( + utils.DeprecatedIn51, + match="cryptography.hazmat.decrepit.asymmetric.dh", + ): + loaded_private = serialization.load_pem_private_key( + private_bytes, None + ) + assert isinstance(loaded_private, dh.DHPrivateKey) + with pytest.warns( + utils.DeprecatedIn51, + match="cryptography.hazmat.decrepit.asymmetric.dh", + ): + loaded_public = serialization.load_pem_public_key(public_bytes) + assert isinstance(loaded_public, dh.DHPublicKey) diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 6c280c10acc2..297f32ede77c 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -16,10 +16,10 @@ from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.decrepit.asymmetric import dh from cryptography.hazmat.decrepit.ciphers.algorithms import _DES, ARC4, RC2 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ( - dh, dsa, ec, ed448, @@ -428,7 +428,7 @@ def test_load_ec_public_key(self, backend): def test_wrong_parameters_format(self): param_data = b"---- NOT A KEY ----\n" - with pytest.raises(ValueError), pytest.warns(utils.DeprecatedIn50): + with pytest.raises(ValueError), pytest.warns(utils.DeprecatedIn51): serialization.load_der_parameters(param_data) def test_load_pkcs8_private_key_invalid_version(self): @@ -499,15 +499,15 @@ def test_load_pkcs8_private_key_v1_with_public_key(self): ), ( "dh-pkcs3", - dh._DHPrivateKey, + dh.DHPrivateKey, lambda backend: backend.dh_supported(), - utils.DeprecatedIn50, + utils.DeprecatedIn51, ), ( "dh-x942", - dh._DHPrivateKey, + dh.DHPrivateKey, lambda backend: backend.dh_supported(), - utils.DeprecatedIn50, + utils.DeprecatedIn51, ), ( "x25519", @@ -1157,7 +1157,7 @@ def test_wrong_public_format(self): def test_wrong_parameters_format(self): param_data = b"---- NOT A KEY ----\n" - with pytest.raises(ValueError), pytest.warns(utils.DeprecatedIn50): + with pytest.raises(ValueError), pytest.warns(utils.DeprecatedIn51): serialization.load_pem_parameters(param_data) def test_corrupt_traditional_format(self): @@ -1927,7 +1927,7 @@ def test_dh_public_key(self): lambda pemfile: pemfile.read(), mode="rb", ) - with pytest.warns(utils.DeprecatedIn50): + with pytest.warns(utils.DeprecatedIn51): private_key = load_pem_private_key(data, None) public_key = private_key.public_key() for enc in ( @@ -1961,7 +1961,7 @@ def test_dh_private_key(self): lambda pemfile: pemfile.read(), mode="rb", ) - with pytest.warns(utils.DeprecatedIn50): + with pytest.warns(utils.DeprecatedIn51): private_key = load_pem_private_key(data, None) for enc in ( Encoding.PEM, diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 76f1349fa8a7..8dfe0a9d1076 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -15,9 +15,9 @@ from cryptography import utils, x509 from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm from cryptography.hazmat.bindings._rust import test_support +from cryptography.hazmat.decrepit.asymmetric import dh from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( - dh, dsa, ec, ed448, @@ -7124,8 +7124,7 @@ def load_key(self): load_nist_vectors, )[1] p = int.from_bytes(binascii.unhexlify(vector["p"]), "big") - with pytest.warns(utils.DeprecatedIn50): - params = dh.DHParameterNumbers(p, int(vector["g"])) + params = dh.DHParameterNumbers(p, int(vector["g"])) param = params.parameters() return param.generate_private_key() From 8b73eae942935e66a02602994c9058fdda06faac Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 17:10:21 +0000 Subject: [PATCH 2/5] Keep the full DH docs at the old path with a moved warning Historical changelog entries link to the classes at their original documentation path, and Sphinx runs in nitpicky mode, so the page keeps documenting them (as the decrepit cipher and mode pages do) with a warning pointing at the decrepit module. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01TmnZQpY2o4HeBNFN5tfqYE --- docs/hazmat/primitives/asymmetric/dh.rst | 381 ++++++++++++++++++++++- 1 file changed, 367 insertions(+), 14 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index 15f53cbb15b5..b1273635ca65 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -8,17 +8,370 @@ Diffie-Hellman key exchange .. warning:: Diffie-Hellman over finite fields (FFDH) has been deprecated and moved to - the :doc:`/hazmat/decrepit/index` module. If you need to continue using it - then update your code to use - :mod:`cryptography.hazmat.decrepit.asymmetric.dh`. Starting in 53.0.0 it - will only be available from that module. Users should migrate to a more - modern key exchange algorithm such as - :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` or - :class:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey` - where possible. - -The classes and functions previously documented here, ``generate_parameters``, -``DHParameters``, ``DHPrivateKey``, ``DHPublicKey``, ``DHParameterNumbers``, -``DHPrivateNumbers``, and ``DHPublicNumbers``, are documented in -:doc:`/hazmat/decrepit/dh`. Accessing any of them through this module emits a -``CryptographyDeprecationWarning``. + the :doc:`/hazmat/decrepit/index` module as + :mod:`cryptography.hazmat.decrepit.asymmetric.dh`. If you need to + continue using it then update your code to use the new module path. + Starting in 53.0.0 it will only be available from that module. Users + should migrate to a more modern key exchange algorithm such as + :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` where + possible. + + +`Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties +to jointly agree on a shared secret using an insecure channel. + + +Exchange Algorithm +~~~~~~~~~~~~~~~~~~ + +For most applications the ``shared_key`` should be passed to a key +derivation function. This allows mixing of additional information into the +key, derivation of multiple keys, and destroys any structure that may be +present. + +.. warning:: + + This example does not give `forward secrecy`_ and is only provided as a + demonstration of the basic Diffie-Hellman construction. For real world + applications always use the ephemeral form described after this example. + +.. code-block:: pycon + + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.primitives.asymmetric import dh + >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF + >>> # Generate some parameters. These can be reused. + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) + >>> # Generate a private key for use in the exchange. + >>> server_private_key = parameters.generate_private_key() + >>> # In a real handshake the peer is a remote client. For this + >>> # example we'll generate another local private key though. Note that in + >>> # a DH handshake both peers must agree on a common set of parameters. + >>> peer_private_key = parameters.generate_private_key() + >>> shared_key = server_private_key.exchange(peer_private_key.public_key()) + >>> # Perform key derivation. + >>> derived_key = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(shared_key) + >>> # And now we can demonstrate that the handshake performed in the + >>> # opposite direction gives the same final value + >>> same_shared_key = peer_private_key.exchange( + ... server_private_key.public_key() + ... ) + >>> same_derived_key = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(same_shared_key) + >>> derived_key == same_derived_key + +DHE (or EDH), the ephemeral form of this exchange, is **strongly +preferred** over simple DH and provides `forward secrecy`_ when used. You must +generate a new private key using :func:`~DHParameters.generate_private_key` for +each :meth:`~DHPrivateKey.exchange` when performing an DHE key exchange. An +example of the ephemeral form: + +.. code-block:: pycon + + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.primitives.asymmetric import dh + >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF + >>> # Generate some parameters. These can be reused. + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) + >>> # Generate a private key for use in the exchange. + >>> private_key = parameters.generate_private_key() + >>> # In a real handshake the peer_public_key will be received from the + >>> # other party. For this example we'll generate another private key and + >>> # get a public key from that. Note that in a DH handshake both peers + >>> # must agree on a common set of parameters. + >>> peer_public_key = parameters.generate_private_key().public_key() + >>> shared_key = private_key.exchange(peer_public_key) + >>> # Perform key derivation. + >>> derived_key = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(shared_key) + >>> # For the next handshake we MUST generate another private key, but + >>> # we can reuse the parameters. + >>> private_key_2 = parameters.generate_private_key() + >>> peer_public_key_2 = parameters.generate_private_key().public_key() + >>> shared_key_2 = private_key_2.exchange(peer_public_key_2) + >>> derived_key_2 = HKDF( + ... algorithm=hashes.SHA256(), + ... length=32, + ... salt=None, + ... info=b'handshake data', + ... ).derive(shared_key_2) + +To assemble a :class:`~DHParameters` and a :class:`~DHPublicKey` from +primitive integers, you must first create the +:class:`~DHParameterNumbers` and :class:`~DHPublicNumbers` objects. For +example, if **p**, **g**, and **y** are :class:`int` objects received from a +peer:: + + pn = dh.DHParameterNumbers(p, g) + parameters = pn.parameters() + peer_public_numbers = dh.DHPublicNumbers(y, pn) + peer_public_key = peer_public_numbers.public_key() + + +Group parameters +~~~~~~~~~~~~~~~~ + +.. function:: generate_parameters(generator, key_size) + + .. versionadded:: 1.7 + + Generate a new DH parameter group. + + :param generator: The :class:`int` to use as a generator. Must be + 2 or 5. + + :param key_size: The bit length of the prime modulus to generate. + + :returns: DH parameters as a new instance of + :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. + + :raises ValueError: If ``key_size`` is not at least 512. + + +.. class:: DHParameters + + .. versionadded:: 1.7 + + + .. method:: generate_private_key() + + Generate a DH private key. This method can be used to generate many + new private keys from a single set of parameters. + + :return: An instance of + :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`. + + .. method:: parameter_numbers() + + Return the numbers that make up this set of parameters. + + :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`. + + .. method:: parameter_bytes(encoding, format) + + .. versionadded:: 2.0 + + Allows serialization of the parameters to bytes. Encoding ( + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and + format ( + :attr:`~cryptography.hazmat.primitives.serialization.ParameterFormat.PKCS3`) + are chosen to define the exact serialization. + + :param encoding: A value from the + :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. + + :param format: A value from the + :class:`~cryptography.hazmat.primitives.serialization.ParameterFormat` + enum. At the moment only ``PKCS3`` is supported. + + :return bytes: Serialized parameters. + +Key interfaces +~~~~~~~~~~~~~~ + +.. class:: DHPrivateKey + + .. versionadded:: 1.7 + + .. attribute:: key_size + + The bit length of the prime modulus. + + .. method:: public_key() + + Return the public key associated with this private key. + + :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`. + + .. method:: parameters() + + Return the parameters associated with this private key. + + :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. + + .. method:: exchange(peer_public_key) + + .. versionadded:: 1.7 + + :param DHPublicKey peer_public_key: The public key for + the peer. + + :return bytes: The agreed key. The bytes are ordered in 'big' endian. + + .. method:: private_numbers() + + Return the numbers that make up this private key. + + :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateNumbers`. + + .. method:: private_bytes(encoding, format, encryption_algorithm) + + .. versionadded:: 1.8 + + Allows serialization of the key to bytes. Encoding ( + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`), + format ( + :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`) + and encryption algorithm (such as + :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption` + or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`) + are chosen to define the exact serialization. + + :param encoding: A value from the + :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. + + :param format: A value from the + :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat` + enum. + + :param encryption_algorithm: An instance of an object conforming to the + :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption` + interface. + + :return bytes: Serialized key. + + +.. class:: DHPublicKey + + .. versionadded:: 1.7 + + .. attribute:: key_size + + The bit length of the prime modulus. + + .. method:: parameters() + + Return the parameters associated with this private key. + + :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. + + .. method:: public_numbers() + + Return the numbers that make up this public key. + + :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers`. + + .. method:: public_bytes(encoding, format) + + .. versionadded:: 1.8 + + Allows serialization of the key to bytes. Encoding ( + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and + format ( + :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`) + are chosen to define the exact serialization. + + :param encoding: A value from the + :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. + + :param format: A value from the + :class:`~cryptography.hazmat.primitives.serialization.PublicFormat` enum. + + :return bytes: Serialized key. + +Numbers +~~~~~~~ + +.. class:: DHParameterNumbers(p, g, q=None) + + .. versionadded:: 0.8 + + The collection of integers that define a Diffie-Hellman group. + + .. attribute:: p + + :type: int + + The prime modulus value. + + .. attribute:: g + + :type: int + + The generator value. Must be 2 or greater. + + .. attribute:: q + + .. versionadded:: 1.8 + + :type: int + + p subgroup order value. + + .. method:: parameters() + + .. versionadded:: 1.7 + + :returns: A new instance of :class:`DHParameters`. + + :raises ValueError: If the parameters are invalid. + +.. class:: DHPrivateNumbers(x, public_numbers) + + .. versionadded:: 0.8 + + The collection of integers that make up a Diffie-Hellman private key. + + .. attribute:: public_numbers + + :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers` + + The :class:`DHPublicNumbers` which makes up the DH public + key associated with this DH private key. + + .. attribute:: x + + :type: int + + The private value. + + .. method:: private_key() + + .. versionadded:: 1.7 + + :returns: A new instance of :class:`DHPrivateKey`. + + +.. class:: DHPublicNumbers(y, parameter_numbers) + + .. versionadded:: 0.8 + + The collection of integers that make up a Diffie-Hellman public key. + + .. attribute:: parameter_numbers + + :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers` + + The parameters for this DH group. + + .. attribute:: y + + :type: int + + The public value. + + .. method:: public_key() + + .. versionadded:: 1.7 + + :returns: A new instance of :class:`DHPublicKey`. + + +.. _`Diffie-Hellman key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange +.. _`forward secrecy`: https://en.wikipedia.org/wiki/Forward_secrecy From 4b2fd7049baf3f31a72a54c643eb1defc6974c1a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 17:14:20 +0000 Subject: [PATCH 3/5] Skip the FFDH deprecation tests that need DH on backends without it The BoringSSL CI jobs failed because the new tests built DH parameters without the `dh_supported` guard the other DH tests use. Also use the 3072-bit fixture rather than generating 512-bit parameters, which FIPS builds reject. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01TmnZQpY2o4HeBNFN5tfqYE --- tests/hazmat/primitives/test_dh.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index e7739bbe31c8..a9f798fea35e 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -1109,6 +1109,10 @@ def test_primitives_module_deprecated(self, name): # isinstance checks against either module path continue to work. assert value is getattr(dh, name.replace("WithSerialization", "")) + @pytest.mark.supported( + only_if=lambda backend: backend.dh_supported(), + skip_message="DH not supported", + ) @pytest.mark.parametrize( ("name", "encoding"), [ @@ -1129,11 +1133,15 @@ def test_serialization_parameter_loaders_deprecated(self, name, encoding): loaded = loader(serialized) assert loaded.parameter_numbers() == parameters.parameter_numbers() + @pytest.mark.supported( + only_if=lambda backend: backend.dh_supported(), + skip_message="DH not supported", + ) @pytest.mark.filterwarnings( "error::cryptography.utils.CryptographyDeprecationWarning" ) def test_decrepit_module_does_not_warn(self): - parameters = dh.generate_parameters(generator=2, key_size=512) + parameters = FFDH3072_P.parameters() private_key = parameters.generate_private_key() public_key = private_key.public_key() assert isinstance(parameters, dh.DHParameters) @@ -1148,6 +1156,10 @@ def test_decrepit_module_does_not_warn(self): loaded = dh.load_pem_parameters(serialized) assert loaded.parameter_numbers() == parameters.parameter_numbers() + @pytest.mark.supported( + only_if=lambda backend: backend.dh_supported(), + skip_message="DH not supported", + ) def test_loading_dh_keys_deprecated(self): key = FFDH3072_P.parameters().generate_private_key() private_bytes = key.private_bytes( From b984baf434ab920d48e4e4d5316c706438b1eb05 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 17:26:10 +0000 Subject: [PATCH 4/5] Suggest X25519 and ML-KEM alongside ECDH in the FFDH migration notes Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01TmnZQpY2o4HeBNFN5tfqYE --- docs/hazmat/decrepit/dh.rst | 5 +++-- docs/hazmat/primitives/asymmetric/dh.rst | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/hazmat/decrepit/dh.rst b/docs/hazmat/decrepit/dh.rst index 189d9174a717..772aeff6c6a4 100644 --- a/docs/hazmat/decrepit/dh.rst +++ b/docs/hazmat/decrepit/dh.rst @@ -26,8 +26,9 @@ Decrepit Diffie-Hellman key exchange This module contains Diffie-Hellman key exchange over finite fields (FFDH). FFDH should not be used unless necessary for backwards compatibility or interoperability with legacy systems. Its use is **strongly discouraged**; -use :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` or -:class:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey` +use :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH`, +:class:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey`, +or :class:`~cryptography.hazmat.primitives.asymmetric.mlkem.MLKEM768PrivateKey` instead where possible. `Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index b1273635ca65..f31e42e5e4da 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -13,8 +13,10 @@ Diffie-Hellman key exchange continue using it then update your code to use the new module path. Starting in 53.0.0 it will only be available from that module. Users should migrate to a more modern key exchange algorithm such as - :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` where - possible. + :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH`, + :class:`~cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey`, + or :class:`~cryptography.hazmat.primitives.asymmetric.mlkem.MLKEM768PrivateKey` + where possible. `Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties From 6999f2b1632cb6d474eb475da57e42f419ec1985 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 21:50:35 +0000 Subject: [PATCH 5/5] Document FFDH only on the decrepit page The old primitives page is now a short stub pointing at the decrepit module instead of a full copy of the reference. Historical changelog entries cross-reference the decrepit page so Sphinx's nitpicky build still resolves them. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01TmnZQpY2o4HeBNFN5tfqYE --- CHANGELOG.rst | 20 +- docs/hazmat/primitives/asymmetric/dh.rst | 363 +---------------------- 2 files changed, 14 insertions(+), 369 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c8a3fb1c9c8c..aaf9a1e7f93f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -88,7 +88,7 @@ Changelog :func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key` now reject Diffie-Hellman public keys whose modulus is smaller than 512 bits, matching the minimum already enforced when loading DH private keys and when - constructing :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`. + constructing :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameterNumbers`. * Added :class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSAMuHasher` for incrementally computing the ML-DSA ``mu`` (message representative) used by @@ -489,8 +489,8 @@ Changelog :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`, :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`, - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`, and - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey` + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`, and + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey` abstract base classes. * We significantly refactored how private key loading ( :func:`~cryptography.hazmat.primitives.serialization.load_pem_private_key` @@ -747,7 +747,7 @@ Changelog ``X448PrivateKey`` :meth:`~cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.exchange`, and ``DHPrivateKey`` - :meth:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey.exchange`. + :meth:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey.exchange`. .. _v42-0-1: @@ -2100,7 +2100,7 @@ Changelog :func:`~cryptography.hazmat.primitives.serialization.load_pem_parameters`, :func:`~cryptography.hazmat.primitives.serialization.load_der_parameters`, and - :meth:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters.parameter_bytes` + :meth:`~cryptography.hazmat.decrepit.asymmetric.dh.DHParameters.parameter_bytes` . * The ``extensions`` attribute on :class:`~cryptography.x509.Certificate`, :class:`~cryptography.x509.CertificateSigningRequest`, @@ -2181,13 +2181,13 @@ Changelog :meth:`~cryptography.hazmat.primitives.ciphers.CipherContext.update_into` on :class:`~cryptography.hazmat.primitives.ciphers.CipherContext`. * Added - :meth:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey.private_bytes` + :meth:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey.private_bytes` to - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`. + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey`. * Added - :meth:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey.public_bytes` + :meth:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey.public_bytes` to - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`. + :class:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPublicKey`. * :func:`~cryptography.hazmat.primitives.serialization.load_pem_private_key` and :func:`~cryptography.hazmat.primitives.serialization.load_der_private_key` @@ -2221,7 +2221,7 @@ Changelog * Support for OpenSSL 1.0.0 has been removed. Users on older version of OpenSSL will need to upgrade. * Added support for Diffie-Hellman key exchange using - :meth:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey.exchange`. + :meth:`~cryptography.hazmat.decrepit.asymmetric.dh.DHPrivateKey.exchange`. * The OS random engine for OpenSSL has been rewritten to improve compatibility with embedded Python and other edge cases. More information about this change can be found in the diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index f31e42e5e4da..8460b266513c 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -18,362 +18,7 @@ Diffie-Hellman key exchange or :class:`~cryptography.hazmat.primitives.asymmetric.mlkem.MLKEM768PrivateKey` where possible. - -`Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties -to jointly agree on a shared secret using an insecure channel. - - -Exchange Algorithm -~~~~~~~~~~~~~~~~~~ - -For most applications the ``shared_key`` should be passed to a key -derivation function. This allows mixing of additional information into the -key, derivation of multiple keys, and destroys any structure that may be -present. - -.. warning:: - - This example does not give `forward secrecy`_ and is only provided as a - demonstration of the basic Diffie-Hellman construction. For real world - applications always use the ephemeral form described after this example. - -.. code-block:: pycon - - >>> from cryptography.hazmat.primitives import hashes - >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF - >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048) - >>> # Generate a private key for use in the exchange. - >>> server_private_key = parameters.generate_private_key() - >>> # In a real handshake the peer is a remote client. For this - >>> # example we'll generate another local private key though. Note that in - >>> # a DH handshake both peers must agree on a common set of parameters. - >>> peer_private_key = parameters.generate_private_key() - >>> shared_key = server_private_key.exchange(peer_private_key.public_key()) - >>> # Perform key derivation. - >>> derived_key = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(shared_key) - >>> # And now we can demonstrate that the handshake performed in the - >>> # opposite direction gives the same final value - >>> same_shared_key = peer_private_key.exchange( - ... server_private_key.public_key() - ... ) - >>> same_derived_key = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(same_shared_key) - >>> derived_key == same_derived_key - -DHE (or EDH), the ephemeral form of this exchange, is **strongly -preferred** over simple DH and provides `forward secrecy`_ when used. You must -generate a new private key using :func:`~DHParameters.generate_private_key` for -each :meth:`~DHPrivateKey.exchange` when performing an DHE key exchange. An -example of the ephemeral form: - -.. code-block:: pycon - - >>> from cryptography.hazmat.primitives import hashes - >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF - >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048) - >>> # Generate a private key for use in the exchange. - >>> private_key = parameters.generate_private_key() - >>> # In a real handshake the peer_public_key will be received from the - >>> # other party. For this example we'll generate another private key and - >>> # get a public key from that. Note that in a DH handshake both peers - >>> # must agree on a common set of parameters. - >>> peer_public_key = parameters.generate_private_key().public_key() - >>> shared_key = private_key.exchange(peer_public_key) - >>> # Perform key derivation. - >>> derived_key = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(shared_key) - >>> # For the next handshake we MUST generate another private key, but - >>> # we can reuse the parameters. - >>> private_key_2 = parameters.generate_private_key() - >>> peer_public_key_2 = parameters.generate_private_key().public_key() - >>> shared_key_2 = private_key_2.exchange(peer_public_key_2) - >>> derived_key_2 = HKDF( - ... algorithm=hashes.SHA256(), - ... length=32, - ... salt=None, - ... info=b'handshake data', - ... ).derive(shared_key_2) - -To assemble a :class:`~DHParameters` and a :class:`~DHPublicKey` from -primitive integers, you must first create the -:class:`~DHParameterNumbers` and :class:`~DHPublicNumbers` objects. For -example, if **p**, **g**, and **y** are :class:`int` objects received from a -peer:: - - pn = dh.DHParameterNumbers(p, g) - parameters = pn.parameters() - peer_public_numbers = dh.DHPublicNumbers(y, pn) - peer_public_key = peer_public_numbers.public_key() - - -Group parameters -~~~~~~~~~~~~~~~~ - -.. function:: generate_parameters(generator, key_size) - - .. versionadded:: 1.7 - - Generate a new DH parameter group. - - :param generator: The :class:`int` to use as a generator. Must be - 2 or 5. - - :param key_size: The bit length of the prime modulus to generate. - - :returns: DH parameters as a new instance of - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. - - :raises ValueError: If ``key_size`` is not at least 512. - - -.. class:: DHParameters - - .. versionadded:: 1.7 - - - .. method:: generate_private_key() - - Generate a DH private key. This method can be used to generate many - new private keys from a single set of parameters. - - :return: An instance of - :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`. - - .. method:: parameter_numbers() - - Return the numbers that make up this set of parameters. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`. - - .. method:: parameter_bytes(encoding, format) - - .. versionadded:: 2.0 - - Allows serialization of the parameters to bytes. Encoding ( - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and - format ( - :attr:`~cryptography.hazmat.primitives.serialization.ParameterFormat.PKCS3`) - are chosen to define the exact serialization. - - :param encoding: A value from the - :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. - - :param format: A value from the - :class:`~cryptography.hazmat.primitives.serialization.ParameterFormat` - enum. At the moment only ``PKCS3`` is supported. - - :return bytes: Serialized parameters. - -Key interfaces -~~~~~~~~~~~~~~ - -.. class:: DHPrivateKey - - .. versionadded:: 1.7 - - .. attribute:: key_size - - The bit length of the prime modulus. - - .. method:: public_key() - - Return the public key associated with this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicKey`. - - .. method:: parameters() - - Return the parameters associated with this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. - - .. method:: exchange(peer_public_key) - - .. versionadded:: 1.7 - - :param DHPublicKey peer_public_key: The public key for - the peer. - - :return bytes: The agreed key. The bytes are ordered in 'big' endian. - - .. method:: private_numbers() - - Return the numbers that make up this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateNumbers`. - - .. method:: private_bytes(encoding, format, encryption_algorithm) - - .. versionadded:: 1.8 - - Allows serialization of the key to bytes. Encoding ( - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`), - format ( - :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`) - and encryption algorithm (such as - :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption` - or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`) - are chosen to define the exact serialization. - - :param encoding: A value from the - :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. - - :param format: A value from the - :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat` - enum. - - :param encryption_algorithm: An instance of an object conforming to the - :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption` - interface. - - :return bytes: Serialized key. - - -.. class:: DHPublicKey - - .. versionadded:: 1.7 - - .. attribute:: key_size - - The bit length of the prime modulus. - - .. method:: parameters() - - Return the parameters associated with this private key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters`. - - .. method:: public_numbers() - - Return the numbers that make up this public key. - - :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers`. - - .. method:: public_bytes(encoding, format) - - .. versionadded:: 1.8 - - Allows serialization of the key to bytes. Encoding ( - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and - format ( - :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`) - are chosen to define the exact serialization. - - :param encoding: A value from the - :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum. - - :param format: A value from the - :class:`~cryptography.hazmat.primitives.serialization.PublicFormat` enum. - - :return bytes: Serialized key. - -Numbers -~~~~~~~ - -.. class:: DHParameterNumbers(p, g, q=None) - - .. versionadded:: 0.8 - - The collection of integers that define a Diffie-Hellman group. - - .. attribute:: p - - :type: int - - The prime modulus value. - - .. attribute:: g - - :type: int - - The generator value. Must be 2 or greater. - - .. attribute:: q - - .. versionadded:: 1.8 - - :type: int - - p subgroup order value. - - .. method:: parameters() - - .. versionadded:: 1.7 - - :returns: A new instance of :class:`DHParameters`. - - :raises ValueError: If the parameters are invalid. - -.. class:: DHPrivateNumbers(x, public_numbers) - - .. versionadded:: 0.8 - - The collection of integers that make up a Diffie-Hellman private key. - - .. attribute:: public_numbers - - :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers` - - The :class:`DHPublicNumbers` which makes up the DH public - key associated with this DH private key. - - .. attribute:: x - - :type: int - - The private value. - - .. method:: private_key() - - .. versionadded:: 1.7 - - :returns: A new instance of :class:`DHPrivateKey`. - - -.. class:: DHPublicNumbers(y, parameter_numbers) - - .. versionadded:: 0.8 - - The collection of integers that make up a Diffie-Hellman public key. - - .. attribute:: parameter_numbers - - :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers` - - The parameters for this DH group. - - .. attribute:: y - - :type: int - - The public value. - - .. method:: public_key() - - .. versionadded:: 1.7 - - :returns: A new instance of :class:`DHPublicKey`. - - -.. _`Diffie-Hellman key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange -.. _`forward secrecy`: https://en.wikipedia.org/wiki/Forward_secrecy +``generate_parameters``, ``DHParameters``, ``DHPrivateKey``, ``DHPublicKey``, +``DHParameterNumbers``, ``DHPrivateNumbers``, and ``DHPublicNumbers`` are +documented in :doc:`/hazmat/decrepit/dh`. Accessing any of them through this +module emits a ``CryptographyDeprecationWarning``.