aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorAviv Palivoda <palaviv@gmail.com>2016-11-25 18:51:28 +0200
committerPaul Kehrer <paul.l.kehrer@gmail.com>2016-11-26 00:51:28 +0800
commit495f21a9c61befd4a61054dd1b6080164a7400e9 (patch)
treea3352e8640c371d5125fa318dd0ab21ae611957a /tests
parent53412d4faa5efcfc1743cbcddca96df3a7554ecc (diff)
downloadcryptography-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
Diffstat (limited to 'tests')
-rw-r--r--tests/hazmat/backends/test_multibackend.py58
-rw-r--r--tests/hazmat/primitives/test_dh.py207
2 files changed, 250 insertions, 15 deletions
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)