diff options
author | Aviv Palivoda <palaviv@gmail.com> | 2016-11-25 18:51:28 +0200 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2016-11-26 00:51:28 +0800 |
commit | 495f21a9c61befd4a61054dd1b6080164a7400e9 (patch) | |
tree | a3352e8640c371d5125fa318dd0ab21ae611957a | |
parent | 53412d4faa5efcfc1743cbcddca96df3a7554ecc (diff) | |
download | cryptography-495f21a9c61befd4a61054dd1b6080164a7400e9.tar.gz cryptography-495f21a9c61befd4a61054dd1b6080164a7400e9.tar.bz2 cryptography-495f21a9c61befd4a61054dd1b6080164a7400e9.zip |
OpenSSL DH backend implementation [Second attempt] (#2914)
* Start of OpenSSL DH backend implementation
* Supporting DH in MultiBackend
* DHBackend has dh_parameters_supported method
* Removed DHParametersWithNumbers and DHPrivateKeyWithNumbers from documentation
* Removed ExchangeContext. exchange is a method of DHPrivateKeyWithSerialization
* PEP8 fixes
* Fixed TestDH.test_bad_tls_exchange
* Fixed generate_private_key reference in dh documentation
* test DH multibackend support
* testing DH coversion to serialized
* Validating that we receive serialized class in test_generate_dh
* Testing DH exchange symmetric key padding
* struct DH is now opaqued
* PEP8 fixes
* Testing load_dh_private_numbers throws ValueError when DH_check fails
* Using openssl_assert
* Passing keywords arguments in DH key exchange example
* test_dh::test_bad_tls_exchange now uses pre calculated parameters
* TestDH - Add test that the computed secret is equivalent to the definition by comparing with secret computed in pure python
* Add missing generator parameter to DHBackend interface docs.
* Include parameter type in DHBackend abc docs.
* Add docs for dh.generate_parameters function
* Remove the dh Numbers section, and move the DHNumbers class docs to where they are first used.
* Add note of big endian byte packing to DH exchange method.
* DH documentation updates.
Add single sentence overview with wikipedia link.
Add paragraph on assembling using Numbers objects.
Add link to backend interface docs.
First section was all indented, I think by mistake.
* Add exchange method to DHPrivateKey abstract base class.
* Small tweaks to DH documentation - remove Provider.
* Add endian to dictionary
* Use utils.int_from_bytes in test_tls_exchange_algorithm
* Removed duplicate line
* Change dh.rst exchange algorithm from doctest to code-block
The example in the Diffie-Hellman exhange algorithm is using
2048 bits key. Generating the parameters of 2048 takes long
time. This caused the automated tests to fail. In order to
pass the tests we change the example to code-block so it
will not run in the doc tests.
* Fix dh docs
* Document the generator in DHBackend relevant methods
* Fix dh tests
* use DHparams_dup
* Fix key type to unsigned char as expected by DH_compute_key
* Validate that DH generator is 2 or 5
* test dh exchange using botan vectors
* group all numbers classes
* Simplify _DHPrivateKey
* Rename test with serialized to numbers
* Move bad exchange params to external vector file
* update exchange versionadded to 1.7
* Make key_size bit accurate
* Change botan link
* Added CHANGELOG entry
-rw-r--r-- | CHANGELOG.rst | 2 | ||||
-rw-r--r-- | docs/development/test-vectors.rst | 8 | ||||
-rw-r--r-- | docs/hazmat/backends/interfaces.rst | 8 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/dh.rst | 163 | ||||
-rw-r--r-- | docs/spelling_wordlist.txt | 1 | ||||
-rw-r--r-- | src/cryptography/exceptions.py | 1 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 18 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 72 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 118 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/dh.py | 182 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/dh.py | 23 | ||||
-rw-r--r-- | tests/hazmat/backends/test_multibackend.py | 58 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_dh.py | 207 | ||||
-rw-r--r-- | vectors/cryptography_vectors/asymmetric/DH/bad_exchange.txt | 22 | ||||
-rw-r--r-- | vectors/cryptography_vectors/asymmetric/DH/vec.txt | 37 |
15 files changed, 845 insertions, 75 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1a51c42e..0d2801ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,8 @@ 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.DHPrivateKeyWithSerialization.exchange` 1.6 - 2016-11-22 ~~~~~~~~~~~~~~~~ diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 49c5ac23..fb72240d 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -91,6 +91,13 @@ Key exchange * ``vectors/cryptography_vectors/asymmetric/DH/RFC5114.txt`` contains Diffie-Hellman examples from appendix A.1, A.2 and A.3 of :rfc:`5114`. +* ``vectors/cryptography_vectors/asymmetric/DH/vec.txt`` contains + Diffie-Hellman examples from `botan`_. + +* ``vectors/cryptography_vectors/asymmetric/DH/bad_exchange.txt`` contains + Diffie-Hellman vector pairs that were generated using OpenSSL + DH_generate_parameters_ex and DH_generate_key. + X.509 ~~~~~ @@ -463,3 +470,4 @@ header format (substituting the correct information): .. _`Russian CA`: https://e-trust.gosuslugi.ru/MainCA .. _`test/evptests.txt`: https://github.com/openssl/openssl/blob/2d0b44126763f989a4cbffbffe9d0c7518158bb7/test/evptests.txt .. _`unknown signature OID`: https://bugzilla.mozilla.org/show_bug.cgi?id=405966 +.. _`botan`: https://github.com/randombit/botan/blob/57789bdfc55061002b2727d0b32587612829a37c/src/tests/data/pubkey/dh.vec diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst index 42e07d39..87fc6ab7 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -600,7 +600,9 @@ A specific ``backend`` may provide one or more of these interfaces. A backend with methods for doing Diffie-Hellman key exchange. - .. method:: generate_dh_parameters(key_size) + .. method:: generate_dh_parameters(generator, key_size) + + :param int generator: The generator to use. Often 2 or 5. :param int key_size: The bit length of the prime modulus to generate. @@ -617,7 +619,9 @@ A specific ``backend`` may provide one or more of these interfaces. :return: A new instance of :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateKey`. - .. method:: generate_dh_private_key_and_parameters(self, key_size) + .. method:: generate_dh_private_key_and_parameters(generator, key_size) + + :param int generator: The generator to use. Often 2 or 5. :param int key_size: The bit length of the prime modulus to generate. diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index 8cb68280..463df90a 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -6,69 +6,70 @@ Diffie-Hellman key exchange .. currentmodule:: cryptography.hazmat.primitives.asymmetric.dh -Numbers -~~~~~~~ - -.. 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. +`Diffie-Hellman key exchange`_ (D–H) is a method that allows two parties +to jointly agree on a shared secret using an insecure channel. -.. class:: DHPublicNumbers(y, parameter_numbers) - - .. versionadded:: 0.8 +Exchange Algorithm +~~~~~~~~~~~~~~~~~~ - The collection of integers that make up a Diffie-Hellman public key. +For most applications the ``shared_key`` should be passed to a key +derivation function. - .. attribute:: parameter_numbers - - :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers` +.. code-block:: pycon - The parameters for this DH group. + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives.asymmetric import dh + >>> parameters = dh.generate_parameters(generator=2, key_size=2048, + ... backend=default_backend()) + >>> private_key = parameters.generate_private_key() + >>> peer_public_key = parameters.generate_private_key().public_key() + >>> shared_key = private_key.exchange(peer_public_key) - .. attribute:: y +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:`~DHPrivateKeyWithSerialization.exchange` when performing an DHE key +exchange. - :type: int +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:: - The public value. + 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()) -.. class:: DHParameterNumbers(p, g) +See also the :class:`~cryptography.hazmat.backends.interfaces.DHBackend` +API for additional functionality. - .. versionadded:: 0.8 +Group parameters +~~~~~~~~~~~~~~~~ - The collection of integers that define a Diffie-Hellman group. +.. function:: generate_parameters(generator, key_size, backend) - .. attribute:: p + .. versionadded:: 0.9 - :type: int + Generate a new DH parameter group for use with ``backend``. - The prime modulus value. + :param generator: The :class:`int` to use as a generator. Must be + 2 or 5. - .. attribute:: g + :param key_size: The bit length of the prime modulus to generate. - :type: int + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.DHBackend` + instance. - The generator value. + :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. -Key interfaces -~~~~~~~~~~~~~~ .. class:: DHParameters @@ -99,6 +100,9 @@ Key interfaces :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`. +Key interfaces +~~~~~~~~~~~~~~ + .. class:: DHPrivateKey .. versionadded:: 0.9 @@ -132,6 +136,15 @@ Key interfaces :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPrivateNumbers`. + .. method:: exchange(peer_public_key) + + .. versionadded:: 1.7 + + :param DHPublicKeyWithSerialization peer_public_key: The public key for the + peer. + + :return bytes: The agreed key. The bytes are ordered in 'big' endian. + .. class:: DHPublicKey @@ -159,3 +172,67 @@ Key interfaces Return the numbers that make up this public key. :return: A :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers`. + + +Numbers +~~~~~~~ + +.. class:: DHParameterNumbers(p, g) + + .. 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 5. + +.. 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. + + +.. 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. + + +.. _`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/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 5efbbdcd..186b7eeb 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -32,6 +32,7 @@ Django Docstrings El Encodings +endian Fernet fernet FIPS diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py index ee43fed7..648cf9df 100644 --- a/src/cryptography/exceptions.py +++ b/src/cryptography/exceptions.py @@ -18,6 +18,7 @@ class _Reasons(Enum): UNSUPPORTED_SERIALIZATION = 7 UNSUPPORTED_X509 = 8 UNSUPPORTED_EXCHANGE_ALGORITHM = 9 + UNSUPPORTED_DIFFIE_HELLMAN = 10 class UnsupportedAlgorithm(Exception): diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index e15a7ca4..c5f2951c 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -322,9 +322,10 @@ class X509Backend(object): @six.add_metaclass(abc.ABCMeta) class DHBackend(object): @abc.abstractmethod - def generate_dh_parameters(self, key_size): + def generate_dh_parameters(self, generator, key_size): """ Generate a DHParameters instance with a modulus of key_size bits. + Using the given generator. Often 2 or 5. """ @abc.abstractmethod @@ -335,33 +336,28 @@ class DHBackend(object): """ @abc.abstractmethod - def generate_dh_private_key_and_parameters(self, key_size): + def generate_dh_private_key_and_parameters(self, generator, key_size): """ Generate a DHPrivateKey instance using key size only. + Using the given generator. Often 2 or 5. """ @abc.abstractmethod def load_dh_private_numbers(self, numbers): """ - Returns a DHPrivateKey provider. + Load a DHPrivateKey from DHPrivateNumbers """ @abc.abstractmethod def load_dh_public_numbers(self, numbers): """ - Returns a DHPublicKey provider. + Load a DHPublicKey from DHPublicNumbers. """ @abc.abstractmethod def load_dh_parameter_numbers(self, numbers): """ - Returns a DHParameters provider. - """ - - @abc.abstractmethod - def dh_exchange_algorithm_supported(self, exchange_algorithm): - """ - Returns whether the exchange algorithm is supported by this backend. + Load DHParameters from DHParameterNumbers. """ @abc.abstractmethod diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index bcd9c520..097b4908 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -7,9 +7,10 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CMACBackend, CipherBackend, DERSerializationBackend, DSABackend, - EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend + CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, + DSABackend, EllipticCurveBackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, PEMSerializationBackend, RSABackend, ScryptBackend, + X509Backend ) @@ -24,6 +25,7 @@ from cryptography.hazmat.backends.interfaces import ( @utils.register_interface(EllipticCurveBackend) @utils.register_interface(PEMSerializationBackend) @utils.register_interface(X509Backend) +@utils.register_interface(DHBackend) @utils.register_interface(ScryptBackend) class MultiBackend(object): name = "multibackend" @@ -424,6 +426,70 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_X509 ) + def generate_dh_parameters(self, generator, key_size): + for b in self._filtered_backends(DHBackend): + return b.generate_dh_parameters(generator, key_size) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def load_dh_parameter_numbers(self, numbers): + for b in self._filtered_backends(DHBackend): + return b.load_dh_parameter_numbers(numbers) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def generate_dh_private_key(self, parameters): + for b in self._filtered_backends(DHBackend): + return b.generate_dh_private_key(parameters) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def load_dh_private_numbers(self, numbers): + for b in self._filtered_backends(DHBackend): + return b.load_dh_private_numbers(numbers) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def load_dh_public_numbers(self, numbers): + for b in self._filtered_backends(DHBackend): + return b.load_dh_public_numbers(numbers) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def generate_dh_private_key_and_parameters(self, generator, key_size): + for b in self._filtered_backends(DHBackend): + return b.generate_dh_private_key_and_parameters(generator, + key_size) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def dh_parameters_supported(self, p, g): + for b in self._filtered_backends(DHBackend): + return b.dh_parameters_supported(p, g) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + def x509_name_bytes(self, name): for b in self._filtered_backends(X509Backend): return b.x509_name_bytes(name) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 4a341fc2..9df113b6 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -16,7 +16,7 @@ import six from cryptography import utils, x509 from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CMACBackend, CipherBackend, DERSerializationBackend, DSABackend, + CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, DSABackend, EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend ) @@ -24,6 +24,9 @@ from cryptography.hazmat.backends.openssl.ciphers import ( _AESCTRCipherContext, _CipherContext ) from cryptography.hazmat.backends.openssl.cmac import _CMACContext +from cryptography.hazmat.backends.openssl.dh import ( + _DHParameters, _DHPrivateKey, _DHPublicKey +) from cryptography.hazmat.backends.openssl.dsa import ( _DSAParameters, _DSAPrivateKey, _DSAPublicKey ) @@ -107,6 +110,7 @@ def _pem_password_cb(buf, size, writing, userdata_handle): @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) +@utils.register_interface(DHBackend) @utils.register_interface(DSABackend) @utils.register_interface(EllipticCurveBackend) @utils.register_interface(HashBackend) @@ -329,6 +333,7 @@ class Backend(object): def _bn_to_int(self, bn): assert bn != self._ffi.NULL + if six.PY3: # Python 3 has constant time from_bytes, so use that. bn_num_bytes = self._lib.BN_num_bytes(bn) @@ -1734,6 +1739,117 @@ class Backend(object): serialization._ssh_write_string(public_numbers.encode_point()) ) + def generate_dh_parameters(self, generator, key_size): + if key_size < 512: + raise ValueError("DH key_size must be at least 512 bits") + + if generator not in (2, 5): + raise ValueError("DH generator must be 2 or 5") + + dh_param_cdata = self._lib.DH_new() + self.openssl_assert(dh_param_cdata != self._ffi.NULL) + dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free) + + res = self._lib.DH_generate_parameters_ex( + dh_param_cdata, + key_size, + generator, + self._ffi.NULL + ) + self.openssl_assert(res == 1) + + return _DHParameters(self, dh_param_cdata) + + def generate_dh_private_key(self, parameters): + dh_key_cdata = self._lib.DHparams_dup(parameters._dh_cdata) + self.openssl_assert(dh_key_cdata != self._ffi.NULL) + dh_key_cdata = self._ffi.gc(dh_key_cdata, self._lib.DH_free) + + res = self._lib.DH_generate_key(dh_key_cdata) + self.openssl_assert(res == 1) + + return _DHPrivateKey(self, dh_key_cdata) + + def generate_dh_private_key_and_parameters(self, generator, key_size): + return self.generate_dh_private_key( + self.generate_dh_parameters(generator, key_size)) + + def load_dh_private_numbers(self, numbers): + parameter_numbers = numbers.public_numbers.parameter_numbers + + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(parameter_numbers.p) + g = self._int_to_bn(parameter_numbers.g) + pub_key = self._int_to_bn(numbers.public_numbers.y) + priv_key = self._int_to_bn(numbers.x) + + res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g) + self.openssl_assert(res == 1) + + res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key) + self.openssl_assert(res == 1) + + codes = self._ffi.new("int[]", 1) + res = self._lib.DH_check(dh_cdata, codes) + self.openssl_assert(res == 1) + + if codes[0] != 0: + raise ValueError("DH private numbers did not pass safety checks.") + + return _DHPrivateKey(self, dh_cdata) + + def load_dh_public_numbers(self, numbers): + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + parameter_numbers = numbers.parameter_numbers + + p = self._int_to_bn(parameter_numbers.p) + g = self._int_to_bn(parameter_numbers.g) + pub_key = self._int_to_bn(numbers.y) + + res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g) + self.openssl_assert(res == 1) + + res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL) + self.openssl_assert(res == 1) + + return _DHPublicKey(self, dh_cdata) + + def load_dh_parameter_numbers(self, numbers): + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(numbers.p) + g = self._int_to_bn(numbers.g) + + res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g) + self.openssl_assert(res == 1) + + return _DHParameters(self, dh_cdata) + + def dh_parameters_supported(self, p, g): + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(p) + g = self._int_to_bn(g) + + res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g) + self.openssl_assert(res == 1) + + codes = self._ffi.new("int[]", 1) + res = self._lib.DH_check(dh_cdata, codes) + self.openssl_assert(res == 1) + + return codes[0] == 0 + def x509_name_bytes(self, name): x509_name = _encode_name_gc(self, name) pp = self._ffi.new("unsigned char **") diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py new file mode 100644 index 00000000..666429f2 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/dh.py @@ -0,0 +1,182 @@ +# 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 absolute_import, division, print_function + +from cryptography import utils +from cryptography.hazmat.primitives.asymmetric import dh + + +def _dh_cdata_to_parameters(dh_cdata, backend): + lib = backend._lib + ffi = backend._ffi + + param_cdata = lib.DHparams_dup(dh_cdata) + backend.openssl_assert(param_cdata != ffi.NULL) + param_cdata = ffi.gc(param_cdata, lib.DH_free) + + return _DHParameters(backend, param_cdata) + + +@utils.register_interface(dh.DHParametersWithSerialization) +class _DHParameters(object): + def __init__(self, backend, dh_cdata): + self._backend = backend + self._dh_cdata = dh_cdata + + def parameter_numbers(self): + p = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, + p, self._backend._ffi.NULL, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + return dh.DHParameterNumbers( + p=self._backend._bn_to_int(p[0]), + g=self._backend._bn_to_int(g[0]) + ) + + def generate_private_key(self): + return self._backend.generate_dh_private_key(self) + + +def _handle_dh_compute_key_error(errors, backend): + lib = backend._lib + + backend.openssl_assert(errors[0][1:] == ( + lib.ERR_LIB_DH, + lib.DH_F_COMPUTE_KEY, + lib.DH_R_INVALID_PUBKEY + )) + + raise ValueError("Public key value is invalid for this exchange.") + + +def _get_dh_num_bits(backend, dh_cdata): + p = backend._ffi.new("BIGNUM **") + backend._lib.DH_get0_pqg(dh_cdata, p, + backend._ffi.NULL, + backend._ffi.NULL) + backend.openssl_assert(p[0] != backend._ffi.NULL) + return backend._lib.BN_num_bits(p[0]) + + +@utils.register_interface(dh.DHPrivateKeyWithSerialization) +class _DHPrivateKey(object): + def __init__(self, backend, dh_cdata): + self._backend = backend + self._dh_cdata = dh_cdata + self._key_size_bytes = self._backend._lib.DH_size(dh_cdata) + + @property + def key_size(self): + return _get_dh_num_bits(self._backend, self._dh_cdata) + + def private_numbers(self): + p = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, + p, self._backend._ffi.NULL, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + pub_key = self._backend._ffi.new("BIGNUM **") + priv_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, priv_key) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL) + return dh.DHPrivateNumbers( + public_numbers=dh.DHPublicNumbers( + parameter_numbers=dh.DHParameterNumbers( + p=self._backend._bn_to_int(p[0]), + g=self._backend._bn_to_int(g[0]) + ), + y=self._backend._bn_to_int(pub_key[0]) + ), + x=self._backend._bn_to_int(priv_key[0]) + ) + + def exchange(self, peer_public_key): + + buf = self._backend._ffi.new("unsigned char[]", self._key_size_bytes) + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key(peer_public_key._dh_cdata, pub_key, + self._backend._ffi.NULL) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + res = self._backend._lib.DH_compute_key( + buf, + pub_key[0], + self._dh_cdata + ) + + if res == -1: + errors = self._backend._consume_errors() + return _handle_dh_compute_key_error(errors, self._backend) + else: + self._backend.openssl_assert(res >= 1) + + key = self._backend._ffi.buffer(buf)[:res] + pad = self._key_size_bytes - len(key) + + if pad > 0: + key = (b"\x00" * pad) + key + + return key + + def public_key(self): + dh_cdata = self._backend._lib.DHparams_dup(self._dh_cdata) + self._backend.openssl_assert(dh_cdata != self._backend._ffi.NULL) + dh_cdata = self._backend._ffi.gc( + dh_cdata, self._backend._lib.DH_free + ) + + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key(self._dh_cdata, + pub_key, self._backend._ffi.NULL) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + pub_key_dup = self._backend._lib.BN_dup(pub_key[0]) + self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL) + + res = self._backend._lib.DH_set0_key(dh_cdata, + pub_key_dup, + self._backend._ffi.NULL) + self._backend.openssl_assert(res == 1) + + return _DHPublicKey(self._backend, dh_cdata) + + def parameters(self): + return _dh_cdata_to_parameters(self._dh_cdata, self._backend) + + +@utils.register_interface(dh.DHPublicKeyWithSerialization) +class _DHPublicKey(object): + def __init__(self, backend, dh_cdata): + self._backend = backend + self._dh_cdata = dh_cdata + self._key_size_bits = _get_dh_num_bits(self._backend, self._dh_cdata) + + @property + def key_size(self): + return self._key_size_bits + + def public_numbers(self): + p = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, + p, self._backend._ffi.NULL, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key(self._dh_cdata, + pub_key, self._backend._ffi.NULL) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + return dh.DHPublicNumbers( + parameter_numbers=dh.DHParameterNumbers( + p=self._backend._bn_to_int(p[0]), + g=self._backend._bn_to_int(g[0]) + ), + y=self._backend._bn_to_int(pub_key[0]) + ) + + def parameters(self): + return _dh_cdata_to_parameters(self._dh_cdata, self._backend) diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py index 12d53eed..ec044ddd 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dh.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -11,6 +11,10 @@ import six from cryptography import utils +def generate_parameters(generator, key_size, backend): + return backend.generate_dh_parameters(generator, key_size) + + class DHPrivateNumbers(object): def __init__(self, x, public_numbers): if not isinstance(x, six.integer_types): @@ -35,6 +39,9 @@ class DHPrivateNumbers(object): def __ne__(self, other): return not self == other + def private_key(self, backend): + return backend.load_dh_private_numbers(self) + public_numbers = utils.read_only_property("_public_numbers") x = utils.read_only_property("_x") @@ -63,6 +70,9 @@ class DHPublicNumbers(object): def __ne__(self, other): return not self == other + def public_key(self, backend): + return backend.load_dh_public_numbers(self) + y = utils.read_only_property("_y") parameter_numbers = utils.read_only_property("_parameter_numbers") @@ -75,6 +85,9 @@ class DHParameterNumbers(object): ): raise TypeError("p and g must be integers") + if g not in (2, 5): + raise ValueError("DH generator must be 2 or 5") + self._p = p self._g = g @@ -90,6 +103,9 @@ class DHParameterNumbers(object): def __ne__(self, other): return not self == other + def parameters(self, backend): + return backend.load_dh_parameter_numbers(self) + p = utils.read_only_property("_p") g = utils.read_only_property("_g") @@ -141,6 +157,13 @@ class DHPrivateKeyWithSerialization(DHPrivateKey): Returns a DHPrivateNumbers. """ + @abc.abstractmethod + def exchange(self, peer_public_key): + """ + Given peer's DHPublicKey, carry out the key exchange and + return shared key as bytes. + """ + @six.add_metaclass(abc.ABCMeta) class DHPublicKey(object): diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index ea08e17b..bd806731 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -11,9 +11,10 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CMACBackend, CipherBackend, DERSerializationBackend, DSABackend, - EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend + CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, + DSABackend, EllipticCurveBackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, PEMSerializationBackend, RSABackend, ScryptBackend, + X509Backend ) from cryptography.hazmat.backends.multibackend import MultiBackend from cryptography.hazmat.primitives import cmac, hashes, hmac @@ -244,6 +245,30 @@ class DummyX509Backend(object): pass +@utils.register_interface(DHBackend) +class DummyDHBackend(object): + def generate_dh_parameters(self, generator, key_size): + pass + + def load_dh_parameter_numbers(self, numbers): + pass + + def generate_dh_private_key(self, parameters): + pass + + def load_dh_private_numbers(self, numbers): + pass + + def load_dh_public_numbers(self, numbers): + pass + + def generate_dh_private_key_and_parameters(self, generator, key_size): + pass + + def dh_parameters_supported(self, p, g): + pass + + @utils.register_interface(ScryptBackend) class DummyScryptBackend(object): def derive_scrypt(self, key_material, salt, length, n, r, p): @@ -587,6 +612,33 @@ class TestMultiBackend(object): with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509): backend.x509_name_bytes(object()) + def test_dh_backend(self): + backend = MultiBackend([DummyDHBackend()]) + + backend.generate_dh_parameters(2, 512) + backend.load_dh_parameter_numbers(object()) + backend.generate_dh_private_key(object()) + backend.load_dh_private_numbers(object()) + backend.load_dh_public_numbers(object()) + backend.generate_dh_private_key_and_parameters(2, 512) + backend.dh_parameters_supported(2, 3) + + backend = MultiBackend([DummyBackend()]) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.generate_dh_parameters(2, 512) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.load_dh_parameter_numbers(object()) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.generate_dh_private_key(object()) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.load_dh_private_numbers(object()) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.load_dh_public_numbers(object()) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.generate_dh_private_key_and_parameters(2, 512) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN): + backend.dh_parameters_supported(2, 3) + def test_scrypt(self): backend = MultiBackend([DummyScryptBackend()]) backend.derive_scrypt(b"key", b"salt", 1, 1, 1, 1) diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index d8869de9..ba724bf0 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -4,22 +4,27 @@ from __future__ import absolute_import, division, print_function +import os + import pytest +from cryptography.hazmat.backends.interfaces import DHBackend from cryptography.hazmat.primitives.asymmetric import dh +from cryptography.utils import bit_length, int_from_bytes +from ...utils import load_nist_vectors, load_vectors_from_file def test_dh_parameternumbers(): params = dh.DHParameterNumbers( - 65537, 3 + 65537, 2 ) assert params.p == 65537 - assert params.g == 3 + assert params.g == 2 with pytest.raises(TypeError): dh.DHParameterNumbers( - None, 3 + None, 2 ) with pytest.raises(TypeError): @@ -32,10 +37,15 @@ def test_dh_parameternumbers(): None, None ) + with pytest.raises(ValueError): + dh.DHParameterNumbers( + 65537, 7 + ) + def test_dh_numbers(): params = dh.DHParameterNumbers( - 65537, 3 + 65537, 2 ) public = dh.DHPublicNumbers( @@ -74,14 +84,14 @@ def test_dh_numbers(): def test_dh_parameter_numbers_equality(): - assert dh.DHParameterNumbers(65537, 3) == dh.DHParameterNumbers(65537, 3) - assert dh.DHParameterNumbers(6, 3) != dh.DHParameterNumbers(65537, 3) - assert dh.DHParameterNumbers(65537, 0) != dh.DHParameterNumbers(65537, 3) - assert dh.DHParameterNumbers(65537, 0) != object() + assert dh.DHParameterNumbers(65537, 2) == dh.DHParameterNumbers(65537, 2) + assert dh.DHParameterNumbers(6, 2) != dh.DHParameterNumbers(65537, 2) + assert dh.DHParameterNumbers(65537, 5) != dh.DHParameterNumbers(65537, 2) + assert dh.DHParameterNumbers(65537, 2) != object() def test_dh_private_numbers_equality(): - params = dh.DHParameterNumbers(65537, 3) + params = dh.DHParameterNumbers(65537, 2) public = dh.DHPublicNumbers(1, params) private = dh.DHPrivateNumbers(2, public) @@ -89,16 +99,189 @@ def test_dh_private_numbers_equality(): assert private != dh.DHPrivateNumbers(0, public) assert private != dh.DHPrivateNumbers(2, dh.DHPublicNumbers(0, params)) assert private != dh.DHPrivateNumbers( - 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 0)) + 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) ) assert private != object() def test_dh_public_numbers_equality(): - params = dh.DHParameterNumbers(65537, 3) + params = dh.DHParameterNumbers(65537, 2) public = dh.DHPublicNumbers(1, params) assert public == dh.DHPublicNumbers(1, params) assert public != dh.DHPublicNumbers(0, params) - assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 0)) + assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) assert public != object() + + +@pytest.mark.requires_backend_interface(interface=DHBackend) +class TestDH(object): + def test_small_key_generate_dh(self, backend): + with pytest.raises(ValueError): + dh.generate_parameters(2, 511, backend) + + def test_unsupported_generator_generate_dh(self, backend): + with pytest.raises(ValueError): + dh.generate_parameters(7, 512, backend) + + def test_dh_parameters_supported(self, backend): + assert backend.dh_parameters_supported(23, 5) + assert not backend.dh_parameters_supported(23, 18) + + def test_convert_to_numbers(self, backend): + parameters = backend.generate_dh_private_key_and_parameters(2, 512) + + private = parameters.private_numbers() + + p = private.public_numbers.parameter_numbers.p + g = private.public_numbers.parameter_numbers.g + + params = dh.DHParameterNumbers(p, g) + public = dh.DHPublicNumbers(1, params) + private = dh.DHPrivateNumbers(2, public) + + deserialized_params = params.parameters(backend) + deserialized_public = public.public_key(backend) + deserialized_private = private.private_key(backend) + + assert isinstance(deserialized_params, + dh.DHParametersWithSerialization) + assert isinstance(deserialized_public, + dh.DHPublicKeyWithSerialization) + assert isinstance(deserialized_private, + dh.DHPrivateKeyWithSerialization) + + def test_numbers_unsupported_parameters(self, backend): + params = dh.DHParameterNumbers(23, 2) + public = dh.DHPublicNumbers(1, params) + private = dh.DHPrivateNumbers(2, public) + + with pytest.raises(ValueError): + private.private_key(backend) + + def test_generate_dh(self, backend): + generator = 2 + key_size = 512 + + parameters = dh.generate_parameters(generator, key_size, backend) + assert isinstance(parameters, dh.DHParameters) + + key = parameters.generate_private_key() + assert isinstance(key, dh.DHPrivateKey) + assert key.key_size == key_size + + public = key.public_key() + assert isinstance(public, dh.DHPublicKey) + assert public.key_size == key_size + + assert isinstance(parameters, dh.DHParametersWithSerialization) + parameter_numbers = parameters.parameter_numbers() + assert isinstance(parameter_numbers, dh.DHParameterNumbers) + assert bit_length(parameter_numbers.p) == key_size + + assert isinstance(public, dh.DHPublicKeyWithSerialization) + assert isinstance(public.public_numbers(), dh.DHPublicNumbers) + assert isinstance(public.parameters(), dh.DHParameters) + + assert isinstance(key, dh.DHPrivateKeyWithSerialization) + assert isinstance(key.private_numbers(), dh.DHPrivateNumbers) + assert isinstance(key.parameters(), dh.DHParameters) + + def test_exchange(self, backend): + parameters = dh.generate_parameters(2, 512, backend) + assert isinstance(parameters, dh.DHParameters) + + key1 = parameters.generate_private_key() + key2 = parameters.generate_private_key() + + symkey1 = key1.exchange(key2.public_key()) + assert symkey1 + assert len(symkey1) == 512 // 8 + + symkey2 = key2.exchange(key1.public_key()) + assert symkey1 == symkey2 + + def test_exchange_algorithm(self, backend): + parameters = dh.generate_parameters(2, 512, backend) + + key1 = parameters.generate_private_key() + key2 = parameters.generate_private_key() + + shared_key_bytes = key2.exchange(key1.public_key()) + symkey = int_from_bytes(shared_key_bytes, 'big') + + symkey_manual = pow(key1.public_key().public_numbers().y, + key2.private_numbers().x, + parameters.parameter_numbers().p) + + assert symkey == symkey_manual + + def test_symmetric_key_padding(self, backend): + """ + This test has specific parameters that produce a symmetric key + In length 63 bytes instead 64. We make sure here that we add + padding to the key. + """ + p = int("11859949538425015739337467917303613431031019140213666" + "129025407300654026585086345323066284800963463204246390" + "256567934582260424238844463330887962689642467123") + g = 2 + y = int("32155788395534640648739966373159697798396966919821525" + "72238852825117261342483718574508213761865276905503199" + "969908098203345481366464874759377454476688391248") + x = int("409364065449673443397833358558926598469347813468816037" + "268451847116982490733450463194921405069999008617231539" + "7147035896687401350877308899732826446337707128") + parameters = dh.DHParameterNumbers(p, g) + public = dh.DHPublicNumbers(y, parameters) + private = dh.DHPrivateNumbers(x, public) + key = private.private_key(backend) + symkey = key.exchange(public.public_key(backend)) + assert len(symkey) == 512 // 8 + assert symkey[:1] == b'\x00' + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "DH", "bad_exchange.txt"), + load_nist_vectors)) + def test_bad_exchange(self, backend, vector): + parameters1 = dh.DHParameterNumbers(int(vector["p1"]), + int(vector["g"])) + public1 = dh.DHPublicNumbers(int(vector["y1"]), parameters1) + private1 = dh.DHPrivateNumbers(int(vector["x1"]), public1) + key1 = private1.private_key(backend) + pub_key1 = key1.public_key() + + parameters2 = dh.DHParameterNumbers(int(vector["p2"]), + int(vector["g"])) + public2 = dh.DHPublicNumbers(int(vector["y2"]), parameters2) + private2 = dh.DHPrivateNumbers(int(vector["x2"]), public2) + key2 = private2.private_key(backend) + pub_key2 = key2.public_key() + + if pub_key2.public_numbers().y >= parameters1.p: + with pytest.raises(ValueError): + key1.exchange(pub_key2) + else: + symkey1 = key1.exchange(pub_key2) + assert symkey1 + + symkey2 = key2.exchange(pub_key1) + + assert symkey1 != symkey2 + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "DH", "vec.txt"), + load_nist_vectors)) + def test_dh_vectors(self, backend, vector): + parameters = dh.DHParameterNumbers(int(vector["p"]), + int(vector["g"])) + public = dh.DHPublicNumbers(int(vector["y"]), parameters) + private = dh.DHPrivateNumbers(int(vector["x"]), public) + key = private.private_key(backend) + symkey = key.exchange(public.public_key(backend)) + + assert int_from_bytes(symkey, 'big') == int(vector["k"], 16) diff --git a/vectors/cryptography_vectors/asymmetric/DH/bad_exchange.txt b/vectors/cryptography_vectors/asymmetric/DH/bad_exchange.txt new file mode 100644 index 00000000..c511ce3d --- /dev/null +++ b/vectors/cryptography_vectors/asymmetric/DH/bad_exchange.txt @@ -0,0 +1,22 @@ +# These are pairs of DH vectors that an exchange between them will result in an error. + +# This pair of vector will result in ValueError as the exchange. That is because Y2 > P1. +COUNT = 0 +G = 2 +P1 = 6982449264326893170076800712455263291469349829090516222210222668518102590295128975951345863513171140301173103075603894877550567268976511279764959149528443 +X1 = 4801532380147672323646734353223620022391392516566390315327913029035641903575640872665137366552076226566411004476255204566867879551828094289388660251782046 +Y1 = 821604160094281686420547900781786073619010872682627891698497892534032059614878030140061466421083272893526509531465029773964398063523169243002396742005311 +P2 = 11080956217961768232353507616608271011094948914260968400815080208586343913901873894476784420098894723332990692686674731674801476861138954727825965908678667 +X2 = 5711420683916244371478141649351505929365347129399136675372566081882611630063783016816795794055507751345814825076698378102524000583805069503046990368030659 +Y2 = 10250982112542385918017381009520473894895926030138509814084625750094204966649199270766083680909922864972425983868488727218698464441106269212018923589462134 + +# This pair of vector exchange will result in two different symmetric keys. +# That is because they are not generated from the same parameters. +COUNT = 1 +G = 2 +P1 = 12900013872746249964533588299424217034480441280987975922107615584280973803458959936181179967371408335732783334903224087948303561132833270378314994720245923 +X1 = 6071003937964305313717247598692016437334013449238587402845410045585785082471492220556199473955398532734458121110746407155944589178844230676809305984951446 +Y1 = 7991113599078333875847794507844575617505022450446398197075320323186801365513151014983321390753605753306200081479128423955529753566986512451925943417370573 +P2 = 8230910504630293046781610423436023785570058813983426764127680163386079097481556242295408030406562540441574901920011043362099679138268556410914150106172603 +X2 = 5094233318592725701435923671489794134941722294242428364605342252104678038235605245715640898911801472906659807096135548788171923289152060442445002898139883 +Y2 = 3153667608586790035405826466812972271982421015353174890600325108812327867424805742934205854052751840807361048259220540363086632109209018127565889223022306 diff --git a/vectors/cryptography_vectors/asymmetric/DH/vec.txt b/vectors/cryptography_vectors/asymmetric/DH/vec.txt new file mode 100644 index 00000000..4ee8e903 --- /dev/null +++ b/vectors/cryptography_vectors/asymmetric/DH/vec.txt @@ -0,0 +1,37 @@ +# This vectors are taken from https://raw.githubusercontent.com/randombit/botan/master/src/tests/data/pubkey/dh.vec. +# converted to fit NIST format. + +COUNT = 0 +P = 58458002095536094658683755258523362961421200751439456159756164191494576279467 +G = 2 +X = 46205663093589612668746163860870963912226379131190812163519349848291472898748 +Y = 26821400572298074358375073922715498403273583367617402781946773132088456286733 +K = 5D9A64F9E54B011381308CF462C207CB0DB7630EAB026E06E5B893041207DBD8 + +COUNT = 1 +P = 7080941971697125115953429172307253449997092375902849066092516886443770423993013931939664664691599157495586618571486777257251370592538466283944520569755243 +G = 2 +X = 558545918073450953822828294657166871085534488182588754070944297274702844791239790350137385125663944726718987047123606999344398260535747142299581818644894 +Y = 5414004572904348025296832268296927639985617596751048805496391472266617693297043686410755975231136915922983393130810278045355076974349430254905203521469281 +K = 65F79BCC47862E02DDE775B8FC7B1D0CBA094B753E502D49A4468687681F178CEE98016210E136E80255FC8FDECC4D38D91EBC82C94B17652BDC7569AC383F39 + +COUNT = 2 +P = 13136945886549419892672364204240698856868140920222753719410945199521492957942501101571382423757107983263697942121022398653040312294171805755003420937008819 +G = 2 +X = 7234683127321298509925294581253140833264699266098978220501763131351358687985896274862103003239800546011982186510097828911316488529426006616714526458066271 +Y = 2279687933022692843565305738851942847753122181957871349712298819158246913703861338250576252594546190393887657989733709994119777023140405518896999362296894 +K = 290BF0265BAAD40A5853BAF3CC18B315982FF282C82D7332DA6A8826E789330D875F29F57D9A622DB7DDF156CF4440EDB8455BC32859FA2A178D42528FA64136 + +COUNT = 3 +P = 7837101158566379575244981793821373232935960937791570311529087011989828139360157566031864344629391921990623396775060696298931110532512473525227009216442663 +G = 5 +X = 2271743810121848480269660476736588602223609425319496369070326649477151605521404690612976189919437998885287608828699462183553427369647044489290359586354073 +Y = 5553605535786829473948867606958960228544449485744361474243970893441441245322015477973193059017330938119851955120496598438033407790780406867070511553809237 +K = 4ED3B2ED6B3E8446D8E1C3D2BCA00FB939464874BB6750BDE5F5DACFE9F819597E96F6B1DBA27C785553383AE87188019734A160B6BA5396760B88EC1F2A0E92 + +COUNT = 4 +P = 94601366105683233785857165617633883930888659900865798821537131281240413334589084848277586190796291421413056657355624982032535320546697702506255545831695999765620345337665948695835489391652130862575428732385880123143529399201847515289798598538556585982541405034303884158851145992650670977252891826481077576283 +G = 2 +X = 23484534159498465809072519330053257189446469650913804186985945596423260246286292600333967669376208639922768407894768298151128017428601886623219847603288857771541399565808620552396892534905511436715248032341612931260211253869255846963169818664325386727815821240629227196043893200670349600641190073530521475769 +Y = 5432307605192951130143195594341230106472453193740817014833299044466758796406314885816769577417181043800208414512454882219387455875605089197474590288847925182956692434090736024405561167965732783350054200615740891235826401928590081691818434389717991597889828540215480852496077254876423767132564403274562077989 +K = 2B6D9504C1D7ACAD9652CF79A6A0630EA9D19C197E908E992BC318BE2867FAE3C1AD5BE83E6E4AA1CBE0347774038F20ACD790DEA82B2A5862BF21FA4A1AB464AE985CA8AC4E5076AD0843E144C1305759BA047446A3A7F2426BE0C724F269009B54447B6A970876E5E4C613356805014D6060039C081AFB046863CA9BE9C848 |