.. hazmat::

X25519 key exchange
===================

.. currentmodule:: cryptography.hazmat.primitives.asymmetric.x25519


X25519 is an elliptic curve `Diffie-Hellman key exchange`_ using `Curve25519`_.
It 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.

.. doctest::

    >>> from cryptography.hazmat.backends import default_backend
    >>> from cryptography.hazmat.primitives import hashes
    >>> from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
    >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF
    >>> # Generate a private key for use in the exchange.
    >>> private_key = X25519PrivateKey.generate()
    >>> # 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 = X25519PrivateKey.generate().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',
    ...     backend=default_backend()
    ... ).derive(shared_key)
    >>> # For the next handshake we MUST generate another private key.
    >>> private_key_2 = X25519PrivateKey.generate()
    >>> peer_public_key_2 = X25519PrivateKey.generate().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',
    ...     backend=default_backend()
    ... ).derive(shared_key_2)

Key interfaces
~~~~~~~~~~~~~~

.. class:: X25519PrivateKey

    .. versionadded:: 2.0

    .. classmethod:: generate()

        Generate an X25519 private key.

        :returns: :class:`X25519PrivateKey`

    .. method:: public_key()

        :returns: :class:`X25519PublicKey`

    .. method:: exchange(peer_public_key)

        :param X25519PublicKey peer_public_key: The public key for the
            peer.

        :returns bytes: A shared key.

.. class:: X25519PublicKey

    .. versionadded:: 2.0

    .. classmethod:: from_public_bytes(data)

        :param bytes data: 32 byte public key.

        :returns: :class:`X25519PublicKey`

        .. doctest::

            >>> from cryptography.hazmat.primitives.asymmetric import x25519
            >>> private_key = x25519.X25519PrivateKey.generate()
            >>> public_key = private_key.public_key()
            >>> public_bytes = public_key.public_bytes()
            >>> loaded_public_key = x25519.X25519PublicKey.from_public_bytes(public_bytes)

    .. method:: public_bytes()

        :returns bytes: The raw bytes of the public key.


.. _`Diffie-Hellman key exchange`: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
.. _`Curve25519`: https://en.wikipedia.org/wiki/Curve25519
