.. hazmat::

X448 key exchange
===================

.. currentmodule:: cryptography.hazmat.primitives.asymmetric.x448


X448 is an elliptic curve `Diffie-Hellman key exchange`_ using `Curve448`_.
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.primitives import hashes
    >>> from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey
    >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF
    >>> # Generate a private key for use in the exchange.
    >>> private_key = X448PrivateKey.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 = X448PrivateKey.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',
    ... ).derive(shared_key)
    >>> # For the next handshake we MUST generate another private key.
    >>> private_key_2 = X448PrivateKey.generate()
    >>> peer_public_key_2 = X448PrivateKey.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',
    ... ).derive(shared_key_2)

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

.. class:: X448PrivateKey

    .. versionadded:: 2.5

    .. classmethod:: generate()

        Generate an X448 private key.

        :returns: :class:`X448PrivateKey`

    .. classmethod:: from_private_bytes(data)

        :param data: 56 byte private key.
        :type data: :term:`bytes-like`

        :returns: :class:`X448PrivateKey`

        .. doctest::

            >>> from cryptography.hazmat.primitives import serialization
            >>> from cryptography.hazmat.primitives.asymmetric import x448
            >>> private_key = x448.X448PrivateKey.generate()
            >>> private_bytes = private_key.private_bytes(
            ...     encoding=serialization.Encoding.Raw,
            ...     format=serialization.PrivateFormat.Raw,
            ...     encryption_algorithm=serialization.NoEncryption()
            ... )
            >>> loaded_private_key = x448.X448PrivateKey.from_private_bytes(private_bytes)

    .. method:: public_key()

        :returns: :class:`X448PublicKey`

    .. method:: exchange(peer_public_key)

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

        :returns bytes: A shared key.

    .. method:: private_bytes(encoding, format, encryption_algorithm)

        Allows serialization of the key to bytes. Encoding (
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`,
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`, or
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`) and
        format (
        :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`
        or
        :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.Raw`
        ) 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. If the ``encoding`` is
            :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`
            then ``format`` must be
            :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.Raw`
            , otherwise it must be
            :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`.

        :param encryption_algorithm: An instance of an object conforming to the
            :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption`
            interface.

        :return bytes: Serialized key.

    .. method:: private_bytes_raw()

        .. versionadded:: 40

        Allows serialization of the key to raw bytes. This method is a
        convenience shortcut for calling :meth:`private_bytes` with
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`
        encoding,
        :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.Raw`
        format, and
        :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`.

        :return bytes: Raw key.

.. class:: X448PublicKey

    .. versionadded:: 2.5

    .. classmethod:: from_public_bytes(data)

        :param bytes data: 56 byte public key.

        :returns: :class:`X448PublicKey`

        .. doctest::

            >>> from cryptography.hazmat.primitives import serialization
            >>> from cryptography.hazmat.primitives.asymmetric import x448
            >>> private_key = x448.X448PrivateKey.generate()
            >>> public_key = private_key.public_key()
            >>> public_bytes = public_key.public_bytes(
            ...     encoding=serialization.Encoding.Raw,
            ...     format=serialization.PublicFormat.Raw
            ... )
            >>> loaded_public_key = x448.X448PublicKey.from_public_bytes(public_bytes)

    .. method:: public_bytes(encoding, format)

        Allows serialization of the key to bytes. Encoding (
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`,
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`, or
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`) and
        format (
        :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`
        or
        :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.Raw`
        ) 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. If the ``encoding`` is
            :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`
            then ``format`` must be
            :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.Raw`
            , otherwise it must be
            :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`.

        :returns bytes: The public key bytes.

    .. method:: public_bytes_raw()

        .. versionadded:: 40

        Allows serialization of the key to raw bytes. This method is a
        convenience shortcut for calling :meth:`public_bytes` with
        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.Raw`
        encoding and
        :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.Raw`
        format.

        :return bytes: Raw key.


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