|
|
.. 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.backends import default_backend
|
|
|
>>> 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,
|
|
|
... backend=default_backend())
|
|
|
>>> # 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',
|
|
|
... backend=default_backend()
|
|
|
... ).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',
|
|
|
... backend=default_backend()
|
|
|
... ).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.backends import default_backend
|
|
|
>>> 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,
|
|
|
... backend=default_backend())
|
|
|
>>> # 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',
|
|
|
... backend=default_backend()
|
|
|
... ).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',
|
|
|
... backend=default_backend()
|
|
|
... ).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(default_backend())
|
|
|
peer_public_numbers = dh.DHPublicNumbers(y, pn)
|
|
|
peer_public_key = peer_public_numbers.public_key(default_backend())
|
|
|
|
|
|
|
|
|
See also the :class:`~cryptography.hazmat.backends.interfaces.DHBackend`
|
|
|
API for additional functionality.
|
|
|
|
|
|
Group parameters
|
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
|
|
.. function:: generate_parameters(generator, key_size, backend)
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
Generate a new DH parameter group for use with ``backend``.
|
|
|
|
|
|
: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.
|
|
|
|
|
|
:param backend: A
|
|
|
:class:`~cryptography.hazmat.backends.interfaces.DHBackend`
|
|
|
instance.
|
|
|
|
|
|
: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.
|
|
|
|
|
|
.. class:: DHParametersWithSerialization
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
Alias for :class:`DHParameters`.
|
|
|
|
|
|
|
|
|
Key interfaces
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
.. class:: DHPrivateKey
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
A DH private key that is not an :term:`opaque key` also implements
|
|
|
:class:`DHPrivateKeyWithSerialization` to provide serialization methods.
|
|
|
|
|
|
.. 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.
|
|
|
|
|
|
|
|
|
.. class:: DHPrivateKeyWithSerialization
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
This interface contains additional methods relating to serialization.
|
|
|
Any object with this interface also has all the methods from
|
|
|
:class:`DHPrivateKey`.
|
|
|
|
|
|
.. 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.
|
|
|
|
|
|
.. class:: DHPublicKeyWithSerialization
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
Alias for :class:`DHPublicKey`.
|
|
|
|
|
|
|
|
|
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(backend)
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
:param backend: An instance of
|
|
|
:class:`~cryptography.hazmat.backends.interfaces.DHBackend`.
|
|
|
|
|
|
:returns: A new instance of :class:`DHParameters`.
|
|
|
|
|
|
.. 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(backend)
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
:param backend: An instance of
|
|
|
:class:`~cryptography.hazmat.backends.interfaces.DHBackend`.
|
|
|
|
|
|
: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(backend)
|
|
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
|
|
:param backend: An instance of
|
|
|
:class:`~cryptography.hazmat.backends.interfaces.DHBackend`.
|
|
|
|
|
|
: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
|