.. hazmat::

Diffie-Hellman key exchange
===========================

.. currentmodule:: cryptography.hazmat.primitives.asymmetric.dh

.. 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 key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
.. _`forward secrecy`: https://en.wikipedia.org/wiki/Forward_secrecy
