aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/conftest.py19
-rw-r--r--tests/utils.py41
-rw-r--r--tests/wycheproof/__init__.py0
-rw-r--r--tests/wycheproof/test_keywrap.py61
-rw-r--r--tests/wycheproof/test_rsa.py85
-rw-r--r--tests/wycheproof/test_utils.py21
-rw-r--r--tests/wycheproof/test_x25519.py42
7 files changed, 268 insertions, 1 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index c5efbd36..583c4099 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,13 +8,30 @@ import pytest
from cryptography.hazmat.backends.openssl import backend as openssl_backend
-from .utils import check_backend_support
+from .utils import (
+ check_backend_support, load_wycheproof_tests, skip_if_wycheproof_none
+)
def pytest_report_header(config):
return "OpenSSL: {0}".format(openssl_backend.openssl_version_text())
+def pytest_addoption(parser):
+ parser.addoption("--wycheproof-root", default=None)
+
+
+def pytest_generate_tests(metafunc):
+ if "wycheproof" in metafunc.fixturenames:
+ wycheproof = metafunc.config.getoption("--wycheproof-root")
+ skip_if_wycheproof_none(wycheproof)
+
+ testcases = []
+ for path in metafunc.function.wycheproof_tests.args:
+ testcases.extend(load_wycheproof_tests(wycheproof, path))
+ metafunc.parametrize("wycheproof", testcases)
+
+
@pytest.fixture()
def backend(request):
required_interfaces = [
diff --git a/tests/utils.py b/tests/utils.py
index b721f344..ccc3b7c1 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -6,7 +6,9 @@ from __future__ import absolute_import, division, print_function
import binascii
import collections
+import json
import math
+import os
import re
from contextlib import contextmanager
@@ -884,3 +886,42 @@ def load_nist_ccm_vectors(vector_data):
test_data[name.lower()] = value.encode("ascii")
return data
+
+
+class WycheproofTest(object):
+ def __init__(self, testgroup, testcase):
+ self.testgroup = testgroup
+ self.testcase = testcase
+
+ def __repr__(self):
+ return "<WycheproofTest({!r}, {!r}, tcId={})>".format(
+ self.testgroup, self.testcase, self.testcase["tcId"],
+ )
+
+ @property
+ def valid(self):
+ return self.testcase["result"] == "valid"
+
+ @property
+ def acceptable(self):
+ return self.testcase["result"] == "acceptable"
+
+ def has_flag(self, flag):
+ return flag in self.testcase["flags"]
+
+
+def skip_if_wycheproof_none(wycheproof):
+ # This is factored into its own function so we can easily test both
+ # branches
+ if wycheproof is None:
+ pytest.skip("--wycheproof-root not provided")
+
+
+def load_wycheproof_tests(wycheproof, test_file):
+ path = os.path.join(wycheproof, "testvectors", test_file)
+ with open(path) as f:
+ data = json.load(f)
+ for group in data["testGroups"]:
+ cases = group.pop("tests")
+ for c in cases:
+ yield WycheproofTest(group, c)
diff --git a/tests/wycheproof/__init__.py b/tests/wycheproof/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/wycheproof/__init__.py
diff --git a/tests/wycheproof/test_keywrap.py b/tests/wycheproof/test_keywrap.py
new file mode 100644
index 00000000..5f694e4d
--- /dev/null
+++ b/tests/wycheproof/test_keywrap.py
@@ -0,0 +1,61 @@
+# 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
+
+import binascii
+
+import pytest
+
+from cryptography.hazmat.backends.interfaces import CipherBackend
+from cryptography.hazmat.primitives import keywrap
+
+
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+@pytest.mark.wycheproof_tests("kwp_test.json")
+def test_keywrap_with_padding(backend, wycheproof):
+ wrapping_key = binascii.unhexlify(wycheproof.testcase["key"])
+ key_to_wrap = binascii.unhexlify(wycheproof.testcase["msg"])
+ expected = binascii.unhexlify(wycheproof.testcase["ct"])
+
+ result = keywrap.aes_key_wrap_with_padding(
+ wrapping_key, key_to_wrap, backend
+ )
+ if wycheproof.valid or wycheproof.acceptable:
+ assert result == expected
+
+ if wycheproof.valid or (wycheproof.acceptable and not len(expected) < 16):
+ result = keywrap.aes_key_unwrap_with_padding(
+ wrapping_key, expected, backend
+ )
+ assert result == key_to_wrap
+ else:
+ with pytest.raises(keywrap.InvalidUnwrap):
+ keywrap.aes_key_unwrap_with_padding(
+ wrapping_key, expected, backend
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+@pytest.mark.wycheproof_tests("kw_test.json")
+def test_keywrap(backend, wycheproof):
+ wrapping_key = binascii.unhexlify(wycheproof.testcase["key"])
+ key_to_wrap = binascii.unhexlify(wycheproof.testcase["msg"])
+ expected = binascii.unhexlify(wycheproof.testcase["ct"])
+
+ if (
+ wycheproof.valid or (
+ wycheproof.acceptable and
+ wycheproof.testcase["comment"] != "invalid size of wrapped key"
+ )
+ ):
+ result = keywrap.aes_key_wrap(wrapping_key, key_to_wrap, backend)
+ assert result == expected
+
+ if wycheproof.valid or wycheproof.acceptable:
+ result = keywrap.aes_key_unwrap(wrapping_key, expected, backend)
+ assert result == key_to_wrap
+ else:
+ with pytest.raises(keywrap.InvalidUnwrap):
+ keywrap.aes_key_unwrap(wrapping_key, expected, backend)
diff --git a/tests/wycheproof/test_rsa.py b/tests/wycheproof/test_rsa.py
new file mode 100644
index 00000000..b8f2e19d
--- /dev/null
+++ b/tests/wycheproof/test_rsa.py
@@ -0,0 +1,85 @@
+# 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
+
+import binascii
+
+import pytest
+
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.backends.interfaces import RSABackend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import padding
+
+
+_DIGESTS = {
+ "SHA-1": hashes.SHA1(),
+ "SHA-224": hashes.SHA224(),
+ "SHA-256": hashes.SHA256(),
+ "SHA-384": hashes.SHA384(),
+ "SHA-512": hashes.SHA512(),
+}
+
+
+def should_verify(backend, wycheproof):
+ if wycheproof.valid:
+ return True
+
+ if wycheproof.acceptable:
+ if (
+ backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER and
+ wycheproof.has_flag("MissingNull")
+ ):
+ return False
+ return True
+
+ return False
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.supported(
+ only_if=lambda backend: (
+ # TODO: this also skips on LibreSSL, which is ok for now, since these
+ # don't pass on Libre, but we'll need to fix this after they resolve
+ # it.
+ not backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102
+ ),
+ skip_message=(
+ "Many of these tests fail on OpenSSL < 1.0.2 and since upstream isn't"
+ " maintaining it, they'll never be fixed."
+ ),
+)
+@pytest.mark.wycheproof_tests(
+ "rsa_signature_test.json",
+ "rsa_signature_2048_sha224_test.json",
+ "rsa_signature_2048_sha256_test.json",
+ "rsa_signature_2048_sha512_test.json",
+ "rsa_signature_3072_sha256_test.json",
+ "rsa_signature_3072_sha384_test.json",
+ "rsa_signature_3072_sha512_test.json",
+ "rsa_signature_4096_sha384_test.json",
+ "rsa_signature_4096_sha512_test.json",
+)
+def test_rsa_signature(backend, wycheproof):
+ key = serialization.load_der_public_key(
+ binascii.unhexlify(wycheproof.testgroup["keyDer"]), backend
+ )
+ digest = _DIGESTS[wycheproof.testgroup["sha"]]
+
+ if should_verify(backend, wycheproof):
+ key.verify(
+ binascii.unhexlify(wycheproof.testcase["sig"]),
+ binascii.unhexlify(wycheproof.testcase["msg"]),
+ padding.PKCS1v15(),
+ digest,
+ )
+ else:
+ with pytest.raises(InvalidSignature):
+ key.verify(
+ binascii.unhexlify(wycheproof.testcase["sig"]),
+ binascii.unhexlify(wycheproof.testcase["msg"]),
+ padding.PKCS1v15(),
+ digest,
+ )
diff --git a/tests/wycheproof/test_utils.py b/tests/wycheproof/test_utils.py
new file mode 100644
index 00000000..82c0a359
--- /dev/null
+++ b/tests/wycheproof/test_utils.py
@@ -0,0 +1,21 @@
+# 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
+
+import pytest
+
+from ..utils import WycheproofTest, skip_if_wycheproof_none
+
+
+def test_wycheproof_test_repr():
+ wycheproof = WycheproofTest({}, {"tcId": 3})
+ assert repr(wycheproof) == "<WycheproofTest({}, {'tcId': 3}, tcId=3)>"
+
+
+def test_skip_if_wycheproof_none():
+ with pytest.raises(pytest.skip.Exception):
+ skip_if_wycheproof_none(None)
+
+ skip_if_wycheproof_none("abc")
diff --git a/tests/wycheproof/test_x25519.py b/tests/wycheproof/test_x25519.py
new file mode 100644
index 00000000..5e6253ce
--- /dev/null
+++ b/tests/wycheproof/test_x25519.py
@@ -0,0 +1,42 @@
+# 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
+
+import binascii
+
+import pytest
+
+from cryptography.hazmat.backends.interfaces import DHBackend
+from cryptography.hazmat.primitives.asymmetric.x25519 import (
+ X25519PrivateKey, X25519PublicKey
+)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x25519_supported(),
+ skip_message="Requires OpenSSL with X25519 support"
+)
+@pytest.mark.requires_backend_interface(interface=DHBackend)
+@pytest.mark.wycheproof_tests("x25519_test.json")
+def test_x25519(backend, wycheproof):
+ assert list(wycheproof.testgroup.items()) == [("curve", "curve25519")]
+
+ private_key = X25519PrivateKey._from_private_bytes(
+ binascii.unhexlify(wycheproof.testcase["private"])
+ )
+ public_key = X25519PublicKey.from_public_bytes(
+ binascii.unhexlify(wycheproof.testcase["public"])
+ )
+
+ assert wycheproof.valid or wycheproof.acceptable
+
+ expected = binascii.unhexlify(wycheproof.testcase["shared"])
+ if expected == b"\x00" * 32:
+ assert wycheproof.acceptable
+ # OpenSSL returns an error on all zeros shared key
+ with pytest.raises(ValueError):
+ private_key.exchange(public_key)
+ else:
+ assert private_key.exchange(public_key) == expected