aboutsummaryrefslogtreecommitdiffstats
path: root/tests/hazmat
diff options
context:
space:
mode:
Diffstat (limited to 'tests/hazmat')
-rw-r--r--tests/hazmat/backends/test_commoncrypto.py61
-rw-r--r--tests/hazmat/backends/test_multibackend.py498
-rw-r--r--tests/hazmat/backends/test_openssl.py472
-rw-r--r--tests/hazmat/backends/test_openssl_memleak.py451
-rw-r--r--tests/hazmat/bindings/test_commoncrypto.py26
-rw-r--r--tests/hazmat/bindings/test_openssl.py135
-rw-r--r--tests/hazmat/primitives/fixtures_ec.py296
-rw-r--r--tests/hazmat/primitives/fixtures_rsa.py72
-rw-r--r--tests/hazmat/primitives/test_3des.py30
-rw-r--r--tests/hazmat/primitives/test_aead.py446
-rw-r--r--tests/hazmat/primitives/test_aes.py236
-rw-r--r--tests/hazmat/primitives/test_arc4.py3
-rw-r--r--tests/hazmat/primitives/test_asym_utils.py53
-rw-r--r--tests/hazmat/primitives/test_block.py67
-rw-r--r--tests/hazmat/primitives/test_blowfish.py16
-rw-r--r--tests/hazmat/primitives/test_camellia.py16
-rw-r--r--tests/hazmat/primitives/test_cast5.py33
-rw-r--r--tests/hazmat/primitives/test_chacha20.py76
-rw-r--r--tests/hazmat/primitives/test_ciphers.py184
-rw-r--r--tests/hazmat/primitives/test_cmac.py26
-rw-r--r--tests/hazmat/primitives/test_concatkdf.py56
-rw-r--r--tests/hazmat/primitives/test_dh.py802
-rw-r--r--tests/hazmat/primitives/test_dsa.py968
-rw-r--r--tests/hazmat/primitives/test_ec.py734
-rw-r--r--tests/hazmat/primitives/test_ed25519.py226
-rw-r--r--tests/hazmat/primitives/test_ed448.py239
-rw-r--r--tests/hazmat/primitives/test_hash_vectors.py231
-rw-r--r--tests/hazmat/primitives/test_hashes.py127
-rw-r--r--tests/hazmat/primitives/test_hkdf.py61
-rw-r--r--tests/hazmat/primitives/test_hkdf_vectors.py4
-rw-r--r--tests/hazmat/primitives/test_hmac.py30
-rw-r--r--tests/hazmat/primitives/test_hmac_vectors.py36
-rw-r--r--tests/hazmat/primitives/test_idea.py16
-rw-r--r--tests/hazmat/primitives/test_kbkdf.py158
-rw-r--r--tests/hazmat/primitives/test_kbkdf_vectors.py23
-rw-r--r--tests/hazmat/primitives/test_keywrap.py207
-rw-r--r--tests/hazmat/primitives/test_padding.py108
-rw-r--r--tests/hazmat/primitives/test_pbkdf2hmac.py18
-rw-r--r--tests/hazmat/primitives/test_pkcs12.py139
-rw-r--r--tests/hazmat/primitives/test_poly1305.py152
-rw-r--r--tests/hazmat/primitives/test_rsa.py1273
-rw-r--r--tests/hazmat/primitives/test_scrypt.py183
-rw-r--r--tests/hazmat/primitives/test_seed.py16
-rw-r--r--tests/hazmat/primitives/test_serialization.py648
-rw-r--r--tests/hazmat/primitives/test_x25519.py260
-rw-r--r--tests/hazmat/primitives/test_x448.py239
-rw-r--r--tests/hazmat/primitives/test_x963_vectors.py66
-rw-r--r--tests/hazmat/primitives/test_x963kdf.py131
-rw-r--r--tests/hazmat/primitives/twofactor/test_hotp.py9
-rw-r--r--tests/hazmat/primitives/twofactor/test_totp.py6
-rw-r--r--tests/hazmat/primitives/utils.py101
-rw-r--r--tests/hazmat/test_der.py225
-rw-r--r--tests/hazmat/test_oid.py39
53 files changed, 8453 insertions, 2275 deletions
diff --git a/tests/hazmat/backends/test_commoncrypto.py b/tests/hazmat/backends/test_commoncrypto.py
deleted file mode 100644
index f7200016..00000000
--- a/tests/hazmat/backends/test_commoncrypto.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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 cryptography import utils
-from cryptography.exceptions import InternalError, _Reasons
-from cryptography.hazmat.backends import _available_backends
-from cryptography.hazmat.primitives.ciphers import Cipher, CipherAlgorithm
-from cryptography.hazmat.primitives.ciphers.algorithms import AES
-from cryptography.hazmat.primitives.ciphers.modes import CBC, GCM
-
-from ...utils import raises_unsupported_algorithm
-
-
-@utils.register_interface(CipherAlgorithm)
-class DummyCipher(object):
- name = "dummy-cipher"
- block_size = None
- key_size = None
-
-
-@pytest.mark.skipif("commoncrypto" not in
- [i.name for i in _available_backends()],
- reason="CommonCrypto not available")
-class TestCommonCrypto(object):
- def test_supports_cipher(self):
- from cryptography.hazmat.backends.commoncrypto.backend import backend
- assert backend.cipher_supported(None, None) is False
-
- def test_register_duplicate_cipher_adapter(self):
- from cryptography.hazmat.backends.commoncrypto.backend import backend
- with pytest.raises(ValueError):
- backend._register_cipher_adapter(
- AES, backend._lib.kCCAlgorithmAES128,
- CBC, backend._lib.kCCModeCBC
- )
-
- def test_handle_response(self):
- from cryptography.hazmat.backends.commoncrypto.backend import backend
-
- with pytest.raises(ValueError):
- backend._check_cipher_response(backend._lib.kCCAlignmentError)
-
- with pytest.raises(InternalError):
- backend._check_cipher_response(backend._lib.kCCMemoryFailure)
-
- with pytest.raises(InternalError):
- backend._check_cipher_response(backend._lib.kCCDecodeError)
-
- def test_nonexistent_aead_cipher(self):
- from cryptography.hazmat.backends.commoncrypto.backend import Backend
- b = Backend()
- cipher = Cipher(
- DummyCipher(), GCM(b"fake_iv_here"), backend=b,
- )
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
- cipher.encryptor()
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
deleted file mode 100644
index 3c05cdfa..00000000
--- a/tests/hazmat/backends/test_multibackend.py
+++ /dev/null
@@ -1,498 +0,0 @@
-# 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.exceptions import (
- UnsupportedAlgorithm, _Reasons
-)
-from cryptography.hazmat.backends.interfaces import (
- CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
- EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
- PEMSerializationBackend, RSABackend, X509Backend
-)
-from cryptography.hazmat.backends.multibackend import MultiBackend
-from cryptography.hazmat.primitives import cmac, hashes, hmac
-from cryptography.hazmat.primitives.asymmetric import ec, padding
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-
-from ...utils import raises_unsupported_algorithm
-
-
-@utils.register_interface(CipherBackend)
-class DummyCipherBackend(object):
- def __init__(self, supported_ciphers):
- self._ciphers = supported_ciphers
-
- def cipher_supported(self, cipher, mode):
- return (type(cipher), type(mode)) in self._ciphers
-
- def create_symmetric_encryption_ctx(self, cipher, mode):
- if not self.cipher_supported(cipher, mode):
- raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_CIPHER)
-
- def create_symmetric_decryption_ctx(self, cipher, mode):
- if not self.cipher_supported(cipher, mode):
- raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_CIPHER)
-
-
-@utils.register_interface(HashBackend)
-class DummyHashBackend(object):
- def __init__(self, supported_algorithms):
- self._algorithms = supported_algorithms
-
- def hash_supported(self, algorithm):
- return type(algorithm) in self._algorithms
-
- def create_hash_ctx(self, algorithm):
- if not self.hash_supported(algorithm):
- raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_HASH)
-
-
-@utils.register_interface(HMACBackend)
-class DummyHMACBackend(object):
- def __init__(self, supported_algorithms):
- self._algorithms = supported_algorithms
-
- def hmac_supported(self, algorithm):
- return type(algorithm) in self._algorithms
-
- def create_hmac_ctx(self, key, algorithm):
- if not self.hmac_supported(algorithm):
- raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_HASH)
-
-
-@utils.register_interface(PBKDF2HMACBackend)
-class DummyPBKDF2HMACBackend(object):
- def __init__(self, supported_algorithms):
- self._algorithms = supported_algorithms
-
- def pbkdf2_hmac_supported(self, algorithm):
- return type(algorithm) in self._algorithms
-
- def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
- key_material):
- if not self.pbkdf2_hmac_supported(algorithm):
- raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_HASH)
-
-
-@utils.register_interface(RSABackend)
-class DummyRSABackend(object):
- def generate_rsa_private_key(self, public_exponent, key_size):
- pass
-
- def rsa_padding_supported(self, padding):
- pass
-
- def generate_rsa_parameters_supported(self, public_exponent, key_size):
- pass
-
- def load_rsa_private_numbers(self, numbers):
- pass
-
- def load_rsa_public_numbers(self, numbers):
- pass
-
-
-@utils.register_interface(DSABackend)
-class DummyDSABackend(object):
- def generate_dsa_parameters(self, key_size):
- pass
-
- def generate_dsa_private_key(self, parameters):
- pass
-
- def generate_dsa_private_key_and_parameters(self, key_size):
- pass
-
- def dsa_hash_supported(self, algorithm):
- pass
-
- def dsa_parameters_supported(self, p, q, g):
- pass
-
- def load_dsa_private_numbers(self, numbers):
- pass
-
- def load_dsa_public_numbers(self, numbers):
- pass
-
- def load_dsa_parameter_numbers(self, numbers):
- pass
-
-
-@utils.register_interface(CMACBackend)
-class DummyCMACBackend(object):
- def __init__(self, supported_algorithms):
- self._algorithms = supported_algorithms
-
- def cmac_algorithm_supported(self, algorithm):
- return type(algorithm) in self._algorithms
-
- def create_cmac_ctx(self, algorithm):
- if not self.cmac_algorithm_supported(algorithm):
- raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_CIPHER)
-
-
-@utils.register_interface(EllipticCurveBackend)
-class DummyEllipticCurveBackend(object):
- def __init__(self, supported_curves):
- self._curves = supported_curves
-
- def elliptic_curve_supported(self, curve):
- return any(
- isinstance(curve, curve_type)
- for curve_type in self._curves
- )
-
- def elliptic_curve_signature_algorithm_supported(
- self, signature_algorithm, curve
- ):
- return (
- isinstance(signature_algorithm, ec.ECDSA) and
- any(
- isinstance(curve, curve_type)
- for curve_type in self._curves
- )
- )
-
- def generate_elliptic_curve_private_key(self, curve):
- if not self.elliptic_curve_supported(curve):
- raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
-
- def load_elliptic_curve_private_numbers(self, numbers):
- if not self.elliptic_curve_supported(numbers.public_numbers.curve):
- raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
-
- def load_elliptic_curve_public_numbers(self, numbers):
- if not self.elliptic_curve_supported(numbers.curve):
- raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
-
-
-@utils.register_interface(PEMSerializationBackend)
-class DummyPEMSerializationBackend(object):
- def load_pem_private_key(self, data, password):
- pass
-
- def load_pem_public_key(self, data):
- pass
-
-
-@utils.register_interface(DERSerializationBackend)
-class DummyDERSerializationBackend(object):
- def load_der_private_key(self, data, password):
- pass
-
- def load_der_public_key(self, data):
- pass
-
-
-@utils.register_interface(X509Backend)
-class DummyX509Backend(object):
- def load_pem_x509_certificate(self, data):
- pass
-
- def load_der_x509_certificate(self, data):
- pass
-
- def load_pem_x509_csr(self, data):
- pass
-
- def load_der_x509_csr(self, data):
- pass
-
- def create_x509_csr(self, builder, private_key, algorithm):
- pass
-
-
-class TestMultiBackend(object):
- def test_ciphers(self):
- backend = MultiBackend([
- DummyHashBackend([]),
- DummyCipherBackend([
- (algorithms.AES, modes.CBC),
- ])
- ])
- assert backend.cipher_supported(
- algorithms.AES(b"\x00" * 16), modes.CBC(b"\x00" * 16)
- )
-
- cipher = Cipher(
- algorithms.AES(b"\x00" * 16),
- modes.CBC(b"\x00" * 16),
- backend=backend
- )
- cipher.encryptor()
- cipher.decryptor()
-
- cipher = Cipher(
- algorithms.Camellia(b"\x00" * 16),
- modes.CBC(b"\x00" * 16),
- backend=backend
- )
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
- cipher.encryptor()
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
- cipher.decryptor()
-
- def test_hashes(self):
- backend = MultiBackend([
- DummyHashBackend([hashes.MD5])
- ])
- assert backend.hash_supported(hashes.MD5())
-
- hashes.Hash(hashes.MD5(), backend=backend)
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- hashes.Hash(hashes.SHA1(), backend=backend)
-
- def test_hmac(self):
- backend = MultiBackend([
- DummyHMACBackend([hashes.MD5])
- ])
- assert backend.hmac_supported(hashes.MD5())
-
- hmac.HMAC(b"", hashes.MD5(), backend=backend)
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- hmac.HMAC(b"", hashes.SHA1(), backend=backend)
-
- def test_pbkdf2(self):
- backend = MultiBackend([
- DummyPBKDF2HMACBackend([hashes.MD5])
- ])
- assert backend.pbkdf2_hmac_supported(hashes.MD5())
-
- backend.derive_pbkdf2_hmac(hashes.MD5(), 10, b"", 10, b"")
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- backend.derive_pbkdf2_hmac(hashes.SHA1(), 10, b"", 10, b"")
-
- def test_rsa(self):
- backend = MultiBackend([
- DummyRSABackend()
- ])
-
- backend.generate_rsa_private_key(
- key_size=1024, public_exponent=65537
- )
-
- backend.rsa_padding_supported(padding.PKCS1v15())
-
- backend.generate_rsa_parameters_supported(65537, 1024)
-
- backend.load_rsa_private_numbers("private_numbers")
-
- backend.load_rsa_public_numbers("public_numbers")
-
- backend = MultiBackend([])
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.generate_rsa_private_key(key_size=1024, public_exponent=3)
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.rsa_padding_supported(padding.PKCS1v15())
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.generate_rsa_parameters_supported(65537, 1024)
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.load_rsa_private_numbers("private_numbers")
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.load_rsa_public_numbers("public_numbers")
-
- def test_dsa(self):
- backend = MultiBackend([
- DummyDSABackend()
- ])
-
- backend.generate_dsa_parameters(key_size=1024)
-
- parameters = object()
- backend.generate_dsa_private_key(parameters)
- backend.generate_dsa_private_key_and_parameters(key_size=1024)
-
- backend.dsa_hash_supported(hashes.SHA1())
- backend.dsa_parameters_supported(1, 2, 3)
- backend.load_dsa_private_numbers("numbers")
- backend.load_dsa_public_numbers("numbers")
- backend.load_dsa_parameter_numbers("numbers")
-
- backend = MultiBackend([])
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.generate_dsa_parameters(key_size=1024)
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.generate_dsa_private_key(parameters)
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.generate_dsa_private_key_and_parameters(key_size=1024)
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.dsa_hash_supported(hashes.SHA1())
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.dsa_parameters_supported('p', 'q', 'g')
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.load_dsa_private_numbers("numbers")
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.load_dsa_public_numbers("numbers")
-
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
- backend.load_dsa_parameter_numbers("numbers")
-
- def test_cmac(self):
- backend = MultiBackend([
- DummyCMACBackend([algorithms.AES])
- ])
-
- fake_key = b"\x00" * 16
-
- assert backend.cmac_algorithm_supported(
- algorithms.AES(fake_key)) is True
-
- cmac.CMAC(algorithms.AES(fake_key), backend)
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
- cmac.CMAC(algorithms.TripleDES(fake_key), backend)
-
- def test_elliptic_curve(self):
- backend = MultiBackend([
- DummyEllipticCurveBackend([
- ec.SECT283K1
- ])
- ])
-
- assert backend.elliptic_curve_supported(ec.SECT283K1()) is True
-
- assert backend.elliptic_curve_signature_algorithm_supported(
- ec.ECDSA(hashes.SHA256()),
- ec.SECT283K1()
- ) is True
-
- backend.generate_elliptic_curve_private_key(ec.SECT283K1())
-
- backend.load_elliptic_curve_private_numbers(
- ec.EllipticCurvePrivateNumbers(
- 1,
- ec.EllipticCurvePublicNumbers(
- 2,
- 3,
- ec.SECT283K1()
- )
- )
- )
-
- backend.load_elliptic_curve_public_numbers(
- ec.EllipticCurvePublicNumbers(
- 2,
- 3,
- ec.SECT283K1()
- )
- )
-
- assert backend.elliptic_curve_supported(ec.SECT163K1()) is False
-
- assert backend.elliptic_curve_signature_algorithm_supported(
- ec.ECDSA(hashes.SHA256()),
- ec.SECT163K1()
- ) is False
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
- backend.generate_elliptic_curve_private_key(ec.SECT163K1())
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
- backend.load_elliptic_curve_private_numbers(
- ec.EllipticCurvePrivateNumbers(
- 1,
- ec.EllipticCurvePublicNumbers(
- 2,
- 3,
- ec.SECT163K1()
- )
- )
- )
-
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
- backend.load_elliptic_curve_public_numbers(
- ec.EllipticCurvePublicNumbers(
- 2,
- 3,
- ec.SECT163K1()
- )
- )
-
- def test_pem_serialization_backend(self):
- backend = MultiBackend([DummyPEMSerializationBackend()])
-
- backend.load_pem_private_key(b"keydata", None)
- backend.load_pem_public_key(b"keydata")
-
- backend = MultiBackend([])
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
- backend.load_pem_private_key(b"keydata", None)
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
- backend.load_pem_public_key(b"keydata")
-
- def test_der_serialization_backend(self):
- backend = MultiBackend([DummyDERSerializationBackend()])
-
- backend.load_der_private_key(b"keydata", None)
- backend.load_der_public_key(b"keydata")
-
- backend = MultiBackend([])
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
- backend.load_der_private_key(b"keydata", None)
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
- backend.load_der_public_key(b"keydata")
-
- def test_x509_backend(self):
- backend = MultiBackend([DummyX509Backend()])
-
- backend.load_pem_x509_certificate(b"certdata")
- backend.load_der_x509_certificate(b"certdata")
- backend.load_pem_x509_csr(b"reqdata")
- backend.load_der_x509_csr(b"reqdata")
- backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
-
- backend = MultiBackend([])
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
- backend.load_pem_x509_certificate(b"certdata")
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
- backend.load_der_x509_certificate(b"certdata")
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
- backend.load_pem_x509_csr(b"reqdata")
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
- backend.load_der_x509_csr(b"reqdata")
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
- backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 6a2e8a77..44fd3db4 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -4,60 +4,49 @@
from __future__ import absolute_import, division, print_function
+import itertools
import os
import subprocess
import sys
import textwrap
-import pretend
-
import pytest
-from cryptography import utils
+from cryptography import x509
from cryptography.exceptions import InternalError, _Reasons
-from cryptography.hazmat.backends.interfaces import RSABackend
+from cryptography.hazmat.backends.interfaces import DHBackend, RSABackend
from cryptography.hazmat.backends.openssl.backend import (
Backend, backend
)
from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve
from cryptography.hazmat.primitives import hashes, serialization
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding
-from cryptography.hazmat.primitives.ciphers import (
- BlockCipherAlgorithm, Cipher, CipherAlgorithm
-)
+from cryptography.hazmat.primitives.asymmetric import dh, dsa, padding
+from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
-from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR, Mode
+from cryptography.hazmat.primitives.ciphers.modes import CBC
-from ..primitives.fixtures_dsa import DSA_KEY_2048
from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
-from ..primitives.test_ec import _skip_curve_unsupported
-from ...utils import load_vectors_from_file, raises_unsupported_algorithm
-
-
-@utils.register_interface(Mode)
-class DummyMode(object):
- name = "dummy-mode"
-
- def validate_for_algorithm(self, algorithm):
- pass
-
+from ...doubles import (
+ DummyAsymmetricPadding, DummyCipherAlgorithm, DummyHashAlgorithm, DummyMode
+)
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
+from ...x509.test_x509 import _load_cert
-@utils.register_interface(CipherAlgorithm)
-class DummyCipher(object):
- name = "dummy-cipher"
- key_size = None
+def skip_if_libre_ssl(openssl_version):
+ if u'LibreSSL' in openssl_version:
+ pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.")
-@utils.register_interface(padding.AsymmetricPadding)
-class DummyPadding(object):
- name = "dummy-cipher"
+class TestLibreSkip(object):
+ def test_skip_no(self):
+ assert skip_if_libre_ssl(u"OpenSSL 1.0.2h 3 May 2016") is None
-@utils.register_interface(hashes.HashAlgorithm)
-class DummyHash(object):
- name = "dummy-hash"
- block_size = None
- digest_size = None
+ def test_skip_yes(self):
+ with pytest.raises(pytest.skip.Exception):
+ skip_if_libre_ssl(u"LibreSSL 2.1.6")
class DummyMGF(object):
@@ -82,14 +71,12 @@ class TestOpenSSL(object):
backend.openssl_version_text().startswith("LibreSSL")
)
+ def test_openssl_version_number(self):
+ assert backend.openssl_version_number() > 0
+
def test_supports_cipher(self):
assert backend.cipher_supported(None, None) is False
- def test_aes_ctr_always_available(self):
- # AES CTR should always be available in both 0.9.8 and 1.0.0+
- assert backend.cipher_supported(AES(b"\x00" * 16),
- CTR(b"\x00" * 16)) is True
-
def test_register_duplicate_cipher_adapter(self):
with pytest.raises(ValueError):
backend.register_cipher_adapter(AES, CBC, None)
@@ -98,16 +85,21 @@ class TestOpenSSL(object):
def test_nonexistent_cipher(self, mode):
b = Backend()
b.register_cipher_adapter(
- DummyCipher,
+ DummyCipherAlgorithm,
type(mode),
lambda backend, cipher, mode: backend._ffi.NULL
)
cipher = Cipher(
- DummyCipher(), mode, backend=b,
+ DummyCipherAlgorithm(), mode, backend=b,
)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
cipher.encryptor()
+ def test_openssl_assert(self):
+ backend.openssl_assert(True)
+ with pytest.raises(InternalError):
+ backend.openssl_assert(False)
+
def test_consume_errors(self):
for i in range(10):
backend._lib.ERR_put_error(backend._lib.ERR_LIB_EVP, 0, 0,
@@ -120,25 +112,8 @@ class TestOpenSSL(object):
assert backend._lib.ERR_peek_error() == 0
assert len(errors) == 10
- def test_openssl_error_string(self):
- backend._lib.ERR_put_error(
- backend._lib.ERR_LIB_EVP,
- backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
- 0,
- b"test_openssl.py",
- -1
- )
-
- errors = backend._consume_errors()
- exc = backend._unknown_error(errors[0])
-
- assert (
- "digital envelope routines:"
- "EVP_DecryptFinal_ex:digital envelope routines" in str(exc)
- )
-
def test_ssl_ciphers_registered(self):
- meth = backend._lib.TLSv1_method()
+ meth = backend._lib.SSLv23_method()
ctx = backend._lib.SSL_CTX_new(meth)
assert ctx != backend._ffi.NULL
backend._lib.SSL_CTX_free(ctx)
@@ -148,12 +123,9 @@ class TestOpenSSL(object):
assert cipher != backend._ffi.NULL
def test_error_strings_loaded(self):
- # returns a value in a static buffer
- err = backend._lib.ERR_error_string(101183626, backend._ffi.NULL)
- assert backend._ffi.string(err) == (
- b"error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:"
- b"data not multiple of block length"
- )
+ buf = backend._ffi.new("char[]", 256)
+ backend._lib.ERR_error_string_n(101183626, buf, len(buf))
+ assert b"data not multiple of block length" in backend._ffi.string(buf)
def test_unknown_error_in_cipher_finalize(self):
cipher = Cipher(AES(b"\0" * 16), CBC(b"\0" * 16), backend=backend)
@@ -164,40 +136,19 @@ class TestOpenSSL(object):
with pytest.raises(InternalError):
enc.finalize()
- def test_derive_pbkdf2_raises_unsupported_on_old_openssl(self):
- if backend.pbkdf2_hmac_supported(hashes.SHA256()):
- pytest.skip("Requires an older OpenSSL")
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- backend.derive_pbkdf2_hmac(hashes.SHA256(), 10, b"", 1000, b"")
-
- @pytest.mark.skipif(
- backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000000f,
- reason="Requires an older OpenSSL. Must be < 1.0.0"
- )
- def test_large_key_size_on_old_openssl(self):
- with pytest.raises(ValueError):
- dsa.generate_parameters(2048, backend=backend)
-
- with pytest.raises(ValueError):
- dsa.generate_parameters(3072, backend=backend)
-
- @pytest.mark.skipif(
- backend._lib.OPENSSL_VERSION_NUMBER < 0x1000000f,
- reason="Requires a newer OpenSSL. Must be >= 1.0.0"
- )
def test_large_key_size_on_new_openssl(self):
parameters = dsa.generate_parameters(2048, backend)
param_num = parameters.parameter_numbers()
- assert utils.bit_length(param_num.p) == 2048
+ assert param_num.p.bit_length() == 2048
parameters = dsa.generate_parameters(3072, backend)
param_num = parameters.parameter_numbers()
- assert utils.bit_length(param_num.p) == 3072
+ assert param_num.p.bit_length() == 3072
def test_int_to_bn(self):
value = (2 ** 4242) - 4242
bn = backend._int_to_bn(value)
assert bn != backend._ffi.NULL
- bn = backend._ffi.gc(bn, backend._lib.BN_free)
+ bn = backend._ffi.gc(bn, backend._lib.BN_clear_free)
assert bn
assert backend._bn_to_int(bn) == value
@@ -217,15 +168,29 @@ class TestOpenSSL(object):
assert backend._bn_to_int(bn) == 0
+@pytest.mark.skipif(
+ backend._lib.Cryptography_HAS_ENGINE == 0,
+ reason="Requires OpenSSL with ENGINE support")
class TestOpenSSLRandomEngine(object):
- def teardown_method(self, method):
+ def setup(self):
+ # The default RAND engine is global and shared between
+ # tests. We make sure that the default engine is osrandom
+ # before we start each test and restore the global state to
+ # that engine in teardown.
+ current_default = backend._lib.ENGINE_get_default_RAND()
+ name = backend._lib.ENGINE_get_name(current_default)
+ assert name == backend._lib.Cryptography_osrandom_engine_name
+
+ def teardown(self):
# we need to reset state to being default. backend is a shared global
# for all these tests.
backend.activate_osrandom_engine()
current_default = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(current_default)
- assert name == backend._binding._osrandom_engine_name
+ assert name == backend._lib.Cryptography_osrandom_engine_name
+ @pytest.mark.skipif(sys.executable is None,
+ reason="No Python interpreter available.")
def test_osrandom_engine_is_default(self, tmpdir):
engine_printer = textwrap.dedent(
"""
@@ -254,18 +219,19 @@ class TestOpenSSLRandomEngine(object):
subprocess.check_call(
[sys.executable, "-c", engine_printer],
env=env,
- stdout=out
+ stdout=out,
+ stderr=subprocess.PIPE,
)
osrandom_engine_name = backend._ffi.string(
- backend._binding._osrandom_engine_name
+ backend._lib.Cryptography_osrandom_engine_name
)
assert engine_name.read().encode('ascii') == osrandom_engine_name
def test_osrandom_sanity_check(self):
# This test serves as a check against catastrophic failure.
- buf = backend._ffi.new("char[]", 500)
+ buf = backend._ffi.new("unsigned char[]", 500)
res = backend._lib.RAND_bytes(buf, 500)
assert res == 1
assert backend._ffi.buffer(buf)[:] != "\x00" * 500
@@ -277,7 +243,7 @@ class TestOpenSSLRandomEngine(object):
backend.activate_osrandom_engine()
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._binding._osrandom_engine_name
+ assert name == backend._lib.Cryptography_osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
@@ -285,7 +251,7 @@ class TestOpenSSLRandomEngine(object):
e = backend._lib.ENGINE_get_default_RAND()
assert e != backend._ffi.NULL
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._binding._osrandom_engine_name
+ assert name == backend._lib.Cryptography_osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
backend.activate_builtin_random()
@@ -300,20 +266,50 @@ class TestOpenSSLRandomEngine(object):
e = backend._lib.ENGINE_get_default_RAND()
assert e == backend._ffi.NULL
+ def test_osrandom_engine_implementation(self):
+ name = backend.osrandom_engine_implementation()
+ assert name in ['/dev/urandom', 'CryptGenRandom', 'getentropy',
+ 'getrandom']
+ if sys.platform.startswith('linux'):
+ assert name in ['getrandom', '/dev/urandom']
+ if sys.platform == 'darwin':
+ assert name in ['getentropy', '/dev/urandom']
+ if sys.platform == 'win32':
+ assert name == 'CryptGenRandom'
+
def test_activate_osrandom_already_default(self):
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._binding._osrandom_engine_name
+ assert name == backend._lib.Cryptography_osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
backend.activate_osrandom_engine()
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._binding._osrandom_engine_name
+ assert name == backend._lib.Cryptography_osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
+@pytest.mark.skipif(
+ backend._lib.Cryptography_HAS_ENGINE == 1,
+ reason="Requires OpenSSL without ENGINE support")
+class TestOpenSSLNoEngine(object):
+ def test_no_engine_support(self):
+ assert backend._ffi.string(
+ backend._lib.Cryptography_osrandom_engine_id
+ ) == b"no-engine-support"
+ assert backend._ffi.string(
+ backend._lib.Cryptography_osrandom_engine_name
+ ) == b"osrandom_engine disabled due to no engine support"
+
+ def test_activate_builtin_random_does_nothing(self):
+ backend.activate_builtin_random()
+
+ def test_activate_osrandom_does_nothing(self):
+ backend.activate_osrandom_engine()
+
+
class TestOpenSSLRSA(object):
def test_generate_rsa_parameters_supported(self):
assert backend.generate_rsa_parameters_supported(1, 1024) is False
@@ -337,42 +333,13 @@ class TestOpenSSLRSA(object):
backend.generate_rsa_private_key(public_exponent=65537,
key_size=256)
- @pytest.mark.skipif(
- backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000100f,
- reason="Requires an older OpenSSL. Must be < 1.0.1"
- )
- def test_non_sha1_pss_mgf1_hash_algorithm_on_old_openssl(self):
- private_key = RSA_KEY_512.private_key(backend)
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- private_key.signer(
- padding.PSS(
- mgf=padding.MGF1(
- algorithm=hashes.SHA256(),
- ),
- salt_length=padding.PSS.MAX_LENGTH
- ),
- hashes.SHA1()
- )
- public_key = private_key.public_key()
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- public_key.verifier(
- b"sig",
- padding.PSS(
- mgf=padding.MGF1(
- algorithm=hashes.SHA256(),
- ),
- salt_length=padding.PSS.MAX_LENGTH
- ),
- hashes.SHA1()
- )
-
def test_rsa_padding_unsupported_pss_mgf1_hash(self):
assert backend.rsa_padding_supported(
- padding.PSS(mgf=padding.MGF1(DummyHash()), salt_length=0)
+ padding.PSS(mgf=padding.MGF1(DummyHashAlgorithm()), salt_length=0)
) is False
def test_rsa_padding_unsupported(self):
- assert backend.rsa_padding_supported(DummyPadding()) is False
+ assert backend.rsa_padding_supported(DummyAsymmetricPadding()) is False
def test_rsa_padding_supported_pkcs1v15(self):
assert backend.rsa_padding_supported(padding.PKCS1v15()) is True
@@ -391,6 +358,27 @@ class TestOpenSSLRSA(object):
),
) is True
+ @pytest.mark.skipif(
+ backend._lib.Cryptography_HAS_RSA_OAEP_MD == 0,
+ reason="Requires OpenSSL with rsa_oaep_md (1.0.2+)"
+ )
+ def test_rsa_padding_supported_oaep_sha2_combinations(self):
+ hashalgs = [
+ hashes.SHA1(),
+ hashes.SHA224(),
+ hashes.SHA256(),
+ hashes.SHA384(),
+ hashes.SHA512(),
+ ]
+ for mgf1alg, oaepalg in itertools.product(hashalgs, hashalgs):
+ assert backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=mgf1alg),
+ algorithm=oaepalg,
+ label=None
+ ),
+ ) is True
+
def test_rsa_padding_unsupported_mgf(self):
assert backend.rsa_padding_supported(
padding.OAEP(
@@ -404,9 +392,13 @@ class TestOpenSSLRSA(object):
padding.PSS(mgf=DummyMGF(), salt_length=0)
) is False
+ @pytest.mark.skipif(
+ backend._lib.Cryptography_HAS_RSA_OAEP_MD == 1,
+ reason="Requires OpenSSL without rsa_oaep_md (< 1.0.2)"
+ )
def test_unsupported_mgf1_hash_algorithm_decrypt(self):
private_key = RSA_KEY_512.private_key(backend)
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
private_key.decrypt(
b"0" * 64,
padding.OAEP(
@@ -416,9 +408,13 @@ class TestOpenSSLRSA(object):
)
)
+ @pytest.mark.skipif(
+ backend._lib.Cryptography_HAS_RSA_OAEP_MD == 1,
+ reason="Requires OpenSSL without rsa_oaep_md (< 1.0.2)"
+ )
def test_unsupported_oaep_hash_algorithm_decrypt(self):
private_key = RSA_KEY_512.private_key(backend)
- with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
private_key.decrypt(
b"0" * 64,
padding.OAEP(
@@ -428,70 +424,96 @@ class TestOpenSSLRSA(object):
)
)
- def test_unsupported_oaep_label_decrypt(self):
+ def test_unsupported_mgf1_hash_algorithm_md5_decrypt(self):
private_key = RSA_KEY_512.private_key(backend)
- with pytest.raises(ValueError):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
private_key.decrypt(
b"0" * 64,
padding.OAEP(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- algorithm=hashes.SHA1(),
- label=b"label"
+ mgf=padding.MGF1(algorithm=hashes.MD5()),
+ algorithm=hashes.MD5(),
+ label=None
)
)
-@pytest.mark.skipif(
- backend._lib.OPENSSL_VERSION_NUMBER <= 0x10001000,
- reason="Requires an OpenSSL version >= 1.0.1"
-)
class TestOpenSSLCMAC(object):
def test_unsupported_cipher(self):
- @utils.register_interface(BlockCipherAlgorithm)
- class FakeAlgorithm(object):
- block_size = 64
-
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
- backend.create_cmac_ctx(FakeAlgorithm())
+ backend.create_cmac_ctx(DummyCipherAlgorithm())
-class TestOpenSSLCreateX509CSR(object):
- @pytest.mark.skipif(
- backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000,
- reason="Requires an older OpenSSL. Must be < 1.0.1"
- )
- def test_unsupported_dsa_keys(self):
- private_key = DSA_KEY_2048.private_key(backend)
+class TestOpenSSLSignX509Certificate(object):
+ def test_requires_certificate_builder(self):
+ private_key = RSA_KEY_2048.private_key(backend)
- with pytest.raises(NotImplementedError):
- backend.create_x509_csr(object(), private_key, hashes.SHA1())
+ with pytest.raises(TypeError):
+ backend.create_x509_certificate(
+ object(), private_key, DummyHashAlgorithm()
+ )
+
+
+class TestOpenSSLSignX509CSR(object):
+ def test_requires_csr_builder(self):
+ private_key = RSA_KEY_2048.private_key(backend)
+
+ with pytest.raises(TypeError):
+ backend.create_x509_csr(
+ object(), private_key, DummyHashAlgorithm()
+ )
- @pytest.mark.skipif(
- backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000,
- reason="Requires an older OpenSSL. Must be < 1.0.1"
- )
- def test_unsupported_ec_keys(self):
- _skip_curve_unsupported(backend, ec.SECP256R1())
- private_key = ec.generate_private_key(ec.SECP256R1(), backend)
- with pytest.raises(NotImplementedError):
- backend.create_x509_csr(object(), private_key, hashes.SHA1())
+class TestOpenSSLSignX509CertificateRevocationList(object):
+ def test_invalid_builder(self):
+ private_key = RSA_KEY_2048.private_key(backend)
+ with pytest.raises(TypeError):
+ backend.create_x509_crl(object(), private_key, hashes.SHA256())
-class TestOpenSSLSerialisationWithOpenSSL(object):
- def test_pem_password_cb_buffer_too_small(self):
- ffi_cb, cb = backend._pem_password_cb(b"aa")
- assert cb(None, 1, False, None) == 0
+
+class TestOpenSSLCreateRevokedCertificate(object):
+ def test_invalid_builder(self):
+ with pytest.raises(TypeError):
+ backend.create_x509_revoked_certificate(object())
+
+
+class TestOpenSSLSerializationWithOpenSSL(object):
+ def test_pem_password_cb(self):
+ userdata = backend._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
+ pw = b"abcdefg"
+ password = backend._ffi.new("char []", pw)
+ userdata.password = password
+ userdata.length = len(pw)
+ buflen = 10
+ buf = backend._ffi.new("char []", buflen)
+ res = backend._lib.Cryptography_pem_password_cb(
+ buf, buflen, 0, userdata
+ )
+ assert res == len(pw)
+ assert userdata.called == 1
+ assert backend._ffi.buffer(buf, len(pw))[:] == pw
+ assert userdata.maxsize == buflen
+ assert userdata.error == 0
+
+ def test_pem_password_cb_no_password(self):
+ userdata = backend._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
+ buflen = 10
+ buf = backend._ffi.new("char []", buflen)
+ res = backend._lib.Cryptography_pem_password_cb(
+ buf, buflen, 0, userdata
+ )
+ assert res == 0
+ assert userdata.error == -1
def test_unsupported_evp_pkey_type(self):
- key = pretend.stub(type="unsupported")
+ key = backend._create_evp_pkey_gc()
with raises_unsupported_algorithm(None):
backend._evp_pkey_to_private_key(key)
with raises_unsupported_algorithm(None):
backend._evp_pkey_to_public_key(key)
def test_very_long_pem_serialization_password(self):
- password = "x" * 1024
+ password = b"x" * 1024
with pytest.raises(ValueError):
load_vectors_from_file(
@@ -507,23 +529,7 @@ class TestOpenSSLSerialisationWithOpenSSL(object):
)
-class DummyLibrary(object):
- Cryptography_HAS_EC = 0
-
-
class TestOpenSSLEllipticCurve(object):
- def test_elliptic_curve_supported(self, monkeypatch):
- monkeypatch.setattr(backend, "_lib", DummyLibrary())
-
- assert backend.elliptic_curve_supported(None) is False
-
- def test_elliptic_curve_signature_algorithm_supported(self, monkeypatch):
- monkeypatch.setattr(backend, "_lib", DummyLibrary())
-
- assert backend.elliptic_curve_signature_algorithm_supported(
- None, None
- ) is False
-
def test_sn_to_elliptic_curve_not_supported(self):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
_sn_to_elliptic_curve(backend, b"fake")
@@ -540,3 +546,101 @@ class TestRSAPEMSerialization(object):
serialization.PrivateFormat.PKCS8,
serialization.BestAvailableEncryption(password)
)
+
+
+class TestGOSTCertificate(object):
+ def test_numeric_string_x509_name_entry(self):
+ cert = _load_cert(
+ os.path.join("x509", "e-trust.ru.der"),
+ x509.load_der_x509_certificate,
+ backend
+ )
+ if backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I:
+ with pytest.raises(ValueError) as exc:
+ cert.subject
+
+ # We assert on the message in this case because if the certificate
+ # fails to load it will also raise a ValueError and this test could
+ # erroneously pass.
+ assert str(exc.value) == "Unsupported ASN1 string type. Type: 18"
+ else:
+ assert cert.subject.get_attributes_for_oid(
+ x509.ObjectIdentifier("1.2.643.3.131.1.1")
+ )[0].value == "007710474375"
+
+
+@pytest.mark.skipif(
+ backend._lib.Cryptography_HAS_EVP_PKEY_DHX == 1,
+ reason="Requires OpenSSL without EVP_PKEY_DHX (< 1.0.2)")
+@pytest.mark.requires_backend_interface(interface=DHBackend)
+class TestOpenSSLDHSerialization(object):
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "RFC5114.txt"),
+ load_nist_vectors))
+ def test_dh_serialization_with_q_unsupported(self, backend, vector):
+ parameters = dh.DHParameterNumbers(int(vector["p"], 16),
+ int(vector["g"], 16),
+ int(vector["q"], 16))
+ public = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters)
+ private = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public)
+ private_key = private.private_key(backend)
+ public_key = private_key.public_key()
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
+ private_key.private_bytes(serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption())
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
+ public_key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.SubjectPublicKeyInfo)
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
+ parameters.parameters(backend).parameter_bytes(
+ serialization.Encoding.PEM,
+ serialization.ParameterFormat.PKCS3)
+
+ @pytest.mark.parametrize(
+ ("key_path", "loader_func"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
+ serialization.load_pem_private_key,
+ ),
+ (
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
+ serialization.load_der_private_key,
+ )
+ ]
+ )
+ def test_private_load_dhx_unsupported(self, key_path, loader_func,
+ backend):
+ key_bytes = load_vectors_from_file(
+ key_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ with pytest.raises(ValueError):
+ loader_func(key_bytes, None, backend)
+
+ @pytest.mark.parametrize(
+ ("key_path", "loader_func"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
+ serialization.load_pem_public_key,
+ ),
+ (
+ os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
+ serialization.load_der_public_key,
+ )
+ ]
+ )
+ def test_public_load_dhx_unsupported(self, key_path, loader_func,
+ backend):
+ key_bytes = load_vectors_from_file(
+ key_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ with pytest.raises(ValueError):
+ loader_func(key_bytes, backend)
diff --git a/tests/hazmat/backends/test_openssl_memleak.py b/tests/hazmat/backends/test_openssl_memleak.py
new file mode 100644
index 00000000..935ea3df
--- /dev/null
+++ b/tests/hazmat/backends/test_openssl_memleak.py
@@ -0,0 +1,451 @@
+# 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 json
+import os
+import subprocess
+import sys
+import textwrap
+
+import pytest
+
+from cryptography.hazmat.bindings.openssl.binding import Binding
+
+
+MEMORY_LEAK_SCRIPT = """
+import sys
+
+
+def main(argv):
+ import gc
+ import json
+
+ import cffi
+
+ from cryptography.hazmat.bindings._openssl import ffi, lib
+
+ heap = {}
+
+ BACKTRACE_ENABLED = False
+ if BACKTRACE_ENABLED:
+ backtrace_ffi = cffi.FFI()
+ backtrace_ffi.cdef('''
+ int backtrace(void **, int);
+ char **backtrace_symbols(void *const *, int);
+ ''')
+ backtrace_lib = backtrace_ffi.dlopen(None)
+
+ def backtrace():
+ buf = backtrace_ffi.new("void*[]", 24)
+ length = backtrace_lib.backtrace(buf, len(buf))
+ return (buf, length)
+
+ def symbolize_backtrace(trace):
+ (buf, length) = trace
+ symbols = backtrace_lib.backtrace_symbols(buf, length)
+ stack = [
+ backtrace_ffi.string(symbols[i]).decode()
+ for i in range(length)
+ ]
+ lib.Cryptography_free_wrapper(symbols, backtrace_ffi.NULL, 0)
+ return stack
+ else:
+ def backtrace():
+ return None
+
+ def symbolize_backtrace(trace):
+ return None
+
+ @ffi.callback("void *(size_t, const char *, int)")
+ def malloc(size, path, line):
+ ptr = lib.Cryptography_malloc_wrapper(size, path, line)
+ heap[ptr] = (size, path, line, backtrace())
+ return ptr
+
+ @ffi.callback("void *(void *, size_t, const char *, int)")
+ def realloc(ptr, size, path, line):
+ if ptr != ffi.NULL:
+ del heap[ptr]
+ new_ptr = lib.Cryptography_realloc_wrapper(ptr, size, path, line)
+ heap[new_ptr] = (size, path, line, backtrace())
+ return new_ptr
+
+ @ffi.callback("void(void *, const char *, int)")
+ def free(ptr, path, line):
+ if ptr != ffi.NULL:
+ del heap[ptr]
+ lib.Cryptography_free_wrapper(ptr, path, line)
+
+ result = lib.Cryptography_CRYPTO_set_mem_functions(malloc, realloc, free)
+ assert result == 1
+
+ # Trigger a bunch of initialization stuff.
+ import cryptography.hazmat.backends.openssl
+
+ start_heap = set(heap)
+
+ func(*argv[1:])
+ gc.collect()
+ gc.collect()
+ gc.collect()
+
+ if lib.Cryptography_HAS_OPENSSL_CLEANUP:
+ lib.OPENSSL_cleanup()
+
+ # Swap back to the original functions so that if OpenSSL tries to free
+ # something from its atexit handle it won't be going through a Python
+ # function, which will be deallocated when this function returns
+ result = lib.Cryptography_CRYPTO_set_mem_functions(
+ ffi.addressof(lib, "Cryptography_malloc_wrapper"),
+ ffi.addressof(lib, "Cryptography_realloc_wrapper"),
+ ffi.addressof(lib, "Cryptography_free_wrapper"),
+ )
+ assert result == 1
+
+ remaining = set(heap) - start_heap
+
+ if remaining:
+ sys.stdout.write(json.dumps(dict(
+ (int(ffi.cast("size_t", ptr)), {
+ "size": heap[ptr][0],
+ "path": ffi.string(heap[ptr][1]).decode(),
+ "line": heap[ptr][2],
+ "backtrace": symbolize_backtrace(heap[ptr][3]),
+ })
+ for ptr in remaining
+ )))
+ sys.stdout.flush()
+ sys.exit(255)
+
+main(sys.argv)
+"""
+
+
+def assert_no_memory_leaks(s, argv=[]):
+ env = os.environ.copy()
+ env["PYTHONPATH"] = os.pathsep.join(sys.path)
+ argv = [
+ sys.executable, "-c", "{}\n\n{}".format(s, MEMORY_LEAK_SCRIPT)
+ ] + argv
+ # Shell out to a fresh Python process because OpenSSL does not allow you to
+ # install new memory hooks after the first malloc/free occurs.
+ proc = subprocess.Popen(
+ argv,
+ env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ try:
+ proc.wait()
+ if proc.returncode == 255:
+ # 255 means there was a leak, load the info about what mallocs
+ # weren't freed.
+ out = json.loads(proc.stdout.read().decode())
+ raise AssertionError(out)
+ elif proc.returncode != 0:
+ # Any exception type will do to be honest
+ raise ValueError(proc.stdout.read(), proc.stderr.read())
+ finally:
+ proc.stdout.close()
+ proc.stderr.close()
+
+
+def skip_if_memtesting_not_supported():
+ return pytest.mark.skipif(
+ not Binding().lib.Cryptography_HAS_MEM_FUNCTIONS,
+ reason="Requires OpenSSL memory functions (>=1.1.0)"
+ )
+
+
+@skip_if_memtesting_not_supported()
+class TestAssertNoMemoryLeaks(object):
+ def test_no_leak_no_malloc(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ pass
+ """))
+
+ def test_no_leak_free(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography.hazmat.bindings.openssl.binding import Binding
+ b = Binding()
+ name = b.lib.X509_NAME_new()
+ b.lib.X509_NAME_free(name)
+ """))
+
+ def test_no_leak_gc(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography.hazmat.bindings.openssl.binding import Binding
+ b = Binding()
+ name = b.lib.X509_NAME_new()
+ b.ffi.gc(name, b.lib.X509_NAME_free)
+ """))
+
+ def test_leak(self):
+ with pytest.raises(AssertionError):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography.hazmat.bindings.openssl.binding import (
+ Binding
+ )
+ b = Binding()
+ b.lib.X509_NAME_new()
+ """))
+
+ def test_errors(self):
+ with pytest.raises(ValueError):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ raise ZeroDivisionError
+ """))
+
+
+@skip_if_memtesting_not_supported()
+class TestOpenSSLMemoryLeaks(object):
+ @pytest.mark.parametrize("path", [
+ "x509/PKITS_data/certs/ValidcRLIssuerTest28EE.crt",
+ ])
+ def test_der_x509_certificate_extensions(self, path):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func(path):
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+
+ import cryptography_vectors
+
+ with cryptography_vectors.open_vector_file(path, "rb") as f:
+ cert = x509.load_der_x509_certificate(
+ f.read(), backend
+ )
+
+ cert.extensions
+ """), [path])
+
+ @pytest.mark.parametrize("path", [
+ "x509/cryptography.io.pem",
+ ])
+ def test_pem_x509_certificate_extensions(self, path):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func(path):
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+
+ import cryptography_vectors
+
+ with cryptography_vectors.open_vector_file(path, "rb") as f:
+ cert = x509.load_pem_x509_certificate(
+ f.read(), backend
+ )
+
+ cert.extensions
+ """), [path])
+
+ def test_x509_csr_extensions(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.hazmat.primitives.asymmetric import rsa
+
+ private_key = rsa.generate_private_key(
+ key_size=2048, public_exponent=65537, backend=backend
+ )
+ cert = x509.CertificateSigningRequestBuilder().subject_name(
+ x509.Name([])
+ ).add_extension(
+ x509.OCSPNoCheck(), critical=False
+ ).sign(private_key, hashes.SHA256(), backend)
+
+ cert.extensions
+ """))
+
+ def test_ec_private_numbers_private_key(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives.asymmetric import ec
+
+ ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '280814107134858470598753916394807521398239633534281633982576099083'
+ '35787109896602102090002196616273211495718603965098'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP384R1(),
+ x=int(
+ '10036914308591746758780165503819213553101287571902957054148542'
+ '504671046744460374996612408381962208627004841444205030'
+ ),
+ y=int(
+ '17337335659928075994560513699823544906448896792102247714689323'
+ '575406618073069185107088229463828921069465902299522926'
+ )
+ )
+ ).private_key(backend)
+ """))
+
+ def test_ec_derive_private_key(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives.asymmetric import ec
+ ec.derive_private_key(1, ec.SECP256R1(), backend)
+ """))
+
+ def test_x25519_pubkey_from_private_key(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography.hazmat.primitives.asymmetric import x25519
+ private_key = x25519.X25519PrivateKey.generate()
+ private_key.public_key()
+ """))
+
+ def test_create_ocsp_request(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.x509 import ocsp
+ import cryptography_vectors
+
+ path = "x509/PKITS_data/certs/ValidcRLIssuerTest28EE.crt"
+ with cryptography_vectors.open_vector_file(path, "rb") as f:
+ cert = x509.load_der_x509_certificate(
+ f.read(), backend
+ )
+ builder = ocsp.OCSPRequestBuilder()
+ builder = builder.add_certificate(
+ cert, cert, hashes.SHA1()
+ ).add_extension(x509.OCSPNonce(b"0000"), False)
+ req = builder.build()
+ """))
+
+ @pytest.mark.parametrize("path", [
+ "pkcs12/cert-aes256cbc-no-key.p12",
+ "pkcs12/cert-key-aes256cbc.p12",
+ ])
+ def test_load_pkcs12_key_and_certificates(self, path):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func(path):
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives.serialization import pkcs12
+ import cryptography_vectors
+
+ with cryptography_vectors.open_vector_file(path, "rb") as f:
+ pkcs12.load_key_and_certificates(
+ f.read(), b"cryptography", backend
+ )
+ """), [path])
+
+ def test_create_crl_with_idp(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ import datetime
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.hazmat.primitives.asymmetric import ec
+ from cryptography.x509.oid import NameOID
+
+ key = ec.generate_private_key(ec.SECP256R1(), backend)
+ last_update = datetime.datetime(2002, 1, 1, 12, 1)
+ next_update = datetime.datetime(2030, 1, 1, 12, 1)
+ idp = x509.IssuingDistributionPoint(
+ full_name=None,
+ relative_name=x509.RelativeDistinguishedName([
+ x509.NameAttribute(
+ oid=x509.NameOID.ORGANIZATION_NAME, value=u"PyCA")
+ ]),
+ only_contains_user_certs=False,
+ only_contains_ca_certs=True,
+ only_some_reasons=None,
+ indirect_crl=False,
+ only_contains_attribute_certs=False,
+ )
+ builder = x509.CertificateRevocationListBuilder().issuer_name(
+ x509.Name([
+ x509.NameAttribute(
+ NameOID.COMMON_NAME, u"cryptography.io CA"
+ )
+ ])
+ ).last_update(
+ last_update
+ ).next_update(
+ next_update
+ ).add_extension(
+ idp, True
+ )
+
+ crl = builder.sign(key, hashes.SHA256(), backend)
+ crl.extensions.get_extension_for_class(
+ x509.IssuingDistributionPoint
+ )
+ """))
+
+ def test_create_certificate_with_extensions(self):
+ assert_no_memory_leaks(textwrap.dedent("""
+ def func():
+ import datetime
+
+ from cryptography import x509
+ from cryptography.hazmat.backends.openssl import backend
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.hazmat.primitives.asymmetric import ec
+ from cryptography.x509.oid import (
+ AuthorityInformationAccessOID, ExtendedKeyUsageOID, NameOID
+ )
+
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+
+ not_valid_before = datetime.datetime.now()
+ not_valid_after = not_valid_before + datetime.timedelta(days=365)
+
+ aia = x509.AuthorityInformationAccess([
+ x509.AccessDescription(
+ AuthorityInformationAccessOID.OCSP,
+ x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+ ),
+ x509.AccessDescription(
+ AuthorityInformationAccessOID.CA_ISSUERS,
+ x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+ )
+ ])
+ sans = [u'*.example.org', u'foobar.example.net']
+ san = x509.SubjectAlternativeName(list(map(x509.DNSName, sans)))
+
+ ski = x509.SubjectKeyIdentifier.from_public_key(
+ private_key.public_key()
+ )
+ eku = x509.ExtendedKeyUsage([
+ ExtendedKeyUsageOID.CLIENT_AUTH,
+ ExtendedKeyUsageOID.SERVER_AUTH,
+ ExtendedKeyUsageOID.CODE_SIGNING,
+ ])
+
+ builder = x509.CertificateBuilder().serial_number(
+ 777
+ ).issuer_name(x509.Name([
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
+ ])).subject_name(x509.Name([
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'),
+ ])).public_key(
+ private_key.public_key()
+ ).add_extension(
+ aia, critical=False
+ ).not_valid_before(
+ not_valid_before
+ ).not_valid_after(
+ not_valid_after
+ )
+
+ cert = builder.sign(private_key, hashes.SHA256(), backend)
+ cert.extensions
+ """))
diff --git a/tests/hazmat/bindings/test_commoncrypto.py b/tests/hazmat/bindings/test_commoncrypto.py
deleted file mode 100644
index b0a2dc43..00000000
--- a/tests/hazmat/bindings/test_commoncrypto.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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
-
-
-ccbinding = pytest.importorskip(
- "cryptography.hazmat.bindings.commoncrypto.binding"
-)
-
-
-class TestCommonCrypto(object):
- def test_binding_loads(self):
- binding = ccbinding.Binding()
- assert binding
- assert binding.lib
- assert binding.ffi
-
- def test_binding_returns_same_lib(self):
- binding = ccbinding.Binding()
- binding2 = ccbinding.Binding()
- assert binding.lib == binding2.lib
- assert binding.ffi == binding2.ffi
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index fe78b0ba..29a1c459 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -6,7 +6,10 @@ from __future__ import absolute_import, division, print_function
import pytest
-from cryptography.hazmat.bindings.openssl.binding import Binding
+from cryptography.exceptions import InternalError
+from cryptography.hazmat.bindings.openssl.binding import (
+ Binding, _consume_errors, _openssl_assert, _verify_package_version
+)
class TestOpenSSL(object):
@@ -18,85 +21,27 @@ class TestOpenSSL(object):
def test_crypto_lock_init(self):
b = Binding()
- b.init_static_locks()
- lock_cb = b.lib.CRYPTO_get_locking_callback()
- assert lock_cb != b.ffi.NULL
-
- def _skip_if_not_fallback_lock(self, b):
- # only run this test if we are using our locking cb
- original_cb = b.lib.CRYPTO_get_locking_callback()
- if original_cb != b._lock_cb_handle:
- pytest.skip(
- "Not using the fallback Python locking callback "
- "implementation. Probably because import _ssl set one"
- )
-
- def test_fallback_crypto_lock_via_openssl_api(self):
- b = Binding()
- b.init_static_locks()
-
- self._skip_if_not_fallback_lock(b)
-
- # check that the lock state changes appropriately
- lock = b._locks[b.lib.CRYPTO_LOCK_SSL]
-
- # starts out unlocked
- assert lock.acquire(False)
- lock.release()
-
- b.lib.CRYPTO_lock(
- b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
- b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0
- )
-
- # becomes locked
- assert not lock.acquire(False)
-
- b.lib.CRYPTO_lock(
- b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
- b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0
- )
- # then unlocked
- assert lock.acquire(False)
- lock.release()
-
- def test_fallback_crypto_lock_via_binding_api(self):
- b = Binding()
b.init_static_locks()
-
- self._skip_if_not_fallback_lock(b)
-
- lock = b._locks[b.lib.CRYPTO_LOCK_SSL]
-
- with pytest.raises(RuntimeError):
- b._lock_cb(0, b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
-
- # errors shouldn't cause locking
- assert lock.acquire(False)
- lock.release()
-
- b._lock_cb(b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
- b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
- # locked
- assert not lock.acquire(False)
-
- b._lock_cb(b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
- b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
- # unlocked
- assert lock.acquire(False)
- lock.release()
+ lock_cb = b.lib.CRYPTO_get_locking_callback()
+ if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
+ assert lock_cb == b.ffi.NULL
+ assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 0
+ else:
+ assert lock_cb != b.ffi.NULL
+ assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 1
def test_add_engine_more_than_once(self):
b = Binding()
- res = b._register_osrandom_engine()
- assert res == 2
+ b._register_osrandom_engine()
+ assert b.lib.ERR_get_error() == 0
def test_ssl_ctx_options(self):
# Test that we're properly handling 32-bit unsigned on all platforms.
b = Binding()
assert b.lib.SSL_OP_ALL > 0
- ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method())
+ ctx = b.lib.SSL_CTX_new(b.lib.SSLv23_method())
+ assert ctx != b.ffi.NULL
ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
current_options = b.lib.SSL_CTX_get_options(ctx)
resp = b.lib.SSL_CTX_set_options(ctx, b.lib.SSL_OP_ALL)
@@ -108,7 +53,8 @@ class TestOpenSSL(object):
# Test that we're properly handling 32-bit unsigned on all platforms.
b = Binding()
assert b.lib.SSL_OP_ALL > 0
- ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method())
+ ctx = b.lib.SSL_CTX_new(b.lib.SSLv23_method())
+ assert ctx != b.ffi.NULL
ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
ssl = b.lib.SSL_new(ctx)
ssl = b.ffi.gc(ssl, b.lib.SSL_free)
@@ -122,7 +68,8 @@ class TestOpenSSL(object):
# Test that we're properly handling 32-bit unsigned on all platforms.
b = Binding()
assert b.lib.SSL_OP_ALL > 0
- ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method())
+ ctx = b.lib.SSL_CTX_new(b.lib.SSLv23_method())
+ assert ctx != b.ffi.NULL
ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
ssl = b.lib.SSL_new(ctx)
ssl = b.ffi.gc(ssl, b.lib.SSL_free)
@@ -131,3 +78,47 @@ class TestOpenSSL(object):
expected_options = current_options | b.lib.SSL_OP_ALL
assert resp == expected_options
assert b.lib.SSL_get_mode(ssl) == expected_options
+
+ def test_conditional_removal(self):
+ b = Binding()
+
+ if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
+ assert b.lib.TLS_ST_OK
+ else:
+ with pytest.raises(AttributeError):
+ b.lib.TLS_ST_OK
+
+ def test_openssl_assert_error_on_stack(self):
+ b = Binding()
+ b.lib.ERR_put_error(
+ b.lib.ERR_LIB_EVP,
+ b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
+ b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
+ b"",
+ -1
+ )
+ with pytest.raises(InternalError) as exc_info:
+ _openssl_assert(b.lib, False)
+
+ error = exc_info.value.err_code[0]
+ assert error.code == 101183626
+ assert error.lib == b.lib.ERR_LIB_EVP
+ assert error.func == b.lib.EVP_F_EVP_ENCRYPTFINAL_EX
+ assert error.reason == b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
+ assert b"data not multiple of block length" in error.reason_text
+
+ def test_check_startup_errors_are_allowed(self):
+ b = Binding()
+ b.lib.ERR_put_error(
+ b.lib.ERR_LIB_EVP,
+ b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
+ b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
+ b"",
+ -1
+ )
+ b._register_osrandom_engine()
+ assert _consume_errors(b.lib) == []
+
+ def test_version_mismatch(self):
+ with pytest.raises(ImportError):
+ _verify_package_version("nottherightversion")
diff --git a/tests/hazmat/primitives/fixtures_ec.py b/tests/hazmat/primitives/fixtures_ec.py
new file mode 100644
index 00000000..21c69031
--- /dev/null
+++ b/tests/hazmat/primitives/fixtures_ec.py
@@ -0,0 +1,296 @@
+# 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.hazmat.primitives.asymmetric import ec
+
+
+EC_KEY_SECT571R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '213997069697108634621868251335076179190383272087548888968788698953'
+ '131928375431570122753130054966269038244076049869476736547896549201'
+ '7388482714521707824160638375437887802901'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT571R1(),
+ x=int(
+ '42585672410900520895287019432267514156432686681290164230262278'
+ '54789182447139054594501570747809649335533486119017169439209005'
+ '883737780433424425566023654583165324498640038089'
+ ),
+ y=int(
+ '13822523320209387572500458104799806851658024537477228250738334'
+ '46977851514777531296572763848253279034733550774927720436494321'
+ '97281333379623823457479233585424800362717541750'
+ )
+ )
+)
+
+EC_KEY_SECT409R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '604993237916498765317587097853603474519114726157206838874832379003'
+ '281871982139714656205843929472002062791572217653118715727'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT409R1(),
+ x=int(
+ '76237701339268928039087238870073679814646664010783544301589269'
+ '2272579213400205907766385199643053767195204247826349822350081'
+ ),
+ y=int(
+ '10056668929618383045204866060110626563392345494925302478351744'
+ '01475129090774493235522729123877384838835703483224447476728811'
+ )
+ )
+)
+
+EC_KEY_SECT283R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '589705077255658434962118789801402573495547207239917043241273753671'
+ '0603230261342427657'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT283R1(),
+ x=int(
+ '10694213430317013187241490088760888472172922291550831393222973'
+ '531614941756901942108493'
+ ),
+ y=int(
+ '11461553100313943515373601367527399649593366728262918214942116'
+ '4359557613202950705170'
+ )
+ )
+)
+
+EC_KEY_SECT233R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '343470067105388144757135261232658742142830154753739648095101899829'
+ '8288'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT233R1(),
+ x=int(
+ '74494951569151557692195071465128140646140765188698294062550374'
+ '71118267'
+ ),
+ y=int(
+ '48699150823022962508544923825876164485917001162461401797511748'
+ '44872205'
+ )
+ )
+)
+
+EC_KEY_SECT163R2 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '11788436193853888218177032687141056784083668635'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT163R2(),
+ x=int(
+ '5247234453330640212490501030772203801908103222463'
+ ),
+ y=int(
+ '3172513801099088785224248292142866317754124455206'
+ )
+ )
+)
+
+EC_KEY_SECT571K1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '592811051234886966121888758661314648311634839499582476726008738218'
+ '165015048237934517672316204181933804884636855291118594744334592153'
+ '883208936227914544246799490897169723387'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT571K1(),
+ x=int(
+ '81362471461936552203898455874182916939857774872643607884250052'
+ '29301336524105230729653881789373412990921493551253481866317181'
+ '50644729351721577822595637058949405764944491655'
+ ),
+ y=int(
+ '14058041260812945396067821061063618047896814719828637241661260'
+ '31235681542401975593036630733881695595289523801041910183736211'
+ '587294494888450327374439795428519848065589000434'
+ )
+ )
+)
+
+EC_KEY_SECT409K1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '110321743150399087059465162400463719641470113494908091197354523708'
+ '934106732952992153105338671368548199643686444619485307877'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT409K1(),
+ x=int(
+ '62280214209410363493525178797944995742119600145953755916426161'
+ '0790364158569265348038207313261547476506319796469776797725796'
+ ),
+ y=int(
+ '46653883749102474289095010108777579907422472804577185369332018'
+ '7318642669590280811057512951467298158275464566214288556375885'
+ )
+ )
+)
+
+EC_KEY_SECT283K1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '182508394415444014156574733141549331538128234395356466108310015130'
+ '3868915489347291850'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT283K1(),
+ x=int(
+ '31141647206111886426350703123670451554123180910379592764773885'
+ '2959123367428352287032'
+ ),
+ y=int(
+ '71787460144483665964585187837283963089964760704065205376175384'
+ '58957627834444017112582'
+ )
+ )
+)
+
+EC_KEY_SECT233K1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '172670089647474613734091436081960550801254775902629891892394471086'
+ '2070'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT233K1(),
+ x=int(
+ '55693911474339510991521579392202889561373678973929426354737048'
+ '68129172'
+ ),
+ y=int(
+ '11025856248546376145959939911850923631416718241836051344384802'
+ '737277815'
+ )
+ )
+)
+
+EC_KEY_SECT163K1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '3699303791425402204035307605170569820290317991287'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECT163K1(),
+ x=int(
+ '4479755902310063321544063130576409926980094120721'
+ ),
+ y=int(
+ '3051218481937171839039826690648109285113977745779'
+ )
+ )
+)
+
+EC_KEY_SECP521R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '662751235215460886290293902658128847495347691199214706697089140769'
+ '672273950767961331442265530524063943548846724348048614239791498442'
+ '5997823106818915698960565'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP521R1(),
+ x=int(
+ '12944742826257420846659527752683763193401384271391513286022917'
+ '29910013082920512632908350502247952686156279140016049549948975'
+ '670668730618745449113644014505462'
+ ),
+ y=int(
+ '10784108810271976186737587749436295782985563640368689081052886'
+ '16296815984553198866894145509329328086635278430266482551941240'
+ '591605833440825557820439734509311'
+ )
+ )
+)
+
+EC_KEY_SECP384R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '280814107134858470598753916394807521398239633534281633982576099083'
+ '35787109896602102090002196616273211495718603965098'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP384R1(),
+ x=int(
+ '10036914308591746758780165503819213553101287571902957054148542'
+ '504671046744460374996612408381962208627004841444205030'
+ ),
+ y=int(
+ '17337335659928075994560513699823544906448896792102247714689323'
+ '575406618073069185107088229463828921069465902299522926'
+ )
+ )
+)
+
+EC_KEY_SECP256R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '271032978511595617649844168316234344656921218699414461240502635010'
+ '25776962849'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP256R1(),
+ x=int(
+ '49325986169170464532722748935508337546545346352733747948730305'
+ '442770101441241'
+ ),
+ y=int(
+ '51709162888529903487188595007092772817469799707382623884187518'
+ '455962250433661'
+ )
+ )
+)
+
+EC_KEY_SECP256K1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '683341569008473593765879222774207677458810362976327530563215318048'
+ '64380736732'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP256K1(),
+ x=int(
+ '59251322975795306609293064274738085741081547489119277536110995'
+ '120127593127884'
+ ),
+ y=int(
+ '10334192001480392039227801832201340147605940717841294644187071'
+ '8261641142297801'
+ )
+ )
+)
+
+EC_KEY_SECP224R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '234854340492774342642505519082413233282383066880756900834047566251'
+ '50'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP224R1(),
+ x=int(
+ '51165676638271204691095081341581621487998422645261573824239666'
+ '1214'
+ ),
+ y=int(
+ '14936601450555711309158397172719963843891926209168533453717969'
+ '1265'
+ )
+ )
+)
+
+EC_KEY_SECP192R1 = ec.EllipticCurvePrivateNumbers(
+ private_value=int(
+ '4534766128536179420071447168915990251715442361606049349869'
+ ),
+ public_numbers=ec.EllipticCurvePublicNumbers(
+ curve=ec.SECP192R1(),
+ x=int(
+ '5415069751170397888083674339683360671310515485781457536999'
+ ),
+ y=int(
+ '18671605334415960797751252911958331304288357195986572776'
+ )
+ )
+)
diff --git a/tests/hazmat/primitives/fixtures_rsa.py b/tests/hazmat/primitives/fixtures_rsa.py
index f93361de..a531783e 100644
--- a/tests/hazmat/primitives/fixtures_rsa.py
+++ b/tests/hazmat/primitives/fixtures_rsa.py
@@ -529,3 +529,75 @@ RSA_KEY_2048 = RSAPrivateNumbers(
"de04fd053846ca10a223b10cc841cc80fdebee44f3114c13e886af583", 16),
)
)
+
+RSA_KEY_2048_ALT = RSAPrivateNumbers(
+ d=int(
+ "7522768467449591813737881904131688860626637897199391200040629"
+ "8641018746450502628484395471408986929218353894683769457466923"
+ "3079369551423094451013669595729568593462009746342148367797495"
+ "5529909313614750246672441810743580455199636293179539903480635"
+ "3091286716112931976896334411287175213124504134181121011488550"
+ "5290054443979198998564749640800633368957384058700741073997703"
+ "8877364695937023906368630297588990131009278072614118207348356"
+ "4640244134189285070202534488517371577359510236833464698189075"
+ "5160693085297816063285814039518178249628112908466649245545732"
+ "5791532385553960363601827996980725025898649392004494256400884"
+ "092073"
+ ),
+ dmp1=int(
+ "5847872614112935747739644055317429405973942336206460017493394"
+ "9737607778799766591021036792892472774720417920838206576785118"
+ "8889624058962939702950175807073343659386156232294197300491647"
+ "1029508414050591959344812347424476498076532682798598325230069"
+ "0925827594762920534235575029199380552228825468180187156871965"
+ "973"
+ ),
+ dmq1=int(
+ "2949536259161239302081155875068405238857801001054083407704879"
+ "8210876832264504685327766351157044892283801611558399025326793"
+ "4131638001934454489864437565651739832511702151461257267169691"
+ "6611992398459006200708626815153304591390855807749769768978152"
+ "9854112656599931724820610358669306523835327459478374630794532"
+ "167"
+ ),
+ iqmp=int(
+ "7331180989818931535458916053540252830484856703208982675535284"
+ "4613815808798190559315018094080936347757336989616401164752221"
+ "8101156529898067044923499386460167055405998646366011838018441"
+ "3678947694258190172377716154009305082091341215866326061721180"
+ "3836418654472188816187630316821692982783286322262994892003058"
+ "782"
+ ),
+ p=int(
+ "1460007723851883695617573533155574746587863843382715314919865"
+ "2434108956187429726002840717317310431378483921058946835896252"
+ "7109559207437158778332364464259678946305487699031865937075508"
+ "8616612925453842458055546540240601585731206561647892336916583"
+ "0023641764106581040198845259766246869529221084602380669333021"
+ "0819"
+ ),
+ q=int(
+ "1433897765867889178402883410610177836503402597775250087462018"
+ "4617952933433119527945447840336616357136736935069377619782227"
+ "2822380830300262175671282877680573202309319960687756231128996"
+ "9764855320953993690199846269451095044922353809602378616938811"
+ "7513900906279873343591486841303392490561500301994171338761080"
+ "4439"
+ ),
+ public_numbers=RSAPublicNumbers(
+ e=65537,
+ n=int(
+ "209350181338107812610165420955871971489973659392253291327"
+ "839812910252466502190690572476688311285621239204212139711"
+ "207388949164851984253143698667018532039612470954223918242"
+ "145976986600705122576087630525229796950722166468064721258"
+ "490916138706756006902066136471049807637157890128560592039"
+ "941717275079733754782848729566190631725183735944031456237"
+ "089928120178187552521649483240599003240074352860189285952"
+ "078970127554801074176375499583703254849309993132931268013"
+ "715070507278514207864914944621214574162116786377990456375"
+ "964817771730371110612100247262908550409785456157505694419"
+ "00451152778245269283276012328748538414051025541"
+ )
+ )
+)
diff --git a/tests/hazmat/primitives/test_3des.py b/tests/hazmat/primitives/test_3des.py
index 0197353e..0f0f1470 100644
--- a/tests/hazmat/primitives/test_3des.py
+++ b/tests/hazmat/primitives/test_3des.py
@@ -22,13 +22,13 @@ from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.TripleDES("\x00" * 8), modes.CBC("\x00" * 8)
+ algorithms.TripleDES(b"\x00" * 8), modes.CBC(b"\x00" * 8)
),
skip_message="Does not support TripleDES CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestTripleDESModeCBC(object):
- test_KAT = generate_encrypt_test(
+ test_kat = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "CBC"),
[
@@ -42,7 +42,7 @@ class TestTripleDESModeCBC(object):
lambda iv, **kwargs: modes.CBC(binascii.unhexlify(iv)),
)
- test_MMT = generate_encrypt_test(
+ test_mmt = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "CBC"),
[
@@ -59,13 +59,13 @@ class TestTripleDESModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.TripleDES("\x00" * 8), modes.OFB("\x00" * 8)
+ algorithms.TripleDES(b"\x00" * 8), modes.OFB(b"\x00" * 8)
),
skip_message="Does not support TripleDES OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestTripleDESModeOFB(object):
- test_KAT = generate_encrypt_test(
+ test_kat = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "OFB"),
[
@@ -79,7 +79,7 @@ class TestTripleDESModeOFB(object):
lambda iv, **kwargs: modes.OFB(binascii.unhexlify(iv)),
)
- test_MMT = generate_encrypt_test(
+ test_mmt = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "OFB"),
[
@@ -96,13 +96,13 @@ class TestTripleDESModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.TripleDES("\x00" * 8), modes.CFB("\x00" * 8)
+ algorithms.TripleDES(b"\x00" * 8), modes.CFB(b"\x00" * 8)
),
skip_message="Does not support TripleDES CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestTripleDESModeCFB(object):
- test_KAT = generate_encrypt_test(
+ test_kat = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "CFB"),
[
@@ -116,7 +116,7 @@ class TestTripleDESModeCFB(object):
lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)),
)
- test_MMT = generate_encrypt_test(
+ test_mmt = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "CFB"),
[
@@ -133,13 +133,13 @@ class TestTripleDESModeCFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.TripleDES("\x00" * 8), modes.CFB8("\x00" * 8)
+ algorithms.TripleDES(b"\x00" * 8), modes.CFB8(b"\x00" * 8)
),
skip_message="Does not support TripleDES CFB8",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestTripleDESModeCFB8(object):
- test_KAT = generate_encrypt_test(
+ test_kat = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "CFB"),
[
@@ -153,7 +153,7 @@ class TestTripleDESModeCFB8(object):
lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
)
- test_MMT = generate_encrypt_test(
+ test_mmt = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "CFB"),
[
@@ -170,13 +170,13 @@ class TestTripleDESModeCFB8(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.TripleDES("\x00" * 8), modes.ECB()
+ algorithms.TripleDES(b"\x00" * 8), modes.ECB()
),
skip_message="Does not support TripleDES ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestTripleDESModeECB(object):
- test_KAT = generate_encrypt_test(
+ test_kat = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "ECB"),
[
@@ -190,7 +190,7 @@ class TestTripleDESModeECB(object):
lambda **kwargs: modes.ECB(),
)
- test_MMT = generate_encrypt_test(
+ test_mmt = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "3DES", "ECB"),
[
diff --git a/tests/hazmat/primitives/test_aead.py b/tests/hazmat/primitives/test_aead.py
new file mode 100644
index 00000000..4f6bc7f4
--- /dev/null
+++ b/tests/hazmat/primitives/test_aead.py
@@ -0,0 +1,446 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends.interfaces import CipherBackend
+from cryptography.hazmat.primitives.ciphers.aead import (
+ AESCCM, AESGCM, ChaCha20Poly1305
+)
+
+from .utils import _load_all_params
+from ...utils import (
+ load_nist_ccm_vectors, load_nist_vectors, load_vectors_from_file,
+ raises_unsupported_algorithm
+)
+
+
+class FakeData(object):
+ def __len__(self):
+ return 2 ** 32 + 1
+
+
+def _aead_supported(cls):
+ try:
+ cls(b"0" * 32)
+ return True
+ except UnsupportedAlgorithm:
+ return False
+
+
+@pytest.mark.skipif(
+ _aead_supported(ChaCha20Poly1305),
+ reason="Requires OpenSSL without ChaCha20Poly1305 support"
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+def test_chacha20poly1305_unsupported_on_older_openssl(backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
+ ChaCha20Poly1305(ChaCha20Poly1305.generate_key())
+
+
+@pytest.mark.skipif(
+ not _aead_supported(ChaCha20Poly1305),
+ reason="Does not support ChaCha20Poly1305"
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestChaCha20Poly1305(object):
+ def test_data_too_large(self):
+ key = ChaCha20Poly1305.generate_key()
+ chacha = ChaCha20Poly1305(key)
+ nonce = b"0" * 12
+
+ with pytest.raises(OverflowError):
+ chacha.encrypt(nonce, FakeData(), b"")
+
+ with pytest.raises(OverflowError):
+ chacha.encrypt(nonce, b"", FakeData())
+
+ def test_generate_key(self):
+ key = ChaCha20Poly1305.generate_key()
+ assert len(key) == 32
+
+ def test_bad_key(self, backend):
+ with pytest.raises(TypeError):
+ ChaCha20Poly1305(object())
+
+ with pytest.raises(ValueError):
+ ChaCha20Poly1305(b"0" * 31)
+
+ @pytest.mark.parametrize(
+ ("nonce", "data", "associated_data"),
+ [
+ [object(), b"data", b""],
+ [b"0" * 12, object(), b""],
+ [b"0" * 12, b"data", object()]
+ ]
+ )
+ def test_params_not_bytes_encrypt(self, nonce, data, associated_data,
+ backend):
+ key = ChaCha20Poly1305.generate_key()
+ chacha = ChaCha20Poly1305(key)
+ with pytest.raises(TypeError):
+ chacha.encrypt(nonce, data, associated_data)
+
+ with pytest.raises(TypeError):
+ chacha.decrypt(nonce, data, associated_data)
+
+ def test_nonce_not_12_bytes(self, backend):
+ key = ChaCha20Poly1305.generate_key()
+ chacha = ChaCha20Poly1305(key)
+ with pytest.raises(ValueError):
+ chacha.encrypt(b"00", b"hello", b"")
+
+ with pytest.raises(ValueError):
+ chacha.decrypt(b"00", b"hello", b"")
+
+ def test_decrypt_data_too_short(self, backend):
+ key = ChaCha20Poly1305.generate_key()
+ chacha = ChaCha20Poly1305(key)
+ with pytest.raises(InvalidTag):
+ chacha.decrypt(b"0" * 12, b"0", None)
+
+ def test_associated_data_none_equal_to_empty_bytestring(self, backend):
+ key = ChaCha20Poly1305.generate_key()
+ chacha = ChaCha20Poly1305(key)
+ nonce = os.urandom(12)
+ ct1 = chacha.encrypt(nonce, b"some_data", None)
+ ct2 = chacha.encrypt(nonce, b"some_data", b"")
+ assert ct1 == ct2
+ pt1 = chacha.decrypt(nonce, ct1, None)
+ pt2 = chacha.decrypt(nonce, ct2, b"")
+ assert pt1 == pt2
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("ciphers", "ChaCha20Poly1305", "openssl.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_openssl_vectors(self, vector, backend):
+ key = binascii.unhexlify(vector["key"])
+ nonce = binascii.unhexlify(vector["iv"])
+ aad = binascii.unhexlify(vector["aad"])
+ tag = binascii.unhexlify(vector["tag"])
+ pt = binascii.unhexlify(vector["plaintext"])
+ ct = binascii.unhexlify(vector["ciphertext"])
+ chacha = ChaCha20Poly1305(key)
+ if vector.get("result") == b"CIPHERFINAL_ERROR":
+ with pytest.raises(InvalidTag):
+ chacha.decrypt(nonce, ct + tag, aad)
+ else:
+ computed_pt = chacha.decrypt(nonce, ct + tag, aad)
+ assert computed_pt == pt
+ computed_ct = chacha.encrypt(nonce, pt, aad)
+ assert computed_ct == ct + tag
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("ciphers", "ChaCha20Poly1305", "boringssl.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_boringssl_vectors(self, vector, backend):
+ key = binascii.unhexlify(vector["key"])
+ nonce = binascii.unhexlify(vector["nonce"])
+ if vector["ad"].startswith(b'"'):
+ aad = vector["ad"][1:-1]
+ else:
+ aad = binascii.unhexlify(vector["ad"])
+ tag = binascii.unhexlify(vector["tag"])
+ if vector["in"].startswith(b'"'):
+ pt = vector["in"][1:-1]
+ else:
+ pt = binascii.unhexlify(vector["in"])
+ ct = binascii.unhexlify(vector["ct"].strip(b'"'))
+ chacha = ChaCha20Poly1305(key)
+ computed_pt = chacha.decrypt(nonce, ct + tag, aad)
+ assert computed_pt == pt
+ computed_ct = chacha.encrypt(nonce, pt, aad)
+ assert computed_ct == ct + tag
+
+ def test_buffer_protocol(self, backend):
+ key = ChaCha20Poly1305.generate_key()
+ chacha = ChaCha20Poly1305(key)
+ pt = b"encrypt me"
+ ad = b"additional"
+ nonce = os.urandom(12)
+ ct = chacha.encrypt(nonce, pt, ad)
+ computed_pt = chacha.decrypt(nonce, ct, ad)
+ assert computed_pt == pt
+ chacha2 = ChaCha20Poly1305(bytearray(key))
+ ct2 = chacha2.encrypt(bytearray(nonce), pt, ad)
+ assert ct2 == ct
+ computed_pt2 = chacha2.decrypt(bytearray(nonce), ct2, ad)
+ assert computed_pt2 == pt
+
+
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestAESCCM(object):
+ def test_data_too_large(self):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ nonce = b"0" * 12
+
+ with pytest.raises(OverflowError):
+ aesccm.encrypt(nonce, FakeData(), b"")
+
+ with pytest.raises(OverflowError):
+ aesccm.encrypt(nonce, b"", FakeData())
+
+ def test_default_tag_length(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ nonce = os.urandom(12)
+ pt = b"hello"
+ ct = aesccm.encrypt(nonce, pt, None)
+ assert len(ct) == len(pt) + 16
+
+ def test_invalid_tag_length(self, backend):
+ key = AESCCM.generate_key(128)
+ with pytest.raises(ValueError):
+ AESCCM(key, tag_length=7)
+
+ with pytest.raises(ValueError):
+ AESCCM(key, tag_length=2)
+
+ with pytest.raises(TypeError):
+ AESCCM(key, tag_length="notanint")
+
+ def test_invalid_nonce_length(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ pt = b"hello"
+ nonce = os.urandom(14)
+ with pytest.raises(ValueError):
+ aesccm.encrypt(nonce, pt, None)
+
+ with pytest.raises(ValueError):
+ aesccm.encrypt(nonce[:6], pt, None)
+
+ @pytest.mark.parametrize(
+ "vector",
+ _load_all_params(
+ os.path.join("ciphers", "AES", "CCM"),
+ [
+ "DVPT128.rsp", "DVPT192.rsp", "DVPT256.rsp",
+ "VADT128.rsp", "VADT192.rsp", "VADT256.rsp",
+ "VNT128.rsp", "VNT192.rsp", "VNT256.rsp",
+ "VPT128.rsp", "VPT192.rsp", "VPT256.rsp",
+ ],
+ load_nist_ccm_vectors
+ )
+ )
+ def test_vectors(self, vector, backend):
+ key = binascii.unhexlify(vector["key"])
+ nonce = binascii.unhexlify(vector["nonce"])
+ adata = binascii.unhexlify(vector["adata"])[:vector["alen"]]
+ ct = binascii.unhexlify(vector["ct"])
+ pt = binascii.unhexlify(vector["payload"])[:vector["plen"]]
+ aesccm = AESCCM(key, vector["tlen"])
+ if vector.get('fail'):
+ with pytest.raises(InvalidTag):
+ aesccm.decrypt(nonce, ct, adata)
+ else:
+ computed_pt = aesccm.decrypt(nonce, ct, adata)
+ assert computed_pt == pt
+ assert aesccm.encrypt(nonce, pt, adata) == ct
+
+ def test_roundtrip(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ pt = b"encrypt me"
+ ad = b"additional"
+ nonce = os.urandom(12)
+ ct = aesccm.encrypt(nonce, pt, ad)
+ computed_pt = aesccm.decrypt(nonce, ct, ad)
+ assert computed_pt == pt
+
+ def test_nonce_too_long(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ pt = b"encrypt me" * 6600
+ # pt can be no more than 65536 bytes when nonce is 13 bytes
+ nonce = os.urandom(13)
+ with pytest.raises(ValueError):
+ aesccm.encrypt(nonce, pt, None)
+
+ @pytest.mark.parametrize(
+ ("nonce", "data", "associated_data"),
+ [
+ [object(), b"data", b""],
+ [b"0" * 12, object(), b""],
+ [b"0" * 12, b"data", object()],
+ ]
+ )
+ def test_params_not_bytes(self, nonce, data, associated_data, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ with pytest.raises(TypeError):
+ aesccm.encrypt(nonce, data, associated_data)
+
+ def test_bad_key(self, backend):
+ with pytest.raises(TypeError):
+ AESCCM(object())
+
+ with pytest.raises(ValueError):
+ AESCCM(b"0" * 31)
+
+ def test_bad_generate_key(self, backend):
+ with pytest.raises(TypeError):
+ AESCCM.generate_key(object())
+
+ with pytest.raises(ValueError):
+ AESCCM.generate_key(129)
+
+ def test_associated_data_none_equal_to_empty_bytestring(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ nonce = os.urandom(12)
+ ct1 = aesccm.encrypt(nonce, b"some_data", None)
+ ct2 = aesccm.encrypt(nonce, b"some_data", b"")
+ assert ct1 == ct2
+ pt1 = aesccm.decrypt(nonce, ct1, None)
+ pt2 = aesccm.decrypt(nonce, ct2, b"")
+ assert pt1 == pt2
+
+ def test_decrypt_data_too_short(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ with pytest.raises(InvalidTag):
+ aesccm.decrypt(b"0" * 12, b"0", None)
+
+ def test_buffer_protocol(self, backend):
+ key = AESCCM.generate_key(128)
+ aesccm = AESCCM(key)
+ pt = b"encrypt me"
+ ad = b"additional"
+ nonce = os.urandom(12)
+ ct = aesccm.encrypt(nonce, pt, ad)
+ computed_pt = aesccm.decrypt(nonce, ct, ad)
+ assert computed_pt == pt
+ aesccm2 = AESCCM(bytearray(key))
+ ct2 = aesccm2.encrypt(bytearray(nonce), pt, ad)
+ assert ct2 == ct
+ computed_pt2 = aesccm2.decrypt(bytearray(nonce), ct2, ad)
+ assert computed_pt2 == pt
+
+
+def _load_gcm_vectors():
+ vectors = _load_all_params(
+ os.path.join("ciphers", "AES", "GCM"),
+ [
+ "gcmDecrypt128.rsp",
+ "gcmDecrypt192.rsp",
+ "gcmDecrypt256.rsp",
+ "gcmEncryptExtIV128.rsp",
+ "gcmEncryptExtIV192.rsp",
+ "gcmEncryptExtIV256.rsp",
+ ],
+ load_nist_vectors
+ )
+ return [x for x in vectors if len(x["tag"]) == 32]
+
+
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestAESGCM(object):
+ def test_data_too_large(self):
+ key = AESGCM.generate_key(128)
+ aesgcm = AESGCM(key)
+ nonce = b"0" * 12
+
+ with pytest.raises(OverflowError):
+ aesgcm.encrypt(nonce, FakeData(), b"")
+
+ with pytest.raises(OverflowError):
+ aesgcm.encrypt(nonce, b"", FakeData())
+
+ @pytest.mark.parametrize("vector", _load_gcm_vectors())
+ def test_vectors(self, vector):
+ key = binascii.unhexlify(vector["key"])
+ nonce = binascii.unhexlify(vector["iv"])
+ aad = binascii.unhexlify(vector["aad"])
+ ct = binascii.unhexlify(vector["ct"])
+ pt = binascii.unhexlify(vector.get("pt", b""))
+ tag = binascii.unhexlify(vector["tag"])
+ aesgcm = AESGCM(key)
+ if vector.get("fail") is True:
+ with pytest.raises(InvalidTag):
+ aesgcm.decrypt(nonce, ct + tag, aad)
+ else:
+ computed_ct = aesgcm.encrypt(nonce, pt, aad)
+ assert computed_ct[:-16] == ct
+ assert computed_ct[-16:] == tag
+ computed_pt = aesgcm.decrypt(nonce, ct + tag, aad)
+ assert computed_pt == pt
+
+ @pytest.mark.parametrize(
+ ("nonce", "data", "associated_data"),
+ [
+ [object(), b"data", b""],
+ [b"0" * 12, object(), b""],
+ [b"0" * 12, b"data", object()]
+ ]
+ )
+ def test_params_not_bytes(self, nonce, data, associated_data, backend):
+ key = AESGCM.generate_key(128)
+ aesgcm = AESGCM(key)
+ with pytest.raises(TypeError):
+ aesgcm.encrypt(nonce, data, associated_data)
+
+ with pytest.raises(TypeError):
+ aesgcm.decrypt(nonce, data, associated_data)
+
+ def test_invalid_nonce_length(self, backend):
+ key = AESGCM.generate_key(128)
+ aesgcm = AESGCM(key)
+ with pytest.raises(ValueError):
+ aesgcm.encrypt(b"", b"hi", None)
+
+ def test_bad_key(self, backend):
+ with pytest.raises(TypeError):
+ AESGCM(object())
+
+ with pytest.raises(ValueError):
+ AESGCM(b"0" * 31)
+
+ def test_bad_generate_key(self, backend):
+ with pytest.raises(TypeError):
+ AESGCM.generate_key(object())
+
+ with pytest.raises(ValueError):
+ AESGCM.generate_key(129)
+
+ def test_associated_data_none_equal_to_empty_bytestring(self, backend):
+ key = AESGCM.generate_key(128)
+ aesgcm = AESGCM(key)
+ nonce = os.urandom(12)
+ ct1 = aesgcm.encrypt(nonce, b"some_data", None)
+ ct2 = aesgcm.encrypt(nonce, b"some_data", b"")
+ assert ct1 == ct2
+ pt1 = aesgcm.decrypt(nonce, ct1, None)
+ pt2 = aesgcm.decrypt(nonce, ct2, b"")
+ assert pt1 == pt2
+
+ def test_buffer_protocol(self, backend):
+ key = AESGCM.generate_key(128)
+ aesgcm = AESGCM(key)
+ pt = b"encrypt me"
+ ad = b"additional"
+ nonce = os.urandom(12)
+ ct = aesgcm.encrypt(nonce, pt, ad)
+ computed_pt = aesgcm.decrypt(nonce, ct, ad)
+ assert computed_pt == pt
+ aesgcm2 = AESGCM(bytearray(key))
+ ct2 = aesgcm2.encrypt(bytearray(nonce), pt, ad)
+ assert ct2 == ct
+ computed_pt2 = aesgcm2.decrypt(bytearray(nonce), ct2, ad)
+ assert computed_pt2 == pt
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 4d48e8ad..d99ba406 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -12,19 +12,54 @@ import pytest
from cryptography.hazmat.backends.interfaces import CipherBackend
from cryptography.hazmat.primitives.ciphers import algorithms, base, modes
-from .utils import generate_aead_test, generate_encrypt_test
+from .utils import _load_all_params, generate_aead_test, generate_encrypt_test
+from ...doubles import DummyMode
from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.CBC("\x00" * 16)
+ algorithms.AES(b"\x00" * 32), modes.XTS(b"\x00" * 16)
+ ),
+ skip_message="Does not support AES XTS",
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestAESModeXTS(object):
+ @pytest.mark.parametrize(
+ "vector",
+ # This list comprehension excludes any vector that does not have a
+ # data unit length that is divisible by 8. The NIST vectors include
+ # tests for implementations that support encryption of data that is
+ # not divisible modulo 8, but OpenSSL is not such an implementation.
+ [x for x in _load_all_params(
+ os.path.join("ciphers", "AES", "XTS", "tweak-128hexstr"),
+ ["XTSGenAES128.rsp", "XTSGenAES256.rsp"],
+ load_nist_vectors
+ ) if int(x["dataunitlen"]) / 8.0 == int(x["dataunitlen"]) // 8]
+ )
+ def test_xts_vectors(self, vector, backend):
+ key = binascii.unhexlify(vector["key"])
+ tweak = binascii.unhexlify(vector["i"])
+ pt = binascii.unhexlify(vector["pt"])
+ ct = binascii.unhexlify(vector["ct"])
+ cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak), backend)
+ enc = cipher.encryptor()
+ computed_ct = enc.update(pt) + enc.finalize()
+ assert computed_ct == ct
+ dec = cipher.decryptor()
+ computed_pt = dec.update(ct) + dec.finalize()
+ assert computed_pt == pt
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.CBC(b"\x00" * 16)
),
skip_message="Does not support AES CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCBC(object):
- test_CBC = generate_encrypt_test(
+ test_cbc = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CBC"),
[
@@ -51,13 +86,13 @@ class TestAESModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.ECB()
+ algorithms.AES(b"\x00" * 16), modes.ECB()
),
skip_message="Does not support AES ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeECB(object):
- test_ECB = generate_encrypt_test(
+ test_ecb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "ECB"),
[
@@ -84,13 +119,13 @@ class TestAESModeECB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.OFB("\x00" * 16)
+ algorithms.AES(b"\x00" * 16), modes.OFB(b"\x00" * 16)
),
skip_message="Does not support AES OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeOFB(object):
- test_OFB = generate_encrypt_test(
+ test_ofb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "OFB"),
[
@@ -117,13 +152,13 @@ class TestAESModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.CFB("\x00" * 16)
+ algorithms.AES(b"\x00" * 16), modes.CFB(b"\x00" * 16)
),
skip_message="Does not support AES CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCFB(object):
- test_CFB = generate_encrypt_test(
+ test_cfb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CFB"),
[
@@ -150,13 +185,13 @@ class TestAESModeCFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.CFB8("\x00" * 16)
+ algorithms.AES(b"\x00" * 16), modes.CFB8(b"\x00" * 16)
),
skip_message="Does not support AES CFB8",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCFB8(object):
- test_CFB8 = generate_encrypt_test(
+ test_cfb8 = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CFB"),
[
@@ -183,13 +218,13 @@ class TestAESModeCFB8(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.CTR("\x00" * 16)
+ algorithms.AES(b"\x00" * 16), modes.CTR(b"\x00" * 16)
),
skip_message="Does not support AES CTR",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeCTR(object):
- test_CTR = generate_encrypt_test(
+ test_ctr = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "CTR"),
["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
@@ -200,13 +235,13 @@ class TestAESModeCTR(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.GCM("\x00" * 12)
+ algorithms.AES(b"\x00" * 16), modes.GCM(b"\x00" * 12)
),
skip_message="Does not support AES GCM",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestAESModeGCM(object):
- test_GCM = generate_aead_test(
+ test_gcm = generate_aead_test(
load_nist_vectors,
os.path.join("ciphers", "AES", "GCM"),
[
@@ -253,3 +288,174 @@ class TestAESModeGCM(object):
computed_ct = encryptor.update(pt) + encryptor.finalize()
assert computed_ct == ct
assert encryptor.tag == tag
+
+ def test_gcm_ciphertext_limit(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor._bytes_processed = modes.GCM._MAX_ENCRYPTED_BYTES - 16
+ encryptor.update(b"0" * 16)
+ assert (
+ encryptor._bytes_processed == modes.GCM._MAX_ENCRYPTED_BYTES
+ )
+ with pytest.raises(ValueError):
+ encryptor.update(b"0")
+
+ def test_gcm_aad_limit(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor._aad_bytes_processed = modes.GCM._MAX_AAD_BYTES - 16
+ encryptor.authenticate_additional_data(b"0" * 16)
+ assert encryptor._aad_bytes_processed == modes.GCM._MAX_AAD_BYTES
+ with pytest.raises(ValueError):
+ encryptor.authenticate_additional_data(b"0")
+
+ def test_gcm_ciphertext_increments(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor.update(b"0" * 8)
+ assert encryptor._bytes_processed == 8
+ encryptor.update(b"0" * 7)
+ assert encryptor._bytes_processed == 15
+ encryptor.update(b"0" * 18)
+ assert encryptor._bytes_processed == 33
+
+ def test_gcm_aad_increments(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor.authenticate_additional_data(b"0" * 8)
+ assert encryptor._aad_bytes_processed == 8
+ encryptor.authenticate_additional_data(b"0" * 18)
+ assert encryptor._aad_bytes_processed == 26
+
+ def test_gcm_tag_decrypt_none(self, backend):
+ key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
+ iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
+ aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
+
+ encryptor = base.Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv),
+ backend=backend
+ ).encryptor()
+ encryptor.authenticate_additional_data(aad)
+ encryptor.finalize()
+
+ decryptor = base.Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv),
+ backend=backend
+ ).decryptor()
+ decryptor.authenticate_additional_data(aad)
+ with pytest.raises(ValueError):
+ decryptor.finalize()
+
+ def test_gcm_tag_decrypt_mode(self, backend):
+ key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
+ iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
+ aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
+
+ encryptor = base.Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv),
+ backend=backend
+ ).encryptor()
+ encryptor.authenticate_additional_data(aad)
+ encryptor.finalize()
+ tag = encryptor.tag
+
+ decryptor = base.Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv, tag),
+ backend=backend
+ ).decryptor()
+ decryptor.authenticate_additional_data(aad)
+ decryptor.finalize()
+
+ def test_gcm_tag_decrypt_finalize(self, backend):
+ key = binascii.unhexlify(b"5211242698bed4774a090620a6ca56f3")
+ iv = binascii.unhexlify(b"b1e1349120b6e832ef976f5d")
+ aad = binascii.unhexlify(b"b6d729aab8e6416d7002b9faa794c410d8d2f193")
+
+ encryptor = base.Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv),
+ backend=backend
+ ).encryptor()
+ encryptor.authenticate_additional_data(aad)
+ encryptor.finalize()
+ tag = encryptor.tag
+
+ decryptor = base.Cipher(
+ algorithms.AES(key),
+ modes.GCM(iv),
+ backend=backend
+ ).decryptor()
+ decryptor.authenticate_additional_data(aad)
+
+ decryptor.finalize_with_tag(tag)
+
+ def test_gcm_tag_decrypt_finalize_tag_length(self, backend):
+ decryptor = base.Cipher(
+ algorithms.AES(b"0" * 16),
+ modes.GCM(b"0" * 12),
+ backend=backend
+ ).decryptor()
+ with pytest.raises(ValueError):
+ decryptor.finalize_with_tag(b"tagtooshort")
+
+ def test_buffer_protocol(self, backend):
+ data = bytearray(b"helloworld")
+ enc = base.Cipher(
+ algorithms.AES(bytearray(b"\x00" * 16)),
+ modes.GCM(bytearray(b"\x00" * 12)),
+ backend
+ ).encryptor()
+ enc.authenticate_additional_data(bytearray(b"foo"))
+ ct = enc.update(data) + enc.finalize()
+ dec = base.Cipher(
+ algorithms.AES(bytearray(b"\x00" * 16)),
+ modes.GCM(bytearray(b"\x00" * 12), enc.tag),
+ backend
+ ).decryptor()
+ dec.authenticate_additional_data(bytearray(b"foo"))
+ pt = dec.update(ct) + dec.finalize()
+ assert pt == data
+
+
+@pytest.mark.parametrize(
+ "mode",
+ [
+ modes.CBC(bytearray(b"\x00" * 16)),
+ modes.CTR(bytearray(b"\x00" * 16)),
+ modes.OFB(bytearray(b"\x00" * 16)),
+ modes.CFB(bytearray(b"\x00" * 16)),
+ modes.CFB8(bytearray(b"\x00" * 16)),
+ modes.XTS(bytearray(b"\x00" * 16)),
+ # Add a dummy mode for coverage of the cipher_supported check.
+ DummyMode(),
+ ]
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+def test_buffer_protocol_alternate_modes(mode, backend):
+ data = bytearray(b"sixteen_byte_msg")
+ key = algorithms.AES(bytearray(os.urandom(32)))
+ if not backend.cipher_supported(key, mode):
+ pytest.skip("AES in {} mode not supported".format(mode.name))
+ cipher = base.Cipher(key, mode, backend)
+ enc = cipher.encryptor()
+ ct = enc.update(data) + enc.finalize()
+ dec = cipher.decryptor()
+ pt = dec.update(ct) + dec.finalize()
+ assert pt == data
diff --git a/tests/hazmat/primitives/test_arc4.py b/tests/hazmat/primitives/test_arc4.py
index acd306b2..1a173444 100644
--- a/tests/hazmat/primitives/test_arc4.py
+++ b/tests/hazmat/primitives/test_arc4.py
@@ -18,7 +18,7 @@ from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.ARC4("\x00" * 16), None
+ algorithms.ARC4(b"\x00" * 16), None
),
skip_message="Does not support ARC4",
)
@@ -35,6 +35,7 @@ class TestARC4(object):
"rfc-6229-128.txt",
"rfc-6229-192.txt",
"rfc-6229-256.txt",
+ "arc4.txt"
],
lambda key, **kwargs: algorithms.ARC4(binascii.unhexlify(key)),
)
diff --git a/tests/hazmat/primitives/test_asym_utils.py b/tests/hazmat/primitives/test_asym_utils.py
index 35b77ca4..b49ca3f2 100644
--- a/tests/hazmat/primitives/test_asym_utils.py
+++ b/tests/hazmat/primitives/test_asym_utils.py
@@ -7,64 +7,69 @@ from __future__ import absolute_import, division, print_function
import pytest
from cryptography.hazmat.primitives.asymmetric.utils import (
- decode_rfc6979_signature, encode_rfc6979_signature
+ Prehashed, decode_dss_signature, encode_dss_signature
)
-def test_rfc6979_signature():
- sig = encode_rfc6979_signature(1, 1)
+def test_dss_signature():
+ sig = encode_dss_signature(1, 1)
assert sig == b"0\x06\x02\x01\x01\x02\x01\x01"
- assert decode_rfc6979_signature(sig) == (1, 1)
+ assert decode_dss_signature(sig) == (1, 1)
r_s1 = (
1037234182290683143945502320610861668562885151617,
559776156650501990899426031439030258256861634312
)
- sig2 = encode_rfc6979_signature(*r_s1)
+ sig2 = encode_dss_signature(*r_s1)
assert sig2 == (
b'0-\x02\x15\x00\xb5\xaf0xg\xfb\x8bT9\x00\x13\xccg\x02\r\xdf\x1f,\x0b'
b'\x81\x02\x14b\r;"\xabP1D\x0c>5\xea\xb6\xf4\x81)\x8f\x9e\x9f\x08'
)
- assert decode_rfc6979_signature(sig2) == r_s1
+ assert decode_dss_signature(sig2) == r_s1
- sig3 = encode_rfc6979_signature(0, 0)
+ sig3 = encode_dss_signature(0, 0)
assert sig3 == b"0\x06\x02\x01\x00\x02\x01\x00"
- assert decode_rfc6979_signature(sig3) == (0, 0)
+ assert decode_dss_signature(sig3) == (0, 0)
- sig4 = encode_rfc6979_signature(-1, 0)
- assert sig4 == b"0\x06\x02\x01\xFF\x02\x01\x00"
- assert decode_rfc6979_signature(sig4) == (-1, 0)
+def test_encode_dss_non_integer():
+ with pytest.raises(ValueError):
+ encode_dss_signature("h", 3)
-def test_encode_rfc6979_non_integer():
with pytest.raises(ValueError):
- encode_rfc6979_signature("h", 3)
+ encode_dss_signature("3", "2")
with pytest.raises(ValueError):
- encode_rfc6979_signature("3", "2")
+ encode_dss_signature(3, "h")
with pytest.raises(ValueError):
- encode_rfc6979_signature(3, "h")
+ encode_dss_signature(3.3, 1.2)
with pytest.raises(ValueError):
- encode_rfc6979_signature(3.3, 1.2)
+ encode_dss_signature("hello", "world")
+
+def test_encode_dss_negative():
with pytest.raises(ValueError):
- encode_rfc6979_signature("hello", "world")
+ encode_dss_signature(-1, 0)
-def test_decode_rfc6979_trailing_bytes():
+def test_decode_dss_trailing_bytes():
with pytest.raises(ValueError):
- decode_rfc6979_signature(b"0\x06\x02\x01\x01\x02\x01\x01\x00\x00\x00")
+ decode_dss_signature(b"0\x06\x02\x01\x01\x02\x01\x01\x00\x00\x00")
-def test_decode_rfc6979_invalid_asn1():
+def test_decode_dss_invalid_asn1():
with pytest.raises(ValueError):
# This byte sequence has an invalid ASN.1 sequence length as well as
# an invalid integer length for the second integer.
- decode_rfc6979_signature(b"0\x07\x02\x01\x01\x02\x02\x01")
+ decode_dss_signature(b"0\x07\x02\x01\x01\x02\x02\x01")
with pytest.raises(ValueError):
- # This is the BER "end-of-contents octets," which older versions of
- # pyasn1 are wrongly willing to return from top-level DER decoding.
- decode_rfc6979_signature(b"\x00\x00")
+ # This is the BER "end-of-contents octets".
+ decode_dss_signature(b"\x00\x00")
+
+
+def test_pass_invalid_prehashed_arg():
+ with pytest.raises(TypeError):
+ Prehashed(object())
diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py
index 1b3fc1cb..37158f15 100644
--- a/tests/hazmat/primitives/test_block.py
+++ b/tests/hazmat/primitives/test_block.py
@@ -8,7 +8,6 @@ import binascii
import pytest
-from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, _Reasons
)
@@ -20,23 +19,10 @@ from cryptography.hazmat.primitives.ciphers import (
from .utils import (
generate_aead_exception_test, generate_aead_tag_exception_test
)
+from ...doubles import DummyCipherAlgorithm, DummyMode
from ...utils import raises_unsupported_algorithm
-@utils.register_interface(modes.Mode)
-class DummyMode(object):
- name = "dummy-mode"
-
- def validate_for_algorithm(self, algorithm):
- pass
-
-
-@utils.register_interface(base.CipherAlgorithm)
-class DummyCipher(object):
- name = "dummy-cipher"
- key_size = None
-
-
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCipher(object):
def test_creates_encryptor(self, backend):
@@ -84,6 +70,19 @@ class TestCipherContext(object):
with pytest.raises(AlreadyFinalized):
decryptor.finalize()
+ def test_use_update_into_after_finalize(self, backend):
+ cipher = Cipher(
+ algorithms.AES(binascii.unhexlify(b"0" * 32)),
+ modes.CBC(binascii.unhexlify(b"0" * 32)),
+ backend
+ )
+ encryptor = cipher.encryptor()
+ encryptor.update(b"a" * 16)
+ encryptor.finalize()
+ with pytest.raises(AlreadyFinalized):
+ buf = bytearray(31)
+ encryptor.update_into(b"b" * 16, buf)
+
def test_unaligned_block_encryption(self, backend):
cipher = Cipher(
algorithms.AES(binascii.unhexlify(b"0" * 32)),
@@ -107,7 +106,7 @@ class TestCipherContext(object):
@pytest.mark.parametrize("mode", [DummyMode(), None])
def test_nonexistent_cipher(self, backend, mode):
cipher = Cipher(
- DummyCipher(), mode, backend
+ DummyCipherAlgorithm(), mode, backend
)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
cipher.encryptor()
@@ -134,7 +133,7 @@ class TestCipherContext(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.AES("\x00" * 16), modes.GCM("\x00" * 12)
+ algorithms.AES(b"\x00" * 16), modes.GCM(b"\x00" * 12)
),
skip_message="Does not support AES GCM",
)
@@ -191,3 +190,37 @@ class TestModeValidation(object):
modes.CTR(b"abc"),
backend,
)
+
+ def test_gcm(self):
+ with pytest.raises(ValueError):
+ modes.GCM(b"")
+
+
+class TestModesRequireBytes(object):
+ def test_cbc(self):
+ with pytest.raises(TypeError):
+ modes.CBC([1] * 16)
+
+ def test_cfb(self):
+ with pytest.raises(TypeError):
+ modes.CFB([1] * 16)
+
+ def test_cfb8(self):
+ with pytest.raises(TypeError):
+ modes.CFB8([1] * 16)
+
+ def test_ofb(self):
+ with pytest.raises(TypeError):
+ modes.OFB([1] * 16)
+
+ def test_ctr(self):
+ with pytest.raises(TypeError):
+ modes.CTR([1] * 16)
+
+ def test_gcm_iv(self):
+ with pytest.raises(TypeError):
+ modes.GCM([1] * 16)
+
+ def test_gcm_tag(self):
+ with pytest.raises(TypeError):
+ modes.GCM(b"\x00" * 16, [1] * 16)
diff --git a/tests/hazmat/primitives/test_blowfish.py b/tests/hazmat/primitives/test_blowfish.py
index de49e86d..5f7480ec 100644
--- a/tests/hazmat/primitives/test_blowfish.py
+++ b/tests/hazmat/primitives/test_blowfish.py
@@ -18,13 +18,13 @@ from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Blowfish("\x00" * 56), modes.ECB()
+ algorithms.Blowfish(b"\x00" * 56), modes.ECB()
),
skip_message="Does not support Blowfish ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestBlowfishModeECB(object):
- test_ECB = generate_encrypt_test(
+ test_ecb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Blowfish"),
["bf-ecb.txt"],
@@ -35,13 +35,13 @@ class TestBlowfishModeECB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Blowfish("\x00" * 56), modes.CBC("\x00" * 8)
+ algorithms.Blowfish(b"\x00" * 56), modes.CBC(b"\x00" * 8)
),
skip_message="Does not support Blowfish CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestBlowfishModeCBC(object):
- test_CBC = generate_encrypt_test(
+ test_cbc = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Blowfish"),
["bf-cbc.txt"],
@@ -52,13 +52,13 @@ class TestBlowfishModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Blowfish("\x00" * 56), modes.OFB("\x00" * 8)
+ algorithms.Blowfish(b"\x00" * 56), modes.OFB(b"\x00" * 8)
),
skip_message="Does not support Blowfish OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestBlowfishModeOFB(object):
- test_OFB = generate_encrypt_test(
+ test_ofb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Blowfish"),
["bf-ofb.txt"],
@@ -69,13 +69,13 @@ class TestBlowfishModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Blowfish("\x00" * 56), modes.CFB("\x00" * 8)
+ algorithms.Blowfish(b"\x00" * 56), modes.CFB(b"\x00" * 8)
),
skip_message="Does not support Blowfish CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestBlowfishModeCFB(object):
- test_CFB = generate_encrypt_test(
+ test_cfb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Blowfish"),
["bf-cfb.txt"],
diff --git a/tests/hazmat/primitives/test_camellia.py b/tests/hazmat/primitives/test_camellia.py
index 8bdcb309..07a735ad 100644
--- a/tests/hazmat/primitives/test_camellia.py
+++ b/tests/hazmat/primitives/test_camellia.py
@@ -20,13 +20,13 @@ from ...utils import (
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Camellia("\x00" * 16), modes.ECB()
+ algorithms.Camellia(b"\x00" * 16), modes.ECB()
),
skip_message="Does not support Camellia ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCamelliaModeECB(object):
- test_ECB = generate_encrypt_test(
+ test_ecb = generate_encrypt_test(
load_cryptrec_vectors,
os.path.join("ciphers", "Camellia"),
[
@@ -41,13 +41,13 @@ class TestCamelliaModeECB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Camellia("\x00" * 16), modes.CBC("\x00" * 16)
+ algorithms.Camellia(b"\x00" * 16), modes.CBC(b"\x00" * 16)
),
skip_message="Does not support Camellia CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCamelliaModeCBC(object):
- test_CBC = generate_encrypt_test(
+ test_cbc = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Camellia"),
["camellia-cbc.txt"],
@@ -58,13 +58,13 @@ class TestCamelliaModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Camellia("\x00" * 16), modes.OFB("\x00" * 16)
+ algorithms.Camellia(b"\x00" * 16), modes.OFB(b"\x00" * 16)
),
skip_message="Does not support Camellia OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCamelliaModeOFB(object):
- test_OFB = generate_encrypt_test(
+ test_ofb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Camellia"),
["camellia-ofb.txt"],
@@ -75,13 +75,13 @@ class TestCamelliaModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.Camellia("\x00" * 16), modes.CFB("\x00" * 16)
+ algorithms.Camellia(b"\x00" * 16), modes.CFB(b"\x00" * 16)
),
skip_message="Does not support Camellia CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCamelliaModeCFB(object):
- test_CFB = generate_encrypt_test(
+ test_cfb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "Camellia"),
["camellia-cfb.txt"],
diff --git a/tests/hazmat/primitives/test_cast5.py b/tests/hazmat/primitives/test_cast5.py
index 0e4f879c..87e12a33 100644
--- a/tests/hazmat/primitives/test_cast5.py
+++ b/tests/hazmat/primitives/test_cast5.py
@@ -18,13 +18,13 @@ from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.CAST5("\x00" * 16), modes.ECB()
+ algorithms.CAST5(b"\x00" * 16), modes.ECB()
),
skip_message="Does not support CAST5 ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCAST5ModeECB(object):
- test_ECB = generate_encrypt_test(
+ test_ecb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "CAST5"),
["cast5-ecb.txt"],
@@ -35,13 +35,13 @@ class TestCAST5ModeECB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.CAST5("\x00" * 16), modes.CBC("\x00" * 8)
+ algorithms.CAST5(b"\x00" * 16), modes.CBC(b"\x00" * 8)
),
skip_message="Does not support CAST5 CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCAST5ModeCBC(object):
- test_CBC = generate_encrypt_test(
+ test_cbc = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "CAST5"),
["cast5-cbc.txt"],
@@ -52,13 +52,13 @@ class TestCAST5ModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.CAST5("\x00" * 16), modes.OFB("\x00" * 8)
+ algorithms.CAST5(b"\x00" * 16), modes.OFB(b"\x00" * 8)
),
skip_message="Does not support CAST5 OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCAST5ModeOFB(object):
- test_OFB = generate_encrypt_test(
+ test_ofb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "CAST5"),
["cast5-ofb.txt"],
@@ -69,33 +69,16 @@ class TestCAST5ModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.CAST5("\x00" * 16), modes.CFB("\x00" * 8)
+ algorithms.CAST5(b"\x00" * 16), modes.CFB(b"\x00" * 8)
),
skip_message="Does not support CAST5 CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestCAST5ModeCFB(object):
- test_CFB = generate_encrypt_test(
+ test_cfb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "CAST5"),
["cast5-cfb.txt"],
lambda key, **kwargs: algorithms.CAST5(binascii.unhexlify((key))),
lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv))
)
-
-
-@pytest.mark.supported(
- only_if=lambda backend: backend.cipher_supported(
- algorithms.CAST5("\x00" * 16), modes.CTR("\x00" * 8)
- ),
- skip_message="Does not support CAST5 CTR",
-)
-@pytest.mark.requires_backend_interface(interface=CipherBackend)
-class TestCAST5ModeCTR(object):
- test_CTR = generate_encrypt_test(
- load_nist_vectors,
- os.path.join("ciphers", "CAST5"),
- ["cast5-ctr.txt"],
- lambda key, **kwargs: algorithms.CAST5(binascii.unhexlify((key))),
- lambda iv, **kwargs: modes.CTR(binascii.unhexlify(iv))
- )
diff --git a/tests/hazmat/primitives/test_chacha20.py b/tests/hazmat/primitives/test_chacha20.py
new file mode 100644
index 00000000..7c475c0f
--- /dev/null
+++ b/tests/hazmat/primitives/test_chacha20.py
@@ -0,0 +1,76 @@
+# 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 os
+import struct
+
+import pytest
+
+from cryptography.hazmat.backends.interfaces import CipherBackend
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
+
+from .utils import _load_all_params
+from ...utils import load_nist_vectors
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.ChaCha20(b"\x00" * 32, b"0" * 16), None
+ ),
+ skip_message="Does not support ChaCha20",
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestChaCha20(object):
+ @pytest.mark.parametrize(
+ "vector",
+ _load_all_params(
+ os.path.join("ciphers", "ChaCha20"),
+ ["rfc7539.txt"],
+ load_nist_vectors
+ )
+ )
+ def test_vectors(self, vector, backend):
+ key = binascii.unhexlify(vector["key"])
+ nonce = binascii.unhexlify(vector["nonce"])
+ ibc = struct.pack("<i", int(vector["initial_block_counter"]))
+ pt = binascii.unhexlify(vector["plaintext"])
+ encryptor = Cipher(
+ algorithms.ChaCha20(key, ibc + nonce), None, backend
+ ).encryptor()
+ computed_ct = encryptor.update(pt) + encryptor.finalize()
+ assert binascii.hexlify(computed_ct) == vector["ciphertext"]
+
+ def test_buffer_protocol(self, backend):
+ key = bytearray(os.urandom(32))
+ nonce = bytearray(os.urandom(16))
+ cipher = Cipher(
+ algorithms.ChaCha20(key, nonce), None, backend
+ )
+ enc = cipher.encryptor()
+ ct = enc.update(bytearray(b"hello")) + enc.finalize()
+ dec = cipher.decryptor()
+ pt = dec.update(ct) + dec.finalize()
+ assert pt == b"hello"
+
+ def test_key_size(self):
+ chacha = algorithms.ChaCha20(b"0" * 32, b"0" * 16)
+ assert chacha.key_size == 256
+
+ def test_invalid_key_size(self):
+ with pytest.raises(ValueError):
+ algorithms.ChaCha20(b"wrongsize", b"0" * 16)
+
+ def test_invalid_nonce(self):
+ with pytest.raises(ValueError):
+ algorithms.ChaCha20(b"0" * 32, b"0")
+
+ with pytest.raises(TypeError):
+ algorithms.ChaCha20(b"0" * 32, object())
+
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ algorithms.ChaCha20(u"0" * 32, b"0" * 16)
diff --git a/tests/hazmat/primitives/test_ciphers.py b/tests/hazmat/primitives/test_ciphers.py
index d9a07ff6..f29ba9a9 100644
--- a/tests/hazmat/primitives/test_ciphers.py
+++ b/tests/hazmat/primitives/test_ciphers.py
@@ -5,17 +5,21 @@
from __future__ import absolute_import, division, print_function
import binascii
+import os
import pytest
-from cryptography.exceptions import _Reasons
+from cryptography.exceptions import AlreadyFinalized, _Reasons
+from cryptography.hazmat.backends.interfaces import CipherBackend
from cryptography.hazmat.primitives import ciphers
+from cryptography.hazmat.primitives.ciphers import modes
from cryptography.hazmat.primitives.ciphers.algorithms import (
AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES
)
-from cryptography.hazmat.primitives.ciphers.modes import ECB
-from ...utils import raises_unsupported_algorithm
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
class TestAES(object):
@@ -32,6 +36,34 @@ class TestAES(object):
with pytest.raises(ValueError):
AES(binascii.unhexlify(b"0" * 12))
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ AES(u"0" * 32)
+
+
+class TestAESXTS(object):
+ @pytest.mark.requires_backend_interface(interface=CipherBackend)
+ @pytest.mark.parametrize(
+ "mode",
+ (modes.CBC, modes.CTR, modes.CFB, modes.CFB8, modes.OFB)
+ )
+ def test_invalid_key_size_with_mode(self, mode, backend):
+ with pytest.raises(ValueError):
+ ciphers.Cipher(AES(b"0" * 64), mode(b"0" * 16), backend)
+
+ def test_xts_tweak_not_bytes(self):
+ with pytest.raises(TypeError):
+ modes.XTS(32)
+
+ def test_xts_tweak_too_small(self):
+ with pytest.raises(ValueError):
+ modes.XTS(b"0")
+
+ @pytest.mark.requires_backend_interface(interface=CipherBackend)
+ def test_xts_wrong_key_size(self, backend):
+ with pytest.raises(ValueError):
+ ciphers.Cipher(AES(b"0" * 16), modes.XTS(b"0" * 16), backend)
+
class TestCamellia(object):
@pytest.mark.parametrize(("key", "keysize"), [
@@ -47,6 +79,10 @@ class TestCamellia(object):
with pytest.raises(ValueError):
Camellia(binascii.unhexlify(b"0" * 12))
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ Camellia(u"0" * 32)
+
class TestTripleDES(object):
@pytest.mark.parametrize("key", [
@@ -62,6 +98,10 @@ class TestTripleDES(object):
with pytest.raises(ValueError):
TripleDES(binascii.unhexlify(b"0" * 12))
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ TripleDES(u"0" * 16)
+
class TestBlowfish(object):
@pytest.mark.parametrize(("key", "keysize"), [
@@ -75,6 +115,10 @@ class TestBlowfish(object):
with pytest.raises(ValueError):
Blowfish(binascii.unhexlify(b"0" * 6))
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ Blowfish(u"0" * 8)
+
class TestCAST5(object):
@pytest.mark.parametrize(("key", "keysize"), [
@@ -88,6 +132,10 @@ class TestCAST5(object):
with pytest.raises(ValueError):
CAST5(binascii.unhexlify(b"0" * 34))
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ CAST5(u"0" * 10)
+
class TestARC4(object):
@pytest.mark.parametrize(("key", "keysize"), [
@@ -107,6 +155,10 @@ class TestARC4(object):
with pytest.raises(ValueError):
ARC4(binascii.unhexlify(b"0" * 34))
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ ARC4(u"0" * 10)
+
class TestIDEA(object):
def test_key_size(self):
@@ -117,6 +169,10 @@ class TestIDEA(object):
with pytest.raises(ValueError):
IDEA(b"\x00" * 17)
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ IDEA(u"0" * 16)
+
class TestSEED(object):
def test_key_size(self):
@@ -127,9 +183,129 @@ class TestSEED(object):
with pytest.raises(ValueError):
SEED(b"\x00" * 17)
+ def test_invalid_key_type(self):
+ with pytest.raises(TypeError, match="key must be bytes"):
+ SEED(u"0" * 16)
+
def test_invalid_backend():
pretend_backend = object()
with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
- ciphers.Cipher(AES(b"AAAAAAAAAAAAAAAA"), ECB, pretend_backend)
+ ciphers.Cipher(AES(b"AAAAAAAAAAAAAAAA"), modes.ECB, pretend_backend)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES ECB",
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestCipherUpdateInto(object):
+ @pytest.mark.parametrize(
+ "params",
+ load_vectors_from_file(
+ os.path.join("ciphers", "AES", "ECB", "ECBGFSbox128.rsp"),
+ load_nist_vectors
+ )
+ )
+ def test_update_into(self, params, backend):
+ key = binascii.unhexlify(params["key"])
+ pt = binascii.unhexlify(params["plaintext"])
+ ct = binascii.unhexlify(params["ciphertext"])
+ c = ciphers.Cipher(AES(key), modes.ECB(), backend)
+ encryptor = c.encryptor()
+ buf = bytearray(len(pt) + 15)
+ res = encryptor.update_into(pt, buf)
+ assert res == len(pt)
+ assert bytes(buf)[:res] == ct
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ AES(b"\x00" * 16), modes.GCM(b"0" * 12)
+ ),
+ skip_message="Does not support AES GCM",
+ )
+ def test_update_into_gcm(self, backend):
+ key = binascii.unhexlify(b"e98b72a9881a84ca6b76e0f43e68647a")
+ iv = binascii.unhexlify(b"8b23299fde174053f3d652ba")
+ ct = binascii.unhexlify(b"5a3c1cf1985dbb8bed818036fdd5ab42")
+ pt = binascii.unhexlify(b"28286a321293253c3e0aa2704a278032")
+ c = ciphers.Cipher(AES(key), modes.GCM(iv), backend)
+ encryptor = c.encryptor()
+ buf = bytearray(len(pt) + 15)
+ res = encryptor.update_into(pt, buf)
+ assert res == len(pt)
+ assert bytes(buf)[:res] == ct
+ encryptor.finalize()
+ c = ciphers.Cipher(AES(key), modes.GCM(iv, encryptor.tag), backend)
+ decryptor = c.decryptor()
+ res = decryptor.update_into(ct, buf)
+ decryptor.finalize()
+ assert res == len(pt)
+ assert bytes(buf)[:res] == pt
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ AES(b"\x00" * 16), modes.GCM(b"0" * 12)
+ ),
+ skip_message="Does not support AES GCM",
+ )
+ def test_finalize_with_tag_already_finalized(self, backend):
+ key = binascii.unhexlify(b"e98b72a9881a84ca6b76e0f43e68647a")
+ iv = binascii.unhexlify(b"8b23299fde174053f3d652ba")
+ encryptor = ciphers.Cipher(
+ AES(key), modes.GCM(iv), backend
+ ).encryptor()
+ ciphertext = encryptor.update(b"abc") + encryptor.finalize()
+
+ decryptor = ciphers.Cipher(
+ AES(key), modes.GCM(iv, tag=encryptor.tag), backend
+ ).decryptor()
+ decryptor.update(ciphertext)
+ decryptor.finalize()
+ with pytest.raises(AlreadyFinalized):
+ decryptor.finalize_with_tag(encryptor.tag)
+
+ @pytest.mark.parametrize(
+ "params",
+ load_vectors_from_file(
+ os.path.join("ciphers", "AES", "ECB", "ECBGFSbox128.rsp"),
+ load_nist_vectors
+ )
+ )
+ def test_update_into_multiple_calls(self, params, backend):
+ key = binascii.unhexlify(params["key"])
+ pt = binascii.unhexlify(params["plaintext"])
+ ct = binascii.unhexlify(params["ciphertext"])
+ c = ciphers.Cipher(AES(key), modes.ECB(), backend)
+ encryptor = c.encryptor()
+ buf = bytearray(len(pt) + 15)
+ res = encryptor.update_into(pt[:3], buf)
+ assert res == 0
+ res = encryptor.update_into(pt[3:], buf)
+ assert res == len(pt)
+ assert bytes(buf)[:res] == ct
+
+ def test_update_into_buffer_too_small(self, backend):
+ key = b"\x00" * 16
+ c = ciphers.Cipher(AES(key), modes.ECB(), backend)
+ encryptor = c.encryptor()
+ buf = bytearray(16)
+ with pytest.raises(ValueError):
+ encryptor.update_into(b"testing", buf)
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ AES(b"\x00" * 16), modes.GCM(b"\x00" * 12)
+ ),
+ skip_message="Does not support AES GCM",
+ )
+ def test_update_into_buffer_too_small_gcm(self, backend):
+ key = b"\x00" * 16
+ c = ciphers.Cipher(AES(key), modes.GCM(b"\x00" * 12), backend)
+ encryptor = c.encryptor()
+ buf = bytearray(5)
+ with pytest.raises(ValueError):
+ encryptor.update_into(b"testing", buf)
diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py
index 08b6bf5d..e319396d 100644
--- a/tests/hazmat/primitives/test_cmac.py
+++ b/tests/hazmat/primitives/test_cmac.py
@@ -6,8 +6,6 @@ from __future__ import absolute_import, division, print_function
import binascii
-import pretend
-
import pytest
from cryptography.exceptions import (
@@ -19,11 +17,11 @@ from cryptography.hazmat.primitives.ciphers.algorithms import (
)
from cryptography.hazmat.primitives.cmac import CMAC
-from ..backends.test_multibackend import DummyCMACBackend
from ...utils import (
load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
)
+
vectors_aes128 = load_vectors_from_file(
"CMAC/nist-800-38b-aes128.txt", load_nist_vectors)
@@ -185,16 +183,18 @@ class TestCMAC(object):
copy_cmac = cmac.copy()
assert cmac.finalize() == copy_cmac.finalize()
-
-def test_copy():
- backend = DummyCMACBackend([AES])
- copied_ctx = pretend.stub()
- pretend_ctx = pretend.stub(copy=lambda: copied_ctx)
- key = b"2b7e151628aed2a6abf7158809cf4f3c"
- cmac = CMAC(AES(key), backend=backend, ctx=pretend_ctx)
-
- assert cmac._backend is backend
- assert cmac.copy()._backend is backend
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cmac_algorithm_supported(
+ AES(fake_key)),
+ skip_message="Does not support CMAC."
+ )
+ def test_buffer_protocol(self, backend):
+ key = bytearray(b"2b7e151628aed2a6abf7158809cf4f3c")
+ cmac = CMAC(AES(key), backend)
+ cmac.update(b"6bc1bee22e409f96e93d7e117393172a")
+ assert cmac.finalize() == binascii.unhexlify(
+ b"a21e6e647bfeaf5ca0a5e1bcd957dfad"
+ )
def test_invalid_backend():
diff --git a/tests/hazmat/primitives/test_concatkdf.py b/tests/hazmat/primitives/test_concatkdf.py
index 27e5460e..67315099 100644
--- a/tests/hazmat/primitives/test_concatkdf.py
+++ b/tests/hazmat/primitives/test_concatkdf.py
@@ -52,6 +52,22 @@ class TestConcatKDFHash(object):
assert ckdf.derive(prk) == okm
+ def test_buffer_protocol(self, backend):
+ prk = binascii.unhexlify(
+ b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23"
+ )
+
+ okm = binascii.unhexlify(b"1c3bc9e7c4547c5191c0d478cccaed55")
+
+ oinfo = binascii.unhexlify(
+ b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2"
+ b"46f72971f292badaa2fe4124612cba"
+ )
+
+ ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend)
+
+ assert ckdf.derive(bytearray(prk)) == okm
+
def test_verify(self, backend):
prk = binascii.unhexlify(
b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23"
@@ -158,6 +174,46 @@ class TestConcatKDFHMAC(object):
assert ckdf.derive(prk) == okm
+ def test_buffer_protocol(self, backend):
+ prk = binascii.unhexlify(
+ b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
+ b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
+ b"8f0af7fce1d045edbc5790931e8d5ca79c73"
+ )
+
+ okm = binascii.unhexlify(b"64ce901db10d558661f10b6836a122a7"
+ b"605323ce2f39bf27eaaac8b34cf89f2f")
+
+ oinfo = binascii.unhexlify(
+ b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
+ b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
+ )
+
+ ckdf = ConcatKDFHMAC(hashes.SHA512(), 32, None, oinfo, backend)
+
+ assert ckdf.derive(bytearray(prk)) == okm
+
+ def test_derive_explicit_salt(self, backend):
+ prk = binascii.unhexlify(
+ b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
+ b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
+ b"8f0af7fce1d045edbc5790931e8d5ca79c73"
+ )
+
+ okm = binascii.unhexlify(b"64ce901db10d558661f10b6836a122a7"
+ b"605323ce2f39bf27eaaac8b34cf89f2f")
+
+ oinfo = binascii.unhexlify(
+ b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
+ b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
+ )
+
+ ckdf = ConcatKDFHMAC(
+ hashes.SHA512(), 32, b"\x00" * 128, oinfo, backend
+ )
+
+ assert ckdf.derive(prk) == okm
+
def test_verify(self, backend):
prk = binascii.unhexlify(
b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py
index d8869de9..43f2ce5c 100644
--- a/tests/hazmat/primitives/test_dh.py
+++ b/tests/hazmat/primitives/test_dh.py
@@ -4,22 +4,42 @@
from __future__ import absolute_import, division, print_function
+import binascii
+import itertools
+import os
+
import pytest
+from cryptography.hazmat.backends.interfaces import (
+ DERSerializationBackend, DHBackend, PEMSerializationBackend)
+from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import dh
+from cryptography.utils import int_from_bytes
+
+from ...doubles import DummyKeySerializationEncryption
+from ...utils import load_nist_vectors, load_vectors_from_file
+
+
+def _skip_dhx_unsupported(backend, is_dhx):
+ if not is_dhx:
+ return
+ if not backend.dh_x942_serialization_supported():
+ pytest.skip(
+ "DH x9.42 serialization is not supported"
+ )
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 +52,28 @@ def test_dh_parameternumbers():
None, None
)
+ with pytest.raises(ValueError):
+ dh.DHParameterNumbers(
+ 65537, 1
+ )
+
+ params = dh.DHParameterNumbers(
+ 65537, 7, 1245
+ )
+
+ assert params.p == 65537
+ assert params.g == 7
+ assert params.q == 1245
+
+ with pytest.raises(TypeError):
+ dh.DHParameterNumbers(
+ 65537, 2, "hello"
+ )
+
def test_dh_numbers():
params = dh.DHParameterNumbers(
- 65537, 3
+ 65537, 2
)
public = dh.DHPublicNumbers(
@@ -74,14 +112,18 @@ 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(65537, 7, 12345) == dh.DHParameterNumbers(
+ 65537, 7, 12345)
+ assert dh.DHParameterNumbers(6, 2) != dh.DHParameterNumbers(65537, 2)
+ assert dh.DHParameterNumbers(65537, 2, 123) != dh.DHParameterNumbers(
+ 65537, 2, 456)
+ 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 +131,752 @@ 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):
+ valid_p = int(
+ b"907c7211ae61aaaba1825ff53b6cb71ac6df9f1a424c033f4a0a41ac42fad3a9"
+ b"bcfc7f938a269710ed69e330523e4039029b7900977c740990d46efed79b9bbe"
+ b"73505ae878808944ce4d9c6c52daecc0a87dc889c53499be93db8551ee685f30"
+ b"349bf1b443d4ebaee0d5e8b441a40d4e8178f8f612f657a5eb91e0a8e"
+ b"107755f", 16
+ )
+ assert backend.dh_parameters_supported(valid_p, 5)
+ assert not backend.dh_parameters_supported(23, 22)
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "rfc3526.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_dh_parameters_allows_rfc3526_groups(self, backend, vector):
+ p = int_from_bytes(binascii.unhexlify(vector["p"]), 'big')
+ params = dh.DHParameterNumbers(p, int(vector["g"]))
+ param = params.parameters(backend)
+ key = param.generate_private_key()
+ # This confirms that a key generated with this group
+ # will pass DH_check when we serialize and de-serialize it via
+ # the Numbers path.
+ roundtripped_key = key.private_numbers().private_key(backend)
+ assert key.private_numbers() == roundtripped_key.private_numbers()
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "RFC5114.txt"),
+ load_nist_vectors))
+ def test_dh_parameters_supported_with_q(self, backend, vector):
+ assert backend.dh_parameters_supported(int(vector["p"], 16),
+ int(vector["g"], 16),
+ int(vector["q"], 16))
+
+ @pytest.mark.parametrize("with_q", [False, True])
+ def test_convert_to_numbers(self, backend, with_q):
+ if with_q:
+ vector = load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "RFC5114.txt"),
+ load_nist_vectors)[0]
+ p = int(vector["p"], 16)
+ g = int(vector["g"], 16)
+ q = int(vector["q"], 16)
+ else:
+ 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
+ q = None
+
+ params = dh.DHParameterNumbers(p, g, q)
+ 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):
+ # p is set to 21 because when calling private_key we want it to
+ # fail the DH_check call OpenSSL does. Originally this was 23, but
+ # we are allowing p % 24 to == 23 with this PR (see #3768 for more)
+ # By setting it to 21 it fails later in DH_check in a primality check
+ # which triggers the code path we want to test
+ params = dh.DHParameterNumbers(21, 2)
+ public = dh.DHPublicNumbers(1, params)
+ private = dh.DHPrivateNumbers(2, public)
+
+ with pytest.raises(ValueError):
+ private.private_key(backend)
+
+ @pytest.mark.parametrize("with_q", [False, True])
+ def test_generate_dh(self, backend, with_q):
+ if with_q:
+ vector = load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "RFC5114.txt"),
+ load_nist_vectors)[0]
+ p = int(vector["p"], 16)
+ g = int(vector["g"], 16)
+ q = int(vector["q"], 16)
+ parameters = dh.DHParameterNumbers(p, g, q).parameters(backend)
+ key_size = 1024
+ else:
+ 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 parameter_numbers.p.bit_length() == 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)
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "RFC5114.txt"),
+ load_nist_vectors))
+ def test_dh_vectors_with_q(self, backend, vector):
+ parameters = dh.DHParameterNumbers(int(vector["p"], 16),
+ int(vector["g"], 16),
+ int(vector["q"], 16))
+ public1 = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters)
+ private1 = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public1)
+ public2 = dh.DHPublicNumbers(int(vector["ystatiut"], 16), parameters)
+ private2 = dh.DHPrivateNumbers(int(vector["xstatiut"], 16), public2)
+ key1 = private1.private_key(backend)
+ key2 = private2.private_key(backend)
+ symkey1 = key1.exchange(public2.public_key(backend))
+ symkey2 = key2.exchange(public1.public_key(backend))
+
+ assert int_from_bytes(symkey1, 'big') == int(vector["z"], 16)
+ assert int_from_bytes(symkey2, 'big') == int(vector["z"], 16)
+
+
+@pytest.mark.requires_backend_interface(interface=DHBackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+@pytest.mark.requires_backend_interface(interface=DERSerializationBackend)
+class TestDHPrivateKeySerialization(object):
+
+ @pytest.mark.parametrize(
+ ("encoding", "loader_func"),
+ [
+ [
+ serialization.Encoding.PEM,
+ serialization.load_pem_private_key
+ ],
+ [
+ serialization.Encoding.DER,
+ serialization.load_der_private_key
+ ],
+ ]
+ )
+ def test_private_bytes_unencrypted(self, backend, encoding,
+ loader_func):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ serialized = key.private_bytes(
+ encoding, serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+ loaded_key = loader_func(serialized, None, backend)
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8),
+ ]
+ )
+ def test_private_bytes_rejects_invalid(self, encoding, fmt, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
+ ("key_path", "loader_func", "encoding", "is_dhx"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhkey.pem"),
+ serialization.load_pem_private_key,
+ serialization.Encoding.PEM,
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhkey.der"),
+ serialization.load_der_private_key,
+ serialization.Encoding.DER,
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
+ serialization.load_pem_private_key,
+ serialization.Encoding.PEM,
+ True,
+ ), (
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
+ serialization.load_der_private_key,
+ serialization.Encoding.DER,
+ True,
+ )
+ ]
+ )
+ def test_private_bytes_match(self, key_path, loader_func,
+ encoding, is_dhx, backend):
+ _skip_dhx_unsupported(backend, is_dhx)
+ key_bytes = load_vectors_from_file(
+ key_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ key = loader_func(key_bytes, None, backend)
+ serialized = key.private_bytes(
+ encoding, serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+ assert serialized == key_bytes
+
+ @pytest.mark.parametrize(
+ ("key_path", "loader_func", "vec_path", "is_dhx"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhkey.pem"),
+ serialization.load_pem_private_key,
+ os.path.join("asymmetric", "DH", "dhkey.txt"),
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhkey.der"),
+ serialization.load_der_private_key,
+ os.path.join("asymmetric", "DH", "dhkey.txt"),
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
+ serialization.load_pem_private_key,
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+ True,
+ ), (
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
+ serialization.load_der_private_key,
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+ True,
+ )
+ ]
+ )
+ def test_private_bytes_values(self, key_path, loader_func,
+ vec_path, is_dhx, backend):
+ _skip_dhx_unsupported(backend, is_dhx)
+ key_bytes = load_vectors_from_file(
+ key_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ vec = load_vectors_from_file(vec_path, load_nist_vectors)[0]
+ key = loader_func(key_bytes, None, backend)
+ private_numbers = key.private_numbers()
+ assert private_numbers.x == int(vec["x"], 16)
+ assert private_numbers.public_numbers.y == int(vec["y"], 16)
+ assert private_numbers.public_numbers.parameter_numbers.g == int(
+ vec["g"], 16)
+ assert private_numbers.public_numbers.parameter_numbers.p == int(
+ vec["p"], 16)
+ if "q" in vec:
+ assert private_numbers.public_numbers.parameter_numbers.q == int(
+ vec["q"], 16)
+ else:
+ assert private_numbers.public_numbers.parameter_numbers.q is None
+
+ def test_private_bytes_traditional_openssl_invalid(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_encoding(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ "notencoding",
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_format(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ "invalidformat",
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_encryption_algorithm(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ "notanencalg"
+ )
+
+ def test_private_bytes_unsupported_encryption_type(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ DummyKeySerializationEncryption()
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=DHBackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+@pytest.mark.requires_backend_interface(interface=DERSerializationBackend)
+class TestDHPublicKeySerialization(object):
+
+ @pytest.mark.parametrize(
+ ("encoding", "loader_func"),
+ [
+ [
+ serialization.Encoding.PEM,
+ serialization.load_pem_public_key
+ ],
+ [
+ serialization.Encoding.DER,
+ serialization.load_der_public_key
+ ],
+ ]
+ )
+ def test_public_bytes(self, backend, encoding,
+ loader_func):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key().public_key()
+ serialized = key.public_bytes(
+ encoding, serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+ loaded_key = loader_func(serialized, backend)
+ loaded_pub_num = loaded_key.public_numbers()
+ pub_num = key.public_numbers()
+ assert loaded_pub_num == pub_num
+
+ @pytest.mark.parametrize(
+ ("key_path", "loader_func", "encoding", "is_dhx"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhpub.pem"),
+ serialization.load_pem_public_key,
+ serialization.Encoding.PEM,
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhpub.der"),
+ serialization.load_der_public_key,
+ serialization.Encoding.DER,
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
+ serialization.load_pem_public_key,
+ serialization.Encoding.PEM,
+ True,
+ ), (
+ os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
+ serialization.load_der_public_key,
+ serialization.Encoding.DER,
+ True,
+ )
+ ]
+ )
+ def test_public_bytes_match(self, key_path, loader_func,
+ encoding, is_dhx, backend):
+ _skip_dhx_unsupported(backend, is_dhx)
+ key_bytes = load_vectors_from_file(
+ key_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ pub_key = loader_func(key_bytes, backend)
+ serialized = pub_key.public_bytes(
+ encoding,
+ serialization.PublicFormat.SubjectPublicKeyInfo,
+ )
+ assert serialized == key_bytes
+
+ @pytest.mark.parametrize(
+ ("key_path", "loader_func", "vec_path", "is_dhx"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhpub.pem"),
+ serialization.load_pem_public_key,
+ os.path.join("asymmetric", "DH", "dhkey.txt"),
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhpub.der"),
+ serialization.load_der_public_key,
+ os.path.join("asymmetric", "DH", "dhkey.txt"),
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
+ serialization.load_pem_public_key,
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+ True,
+ ), (
+ os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
+ serialization.load_der_public_key,
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+ True,
+ )
+ ]
+ )
+ def test_public_bytes_values(self, key_path, loader_func,
+ vec_path, is_dhx, backend):
+ _skip_dhx_unsupported(backend, is_dhx)
+ key_bytes = load_vectors_from_file(
+ key_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ vec = load_vectors_from_file(vec_path, load_nist_vectors)[0]
+ pub_key = loader_func(key_bytes, backend)
+ public_numbers = pub_key.public_numbers()
+ assert public_numbers.y == int(vec["y"], 16)
+ assert public_numbers.parameter_numbers.g == int(vec["g"], 16)
+ assert public_numbers.parameter_numbers.p == int(vec["p"], 16)
+ if "q" in vec:
+ assert public_numbers.parameter_numbers.q == int(vec["q"], 16)
+ else:
+ assert public_numbers.parameter_numbers.q is None
+
+ def test_public_bytes_invalid_encoding(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key().public_key()
+ with pytest.raises(TypeError):
+ key.public_bytes(
+ "notencoding",
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ def test_public_bytes_pkcs1_unsupported(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=DHBackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+@pytest.mark.requires_backend_interface(interface=DERSerializationBackend)
+class TestDHParameterSerialization(object):
+
+ @pytest.mark.parametrize(
+ ("encoding", "loader_func"),
+ [
+ [
+ serialization.Encoding.PEM,
+ serialization.load_pem_parameters
+ ],
+ [
+ serialization.Encoding.DER,
+ serialization.load_der_parameters
+ ],
+ ]
+ )
+ def test_parameter_bytes(self, backend, encoding,
+ loader_func):
+ parameters = dh.generate_parameters(2, 512, backend)
+ serialized = parameters.parameter_bytes(
+ encoding, serialization.ParameterFormat.PKCS3
+ )
+ loaded_key = loader_func(serialized, backend)
+ loaded_param_num = loaded_key.parameter_numbers()
+ assert loaded_param_num == parameters.parameter_numbers()
+
+ @pytest.mark.parametrize(
+ ("param_path", "loader_func", "encoding", "is_dhx"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhp.pem"),
+ serialization.load_pem_parameters,
+ serialization.Encoding.PEM,
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhp.der"),
+ serialization.load_der_parameters,
+ serialization.Encoding.DER,
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhp_rfc5114_2.pem"),
+ serialization.load_pem_parameters,
+ serialization.Encoding.PEM,
+ True,
+ ), (
+ os.path.join("asymmetric", "DH", "dhp_rfc5114_2.der"),
+ serialization.load_der_parameters,
+ serialization.Encoding.DER,
+ True,
+ )
+ ]
+ )
+ def test_parameter_bytes_match(self, param_path, loader_func,
+ encoding, backend, is_dhx):
+ _skip_dhx_unsupported(backend, is_dhx)
+ param_bytes = load_vectors_from_file(
+ param_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ parameters = loader_func(param_bytes, backend)
+ serialized = parameters.parameter_bytes(
+ encoding,
+ serialization.ParameterFormat.PKCS3,
+ )
+ assert serialized == param_bytes
+
+ @pytest.mark.parametrize(
+ ("param_path", "loader_func", "vec_path", "is_dhx"),
+ [
+ (
+ os.path.join("asymmetric", "DH", "dhp.pem"),
+ serialization.load_pem_parameters,
+ os.path.join("asymmetric", "DH", "dhkey.txt"),
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhp.der"),
+ serialization.load_der_parameters,
+ os.path.join("asymmetric", "DH", "dhkey.txt"),
+ False,
+ ), (
+ os.path.join("asymmetric", "DH", "dhp_rfc5114_2.pem"),
+ serialization.load_pem_parameters,
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+ True,
+ ), (
+ os.path.join("asymmetric", "DH", "dhp_rfc5114_2.der"),
+ serialization.load_der_parameters,
+ os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+ True,
+ )
+ ]
+ )
+ def test_public_bytes_values(self, param_path, loader_func,
+ vec_path, backend, is_dhx):
+ _skip_dhx_unsupported(backend, is_dhx)
+ key_bytes = load_vectors_from_file(
+ param_path,
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ vec = load_vectors_from_file(vec_path, load_nist_vectors)[0]
+ parameters = loader_func(key_bytes, backend)
+ parameter_numbers = parameters.parameter_numbers()
+ assert parameter_numbers.g == int(vec["g"], 16)
+ assert parameter_numbers.p == int(vec["p"], 16)
+ if "q" in vec:
+ assert parameter_numbers.q == int(vec["q"], 16)
+ else:
+ assert parameter_numbers.q is None
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ ),
+ (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1),
+ ] + list(itertools.product(
+ [
+ serialization.Encoding.Raw,
+ serialization.Encoding.X962,
+ serialization.Encoding.PEM,
+ serialization.Encoding.DER
+ ],
+ [
+ serialization.PublicFormat.Raw,
+ serialization.PublicFormat.UncompressedPoint,
+ serialization.PublicFormat.CompressedPoint
+ ]
+ ))
+ )
+ def test_public_bytes_rejects_invalid(self, encoding, fmt, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ key = parameters.generate_private_key().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
+
+ def test_parameter_bytes_invalid_encoding(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ with pytest.raises(TypeError):
+ parameters.parameter_bytes(
+ "notencoding",
+ serialization.ParameterFormat.PKCS3
+ )
+
+ def test_parameter_bytes_invalid_format(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ with pytest.raises(ValueError):
+ parameters.parameter_bytes(
+ serialization.Encoding.PEM,
+ "notformat"
+ )
+
+ def test_parameter_bytes_openssh_unsupported(self, backend):
+ parameters = dh.generate_parameters(2, 512, backend)
+ with pytest.raises(TypeError):
+ parameters.parameter_bytes(
+ serialization.Encoding.OpenSSH,
+ serialization.ParameterFormat.PKCS3
+ )
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index 5c83d5c7..9e1acf93 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -9,7 +9,6 @@ import os
import pytest
-from cryptography import utils
from cryptography.exceptions import AlreadyFinalized, InvalidSignature
from cryptography.hazmat.backends.interfaces import (
DSABackend, PEMSerializationBackend
@@ -17,37 +16,34 @@ from cryptography.hazmat.backends.interfaces import (
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives.asymmetric.utils import (
- encode_rfc6979_signature
+ Prehashed, encode_dss_signature
)
-from cryptography.utils import bit_length
+from cryptography.utils import CryptographyDeprecationWarning
from .fixtures_dsa import (
DSA_KEY_1024, DSA_KEY_2048, DSA_KEY_3072
)
+from ...doubles import DummyHashAlgorithm, DummyKeySerializationEncryption
from ...utils import (
load_fips_dsa_key_pair_vectors, load_fips_dsa_sig_vectors,
load_vectors_from_file,
)
-def _skip_if_no_serialization(key, backend):
- if not isinstance(
- key,
- (dsa.DSAPrivateKeyWithSerialization, dsa.DSAPublicKeyWithSerialization)
+def _skip_if_dsa_not_supported(backend, algorithm, p, q, g):
+ if (
+ not backend.dsa_parameters_supported(p, q, g) or
+ not backend.dsa_hash_supported(algorithm)
):
pytest.skip(
- "{0} does not support DSA key serialization".format(backend)
+ "{} does not support the provided parameters".format(backend)
)
-def test_skip_if_no_serialization():
+@pytest.mark.requires_backend_interface(interface=DSABackend)
+def test_skip_if_dsa_not_supported(backend):
with pytest.raises(pytest.skip.Exception):
- _skip_if_no_serialization("notakeywithserialization", "backend")
-
-
-@utils.register_interface(serialization.KeySerializationEncryption)
-class DummyKeyEncryption(object):
- pass
+ _skip_if_dsa_not_supported(backend, DummyHashAlgorithm(), 1, 1, 1)
@pytest.mark.requires_backend_interface(interface=DSABackend)
@@ -75,476 +71,282 @@ class TestDSA(object):
g=vector['g']
).parameters(backend)
skey = parameters.generate_private_key()
- if isinstance(skey, dsa.DSAPrivateKeyWithSerialization):
- numbers = skey.private_numbers()
- skey_parameters = numbers.public_numbers.parameter_numbers
- pkey = skey.public_key()
- parameters = pkey.parameters()
- parameter_numbers = parameters.parameter_numbers()
- assert parameter_numbers.p == skey_parameters.p
- assert parameter_numbers.q == skey_parameters.q
- assert parameter_numbers.g == skey_parameters.g
- assert skey_parameters.p == vector['p']
- assert skey_parameters.q == vector['q']
- assert skey_parameters.g == vector['g']
- assert skey.key_size == bit_length(vector['p'])
- assert pkey.key_size == skey.key_size
- public_numbers = pkey.public_numbers()
- assert numbers.public_numbers.y == public_numbers.y
- assert numbers.public_numbers.y == pow(
- skey_parameters.g, numbers.x, skey_parameters.p
- )
+ numbers = skey.private_numbers()
+ skey_parameters = numbers.public_numbers.parameter_numbers
+ pkey = skey.public_key()
+ parameters = pkey.parameters()
+ parameter_numbers = parameters.parameter_numbers()
+ assert parameter_numbers.p == skey_parameters.p
+ assert parameter_numbers.q == skey_parameters.q
+ assert parameter_numbers.g == skey_parameters.g
+ assert skey_parameters.p == vector['p']
+ assert skey_parameters.q == vector['q']
+ assert skey_parameters.g == vector['g']
+ assert skey.key_size == vector['p'].bit_length()
+ assert pkey.key_size == skey.key_size
+ public_numbers = pkey.public_numbers()
+ assert numbers.public_numbers.y == public_numbers.y
+ assert numbers.public_numbers.y == pow(
+ skey_parameters.g, numbers.x, skey_parameters.p
+ )
def test_generate_dsa_private_key_and_parameters(self, backend):
skey = dsa.generate_private_key(1024, backend)
assert skey
- if isinstance(skey, dsa.DSAPrivateKeyWithSerialization):
- numbers = skey.private_numbers()
- skey_parameters = numbers.public_numbers.parameter_numbers
- assert numbers.public_numbers.y == pow(
- skey_parameters.g, numbers.x, skey_parameters.p
- )
-
- def test_invalid_parameters_values(self, backend):
- # Test a p < 1024 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=2 ** 1000,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ).parameters(backend)
-
- # Test a p < 2048 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=2 ** 2000,
- q=DSA_KEY_2048.public_numbers.parameter_numbers.q,
- g=DSA_KEY_2048.public_numbers.parameter_numbers.g,
- ).parameters(backend)
-
- # Test a p < 3072 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=2 ** 3000,
- q=DSA_KEY_3072.public_numbers.parameter_numbers.q,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ).parameters(backend)
-
- # Test a p > 3072 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=2 ** 3100,
- q=DSA_KEY_3072.public_numbers.parameter_numbers.q,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ).parameters(backend)
-
- # Test a q < 160 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=2 ** 150,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ).parameters(backend)
-
- # Test a q < 256 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=DSA_KEY_2048.public_numbers.parameter_numbers.p,
- q=2 ** 250,
- g=DSA_KEY_2048.public_numbers.parameter_numbers.g
- ).parameters(backend)
-
- # Test a q > 256 bits in length
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=DSA_KEY_3072.public_numbers.parameter_numbers.p,
- q=2 ** 260,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ).parameters(backend)
-
- # Test a g < 1
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=0
- ).parameters(backend)
-
- # Test a g = 1
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=1
- ).parameters(backend)
-
- # Test a g > p
- with pytest.raises(ValueError):
- dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=2 ** 1200
- ).parameters(backend)
-
- def test_invalid_dsa_private_key_arguments(self, backend):
- # Test a p < 1024 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 1000,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=DSA_KEY_1024.x
- ).private_key(backend)
-
- # Test a p < 2048 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 2000,
- q=DSA_KEY_2048.public_numbers.parameter_numbers.q,
- g=DSA_KEY_2048.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_2048.public_numbers.y
- ),
- x=DSA_KEY_2048.x,
- ).private_key(backend)
-
- # Test a p < 3072 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 3000,
- q=DSA_KEY_3072.public_numbers.parameter_numbers.q,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_3072.public_numbers.y
- ),
- x=DSA_KEY_3072.x,
- ).private_key(backend)
-
- # Test a p > 3072 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 3100,
- q=DSA_KEY_3072.public_numbers.parameter_numbers.q,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_3072.public_numbers.y
- ),
- x=DSA_KEY_3072.x,
- ).private_key(backend)
-
- # Test a q < 160 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=2 ** 150,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=DSA_KEY_1024.x,
- ).private_key(backend)
-
- # Test a q < 256 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_2048.public_numbers.parameter_numbers.p,
- q=2 ** 250,
- g=DSA_KEY_2048.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_2048.public_numbers.y
- ),
- x=DSA_KEY_2048.x,
- ).private_key(backend)
-
- # Test a q > 256 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_3072.public_numbers.parameter_numbers.p,
- q=2 ** 260,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_3072.public_numbers.y
- ),
- x=DSA_KEY_3072.x,
- ).private_key(backend)
-
- # Test a g < 1
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=0,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=DSA_KEY_1024.x,
- ).private_key(backend)
-
- # Test a g = 1
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=1,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=DSA_KEY_1024.x,
- ).private_key(backend)
-
- # Test a g > p
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=2 ** 1200,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=DSA_KEY_1024.x,
- ).private_key(backend)
-
- # Test x = 0
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=0,
- ).private_key(backend)
-
- # Test x < 0
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=-2,
- ).private_key(backend)
-
- # Test x = q
- with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=2 ** 159,
- ).private_key(backend)
+ numbers = skey.private_numbers()
+ skey_parameters = numbers.public_numbers.parameter_numbers
+ assert numbers.public_numbers.y == pow(
+ skey_parameters.g, numbers.x, skey_parameters.p
+ )
- # Test x > q
+ @pytest.mark.parametrize(
+ ("p", "q", "g"),
+ [
+ (
+ 2 ** 1000,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ ),
+ (
+ 2 ** 2000,
+ DSA_KEY_2048.public_numbers.parameter_numbers.q,
+ DSA_KEY_2048.public_numbers.parameter_numbers.g,
+ ),
+ (
+ 2 ** 3000,
+ DSA_KEY_3072.public_numbers.parameter_numbers.q,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ ),
+ (
+ 2 ** 3100,
+ DSA_KEY_3072.public_numbers.parameter_numbers.q,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ 2 ** 150,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ ),
+ (
+ DSA_KEY_2048.public_numbers.parameter_numbers.p,
+ 2 ** 250,
+ DSA_KEY_2048.public_numbers.parameter_numbers.g
+ ),
+ (
+ DSA_KEY_3072.public_numbers.parameter_numbers.p,
+ 2 ** 260,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 0
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 1
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 2 ** 1200
+ ),
+ ]
+ )
+ def test_invalid_parameters_values(self, p, q, g, backend):
with pytest.raises(ValueError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=2 ** 200,
- ).private_key(backend)
+ dsa.DSAParameterNumbers(p, q, g).parameters(backend)
- # Test y != (g ** x) % p
+ @pytest.mark.parametrize(
+ ("p", "q", "g", "y", "x"),
+ [
+ (
+ 2 ** 1000,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ DSA_KEY_1024.x,
+ ),
+ (
+ 2 ** 2000,
+ DSA_KEY_2048.public_numbers.parameter_numbers.q,
+ DSA_KEY_2048.public_numbers.parameter_numbers.g,
+ DSA_KEY_2048.public_numbers.y,
+ DSA_KEY_2048.x,
+ ),
+ (
+ 2 ** 3000,
+ DSA_KEY_3072.public_numbers.parameter_numbers.q,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ DSA_KEY_3072.public_numbers.y,
+ DSA_KEY_3072.x,
+ ),
+ (
+ 2 ** 3100,
+ DSA_KEY_3072.public_numbers.parameter_numbers.q,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ DSA_KEY_3072.public_numbers.y,
+ DSA_KEY_3072.x,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ 2 ** 150,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ DSA_KEY_1024.x,
+ ),
+ (
+ DSA_KEY_2048.public_numbers.parameter_numbers.p,
+ 2 ** 250,
+ DSA_KEY_2048.public_numbers.parameter_numbers.g,
+ DSA_KEY_2048.public_numbers.y,
+ DSA_KEY_2048.x,
+ ),
+ (
+ DSA_KEY_3072.public_numbers.parameter_numbers.p,
+ 2 ** 260,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ DSA_KEY_3072.public_numbers.y,
+ DSA_KEY_3072.x,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 0,
+ DSA_KEY_1024.public_numbers.y,
+ DSA_KEY_1024.x,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 1,
+ DSA_KEY_1024.public_numbers.y,
+ DSA_KEY_1024.x,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 2 ** 1200,
+ DSA_KEY_1024.public_numbers.y,
+ DSA_KEY_1024.x,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ 0,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ -2,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ 2 ** 159,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ 2 ** 200,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ 2 ** 100,
+ DSA_KEY_1024.x
+ ),
+ ]
+ )
+ def test_invalid_dsa_private_key_arguments(self, p, q, g, y, x, backend):
with pytest.raises(ValueError):
dsa.DSAPrivateNumbers(
public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=2 ** 100
- ),
- x=DSA_KEY_1024.x,
+ parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g),
+ y=y
+ ), x=x
).private_key(backend)
- # Test a non-integer y value
- with pytest.raises(TypeError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=None
- ),
- x=DSA_KEY_1024.x,
- ).private_key(backend)
-
- # Test a non-integer x value
- with pytest.raises(TypeError):
- dsa.DSAPrivateNumbers(
- public_numbers=dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ),
- x=None,
- ).private_key(backend)
-
- def test_invalid_dsa_public_key_arguments(self, backend):
- # Test a p < 1024 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 1000,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ).public_key(backend)
-
- # Test a p < 2048 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 2000,
- q=DSA_KEY_2048.public_numbers.parameter_numbers.q,
- g=DSA_KEY_2048.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_2048.public_numbers.y
- ).public_key(backend)
-
- # Test a p < 3072 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 3000,
- q=DSA_KEY_3072.public_numbers.parameter_numbers.q,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_3072.public_numbers.y
- ).public_key(backend)
-
- # Test a p > 3072 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=2 ** 3100,
- q=DSA_KEY_3072.public_numbers.parameter_numbers.q,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_3072.public_numbers.y
- ).public_key(backend)
-
- # Test a q < 160 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=2 ** 150,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ).public_key(backend)
-
- # Test a q < 256 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_2048.public_numbers.parameter_numbers.p,
- q=2 ** 250,
- g=DSA_KEY_2048.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_2048.public_numbers.y
- ).public_key(backend)
-
- # Test a q > 256 bits in length
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_3072.public_numbers.parameter_numbers.p,
- q=2 ** 260,
- g=DSA_KEY_3072.public_numbers.parameter_numbers.g,
- ),
- y=DSA_KEY_3072.public_numbers.y
- ).public_key(backend)
-
- # Test a g < 1
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=0,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ).public_key(backend)
-
- # Test a g = 1
- with pytest.raises(ValueError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=1,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ).public_key(backend)
-
- # Test a g > p
+ @pytest.mark.parametrize(
+ ("p", "q", "g", "y"),
+ [
+ (
+ 2 ** 1000,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ ),
+ (
+ 2 ** 2000,
+ DSA_KEY_2048.public_numbers.parameter_numbers.q,
+ DSA_KEY_2048.public_numbers.parameter_numbers.g,
+ DSA_KEY_2048.public_numbers.y,
+ ),
+ (
+ 2 ** 3000,
+ DSA_KEY_3072.public_numbers.parameter_numbers.q,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ DSA_KEY_3072.public_numbers.y,
+ ),
+ (
+ 2 ** 3100,
+ DSA_KEY_3072.public_numbers.parameter_numbers.q,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ DSA_KEY_3072.public_numbers.y,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ 2 ** 150,
+ DSA_KEY_1024.public_numbers.parameter_numbers.g,
+ DSA_KEY_1024.public_numbers.y,
+ ),
+ (
+ DSA_KEY_2048.public_numbers.parameter_numbers.p,
+ 2 ** 250,
+ DSA_KEY_2048.public_numbers.parameter_numbers.g,
+ DSA_KEY_2048.public_numbers.y,
+ ),
+ (
+ DSA_KEY_3072.public_numbers.parameter_numbers.p,
+ 2 ** 260,
+ DSA_KEY_3072.public_numbers.parameter_numbers.g,
+ DSA_KEY_3072.public_numbers.y,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 0,
+ DSA_KEY_1024.public_numbers.y,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 1,
+ DSA_KEY_1024.public_numbers.y,
+ ),
+ (
+ DSA_KEY_1024.public_numbers.parameter_numbers.p,
+ DSA_KEY_1024.public_numbers.parameter_numbers.q,
+ 2 ** 1200,
+ DSA_KEY_1024.public_numbers.y,
+ ),
+ ]
+ )
+ def test_invalid_dsa_public_key_arguments(self, p, q, g, y, backend):
with pytest.raises(ValueError):
dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=2 ** 1200,
- ),
- y=DSA_KEY_1024.public_numbers.y
- ).public_key(backend)
-
- # Test a non-integer y value
- with pytest.raises(TypeError):
- dsa.DSAPublicNumbers(
- parameter_numbers=dsa.DSAParameterNumbers(
- p=DSA_KEY_1024.public_numbers.parameter_numbers.p,
- q=DSA_KEY_1024.public_numbers.parameter_numbers.q,
- g=DSA_KEY_1024.public_numbers.parameter_numbers.g,
- ),
- y=None
+ parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g),
+ y=y
).public_key(backend)
@@ -569,14 +371,10 @@ class TestDSAVerification(object):
def test_dsa_verification(self, vector, backend):
digest_algorithm = vector['digest_algorithm'].replace("-", "")
algorithm = self._algorithms_dict[digest_algorithm]
- if (
- not backend.dsa_parameters_supported(
- vector['p'], vector['q'], vector['g']
- ) or not backend.dsa_hash_supported(algorithm)
- ):
- pytest.skip(
- "{0} does not support the provided parameters".format(backend)
- )
+
+ _skip_if_dsa_not_supported(
+ backend, algorithm, vector['p'], vector['q'], vector['g']
+ )
public_key = dsa.DSAPublicNumbers(
parameter_numbers=dsa.DSAParameterNumbers(
@@ -584,25 +382,29 @@ class TestDSAVerification(object):
),
y=vector['y']
).public_key(backend)
- sig = encode_rfc6979_signature(vector['r'], vector['s'])
- verifier = public_key.verifier(sig, algorithm())
- verifier.update(vector['msg'])
+ sig = encode_dss_signature(vector['r'], vector['s'])
+
if vector['result'] == "F":
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(sig, vector['msg'], algorithm())
else:
- verifier.verify()
+ public_key.verify(sig, vector['msg'], algorithm())
def test_dsa_verify_invalid_asn1(self, backend):
public_key = DSA_KEY_1024.public_numbers.public_key(backend)
- verifier = public_key.verifier(b'fakesig', hashes.SHA1())
- verifier.update(b'fakesig')
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(b'fakesig', b'fakemsg', hashes.SHA1())
+
+ def test_signature_not_bytes(self, backend):
+ public_key = DSA_KEY_1024.public_numbers.public_key(backend)
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ public_key.verifier(1234, hashes.SHA1())
def test_use_after_finalize(self, backend):
public_key = DSA_KEY_1024.public_numbers.public_key(backend)
- verifier = public_key.verifier(b'fakesig', hashes.SHA1())
+ with pytest.warns(CryptographyDeprecationWarning):
+ verifier = public_key.verifier(b'fakesig', hashes.SHA1())
verifier.update(b'irrelevant')
with pytest.raises(InvalidSignature):
verifier.verify()
@@ -611,6 +413,50 @@ class TestDSAVerification(object):
with pytest.raises(AlreadyFinalized):
verifier.update(b"more data")
+ def test_verify(self, backend):
+ message = b"one little message"
+ algorithm = hashes.SHA1()
+ private_key = DSA_KEY_1024.private_key(backend)
+ signature = private_key.sign(message, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, algorithm)
+
+ def test_prehashed_verify(self, backend):
+ private_key = DSA_KEY_1024.private_key(backend)
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ digest = h.finalize()
+ prehashed_alg = Prehashed(hashes.SHA1())
+ signature = private_key.sign(message, hashes.SHA1())
+ public_key = private_key.public_key()
+ public_key.verify(signature, digest, prehashed_alg)
+
+ def test_prehashed_digest_mismatch(self, backend):
+ private_key = DSA_KEY_1024.private_key(backend)
+ public_key = private_key.public_key()
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ digest = h.finalize()
+ prehashed_alg = Prehashed(hashes.SHA224())
+ with pytest.raises(ValueError):
+ public_key.verify(b"\x00" * 128, digest, prehashed_alg)
+
+ def test_prehashed_unsupported_in_signer_ctx(self, backend):
+ private_key = DSA_KEY_1024.private_key(backend)
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ private_key.signer(Prehashed(hashes.SHA1()))
+
+ def test_prehashed_unsupported_in_verifier_ctx(self, backend):
+ public_key = DSA_KEY_1024.private_key(backend).public_key()
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ public_key.verifier(
+ b"0" * 64, Prehashed(hashes.SHA1())
+ )
+
@pytest.mark.requires_backend_interface(interface=DSABackend)
class TestDSASignature(object):
@@ -632,14 +478,10 @@ class TestDSASignature(object):
def test_dsa_signing(self, vector, backend):
digest_algorithm = vector['digest_algorithm'].replace("-", "")
algorithm = self._algorithms_dict[digest_algorithm]
- if (
- not backend.dsa_parameters_supported(
- vector['p'], vector['q'], vector['g']
- ) or not backend.dsa_hash_supported(algorithm)
- ):
- pytest.skip(
- "{0} does not support the provided parameters".format(backend)
- )
+
+ _skip_if_dsa_not_supported(
+ backend, algorithm, vector['p'], vector['q'], vector['g']
+ )
private_key = dsa.DSAPrivateNumbers(
public_numbers=dsa.DSAPublicNumbers(
@@ -650,19 +492,15 @@ class TestDSASignature(object):
),
x=vector['x']
).private_key(backend)
- signer = private_key.signer(algorithm())
- signer.update(vector['msg'])
- signature = signer.finalize()
+ signature = private_key.sign(vector['msg'], algorithm())
assert signature
- public_key = private_key.public_key()
- verifier = public_key.verifier(signature, algorithm())
- verifier.update(vector['msg'])
- verifier.verify()
+ private_key.public_key().verify(signature, vector['msg'], algorithm())
def test_use_after_finalize(self, backend):
private_key = DSA_KEY_1024.private_key(backend)
- signer = private_key.signer(hashes.SHA1())
+ with pytest.warns(CryptographyDeprecationWarning):
+ signer = private_key.signer(hashes.SHA1())
signer.update(b"data")
signer.finalize()
with pytest.raises(AlreadyFinalized):
@@ -670,6 +508,35 @@ class TestDSASignature(object):
with pytest.raises(AlreadyFinalized):
signer.update(b"more data")
+ def test_sign(self, backend):
+ private_key = DSA_KEY_1024.private_key(backend)
+ message = b"one little message"
+ algorithm = hashes.SHA1()
+ signature = private_key.sign(message, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, algorithm)
+
+ def test_prehashed_sign(self, backend):
+ private_key = DSA_KEY_1024.private_key(backend)
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ digest = h.finalize()
+ prehashed_alg = Prehashed(hashes.SHA1())
+ signature = private_key.sign(digest, prehashed_alg)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, hashes.SHA1())
+
+ def test_prehashed_digest_mismatch(self, backend):
+ private_key = DSA_KEY_1024.private_key(backend)
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ digest = h.finalize()
+ prehashed_alg = Prehashed(hashes.SHA224())
+ with pytest.raises(ValueError):
+ private_key.sign(digest, prehashed_alg)
+
class TestDSANumbers(object):
def test_dsa_parameter_numbers(self):
@@ -730,6 +597,21 @@ class TestDSANumbers(object):
with pytest.raises(TypeError):
dsa.DSAPrivateNumbers(x=None, public_numbers=public_numbers)
+ def test_repr(self):
+ parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
+ assert (
+ repr(parameter_numbers) == "<DSAParameterNumbers(p=1, q=2, g=3)>"
+ )
+
+ public_numbers = dsa.DSAPublicNumbers(
+ y=4,
+ parameter_numbers=parameter_numbers
+ )
+ assert repr(public_numbers) == (
+ "<DSAPublicNumbers(y=4, parameter_numbers=<DSAParameterNumbers(p=1"
+ ", q=2, g=3)>)>"
+ )
+
class TestDSANumberEquality(object):
def test_parameter_numbers_eq(self):
@@ -819,7 +701,6 @@ class TestDSASerialization(object):
lambda pemfile: pemfile.read().encode()
)
key = serialization.load_pem_private_key(key_bytes, None, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
serialization.Encoding.PEM,
fmt,
@@ -833,6 +714,20 @@ class TestDSASerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8),
+ ]
+ )
+ def test_private_bytes_rejects_invalid(self, encoding, fmt, backend):
+ key = DSA_KEY_1024.private_key(backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("fmt", "password"),
[
[serialization.PrivateFormat.PKCS8, b"s"],
@@ -847,7 +742,6 @@ class TestDSASerialization(object):
lambda pemfile: pemfile.read().encode()
)
key = serialization.load_pem_private_key(key_bytes, None, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
serialization.Encoding.DER,
fmt,
@@ -888,7 +782,6 @@ class TestDSASerialization(object):
def test_private_bytes_unencrypted(self, backend, encoding, fmt,
loader_func):
key = DSA_KEY_1024.private_key(backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
encoding, fmt, serialization.NoEncryption()
)
@@ -934,7 +827,6 @@ class TestDSASerialization(object):
def test_private_bytes_traditional_der_encrypted_invalid(self, backend):
key = DSA_KEY_1024.private_key(backend)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.private_bytes(
serialization.Encoding.DER,
@@ -949,7 +841,6 @@ class TestDSASerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
"notencoding",
@@ -964,7 +855,6 @@ class TestDSASerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
serialization.Encoding.PEM,
@@ -979,7 +869,6 @@ class TestDSASerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
serialization.Encoding.PEM,
@@ -994,12 +883,11 @@ class TestDSASerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
- DummyKeyEncryption()
+ DummyKeySerializationEncryption()
)
@@ -1030,15 +918,36 @@ class TestDSAPEMPublicKeySerialization(object):
key_path, lambda pemfile: pemfile.read(), mode="rb"
)
key = loader_func(key_bytes, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.public_bytes(
encoding, serialization.PublicFormat.SubjectPublicKeyInfo,
)
assert serialized == key_bytes
+ def test_public_bytes_openssh(self, backend):
+ key_bytes = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pub.pem"),
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ key = serialization.load_pem_public_key(key_bytes, backend)
+
+ ssh_bytes = key.public_bytes(
+ serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH
+ )
+ assert ssh_bytes == (
+ b"ssh-dss AAAAB3NzaC1kc3MAAACBAKoJMMwUWCUiHK/6KKwolBlqJ4M95ewhJweR"
+ b"aJQgd3Si57I4sNNvGySZosJYUIPrAUMpJEGNhn+qIS3RBx1NzrJ4J5StOTzAik1K"
+ b"2n9o1ug5pfzTS05ALYLLioy0D+wxkRv5vTYLA0yqy0xelHmSVzyekAmcGw8FlAyr"
+ b"5dLeSaFnAAAAFQCtwOhps28KwBOmgf301ImdaYIEUQAAAIEAjGtFia+lOk0QSL/D"
+ b"RtHzhsp1UhzPct2qJRKGiA7hMgH/SIkLv8M9ebrK7HHnp3hQe9XxpmQi45QVvgPn"
+ b"EUG6Mk9bkxMZKRgsiKn6QGKDYGbOvnS1xmkMfRARBsJAq369VOTjMB/Qhs5q2ski"
+ b"+ycTorCIfLoTubxozlz/8kHNMkYAAACAKyYOqX3GoSrpMsZA5989j/BKigWgMk+N"
+ b"Xxsj8V+hcP8/QgYRJO/yWGyxG0moLc3BuQ/GqE+xAQnLZ9tdLalxrq8Xvl43KEVj"
+ b"5MZNnl/ISAJYsxnw3inVTYNQcNnih5FNd9+BSR9EI7YtqYTrP0XrKin86l2uUlrG"
+ b"q2vM4Ev99bY="
+ )
+
def test_public_bytes_invalid_encoding(self, backend):
key = DSA_KEY_2048.private_key(backend).public_key()
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.public_bytes(
"notencoding",
@@ -1047,14 +956,39 @@ class TestDSAPEMPublicKeySerialization(object):
def test_public_bytes_invalid_format(self, backend):
key = DSA_KEY_2048.private_key(backend).public_key()
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.public_bytes(serialization.Encoding.PEM, "invalidformat")
def test_public_bytes_pkcs1_unsupported(self, backend):
key = DSA_KEY_2048.private_key(backend).public_key()
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.public_bytes(
serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
)
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ ),
+ (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1),
+ ] + list(itertools.product(
+ [
+ serialization.Encoding.Raw,
+ serialization.Encoding.X962,
+ serialization.Encoding.PEM,
+ serialization.Encoding.DER
+ ],
+ [
+ serialization.PublicFormat.Raw,
+ serialization.PublicFormat.UncompressedPoint,
+ serialization.PublicFormat.CompressedPoint
+ ]
+ ))
+ )
+ def test_public_bytes_rejects_invalid(self, encoding, fmt, backend):
+ key = DSA_KEY_2048.private_key(backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index cc185145..987c0ff0 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -4,24 +4,30 @@
from __future__ import absolute_import, division, print_function
+import binascii
import itertools
import os
+from binascii import hexlify
import pytest
-from cryptography import exceptions, utils
+from cryptography import exceptions, utils, x509
from cryptography.hazmat.backends.interfaces import (
EllipticCurveBackend, PEMSerializationBackend
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
- encode_rfc6979_signature
+ Prehashed, encode_dss_signature
)
+from cryptography.utils import CryptographyDeprecationWarning
+from .fixtures_ec import EC_KEY_SECP384R1
+from ...doubles import DummyKeySerializationEncryption
from ...utils import (
load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors,
- load_vectors_from_file, raises_unsupported_algorithm
+ load_kasvs_ecdh_vectors, load_nist_vectors, load_vectors_from_file,
+ raises_unsupported_algorithm
)
_HASH_TYPES = {
@@ -33,25 +39,13 @@ _HASH_TYPES = {
}
-def _skip_if_no_serialization(key, backend):
- if not isinstance(
- key, (
- ec.EllipticCurvePrivateKeyWithSerialization,
- ec.EllipticCurvePublicKeyWithSerialization
- )
- ):
- pytest.skip(
- "{0} does not support EC key serialization".format(backend)
- )
-
-
def _skip_ecdsa_vector(backend, curve_type, hash_type):
if not backend.elliptic_curve_signature_algorithm_supported(
ec.ECDSA(hash_type()),
curve_type()
):
pytest.skip(
- "ECDSA not supported with this hash {0} and curve {1}".format(
+ "ECDSA not supported with this hash {} and curve {}".format(
hash_type().name, curve_type().name
)
)
@@ -60,12 +54,29 @@ def _skip_ecdsa_vector(backend, curve_type, hash_type):
def _skip_curve_unsupported(backend, curve):
if not backend.elliptic_curve_supported(curve):
pytest.skip(
- "Curve {0} is not supported by this backend {1}".format(
+ "Curve {} is not supported by this backend {}".format(
curve.name, backend
)
)
+def _skip_exchange_algorithm_unsupported(backend, algorithm, curve):
+ if not backend.elliptic_curve_exchange_algorithm_supported(
+ algorithm, curve
+ ):
+ pytest.skip(
+ "Exchange with {} curve is not supported by {}".format(
+ curve.name, backend
+ )
+ )
+
+
+def test_get_curve_for_oid():
+ assert ec.get_curve_for_oid(ec.EllipticCurveOID.SECP256R1) == ec.SECP256R1
+ with pytest.raises(LookupError):
+ ec.get_curve_for_oid(x509.ObjectIdentifier("1.1.1.1"))
+
+
@utils.register_interface(ec.EllipticCurve)
class DummyCurve(object):
name = "dummy-curve"
@@ -77,20 +88,51 @@ class DummySignatureAlgorithm(object):
algorithm = None
-@utils.register_interface(serialization.KeySerializationEncryption)
-class DummyKeyEncryption(object):
- pass
-
-
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
def test_skip_curve_unsupported(backend):
with pytest.raises(pytest.skip.Exception):
_skip_curve_unsupported(backend, DummyCurve())
-def test_skip_no_serialization():
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_skip_exchange_algorithm_unsupported(backend):
+ with pytest.raises(pytest.skip.Exception):
+ _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), DummyCurve())
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_skip_ecdsa_vector(backend):
with pytest.raises(pytest.skip.Exception):
- _skip_if_no_serialization("fakebackend", "fakekey")
+ _skip_ecdsa_vector(backend, DummyCurve, hashes.SHA256)
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_derive_private_key_success(backend):
+ curve = ec.SECP256K1()
+ _skip_curve_unsupported(backend, curve)
+
+ private_numbers = ec.generate_private_key(curve, backend).private_numbers()
+
+ derived_key = ec.derive_private_key(
+ private_numbers.private_value, curve, backend
+ )
+
+ assert private_numbers == derived_key.private_numbers()
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_derive_private_key_errors(backend):
+ curve = ec.SECP256K1()
+ _skip_curve_unsupported(backend, curve)
+
+ with pytest.raises(TypeError):
+ ec.derive_private_key('one', curve, backend)
+
+ with pytest.raises(TypeError):
+ ec.derive_private_key(10, 'five', backend)
+
+ with pytest.raises(ValueError):
+ ec.derive_private_key(-7, curve, backend)
def test_ec_numbers():
@@ -106,43 +148,135 @@ def test_ec_numbers():
assert numbers.public_numbers.y == 3
assert isinstance(numbers.public_numbers.curve, DummyCurve)
+
+@pytest.mark.parametrize(
+ ("private_value", "x", "y", "curve"),
+ [
+ (None, 2, 3, DummyCurve()),
+ (1, None, 3, DummyCurve()),
+ (1, 2, None, DummyCurve()),
+ (1, 2, 3, None),
+ ]
+)
+def test_invalid_ec_numbers_args(private_value, x, y, curve):
with pytest.raises(TypeError):
ec.EllipticCurvePrivateNumbers(
- None,
- ec.EllipticCurvePublicNumbers(
- 2, 3, DummyCurve()
- )
+ private_value, ec.EllipticCurvePublicNumbers(x, y, curve)
)
+
+def test_invalid_private_numbers_public_numbers():
with pytest.raises(TypeError):
- ec.EllipticCurvePrivateNumbers(
- 1,
- ec.EllipticCurvePublicNumbers(
- None, 3, DummyCurve()
- )
+ ec.EllipticCurvePrivateNumbers(1, None)
+
+
+def test_encode_point():
+ # secp256r1 point
+ x = int(
+ '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec',
+ 16
+ )
+ y = int(
+ '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e',
+ 16
+ )
+ pn = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1())
+ with pytest.warns(utils.PersistentlyDeprecated2019):
+ data = pn.encode_point()
+ assert data == binascii.unhexlify(
+ "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae"
+ "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e"
+ )
+
+
+def test_from_encoded_point():
+ # secp256r1 point
+ data = binascii.unhexlify(
+ "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae"
+ "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e"
+ )
+ with pytest.warns(CryptographyDeprecationWarning):
+ pn = ec.EllipticCurvePublicNumbers.from_encoded_point(
+ ec.SECP256R1(), data
)
+ assert pn.x == int(
+ '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec',
+ 16
+ )
+ assert pn.y == int(
+ '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e',
+ 16
+ )
- with pytest.raises(TypeError):
- ec.EllipticCurvePrivateNumbers(
- 1,
- ec.EllipticCurvePublicNumbers(
- 2, None, DummyCurve()
+
+def test_from_encoded_point_invalid_length():
+ bad_data = binascii.unhexlify(
+ "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae"
+ "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460"
+ )
+ with pytest.raises(ValueError):
+ with pytest.warns(CryptographyDeprecationWarning):
+ ec.EllipticCurvePublicNumbers.from_encoded_point(
+ ec.SECP384R1(), bad_data
)
- )
- with pytest.raises(TypeError):
- ec.EllipticCurvePrivateNumbers(
- 1,
- ec.EllipticCurvePublicNumbers(
- 2, 3, None
+
+def test_from_encoded_point_unsupported_point_no_backend():
+ # set to point type 2.
+ unsupported_type = binascii.unhexlify(
+ "02233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22a"
+ )
+ with pytest.raises(ValueError):
+ with pytest.warns(CryptographyDeprecationWarning):
+ ec.EllipticCurvePublicNumbers.from_encoded_point(
+ ec.SECP256R1(), unsupported_type
)
- )
+
+def test_from_encoded_point_not_a_curve():
with pytest.raises(TypeError):
- ec.EllipticCurvePrivateNumbers(
- 1,
- None
- )
+ with pytest.warns(CryptographyDeprecationWarning):
+ ec.EllipticCurvePublicNumbers.from_encoded_point(
+ "notacurve", b"\x04data"
+ )
+
+
+def test_ec_public_numbers_repr():
+ pn = ec.EllipticCurvePublicNumbers(2, 3, ec.SECP256R1())
+ assert repr(pn) == "<EllipticCurvePublicNumbers(curve=secp256r1, x=2, y=3>"
+
+
+def test_ec_public_numbers_hash():
+ pn1 = ec.EllipticCurvePublicNumbers(2, 3, ec.SECP256R1())
+ pn2 = ec.EllipticCurvePublicNumbers(2, 3, ec.SECP256R1())
+ pn3 = ec.EllipticCurvePublicNumbers(1, 3, ec.SECP256R1())
+
+ assert hash(pn1) == hash(pn2)
+ assert hash(pn1) != hash(pn3)
+
+
+def test_ec_private_numbers_hash():
+ numbers1 = ec.EllipticCurvePrivateNumbers(
+ 1, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve())
+ )
+ numbers2 = ec.EllipticCurvePrivateNumbers(
+ 1, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve())
+ )
+ numbers3 = ec.EllipticCurvePrivateNumbers(
+ 2, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve())
+ )
+
+ assert hash(numbers1) == hash(numbers2)
+ assert hash(numbers1) != hash(numbers3)
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_ec_key_key_size(backend):
+ curve = ec.SECP256R1()
+ _skip_curve_unsupported(backend, curve)
+ key = ec.generate_private_key(curve, backend)
+ assert key.key_size == 256
+ assert key.public_key().key_size == 256
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
@@ -173,12 +307,11 @@ class TestECWithNumbers(object):
).private_key(backend)
assert key
- if isinstance(key, ec.EllipticCurvePrivateKeyWithSerialization):
- priv_num = key.private_numbers()
- assert priv_num.private_value == vector['d']
- assert priv_num.public_numbers.x == vector['x']
- assert priv_num.public_numbers.y == vector['y']
- assert curve_type().name == priv_num.public_numbers.curve.name
+ priv_num = key.private_numbers()
+ assert priv_num.private_value == vector['d']
+ assert priv_num.public_numbers.x == vector['x']
+ assert priv_num.public_numbers.y == vector['y']
+ assert curve_type().name == priv_num.public_numbers.curve.name
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
@@ -212,11 +345,13 @@ class TestECDSAVectors(object):
pkey = key.public_key()
assert pkey
- signer = key.signer(ec.ECDSA(hash_type()))
+ with pytest.warns(CryptographyDeprecationWarning):
+ signer = key.signer(ec.ECDSA(hash_type()))
signer.update(b"YELLOW SUBMARINE")
signature = signer.finalize()
- verifier = pkey.verifier(signature, ec.ECDSA(hash_type()))
+ with pytest.warns(CryptographyDeprecationWarning):
+ verifier = pkey.verifier(signature, ec.ECDSA(hash_type()))
verifier.update(b"YELLOW SUBMARINE")
verifier.verify()
@@ -254,14 +389,26 @@ class TestECDSAVectors(object):
with raises_unsupported_algorithm(
exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
+ ), pytest.warns(CryptographyDeprecationWarning):
key.signer(DummySignatureAlgorithm())
with raises_unsupported_algorithm(
exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
):
+ key.sign(b"somedata", DummySignatureAlgorithm())
+
+ with raises_unsupported_algorithm(
+ exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ), pytest.warns(CryptographyDeprecationWarning):
key.public_key().verifier(b"", DummySignatureAlgorithm())
+ with raises_unsupported_algorithm(
+ exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ key.public_key().verify(
+ b"signature", b"data", DummySignatureAlgorithm()
+ )
+
assert backend.elliptic_curve_signature_algorithm_supported(
DummySignatureAlgorithm(),
ec.SECP192R1()
@@ -303,6 +450,35 @@ class TestECDSAVectors(object):
with pytest.raises(ValueError):
numbers.private_key(backend)
+ def test_load_invalid_public_ec_key_from_numbers(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP521R1())
+
+ # Bad X coordinate
+ numbers = ec.EllipticCurvePublicNumbers(
+ int("000003647356b91f8ace114c7247ecf4f4a622553fc025e04a178f179ef27"
+ "9090c184af678a4c78f635483bdd8aa544851c6ef291c1f0d6a241ebfd145"
+ "77d1d30d9903ce", 16),
+ int("000001499bc7e079322ea0fcfbd6b40103fa6a1536c2257b182db0df4b369"
+ "6ec643adf100eb4f2025d1b873f82e5a475d6e4400ba777090eeb4563a115"
+ "09e4c87319dc26", 16),
+ ec.SECP521R1()
+ )
+ with pytest.raises(ValueError):
+ numbers.public_key(backend)
+
+ # Bad Y coordinate
+ numbers = ec.EllipticCurvePublicNumbers(
+ int("0000019aadc221cc0525118ab6d5aa1f64720603de0be128cbfea0b381ad8"
+ "02a2facc6370bb58cf88b3f0c692bc654ee19d6cad198f10d4b681b396f20"
+ "d2e40603fa945b", 16),
+ int("0000025da392803a320717a08d4cb3dea932039badff363b71bdb8064e726"
+ "6c7f4f4b748d4d425347fc33e3885d34b750fa7fcd5691f4d90c89522ce33"
+ "feff5db10088a5", 16),
+ ec.SECP521R1()
+ )
+ with pytest.raises(ValueError):
+ numbers.public_key(backend)
+
@pytest.mark.parametrize(
"vector",
itertools.chain(
@@ -330,14 +506,13 @@ class TestECDSAVectors(object):
curve_type()
).public_key(backend)
- signature = encode_rfc6979_signature(vector['r'], vector['s'])
+ signature = encode_dss_signature(vector['r'], vector['s'])
- verifier = key.verifier(
+ key.verify(
signature,
+ vector['message'],
ec.ECDSA(hash_type())
)
- verifier.update(vector['message'])
- assert verifier.verify()
@pytest.mark.parametrize(
"vector",
@@ -359,19 +534,107 @@ class TestECDSAVectors(object):
curve_type()
).public_key(backend)
- signature = encode_rfc6979_signature(vector['r'], vector['s'])
-
- verifier = key.verifier(
- signature,
- ec.ECDSA(hash_type())
- )
- verifier.update(vector['message'])
+ signature = encode_dss_signature(vector['r'], vector['s'])
if vector["fail"] is True:
with pytest.raises(exceptions.InvalidSignature):
- verifier.verify()
+ key.verify(
+ signature,
+ vector['message'],
+ ec.ECDSA(hash_type())
+ )
else:
- verifier.verify()
+ key.verify(
+ signature,
+ vector['message'],
+ ec.ECDSA(hash_type())
+ )
+
+ def test_sign(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ message = b"one little message"
+ algorithm = ec.ECDSA(hashes.SHA1())
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ signature = private_key.sign(message, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, algorithm)
+
+ def test_sign_prehashed(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ data = h.finalize()
+ algorithm = ec.ECDSA(Prehashed(hashes.SHA1()))
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ signature = private_key.sign(data, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, ec.ECDSA(hashes.SHA1()))
+
+ def test_sign_prehashed_digest_mismatch(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ data = h.finalize()
+ algorithm = ec.ECDSA(Prehashed(hashes.SHA256()))
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ with pytest.raises(ValueError):
+ private_key.sign(data, algorithm)
+
+ def test_verify(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ message = b"one little message"
+ algorithm = ec.ECDSA(hashes.SHA1())
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ signature = private_key.sign(message, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, algorithm)
+
+ def test_verify_prehashed(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ message = b"one little message"
+ algorithm = ec.ECDSA(hashes.SHA1())
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ signature = private_key.sign(message, algorithm)
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ data = h.finalize()
+ public_key = private_key.public_key()
+ public_key.verify(
+ signature, data, ec.ECDSA(Prehashed(hashes.SHA1()))
+ )
+
+ def test_verify_prehashed_digest_mismatch(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ message = b"one little message"
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ data = h.finalize()
+ public_key = private_key.public_key()
+ with pytest.raises(ValueError):
+ public_key.verify(
+ b"\x00" * 32, data, ec.ECDSA(Prehashed(hashes.SHA256()))
+ )
+
+ def test_prehashed_unsupported_in_signer_ctx(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ private_key.signer(ec.ECDSA(Prehashed(hashes.SHA1())))
+
+ def test_prehashed_unsupported_in_verifier_ctx(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+ public_key = private_key.public_key()
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ public_key.verifier(
+ b"0" * 64,
+ ec.ECDSA(Prehashed(hashes.SHA1()))
+ )
class TestECNumbersEquality(object):
@@ -437,7 +700,6 @@ class TestECSerialization(object):
lambda pemfile: pemfile.read().encode()
)
key = serialization.load_pem_private_key(key_bytes, None, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
serialization.Encoding.PEM,
fmt,
@@ -451,6 +713,21 @@ class TestECSerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8),
+ ]
+ )
+ def test_private_bytes_rejects_invalid(self, encoding, fmt, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = ec.generate_private_key(ec.SECP256R1(), backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("fmt", "password"),
[
[serialization.PrivateFormat.PKCS8, b"s"],
@@ -467,7 +744,6 @@ class TestECSerialization(object):
lambda pemfile: pemfile.read().encode()
)
key = serialization.load_pem_private_key(key_bytes, None, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
serialization.Encoding.DER,
fmt,
@@ -514,7 +790,6 @@ class TestECSerialization(object):
lambda pemfile: pemfile.read().encode()
)
key = serialization.load_pem_private_key(key_bytes, None, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
encoding, fmt, serialization.NoEncryption()
)
@@ -566,7 +841,6 @@ class TestECSerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.private_bytes(
serialization.Encoding.DER,
@@ -583,7 +857,6 @@ class TestECSerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
"notencoding",
@@ -600,7 +873,6 @@ class TestECSerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
serialization.Encoding.PEM,
@@ -617,7 +889,6 @@ class TestECSerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
serialization.Encoding.PEM,
@@ -634,12 +905,11 @@ class TestECSerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
- DummyKeyEncryption()
+ DummyKeySerializationEncryption()
)
def test_public_bytes_from_derived_public_key(self, backend):
@@ -651,7 +921,6 @@ class TestECSerialization(object):
pemfile.read().encode(), None, backend
)
)
- _skip_if_no_serialization(key, backend)
public = key.public_key()
pem = public.public_bytes(
serialization.Encoding.PEM,
@@ -689,12 +958,39 @@ class TestEllipticCurvePEMPublicKeySerialization(object):
key_path, lambda pemfile: pemfile.read(), mode="rb"
)
key = loader_func(key_bytes, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.public_bytes(
encoding, serialization.PublicFormat.SubjectPublicKeyInfo,
)
assert serialized == key_bytes
+ def test_public_bytes_openssh(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP192R1())
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PEM_Serialization", "ec_public_key.pem"
+ ),
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ key = serialization.load_pem_public_key(key_bytes, backend)
+
+ ssh_bytes = key.public_bytes(
+ serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH
+ )
+ assert ssh_bytes == (
+ b"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy"
+ b"NTYAAABBBCS8827s9rUZyxZTi/um01+oIlWrwLHOjQxRU9CDAndom00zVAw5BRrI"
+ b"KtHB+SWD4P+sVJTARSq1mHt8kOIWrPc="
+ )
+
+ key = ec.generate_private_key(ec.SECP192R1(), backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.OpenSSH,
+ serialization.PublicFormat.OpenSSH
+ )
+
def test_public_bytes_invalid_encoding(self, backend):
_skip_curve_unsupported(backend, ec.SECP256R1())
key = load_vectors_from_file(
@@ -705,13 +1001,40 @@ class TestEllipticCurvePEMPublicKeySerialization(object):
pemfile.read().encode(), backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.public_bytes(
"notencoding",
serialization.PublicFormat.SubjectPublicKeyInfo
)
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ list(itertools.product(
+ [
+ serialization.Encoding.Raw,
+ serialization.Encoding.X962,
+ serialization.Encoding.PEM,
+ serialization.Encoding.DER
+ ],
+ [
+ serialization.PublicFormat.Raw,
+ ]
+ )) + list(itertools.product(
+ [serialization.Encoding.Raw],
+ [
+ serialization.PublicFormat.SubjectPublicKeyInfo,
+ serialization.PublicFormat.PKCS1,
+ serialization.PublicFormat.UncompressedPoint,
+ serialization.PublicFormat.CompressedPoint,
+ ]
+ ))
+ )
+ def test_public_bytes_rejects_invalid(self, encoding, fmt, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = ec.generate_private_key(ec.SECP256R1(), backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
+
def test_public_bytes_invalid_format(self, backend):
_skip_curve_unsupported(backend, ec.SECP256R1())
key = load_vectors_from_file(
@@ -722,7 +1045,6 @@ class TestEllipticCurvePEMPublicKeySerialization(object):
pemfile.read().encode(), backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.public_bytes(serialization.Encoding.PEM, "invalidformat")
@@ -736,8 +1058,250 @@ class TestEllipticCurvePEMPublicKeySerialization(object):
pemfile.read().encode(), backend
)
)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.public_bytes(
serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
)
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "EC", "compressed_points.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_from_encoded_point_compressed(self, vector, backend):
+ curve = {
+ b"SECP256R1": ec.SECP256R1(),
+ b"SECP256K1": ec.SECP256K1(),
+ }[vector["curve"]]
+ _skip_curve_unsupported(backend, curve)
+ point = binascii.unhexlify(vector["point"])
+ pn = ec.EllipticCurvePublicKey.from_encoded_point(curve, point)
+ public_num = pn.public_numbers()
+ assert public_num.x == int(vector["x"], 16)
+ assert public_num.y == int(vector["y"], 16)
+
+ def test_from_encoded_point_notoncurve(self):
+ uncompressed_point = binascii.unhexlify(
+ "047399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac"
+ "686699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f"
+ "6e"
+ )
+ with pytest.raises(ValueError):
+ ec.EllipticCurvePublicKey.from_encoded_point(
+ ec.SECP256R1(), uncompressed_point
+ )
+
+ def test_from_encoded_point_uncompressed(self):
+ uncompressed_point = binascii.unhexlify(
+ "047399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac"
+ "686699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f"
+ "6d"
+ )
+ pn = ec.EllipticCurvePublicKey.from_encoded_point(
+ ec.SECP256R1(), uncompressed_point
+ )
+ assert pn.public_numbers().x == int(
+ '7399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac68',
+ 16
+ )
+ assert pn.public_numbers().y == int(
+ '6699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f6d',
+ 16
+ )
+
+ def test_from_encoded_point_invalid_length(self):
+ bad_data = binascii.unhexlify(
+ "047399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac"
+ "686699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f"
+ "6d"
+ )
+ with pytest.raises(ValueError):
+ ec.EllipticCurvePublicKey.from_encoded_point(
+ ec.SECP384R1(), bad_data
+ )
+
+ def test_from_encoded_point_empty_byte_string(self):
+ with pytest.raises(ValueError):
+ ec.EllipticCurvePublicKey.from_encoded_point(
+ ec.SECP384R1(), b""
+ )
+
+ def test_from_encoded_point_not_a_curve(self):
+ with pytest.raises(TypeError):
+ ec.EllipticCurvePublicKey.from_encoded_point(
+ "notacurve", b"\x04data"
+ )
+
+ def test_from_encoded_point_unsupported_encoding(self):
+ unsupported_type = binascii.unhexlify(
+ "057399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac6"
+ "8"
+ )
+ with pytest.raises(ValueError):
+ ec.EllipticCurvePublicKey.from_encoded_point(
+ ec.SECP256R1(), unsupported_type
+ )
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "EC", "compressed_points.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_serialize_point(self, vector, backend):
+ curve = {
+ b"SECP256R1": ec.SECP256R1(),
+ b"SECP256K1": ec.SECP256K1(),
+ }[vector["curve"]]
+ _skip_curve_unsupported(backend, curve)
+ point = binascii.unhexlify(vector["point"])
+ key = ec.EllipticCurvePublicKey.from_encoded_point(curve, point)
+ key2 = ec.EllipticCurvePublicKey.from_encoded_point(
+ curve,
+ key.public_bytes(
+ serialization.Encoding.X962,
+ serialization.PublicFormat.UncompressedPoint
+ )
+ )
+ assert key.public_bytes(
+ serialization.Encoding.X962,
+ serialization.PublicFormat.CompressedPoint
+ ) == point
+ assert key2.public_bytes(
+ serialization.Encoding.X962,
+ serialization.PublicFormat.CompressedPoint
+ ) == point
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+class TestECDSAVerification(object):
+ def test_signature_not_bytes(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = ec.generate_private_key(ec.SECP256R1(), backend)
+ public_key = key.public_key()
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ public_key.verifier(1234, ec.ECDSA(hashes.SHA256()))
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+class TestECDH(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "ECDH",
+ "KASValidityTest_ECCStaticUnified_NOKC_ZZOnly_init.fax"),
+ load_kasvs_ecdh_vectors
+ )
+ )
+ def test_key_exchange_with_vectors(self, backend, vector):
+ _skip_exchange_algorithm_unsupported(
+ backend, ec.ECDH(), ec._CURVE_TYPES[vector['curve']]
+ )
+
+ key_numbers = vector['IUT']
+ private_numbers = ec.EllipticCurvePrivateNumbers(
+ key_numbers['d'],
+ ec.EllipticCurvePublicNumbers(
+ key_numbers['x'],
+ key_numbers['y'],
+ ec._CURVE_TYPES[vector['curve']]()
+ )
+ )
+ # Errno 5-7 indicates a bad public or private key, this doesn't test
+ # the ECDH code at all
+ if vector['fail'] and vector['errno'] in [5, 6, 7]:
+ with pytest.raises(ValueError):
+ private_numbers.private_key(backend)
+ return
+ else:
+ private_key = private_numbers.private_key(backend)
+
+ peer_numbers = vector['CAVS']
+ public_numbers = ec.EllipticCurvePublicNumbers(
+ peer_numbers['x'],
+ peer_numbers['y'],
+ ec._CURVE_TYPES[vector['curve']]()
+ )
+ # Errno 1 and 2 indicates a bad public key, this doesn't test the ECDH
+ # code at all
+ if vector['fail'] and vector['errno'] in [1, 2]:
+ with pytest.raises(ValueError):
+ public_numbers.public_key(backend)
+ return
+ else:
+ peer_pubkey = public_numbers.public_key(backend)
+
+ z = private_key.exchange(ec.ECDH(), peer_pubkey)
+ z = int(hexlify(z).decode('ascii'), 16)
+ # At this point fail indicates that one of the underlying keys was
+ # changed. This results in a non-matching derived key.
+ if vector['fail']:
+ # Errno 8 indicates Z should be changed.
+ assert vector['errno'] == 8
+ assert z != vector['Z']
+ else:
+ assert z == vector['Z']
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "ECDH", "brainpool.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_brainpool_kex(self, backend, vector):
+ curve = ec._CURVE_TYPES[vector['curve'].decode('ascii')]
+ _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), curve)
+ key = ec.EllipticCurvePrivateNumbers(
+ int(vector['da'], 16),
+ ec.EllipticCurvePublicNumbers(
+ int(vector['x_qa'], 16), int(vector['y_qa'], 16), curve()
+ )
+ ).private_key(backend)
+ peer = ec.EllipticCurvePrivateNumbers(
+ int(vector['db'], 16),
+ ec.EllipticCurvePublicNumbers(
+ int(vector['x_qb'], 16), int(vector['y_qb'], 16), curve()
+ )
+ ).private_key(backend)
+ shared_secret = key.exchange(ec.ECDH(), peer.public_key())
+ assert shared_secret == binascii.unhexlify(vector["x_z"])
+ shared_secret_2 = peer.exchange(ec.ECDH(), key.public_key())
+ assert shared_secret_2 == binascii.unhexlify(vector["x_z"])
+
+ def test_exchange_unsupported_algorithm(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+
+ with raises_unsupported_algorithm(
+ exceptions._Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ ):
+ key.exchange(None, key.public_key())
+
+ def test_exchange_non_matching_curve(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ _skip_curve_unsupported(backend, ec.SECP384R1())
+
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ public_key = EC_KEY_SECP384R1.public_numbers.public_key(backend)
+
+ with pytest.raises(ValueError):
+ key.exchange(ec.ECDH(), public_key)
diff --git a/tests/hazmat/primitives/test_ed25519.py b/tests/hazmat/primitives/test_ed25519.py
new file mode 100644
index 00000000..aecc8572
--- /dev/null
+++ b/tests/hazmat/primitives/test_ed25519.py
@@ -0,0 +1,226 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import InvalidSignature, _Reasons
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.ed25519 import (
+ Ed25519PrivateKey, Ed25519PublicKey
+)
+
+from ...utils import (
+ load_vectors_from_file, raises_unsupported_algorithm
+)
+
+
+def load_ed25519_vectors(vector_data):
+ """
+ djb's ed25519 vectors are structured as a colon delimited array:
+ 0: secret key (32 bytes) + public key (32 bytes)
+ 1: public key (32 bytes)
+ 2: message (0+ bytes)
+ 3: signature + message (64+ bytes)
+ """
+ data = []
+ for line in vector_data:
+ secret_key, public_key, message, signature, _ = line.split(':')
+ secret_key = secret_key[0:64]
+ signature = signature[0:128]
+ data.append({
+ "secret_key": secret_key,
+ "public_key": public_key,
+ "message": message,
+ "signature": signature
+ })
+ return data
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: not backend.ed25519_supported(),
+ skip_message="Requires OpenSSL without Ed25519 support"
+)
+def test_ed25519_unsupported(backend):
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ Ed25519PublicKey.from_public_bytes(b"0" * 32)
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ Ed25519PrivateKey.from_private_bytes(b"0" * 32)
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ Ed25519PrivateKey.generate()
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.ed25519_supported(),
+ skip_message="Requires OpenSSL with Ed25519 support"
+)
+class TestEd25519Signing(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "Ed25519", "sign.input"),
+ load_ed25519_vectors
+ )
+ )
+ def test_sign_verify_input(self, vector, backend):
+ sk = binascii.unhexlify(vector["secret_key"])
+ pk = binascii.unhexlify(vector["public_key"])
+ message = binascii.unhexlify(vector["message"])
+ signature = binascii.unhexlify(vector["signature"])
+ private_key = Ed25519PrivateKey.from_private_bytes(sk)
+ computed_sig = private_key.sign(message)
+ assert computed_sig == signature
+ public_key = private_key.public_key()
+ assert public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == pk
+ public_key.verify(signature, message)
+
+ def test_invalid_signature(self, backend):
+ key = Ed25519PrivateKey.generate()
+ signature = key.sign(b"test data")
+ with pytest.raises(InvalidSignature):
+ key.public_key().verify(signature, b"wrong data")
+
+ with pytest.raises(InvalidSignature):
+ key.public_key().verify(b"0" * 64, b"test data")
+
+ def test_generate(self, backend):
+ key = Ed25519PrivateKey.generate()
+ assert key
+ assert key.public_key()
+
+ def test_load_public_bytes(self, backend):
+ public_key = Ed25519PrivateKey.generate().public_key()
+ public_bytes = public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ )
+ public_key2 = Ed25519PublicKey.from_public_bytes(public_bytes)
+ assert public_bytes == public_key2.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ )
+
+ def test_invalid_type_public_bytes(self, backend):
+ with pytest.raises(TypeError):
+ Ed25519PublicKey.from_public_bytes(object())
+
+ def test_invalid_type_private_bytes(self, backend):
+ with pytest.raises(TypeError):
+ Ed25519PrivateKey.from_private_bytes(object())
+
+ def test_invalid_length_from_public_bytes(self, backend):
+ with pytest.raises(ValueError):
+ Ed25519PublicKey.from_public_bytes(b"a" * 31)
+ with pytest.raises(ValueError):
+ Ed25519PublicKey.from_public_bytes(b"a" * 33)
+
+ def test_invalid_length_from_private_bytes(self, backend):
+ with pytest.raises(ValueError):
+ Ed25519PrivateKey.from_private_bytes(b"a" * 31)
+ with pytest.raises(ValueError):
+ Ed25519PrivateKey.from_private_bytes(b"a" * 33)
+
+ def test_invalid_private_bytes(self, backend):
+ key = Ed25519PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.PKCS8,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ )
+
+ def test_invalid_public_bytes(self, backend):
+ key = Ed25519PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.PKCS1
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.Raw
+ )
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt", "encryption", "passwd", "load_func"),
+ [
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_der_private_key
+ ),
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_der_private_key
+ ),
+ ]
+ )
+ def test_round_trip_private_serialization(self, encoding, fmt, encryption,
+ passwd, load_func, backend):
+ key = Ed25519PrivateKey.generate()
+ serialized = key.private_bytes(encoding, fmt, encryption)
+ loaded_key = load_func(serialized, passwd, backend)
+ assert isinstance(loaded_key, Ed25519PrivateKey)
+
+ def test_buffer_protocol(self, backend):
+ private_bytes = os.urandom(32)
+ key = Ed25519PrivateKey.from_private_bytes(bytearray(private_bytes))
+ assert key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
diff --git a/tests/hazmat/primitives/test_ed448.py b/tests/hazmat/primitives/test_ed448.py
new file mode 100644
index 00000000..b02f821a
--- /dev/null
+++ b/tests/hazmat/primitives/test_ed448.py
@@ -0,0 +1,239 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import InvalidSignature, _Reasons
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.ed448 import (
+ Ed448PrivateKey, Ed448PublicKey
+)
+
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: not backend.ed448_supported(),
+ skip_message="Requires OpenSSL without Ed448 support"
+)
+def test_ed448_unsupported(backend):
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ Ed448PublicKey.from_public_bytes(b"0" * 57)
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ Ed448PrivateKey.from_private_bytes(b"0" * 57)
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ Ed448PrivateKey.generate()
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.ed448_supported(),
+ skip_message="Requires OpenSSL with Ed448 support"
+)
+class TestEd448Signing(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "Ed448", "rfc8032.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_sign_input(self, vector, backend):
+ if vector.get("context") is not None:
+ pytest.skip("ed448 contexts are not currently supported")
+
+ sk = binascii.unhexlify(vector["secret"])
+ pk = binascii.unhexlify(vector["public"])
+ message = binascii.unhexlify(vector["message"])
+ signature = binascii.unhexlify(vector["signature"])
+ private_key = Ed448PrivateKey.from_private_bytes(sk)
+ computed_sig = private_key.sign(message)
+ assert computed_sig == signature
+ public_key = private_key.public_key()
+ assert public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == pk
+ public_key.verify(signature, message)
+
+ def test_invalid_signature(self, backend):
+ key = Ed448PrivateKey.generate()
+ signature = key.sign(b"test data")
+ with pytest.raises(InvalidSignature):
+ key.public_key().verify(signature, b"wrong data")
+
+ with pytest.raises(InvalidSignature):
+ key.public_key().verify(b"0" * 64, b"test data")
+
+ def test_generate(self, backend):
+ key = Ed448PrivateKey.generate()
+ assert key
+ assert key.public_key()
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "Ed448", "rfc8032.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_pub_priv_bytes_raw(self, vector, backend):
+ sk = binascii.unhexlify(vector["secret"])
+ pk = binascii.unhexlify(vector["public"])
+ private_key = Ed448PrivateKey.from_private_bytes(sk)
+ assert private_key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == sk
+ assert private_key.public_key().public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == pk
+ public_key = Ed448PublicKey.from_public_bytes(pk)
+ assert public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == pk
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt", "encryption", "passwd", "load_func"),
+ [
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_der_private_key
+ ),
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_der_private_key
+ ),
+ ]
+ )
+ def test_round_trip_private_serialization(self, encoding, fmt, encryption,
+ passwd, load_func, backend):
+ key = Ed448PrivateKey.generate()
+ serialized = key.private_bytes(encoding, fmt, encryption)
+ loaded_key = load_func(serialized, passwd, backend)
+ assert isinstance(loaded_key, Ed448PrivateKey)
+
+ def test_invalid_type_public_bytes(self, backend):
+ with pytest.raises(TypeError):
+ Ed448PublicKey.from_public_bytes(object())
+
+ def test_invalid_type_private_bytes(self, backend):
+ with pytest.raises(TypeError):
+ Ed448PrivateKey.from_private_bytes(object())
+
+ def test_invalid_length_from_public_bytes(self, backend):
+ with pytest.raises(ValueError):
+ Ed448PublicKey.from_public_bytes(b"a" * 56)
+ with pytest.raises(ValueError):
+ Ed448PublicKey.from_public_bytes(b"a" * 58)
+
+ def test_invalid_length_from_private_bytes(self, backend):
+ with pytest.raises(ValueError):
+ Ed448PrivateKey.from_private_bytes(b"a" * 56)
+ with pytest.raises(ValueError):
+ Ed448PrivateKey.from_private_bytes(b"a" * 58)
+
+ def test_invalid_private_bytes(self, backend):
+ key = Ed448PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.PKCS8,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ )
+
+ def test_invalid_public_bytes(self, backend):
+ key = Ed448PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.PKCS1
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.Raw
+ )
+
+ def test_buffer_protocol(self, backend):
+ private_bytes = os.urandom(57)
+ key = Ed448PrivateKey.from_private_bytes(bytearray(private_bytes))
+ assert key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
+
+ def test_malleability(self, backend):
+ # This is a signature where r > the group order. It should be
+ # rejected to prevent signature malleability issues. This test can
+ # be removed when wycheproof grows ed448 vectors
+ public_bytes = binascii.unhexlify(
+ "fedb02a658d74990244d9d10cf338e977565cbbda6b24c716829ed6ee1e4f28cf"
+ "2620c052db8d878f6243bffc22242816c1aaa67d2f3603600"
+ )
+ signature = binascii.unhexlify(
+ "0cc16ba24d69277f927c1554b0f08a2a711bbdd20b058ccc660d00ca13542a3ce"
+ "f9e5c44c54ab23a2eb14f947e167b990b080863e28b399380f30db6e54d5d1406"
+ "d23378ffde11b1fb81b2b438a3b8e8aa7f7f4e1befcc905023fab5a5465053844"
+ "f04cf0c1b51d84760f869588687f57500"
+ )
+ key = Ed448PublicKey.from_public_bytes(public_bytes)
+ with pytest.raises(InvalidSignature):
+ key.verify(signature, b"8")
diff --git a/tests/hazmat/primitives/test_hash_vectors.py b/tests/hazmat/primitives/test_hash_vectors.py
index c6e98283..0698f413 100644
--- a/tests/hazmat/primitives/test_hash_vectors.py
+++ b/tests/hazmat/primitives/test_hash_vectors.py
@@ -4,6 +4,7 @@
from __future__ import absolute_import, division, print_function
+import binascii
import os
import pytest
@@ -11,8 +12,8 @@ import pytest
from cryptography.hazmat.backends.interfaces import HashBackend
from cryptography.hazmat.primitives import hashes
-from .utils import generate_hash_test, generate_long_string_hash_test
-from ...utils import load_hash_vectors
+from .utils import _load_all_params, generate_hash_test
+from ...utils import load_hash_vectors, load_nist_vectors
@pytest.mark.supported(
@@ -21,7 +22,7 @@ from ...utils import load_hash_vectors
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA1(object):
- test_SHA1 = generate_hash_test(
+ test_sha1 = generate_hash_test(
load_hash_vectors,
os.path.join("hashes", "SHA1"),
[
@@ -38,7 +39,7 @@ class TestSHA1(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA224(object):
- test_SHA224 = generate_hash_test(
+ test_sha224 = generate_hash_test(
load_hash_vectors,
os.path.join("hashes", "SHA2"),
[
@@ -55,7 +56,7 @@ class TestSHA224(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA256(object):
- test_SHA256 = generate_hash_test(
+ test_sha256 = generate_hash_test(
load_hash_vectors,
os.path.join("hashes", "SHA2"),
[
@@ -72,7 +73,7 @@ class TestSHA256(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA384(object):
- test_SHA384 = generate_hash_test(
+ test_sha384 = generate_hash_test(
load_hash_vectors,
os.path.join("hashes", "SHA2"),
[
@@ -89,7 +90,7 @@ class TestSHA384(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA512(object):
- test_SHA512 = generate_hash_test(
+ test_sha512 = generate_hash_test(
load_hash_vectors,
os.path.join("hashes", "SHA2"),
[
@@ -101,46 +102,36 @@ class TestSHA512(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160()),
- skip_message="Does not support RIPEMD160",
+ only_if=lambda backend: backend.hash_supported(hashes.SHA512_224()),
+ skip_message="Does not support SHA512/224",
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
-class TestRIPEMD160(object):
- test_RIPEMD160 = generate_hash_test(
+class TestSHA512224(object):
+ test_sha512_224 = generate_hash_test(
load_hash_vectors,
- os.path.join("hashes", "ripemd160"),
+ os.path.join("hashes", "SHA2"),
[
- "ripevectors.txt",
+ "SHA512_224LongMsg.rsp",
+ "SHA512_224ShortMsg.rsp",
],
- hashes.RIPEMD160(),
- )
-
- test_RIPEMD160_long_string = generate_long_string_hash_test(
- hashes.RIPEMD160(),
- "52783243c1697bdbe16d37f97f68f08325dc1528",
+ hashes.SHA512_224(),
)
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.Whirlpool()),
- skip_message="Does not support Whirlpool",
+ only_if=lambda backend: backend.hash_supported(hashes.SHA512_256()),
+ skip_message="Does not support SHA512/256",
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
-class TestWhirlpool(object):
- test_whirlpool = generate_hash_test(
+class TestSHA512256(object):
+ test_sha512_256 = generate_hash_test(
load_hash_vectors,
- os.path.join("hashes", "whirlpool"),
+ os.path.join("hashes", "SHA2"),
[
- "iso-test-vectors.txt",
+ "SHA512_256LongMsg.rsp",
+ "SHA512_256ShortMsg.rsp",
],
- hashes.Whirlpool(),
- )
-
- test_whirlpool_long_string = generate_long_string_hash_test(
- hashes.Whirlpool(),
- ("0c99005beb57eff50a7cf005560ddf5d29057fd86b2"
- "0bfd62deca0f1ccea4af51fc15490eddc47af32bb2b"
- "66c34ff9ad8c6008ad677f77126953b226e4ed8b01"),
+ hashes.SHA512_256(),
)
@@ -158,3 +149,177 @@ class TestMD5(object):
],
hashes.MD5(),
)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(
+ hashes.BLAKE2b(digest_size=64)),
+ skip_message="Does not support BLAKE2b",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestBLAKE2b(object):
+ test_b2b = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "blake2"),
+ [
+ "blake2b.txt",
+ ],
+ hashes.BLAKE2b(digest_size=64),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(
+ hashes.BLAKE2s(digest_size=32)),
+ skip_message="Does not support BLAKE2s",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestBLAKE2s256(object):
+ test_b2s = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "blake2"),
+ [
+ "blake2s.txt",
+ ],
+ hashes.BLAKE2s(digest_size=32),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(hashes.SHA3_224()),
+ skip_message="Does not support SHA3_224",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestSHA3224(object):
+ test_sha3_224 = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "SHA3"),
+ [
+ "SHA3_224LongMsg.rsp",
+ "SHA3_224ShortMsg.rsp",
+ ],
+ hashes.SHA3_224(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(hashes.SHA3_256()),
+ skip_message="Does not support SHA3_256",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestSHA3256(object):
+ test_sha3_256 = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "SHA3"),
+ [
+ "SHA3_256LongMsg.rsp",
+ "SHA3_256ShortMsg.rsp",
+ ],
+ hashes.SHA3_256(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(hashes.SHA3_384()),
+ skip_message="Does not support SHA3_384",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestSHA3384(object):
+ test_sha3_384 = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "SHA3"),
+ [
+ "SHA3_384LongMsg.rsp",
+ "SHA3_384ShortMsg.rsp",
+ ],
+ hashes.SHA3_384(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(hashes.SHA3_512()),
+ skip_message="Does not support SHA3_512",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestSHA3512(object):
+ test_sha3_512 = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "SHA3"),
+ [
+ "SHA3_512LongMsg.rsp",
+ "SHA3_512ShortMsg.rsp",
+ ],
+ hashes.SHA3_512(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(
+ hashes.SHAKE128(digest_size=16)),
+ skip_message="Does not support SHAKE128",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestSHAKE128(object):
+ test_shake128 = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "SHAKE"),
+ [
+ "SHAKE128LongMsg.rsp",
+ "SHAKE128ShortMsg.rsp",
+ ],
+ hashes.SHAKE128(digest_size=16),
+ )
+
+ @pytest.mark.parametrize(
+ "vector",
+ _load_all_params(
+ os.path.join("hashes", "SHAKE"),
+ [
+ "SHAKE128VariableOut.rsp",
+ ],
+ load_nist_vectors,
+ )
+ )
+ def test_shake128_variable(self, vector, backend):
+ output_length = int(vector['outputlen']) // 8
+ msg = binascii.unhexlify(vector['msg'])
+ shake = hashes.SHAKE128(digest_size=output_length)
+ m = hashes.Hash(shake, backend=backend)
+ m.update(msg)
+ assert m.finalize() == binascii.unhexlify(vector['output'])
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(
+ hashes.SHAKE256(digest_size=32)),
+ skip_message="Does not support SHAKE256",
+)
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestSHAKE256(object):
+ test_shake256 = generate_hash_test(
+ load_hash_vectors,
+ os.path.join("hashes", "SHAKE"),
+ [
+ "SHAKE256LongMsg.rsp",
+ "SHAKE256ShortMsg.rsp",
+ ],
+ hashes.SHAKE256(digest_size=32),
+ )
+
+ @pytest.mark.parametrize(
+ "vector",
+ _load_all_params(
+ os.path.join("hashes", "SHAKE"),
+ [
+ "SHAKE256VariableOut.rsp",
+ ],
+ load_nist_vectors,
+ )
+ )
+ def test_shake256_variable(self, vector, backend):
+ output_length = int(vector['outputlen']) // 8
+ msg = binascii.unhexlify(vector['msg'])
+ shake = hashes.SHAKE256(digest_size=output_length)
+ m = hashes.Hash(shake, backend=backend)
+ m.update(msg)
+ assert m.finalize() == binascii.unhexlify(vector['output'])
diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py
index 8f7fdb18..a743045c 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -4,27 +4,19 @@
from __future__ import absolute_import, division, print_function
-import pretend
+import binascii
import pytest
-from cryptography import utils
from cryptography.exceptions import AlreadyFinalized, _Reasons
from cryptography.hazmat.backends.interfaces import HashBackend
from cryptography.hazmat.primitives import hashes
from .utils import generate_base_hash_test
-from ..backends.test_multibackend import DummyHashBackend
+from ...doubles import DummyHashAlgorithm
from ...utils import raises_unsupported_algorithm
-@utils.register_interface(hashes.HashAlgorithm)
-class UnsupportedDummyHash(object):
- name = "unsupported-dummy-hash"
- block_size = None
- digest_size = None
-
-
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestHashContext(object):
def test_hash_reject_unicode(self, backend):
@@ -32,14 +24,6 @@ class TestHashContext(object):
with pytest.raises(TypeError):
m.update(u"\u00FC")
- def test_copy_backend_object(self):
- backend = DummyHashBackend([hashes.SHA1])
- copied_ctx = pretend.stub()
- pretend_ctx = pretend.stub(copy=lambda: copied_ctx)
- h = hashes.Hash(hashes.SHA1(), backend=backend, ctx=pretend_ctx)
- assert h._backend is backend
- assert h.copy()._backend is h._backend
-
def test_hash_algorithm_instance(self, backend):
with pytest.raises(TypeError):
hashes.Hash(hashes.SHA1, backend=backend)
@@ -59,7 +43,7 @@ class TestHashContext(object):
def test_unsupported_hash(self, backend):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- hashes.Hash(UnsupportedDummyHash(), backend)
+ hashes.Hash(DummyHashAlgorithm(), backend)
@pytest.mark.supported(
@@ -68,10 +52,9 @@ class TestHashContext(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA1(object):
- test_SHA1 = generate_base_hash_test(
+ test_sha1 = generate_base_hash_test(
hashes.SHA1(),
digest_size=20,
- block_size=64,
)
@@ -81,10 +64,9 @@ class TestSHA1(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA224(object):
- test_SHA224 = generate_base_hash_test(
+ test_sha224 = generate_base_hash_test(
hashes.SHA224(),
digest_size=28,
- block_size=64,
)
@@ -94,10 +76,9 @@ class TestSHA224(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA256(object):
- test_SHA256 = generate_base_hash_test(
+ test_sha256 = generate_base_hash_test(
hashes.SHA256(),
digest_size=32,
- block_size=64,
)
@@ -107,10 +88,9 @@ class TestSHA256(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA384(object):
- test_SHA384 = generate_base_hash_test(
+ test_sha384 = generate_base_hash_test(
hashes.SHA384(),
digest_size=48,
- block_size=128,
)
@@ -120,54 +100,103 @@ class TestSHA384(object):
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
class TestSHA512(object):
- test_SHA512 = generate_base_hash_test(
+ test_sha512 = generate_base_hash_test(
hashes.SHA512(),
digest_size=64,
- block_size=128,
)
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160()),
- skip_message="Does not support RIPEMD160",
+ only_if=lambda backend: backend.hash_supported(hashes.MD5()),
+ skip_message="Does not support MD5",
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
-class TestRIPEMD160(object):
- test_RIPEMD160 = generate_base_hash_test(
- hashes.RIPEMD160(),
- digest_size=20,
- block_size=64,
+class TestMD5(object):
+ test_md5 = generate_base_hash_test(
+ hashes.MD5(),
+ digest_size=16,
)
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.Whirlpool()),
- skip_message="Does not support Whirlpool",
+ only_if=lambda backend: backend.hash_supported(
+ hashes.BLAKE2b(digest_size=64)),
+ skip_message="Does not support BLAKE2b",
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
-class TestWhirlpool(object):
- test_Whirlpool = generate_base_hash_test(
- hashes.Whirlpool(),
+class TestBLAKE2b(object):
+ test_blake2b = generate_base_hash_test(
+ hashes.BLAKE2b(digest_size=64),
digest_size=64,
- block_size=64,
)
+ def test_invalid_digest_size(self, backend):
+ with pytest.raises(ValueError):
+ hashes.BLAKE2b(digest_size=65)
+
+ with pytest.raises(ValueError):
+ hashes.BLAKE2b(digest_size=0)
+
+ with pytest.raises(ValueError):
+ hashes.BLAKE2b(digest_size=-1)
+
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.MD5()),
- skip_message="Does not support MD5",
+ only_if=lambda backend: backend.hash_supported(
+ hashes.BLAKE2s(digest_size=32)),
+ skip_message="Does not support BLAKE2s",
)
@pytest.mark.requires_backend_interface(interface=HashBackend)
-class TestMD5(object):
- test_MD5 = generate_base_hash_test(
- hashes.MD5(),
- digest_size=16,
- block_size=64,
+class TestBLAKE2s(object):
+ test_blake2s = generate_base_hash_test(
+ hashes.BLAKE2s(digest_size=32),
+ digest_size=32,
)
+ def test_invalid_digest_size(self, backend):
+ with pytest.raises(ValueError):
+ hashes.BLAKE2s(digest_size=33)
+
+ with pytest.raises(ValueError):
+ hashes.BLAKE2s(digest_size=0)
+
+ with pytest.raises(ValueError):
+ hashes.BLAKE2s(digest_size=-1)
+
def test_invalid_backend():
pretend_backend = object()
with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
hashes.Hash(hashes.SHA1(), pretend_backend)
+
+
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+def test_buffer_protocol_hash(backend):
+ data = binascii.unhexlify(b"b4190e")
+ h = hashes.Hash(hashes.SHA256(), backend)
+ h.update(bytearray(data))
+ assert h.finalize() == binascii.unhexlify(
+ b"dff2e73091f6c05e528896c4c831b9448653dc2ff043528f6769437bc7b975c2"
+ )
+
+
+class TestSHAKE(object):
+ @pytest.mark.parametrize(
+ "xof",
+ [hashes.SHAKE128, hashes.SHAKE256]
+ )
+ def test_invalid_digest_type(self, xof):
+ with pytest.raises(TypeError):
+ xof(digest_size=object())
+
+ @pytest.mark.parametrize(
+ "xof",
+ [hashes.SHAKE128, hashes.SHAKE256]
+ )
+ def test_invalid_digest_size(self, xof):
+ with pytest.raises(ValueError):
+ xof(digest_size=-5)
+
+ with pytest.raises(ValueError):
+ xof(digest_size=0)
diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py
index e33529c9..195bfb3a 100644
--- a/tests/hazmat/primitives/test_hkdf.py
+++ b/tests/hazmat/primitives/test_hkdf.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
import binascii
+import os
import pytest
@@ -15,13 +16,15 @@ from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
-from ...utils import raises_unsupported_algorithm
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
@pytest.mark.requires_backend_interface(interface=HMACBackend)
class TestHKDF(object):
def test_length_limit(self, backend):
- big_length = 255 * (hashes.SHA256().digest_size // 8) + 1
+ big_length = 255 * hashes.SHA256().digest_size + 1
with pytest.raises(ValueError):
HKDF(
@@ -142,6 +145,47 @@ class TestHKDF(object):
hkdf.verify(b"foo", u"bar")
+ def test_derive_short_output(self, backend):
+ hkdf = HKDF(
+ hashes.SHA256(),
+ 4,
+ salt=None,
+ info=None,
+ backend=backend
+ )
+
+ assert hkdf.derive(b"\x01" * 16) == b"gJ\xfb{"
+
+ def test_derive_long_output(self, backend):
+ vector = load_vectors_from_file(
+ os.path.join("KDF", "hkdf-generated.txt"), load_nist_vectors
+ )[0]
+ hkdf = HKDF(
+ hashes.SHA256(),
+ int(vector["l"]),
+ salt=vector["salt"],
+ info=vector["info"],
+ backend=backend
+ )
+ ikm = binascii.unhexlify(vector["ikm"])
+
+ assert hkdf.derive(ikm) == binascii.unhexlify(vector["okm"])
+
+ def test_buffer_protocol(self, backend):
+ vector = load_vectors_from_file(
+ os.path.join("KDF", "hkdf-generated.txt"), load_nist_vectors
+ )[0]
+ hkdf = HKDF(
+ hashes.SHA256(),
+ int(vector["l"]),
+ salt=vector["salt"],
+ info=vector["info"],
+ backend=backend
+ )
+ ikm = bytearray(binascii.unhexlify(vector["ikm"]))
+
+ assert hkdf.derive(ikm) == binascii.unhexlify(vector["okm"])
+
@pytest.mark.requires_backend_interface(interface=HMACBackend)
class TestHKDFExpand(object):
@@ -158,6 +202,19 @@ class TestHKDFExpand(object):
assert binascii.hexlify(hkdf.derive(prk)) == okm
+ def test_buffer_protocol(self, backend):
+ prk = bytearray(binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ ))
+
+ okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+ b"5bf34007208d5b887185865")
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ assert binascii.hexlify(hkdf.derive(prk)) == okm
+
def test_verify(self, backend):
prk = binascii.unhexlify(
b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
diff --git a/tests/hazmat/primitives/test_hkdf_vectors.py b/tests/hazmat/primitives/test_hkdf_vectors.py
index 74bf9291..290cefbf 100644
--- a/tests/hazmat/primitives/test_hkdf_vectors.py
+++ b/tests/hazmat/primitives/test_hkdf_vectors.py
@@ -21,7 +21,7 @@ from ...utils import load_nist_vectors
)
@pytest.mark.requires_backend_interface(interface=HMACBackend)
class TestHKDFSHA1(object):
- test_HKDFSHA1 = generate_hkdf_test(
+ test_hkdfsha1 = generate_hkdf_test(
load_nist_vectors,
os.path.join("KDF"),
["rfc-5869-HKDF-SHA1.txt"],
@@ -35,7 +35,7 @@ class TestHKDFSHA1(object):
)
@pytest.mark.requires_backend_interface(interface=HMACBackend)
class TestHKDFSHA256(object):
- test_HKDFSHA1 = generate_hkdf_test(
+ test_hkdfsha256 = generate_hkdf_test(
load_nist_vectors,
os.path.join("KDF"),
["rfc-5869-HKDF-SHA256.txt"],
diff --git a/tests/hazmat/primitives/test_hmac.py b/tests/hazmat/primitives/test_hmac.py
index 83b18cbc..0e2fe688 100644
--- a/tests/hazmat/primitives/test_hmac.py
+++ b/tests/hazmat/primitives/test_hmac.py
@@ -4,11 +4,10 @@
from __future__ import absolute_import, division, print_function
-import pretend
+import binascii
import pytest
-from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, _Reasons
)
@@ -16,17 +15,10 @@ from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import hashes, hmac
from .utils import generate_base_hmac_test
-from ..backends.test_multibackend import DummyHMACBackend
+from ...doubles import DummyHashAlgorithm
from ...utils import raises_unsupported_algorithm
-@utils.register_interface(hashes.HashAlgorithm)
-class UnsupportedDummyHash(object):
- name = "unsupported-dummy-hash"
- block_size = None
- digest_size = None
-
-
@pytest.mark.supported(
only_if=lambda backend: backend.hmac_supported(hashes.MD5()),
skip_message="Does not support MD5",
@@ -45,14 +37,6 @@ class TestHMAC(object):
with pytest.raises(TypeError):
h.update(u"\u00FC")
- def test_copy_backend_object(self):
- backend = DummyHMACBackend([hashes.SHA1])
- copied_ctx = pretend.stub()
- pretend_ctx = pretend.stub(copy=lambda: copied_ctx)
- h = hmac.HMAC(b"key", hashes.SHA1(), backend=backend, ctx=pretend_ctx)
- assert h._backend is backend
- assert h.copy()._backend is backend
-
def test_hmac_algorithm_instance(self, backend):
with pytest.raises(TypeError):
hmac.HMAC(b"key", hashes.SHA1, backend=backend)
@@ -95,7 +79,15 @@ class TestHMAC(object):
def test_unsupported_hash(self, backend):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- hmac.HMAC(b"key", UnsupportedDummyHash(), backend)
+ hmac.HMAC(b"key", DummyHashAlgorithm(), backend)
+
+ def test_buffer_protocol(self, backend):
+ key = bytearray(b"2b7e151628aed2a6abf7158809cf4f3c")
+ h = hmac.HMAC(key, hashes.SHA256(), backend)
+ h.update(bytearray(b"6bc1bee22e409f96e93d7e117393172a"))
+ assert h.finalize() == binascii.unhexlify(
+ b"a1bf7169c56a501c6585190ff4f07cad6e492a3ee187c0372614fb444b9fc3f0"
+ )
def test_invalid_backend():
diff --git a/tests/hazmat/primitives/test_hmac_vectors.py b/tests/hazmat/primitives/test_hmac_vectors.py
index 8704e724..6ff71fe3 100644
--- a/tests/hazmat/primitives/test_hmac_vectors.py
+++ b/tests/hazmat/primitives/test_hmac_vectors.py
@@ -4,10 +4,12 @@
from __future__ import absolute_import, division, print_function
+import binascii
+
import pytest
from cryptography.hazmat.backends.interfaces import HMACBackend
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, hmac
from .utils import generate_hmac_test
from ...utils import load_hash_vectors
@@ -110,16 +112,26 @@ class TestHMACSHA512(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.RIPEMD160()),
- skip_message="Does not support RIPEMD160",
+ only_if=lambda backend: backend.hmac_supported(hashes.BLAKE2b(
+ digest_size=64
+ )),
+ skip_message="Does not support BLAKE2",
)
@pytest.mark.requires_backend_interface(interface=HMACBackend)
-class TestHMACRIPEMD160(object):
- test_hmac_ripemd160 = generate_hmac_test(
- load_hash_vectors,
- "HMAC",
- [
- "rfc-2286-ripemd160.txt",
- ],
- hashes.RIPEMD160(),
- )
+class TestHMACBLAKE2(object):
+ def test_blake2b(self, backend):
+ h = hmac.HMAC(b"0" * 64, hashes.BLAKE2b(digest_size=64), backend)
+ h.update(b"test")
+ digest = h.finalize()
+ assert digest == binascii.unhexlify(
+ b"b5319122f8a24ba134a0c9851922448104e25be5d1b91265c0c68b22722f0f29"
+ b"87dba4aeaa69e6bed7edc44f48d6b1be493a3ce583f9c737c53d6bacc09e2f32"
+ )
+
+ def test_blake2s(self, backend):
+ h = hmac.HMAC(b"0" * 32, hashes.BLAKE2s(digest_size=32), backend)
+ h.update(b"test")
+ digest = h.finalize()
+ assert digest == binascii.unhexlify(
+ b"51477cc5bdf1faf952cf97bb934ee936de1f4d5d7448a84eeb6f98d23b392166"
+ )
diff --git a/tests/hazmat/primitives/test_idea.py b/tests/hazmat/primitives/test_idea.py
index 1b15ed67..6b8a2a87 100644
--- a/tests/hazmat/primitives/test_idea.py
+++ b/tests/hazmat/primitives/test_idea.py
@@ -18,13 +18,13 @@ from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.IDEA("\x00" * 16), modes.ECB()
+ algorithms.IDEA(b"\x00" * 16), modes.ECB()
),
skip_message="Does not support IDEA ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestIDEAModeECB(object):
- test_ECB = generate_encrypt_test(
+ test_ecb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "IDEA"),
["idea-ecb.txt"],
@@ -35,13 +35,13 @@ class TestIDEAModeECB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.IDEA("\x00" * 16), modes.CBC("\x00" * 8)
+ algorithms.IDEA(b"\x00" * 16), modes.CBC(b"\x00" * 8)
),
skip_message="Does not support IDEA CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestIDEAModeCBC(object):
- test_CBC = generate_encrypt_test(
+ test_cbc = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "IDEA"),
["idea-cbc.txt"],
@@ -52,13 +52,13 @@ class TestIDEAModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.IDEA("\x00" * 16), modes.OFB("\x00" * 8)
+ algorithms.IDEA(b"\x00" * 16), modes.OFB(b"\x00" * 8)
),
skip_message="Does not support IDEA OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestIDEAModeOFB(object):
- test_OFB = generate_encrypt_test(
+ test_ofb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "IDEA"),
["idea-ofb.txt"],
@@ -69,13 +69,13 @@ class TestIDEAModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.IDEA("\x00" * 16), modes.CFB("\x00" * 8)
+ algorithms.IDEA(b"\x00" * 16), modes.CFB(b"\x00" * 8)
),
skip_message="Does not support IDEA CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestIDEAModeCFB(object):
- test_CFB = generate_encrypt_test(
+ test_cfb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "IDEA"),
["idea-cfb.txt"],
diff --git a/tests/hazmat/primitives/test_kbkdf.py b/tests/hazmat/primitives/test_kbkdf.py
new file mode 100644
index 00000000..a16f1768
--- /dev/null
+++ b/tests/hazmat/primitives/test_kbkdf.py
@@ -0,0 +1,158 @@
+# 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 cryptography.exceptions import (
+ AlreadyFinalized, InvalidKey, _Reasons
+)
+from cryptography.hazmat.backends.interfaces import HMACBackend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.kbkdf import (
+ CounterLocation, KBKDFHMAC, Mode
+)
+
+from ...doubles import DummyHashAlgorithm
+from ...utils import raises_unsupported_algorithm
+
+
+@pytest.mark.requires_backend_interface(interface=HMACBackend)
+class TestKBKDFHMAC(object):
+ def test_invalid_key(self, backend):
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ key = kdf.derive(b"material")
+
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ with pytest.raises(InvalidKey):
+ kdf.verify(b"material2", key)
+
+ def test_already_finalized(self, backend):
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ kdf.derive(b'material')
+
+ with pytest.raises(AlreadyFinalized):
+ kdf.derive(b'material2')
+
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ key = kdf.derive(b'material')
+
+ with pytest.raises(AlreadyFinalized):
+ kdf.verify(b'material', key)
+
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+ kdf.verify(b'material', key)
+
+ with pytest.raises(AlreadyFinalized):
+ kdf.verify(b"material", key)
+
+ def test_key_length(self, backend):
+ kdf = KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 85899345920, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ with pytest.raises(ValueError):
+ kdf.derive(b'material')
+
+ def test_rlen(self, backend):
+ with pytest.raises(ValueError):
+ KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 5, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_r_type(self, backend):
+ with pytest.raises(TypeError):
+ KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 32, b'r', 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_l_type(self, backend):
+ with pytest.raises(TypeError):
+ KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 32, 4, b'l',
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_l(self, backend):
+ with pytest.raises(ValueError):
+ KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 32, 4, None,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_unsupported_mode(self, backend):
+ with pytest.raises(TypeError):
+ KBKDFHMAC(hashes.SHA256(), None, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_unsupported_location(self, backend):
+ with pytest.raises(TypeError):
+ KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ None, b'label', b'context', None,
+ backend=backend)
+
+ def test_unsupported_parameters(self, backend):
+ with pytest.raises(ValueError):
+ KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ b'fixed', backend=backend)
+
+ def test_unsupported_hash(self, backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
+ KBKDFHMAC(object(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_unsupported_algorithm(self, backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
+ KBKDFHMAC(DummyHashAlgorithm(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ def test_invalid_backend(self, backend):
+ with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+ KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=object())
+
+ def test_unicode_error_label(self, backend):
+ with pytest.raises(TypeError):
+ KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, u'label', b'context',
+ backend=backend)
+
+ def test_unicode_error_context(self, backend):
+ with pytest.raises(TypeError):
+ KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label', u'context',
+ None, backend=backend)
+
+ def test_unicode_error_key_material(self, backend):
+ with pytest.raises(TypeError):
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4,
+ CounterLocation.BeforeFixed, b'label',
+ b'context', None, backend=backend)
+ kdf.derive(u'material')
+
+ def test_buffer_protocol(self, backend):
+ kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 10, 4, 4,
+ CounterLocation.BeforeFixed, b'label', b'context',
+ None, backend=backend)
+
+ key = kdf.derive(bytearray(b"material"))
+ assert key == b'\xb7\x01\x05\x98\xf5\x1a\x12L\xc7.'
diff --git a/tests/hazmat/primitives/test_kbkdf_vectors.py b/tests/hazmat/primitives/test_kbkdf_vectors.py
new file mode 100644
index 00000000..7bdbbdc7
--- /dev/null
+++ b/tests/hazmat/primitives/test_kbkdf_vectors.py
@@ -0,0 +1,23 @@
+# 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 os
+
+import pytest
+
+from cryptography.hazmat.backends.interfaces import HMACBackend
+
+from .utils import generate_kbkdf_counter_mode_test
+from ...utils import load_nist_kbkdf_vectors
+
+
+@pytest.mark.requires_backend_interface(interface=HMACBackend)
+class TestCounterKDFCounterMode(object):
+ test_kbkdfctr = generate_kbkdf_counter_mode_test(
+ load_nist_kbkdf_vectors,
+ os.path.join("KDF"),
+ ["nist-800-108-KBKDF-CTR.txt"]
+ )
diff --git a/tests/hazmat/primitives/test_keywrap.py b/tests/hazmat/primitives/test_keywrap.py
new file mode 100644
index 00000000..c74b144b
--- /dev/null
+++ b/tests/hazmat/primitives/test_keywrap.py
@@ -0,0 +1,207 @@
+# 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 os
+
+import pytest
+
+from cryptography.hazmat.backends.interfaces import CipherBackend
+from cryptography.hazmat.primitives import keywrap
+from cryptography.hazmat.primitives.ciphers import algorithms, modes
+
+from .utils import _load_all_params
+from ...utils import load_nist_vectors
+
+
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestAESKeyWrap(object):
+ @pytest.mark.parametrize(
+ "params",
+ _load_all_params(
+ os.path.join("keywrap", "kwtestvectors"),
+ ["KW_AE_128.txt", "KW_AE_192.txt", "KW_AE_256.txt"],
+ load_nist_vectors
+ )
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES key wrap (RFC 3394) because AES-ECB"
+ " is unsupported",
+ )
+ def test_wrap(self, backend, params):
+ wrapping_key = binascii.unhexlify(params["k"])
+ key_to_wrap = binascii.unhexlify(params["p"])
+ wrapped_key = keywrap.aes_key_wrap(wrapping_key, key_to_wrap, backend)
+ assert params["c"] == binascii.hexlify(wrapped_key)
+
+ @pytest.mark.parametrize(
+ "params",
+ _load_all_params(
+ os.path.join("keywrap", "kwtestvectors"),
+ ["KW_AD_128.txt", "KW_AD_192.txt", "KW_AD_256.txt"],
+ load_nist_vectors
+ )
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES key wrap (RFC 3394) because AES-ECB"
+ " is unsupported",
+ )
+ def test_unwrap(self, backend, params):
+ wrapping_key = binascii.unhexlify(params["k"])
+ wrapped_key = binascii.unhexlify(params["c"])
+ if params.get("fail") is True:
+ with pytest.raises(keywrap.InvalidUnwrap):
+ keywrap.aes_key_unwrap(wrapping_key, wrapped_key, backend)
+ else:
+ unwrapped_key = keywrap.aes_key_unwrap(
+ wrapping_key, wrapped_key, backend
+ )
+ assert params["p"] == binascii.hexlify(unwrapped_key)
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES key wrap (RFC 3394) because AES-ECB"
+ " is unsupported",
+ )
+ def test_wrap_invalid_key_length(self, backend):
+ # The wrapping key must be of length [16, 24, 32]
+ with pytest.raises(ValueError):
+ keywrap.aes_key_wrap(b"badkey", b"sixteen_byte_key", backend)
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES key wrap (RFC 3394) because AES-ECB"
+ " is unsupported",
+ )
+ def test_unwrap_invalid_key_length(self, backend):
+ with pytest.raises(ValueError):
+ keywrap.aes_key_unwrap(b"badkey", b"\x00" * 24, backend)
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES key wrap (RFC 3394) because AES-ECB"
+ " is unsupported",
+ )
+ def test_wrap_invalid_key_to_wrap_length(self, backend):
+ # Keys to wrap must be at least 16 bytes long
+ with pytest.raises(ValueError):
+ keywrap.aes_key_wrap(b"sixteen_byte_key", b"\x00" * 15, backend)
+
+ # Keys to wrap must be a multiple of 8 bytes
+ with pytest.raises(ValueError):
+ keywrap.aes_key_wrap(b"sixteen_byte_key", b"\x00" * 23, backend)
+
+ def test_unwrap_invalid_wrapped_key_length(self, backend):
+ # Keys to unwrap must be at least 24 bytes
+ with pytest.raises(keywrap.InvalidUnwrap):
+ keywrap.aes_key_unwrap(b"sixteen_byte_key", b"\x00" * 16, backend)
+
+ # Keys to unwrap must be a multiple of 8 bytes
+ with pytest.raises(keywrap.InvalidUnwrap):
+ keywrap.aes_key_unwrap(b"sixteen_byte_key", b"\x00" * 27, backend)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES(b"\x00" * 16), modes.ECB()
+ ),
+ skip_message="Does not support AES key wrap (RFC 5649) because AES-ECB"
+ " is unsupported",
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+class TestAESKeyWrapWithPadding(object):
+ @pytest.mark.parametrize(
+ "params",
+ _load_all_params(
+ os.path.join("keywrap", "kwtestvectors"),
+ ["KWP_AE_128.txt", "KWP_AE_192.txt", "KWP_AE_256.txt"],
+ load_nist_vectors
+ )
+ )
+ def test_wrap(self, backend, params):
+ wrapping_key = binascii.unhexlify(params["k"])
+ key_to_wrap = binascii.unhexlify(params["p"])
+ wrapped_key = keywrap.aes_key_wrap_with_padding(
+ wrapping_key, key_to_wrap, backend
+ )
+ assert params["c"] == binascii.hexlify(wrapped_key)
+
+ @pytest.mark.parametrize(
+ "params",
+ _load_all_params("keywrap", ["kwp_botan.txt"], load_nist_vectors)
+ )
+ def test_wrap_additional_vectors(self, backend, params):
+ wrapping_key = binascii.unhexlify(params["key"])
+ key_to_wrap = binascii.unhexlify(params["input"])
+ wrapped_key = keywrap.aes_key_wrap_with_padding(
+ wrapping_key, key_to_wrap, backend
+ )
+ assert wrapped_key == binascii.unhexlify(params["output"])
+
+ @pytest.mark.parametrize(
+ "params",
+ _load_all_params(
+ os.path.join("keywrap", "kwtestvectors"),
+ ["KWP_AD_128.txt", "KWP_AD_192.txt", "KWP_AD_256.txt"],
+ load_nist_vectors
+ )
+ )
+ def test_unwrap(self, backend, params):
+ wrapping_key = binascii.unhexlify(params["k"])
+ wrapped_key = binascii.unhexlify(params["c"])
+ if params.get("fail") is True:
+ with pytest.raises(keywrap.InvalidUnwrap):
+ keywrap.aes_key_unwrap_with_padding(
+ wrapping_key, wrapped_key, backend
+ )
+ else:
+ unwrapped_key = keywrap.aes_key_unwrap_with_padding(
+ wrapping_key, wrapped_key, backend
+ )
+ assert params["p"] == binascii.hexlify(unwrapped_key)
+
+ @pytest.mark.parametrize(
+ "params",
+ _load_all_params("keywrap", ["kwp_botan.txt"], load_nist_vectors)
+ )
+ def test_unwrap_additional_vectors(self, backend, params):
+ wrapping_key = binascii.unhexlify(params["key"])
+ wrapped_key = binascii.unhexlify(params["output"])
+ unwrapped_key = keywrap.aes_key_unwrap_with_padding(
+ wrapping_key, wrapped_key, backend
+ )
+ assert unwrapped_key == binascii.unhexlify(params["input"])
+
+ def test_unwrap_invalid_wrapped_key_length(self, backend):
+ # Keys to unwrap must be at least 16 bytes
+ with pytest.raises(
+ keywrap.InvalidUnwrap, match='Must be at least 16 bytes'
+ ):
+ keywrap.aes_key_unwrap_with_padding(
+ b"sixteen_byte_key", b"\x00" * 15, backend
+ )
+
+ def test_wrap_invalid_key_length(self, backend):
+ with pytest.raises(ValueError, match='must be a valid AES key length'):
+ keywrap.aes_key_wrap_with_padding(b"badkey", b"\x00", backend)
+
+ def test_unwrap_invalid_key_length(self, backend):
+ with pytest.raises(ValueError, match='must be a valid AES key length'):
+ keywrap.aes_key_unwrap_with_padding(
+ b"badkey", b"\x00" * 16, backend
+ )
diff --git a/tests/hazmat/primitives/test_padding.py b/tests/hazmat/primitives/test_padding.py
index 392ea737..fb72a794 100644
--- a/tests/hazmat/primitives/test_padding.py
+++ b/tests/hazmat/primitives/test_padding.py
@@ -6,6 +6,8 @@ from __future__ import absolute_import, division, print_function
import pytest
+import six
+
from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.primitives import padding
@@ -99,3 +101,109 @@ class TestPKCS7(object):
unpadder.update(b"")
with pytest.raises(AlreadyFinalized):
unpadder.finalize()
+
+ def test_large_padding(self):
+ padder = padding.PKCS7(2040).padder()
+ padded_data = padder.update(b"")
+ padded_data += padder.finalize()
+
+ for i in six.iterbytes(padded_data):
+ assert i == 255
+
+ unpadder = padding.PKCS7(2040).unpadder()
+ data = unpadder.update(padded_data)
+ data += unpadder.finalize()
+
+ assert data == b""
+
+
+class TestANSIX923(object):
+ @pytest.mark.parametrize("size", [127, 4096, -2])
+ def test_invalid_block_size(self, size):
+ with pytest.raises(ValueError):
+ padding.ANSIX923(size)
+
+ @pytest.mark.parametrize(("size", "padded"), [
+ (128, b"1111"),
+ (128, b"1111111111111111"),
+ (128, b"111111111111111\x06"),
+ (128, b"1111111111\x06\x06\x06\x06\x06\x06"),
+ (128, b""),
+ (128, b"\x06" * 6),
+ (128, b"\x00" * 16),
+ ])
+ def test_invalid_padding(self, size, padded):
+ unpadder = padding.ANSIX923(size).unpadder()
+ with pytest.raises(ValueError):
+ unpadder.update(padded)
+ unpadder.finalize()
+
+ def test_non_bytes(self):
+ padder = padding.ANSIX923(128).padder()
+ with pytest.raises(TypeError):
+ padder.update(u"abc")
+ unpadder = padding.ANSIX923(128).unpadder()
+ with pytest.raises(TypeError):
+ unpadder.update(u"abc")
+
+ @pytest.mark.parametrize(("size", "unpadded", "padded"), [
+ (
+ 128,
+ b"1111111111",
+ b"1111111111\x00\x00\x00\x00\x00\x06",
+ ),
+ (
+ 128,
+ b"111111111111111122222222222222",
+ b"111111111111111122222222222222\x00\x02",
+ ),
+ (
+ 128,
+ b"1" * 16,
+ b"1" * 16 + b"\x00" * 15 + b"\x10",
+ ),
+ (
+ 128,
+ b"1" * 17,
+ b"1" * 17 + b"\x00" * 14 + b"\x0F",
+ )
+ ])
+ def test_pad(self, size, unpadded, padded):
+ padder = padding.ANSIX923(size).padder()
+ result = padder.update(unpadded)
+ result += padder.finalize()
+ assert result == padded
+
+ @pytest.mark.parametrize(("size", "unpadded", "padded"), [
+ (
+ 128,
+ b"1111111111",
+ b"1111111111\x00\x00\x00\x00\x00\x06",
+ ),
+ (
+ 128,
+ b"111111111111111122222222222222",
+ b"111111111111111122222222222222\x00\x02",
+ ),
+ ])
+ def test_unpad(self, size, unpadded, padded):
+ unpadder = padding.ANSIX923(size).unpadder()
+ result = unpadder.update(padded)
+ result += unpadder.finalize()
+ assert result == unpadded
+
+ def test_use_after_finalize(self):
+ padder = padding.ANSIX923(128).padder()
+ b = padder.finalize()
+ with pytest.raises(AlreadyFinalized):
+ padder.update(b"")
+ with pytest.raises(AlreadyFinalized):
+ padder.finalize()
+
+ unpadder = padding.ANSIX923(128).unpadder()
+ unpadder.update(b)
+ unpadder.finalize()
+ with pytest.raises(AlreadyFinalized):
+ unpadder.update(b"")
+ with pytest.raises(AlreadyFinalized):
+ unpadder.finalize()
diff --git a/tests/hazmat/primitives/test_pbkdf2hmac.py b/tests/hazmat/primitives/test_pbkdf2hmac.py
index 7fb6bbd6..0254b216 100644
--- a/tests/hazmat/primitives/test_pbkdf2hmac.py
+++ b/tests/hazmat/primitives/test_pbkdf2hmac.py
@@ -6,7 +6,6 @@ from __future__ import absolute_import, division, print_function
import pytest
-from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, _Reasons
)
@@ -14,16 +13,10 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+from ...doubles import DummyHashAlgorithm
from ...utils import raises_unsupported_algorithm
-@utils.register_interface(hashes.HashAlgorithm)
-class DummyHash(object):
- name = "dummy-hash"
- block_size = None
- digest_size = None
-
-
class TestPBKDF2HMAC(object):
def test_already_finalized(self):
kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
@@ -43,7 +36,9 @@ class TestPBKDF2HMAC(object):
def test_unsupported_algorithm(self):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
- PBKDF2HMAC(DummyHash(), 20, b"salt", 10, default_backend())
+ PBKDF2HMAC(
+ DummyHashAlgorithm(), 20, b"salt", 10, default_backend()
+ )
def test_invalid_key(self):
kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
@@ -62,6 +57,11 @@ class TestPBKDF2HMAC(object):
with pytest.raises(TypeError):
kdf.derive(u"unicode here")
+ def test_buffer_protocol(self, backend):
+ kdf = PBKDF2HMAC(hashes.SHA1(), 10, b"salt", 10, default_backend())
+ data = bytearray(b"data")
+ assert kdf.derive(data) == b"\xe9n\xaa\x81\xbbt\xa4\xf6\x08\xce"
+
def test_invalid_backend():
pretend_backend = object()
diff --git a/tests/hazmat/primitives/test_pkcs12.py b/tests/hazmat/primitives/test_pkcs12.py
new file mode 100644
index 00000000..0bb76e25
--- /dev/null
+++ b/tests/hazmat/primitives/test_pkcs12.py
@@ -0,0 +1,139 @@
+# 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 os
+
+import pytest
+
+from cryptography import x509
+from cryptography.hazmat.backends.interfaces import DERSerializationBackend
+from cryptography.hazmat.backends.openssl.backend import _RC2
+from cryptography.hazmat.primitives.serialization import load_pem_private_key
+from cryptography.hazmat.primitives.serialization.pkcs12 import (
+ load_key_and_certificates
+)
+
+from .utils import load_vectors_from_file
+
+
+@pytest.mark.requires_backend_interface(interface=DERSerializationBackend)
+class TestPKCS12(object):
+ def _test_load_pkcs12_ec_keys(self, filename, password, backend):
+ cert = load_vectors_from_file(
+ os.path.join("x509", "custom", "ca", "ca.pem"),
+ lambda pemfile: x509.load_pem_x509_certificate(
+ pemfile.read(), backend
+ ), mode="rb"
+ )
+ key = load_vectors_from_file(
+ os.path.join("x509", "custom", "ca", "ca_key.pem"),
+ lambda pemfile: load_pem_private_key(
+ pemfile.read(), None, backend
+ ), mode="rb"
+ )
+ parsed_key, parsed_cert, parsed_more_certs = load_vectors_from_file(
+ os.path.join("pkcs12", filename),
+ lambda derfile: load_key_and_certificates(
+ derfile.read(), password, backend
+ ), mode="rb"
+ )
+ assert parsed_cert == cert
+ assert parsed_key.private_numbers() == key.private_numbers()
+ assert parsed_more_certs == []
+
+ @pytest.mark.parametrize(
+ ("filename", "password"),
+ [
+ ("cert-key-aes256cbc.p12", b"cryptography"),
+ ("cert-none-key-none.p12", b"cryptography"),
+ ]
+ )
+ def test_load_pkcs12_ec_keys(self, filename, password, backend):
+ self._test_load_pkcs12_ec_keys(filename, password, backend)
+
+ @pytest.mark.parametrize(
+ ("filename", "password"),
+ [
+ ("cert-rc2-key-3des.p12", b"cryptography"),
+ ("no-password.p12", None),
+ ]
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(_RC2(), None),
+ skip_message="Does not support RC2"
+ )
+ def test_load_pkcs12_ec_keys_rc2(self, filename, password, backend):
+ self._test_load_pkcs12_ec_keys(filename, password, backend)
+
+ def test_load_pkcs12_cert_only(self, backend):
+ cert = load_vectors_from_file(
+ os.path.join("x509", "custom", "ca", "ca.pem"),
+ lambda pemfile: x509.load_pem_x509_certificate(
+ pemfile.read(), backend
+ ), mode="rb"
+ )
+ parsed_key, parsed_cert, parsed_more_certs = load_vectors_from_file(
+ os.path.join("pkcs12", "cert-aes256cbc-no-key.p12"),
+ lambda data: load_key_and_certificates(
+ data.read(), b"cryptography", backend
+ ),
+ mode="rb"
+ )
+ assert parsed_cert is None
+ assert parsed_key is None
+ assert parsed_more_certs == [cert]
+
+ def test_load_pkcs12_key_only(self, backend):
+ key = load_vectors_from_file(
+ os.path.join("x509", "custom", "ca", "ca_key.pem"),
+ lambda pemfile: load_pem_private_key(
+ pemfile.read(), None, backend
+ ), mode="rb"
+ )
+ parsed_key, parsed_cert, parsed_more_certs = load_vectors_from_file(
+ os.path.join("pkcs12", "no-cert-key-aes256cbc.p12"),
+ lambda data: load_key_and_certificates(
+ data.read(), b"cryptography", backend
+ ),
+ mode="rb"
+ )
+ assert parsed_key.private_numbers() == key.private_numbers()
+ assert parsed_cert is None
+ assert parsed_more_certs == []
+
+ def test_non_bytes(self, backend):
+ with pytest.raises(TypeError):
+ load_key_and_certificates(
+ b"irrelevant", object(), backend
+ )
+
+ def test_not_a_pkcs12(self, backend):
+ with pytest.raises(ValueError):
+ load_key_and_certificates(
+ b"invalid", b"pass", backend
+ )
+
+ def test_invalid_password(self, backend):
+ with pytest.raises(ValueError):
+ load_vectors_from_file(
+ os.path.join("pkcs12", "cert-key-aes256cbc.p12"),
+ lambda derfile: load_key_and_certificates(
+ derfile.read(), b"invalid", backend
+ ), mode="rb"
+ )
+
+ def test_buffer_protocol(self, backend):
+ p12 = load_vectors_from_file(
+ os.path.join("pkcs12", "cert-key-aes256cbc.p12"),
+ lambda derfile: derfile.read(), mode="rb"
+ )
+ p12buffer = bytearray(p12)
+ parsed_key, parsed_cert, parsed_more_certs = load_key_and_certificates(
+ p12buffer, bytearray(b"cryptography"), backend
+ )
+ assert parsed_key is not None
+ assert parsed_cert is not None
+ assert parsed_more_certs == []
diff --git a/tests/hazmat/primitives/test_poly1305.py b/tests/hazmat/primitives/test_poly1305.py
new file mode 100644
index 00000000..edca4623
--- /dev/null
+++ b/tests/hazmat/primitives/test_poly1305.py
@@ -0,0 +1,152 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import (
+ AlreadyFinalized, InvalidSignature, _Reasons
+)
+from cryptography.hazmat.primitives.poly1305 import Poly1305
+
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: not backend.poly1305_supported(),
+ skip_message="Requires OpenSSL without poly1305 support"
+)
+def test_poly1305_unsupported(backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MAC):
+ Poly1305(b"0" * 32)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.poly1305_supported(),
+ skip_message="Requires OpenSSL with poly1305 support"
+)
+class TestPoly1305(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("poly1305", "rfc7539.txt"), load_nist_vectors
+ )
+ )
+ def test_vectors(self, vector, backend):
+ key = binascii.unhexlify(vector["key"])
+ msg = binascii.unhexlify(vector["msg"])
+ tag = binascii.unhexlify(vector["tag"])
+ poly = Poly1305(key)
+ poly.update(msg)
+ assert poly.finalize() == tag
+
+ assert Poly1305.generate_tag(key, msg) == tag
+ Poly1305.verify_tag(key, msg, tag)
+
+ def test_key_with_no_additional_references(self, backend):
+ poly = Poly1305(os.urandom(32))
+ assert len(poly.finalize()) == 16
+
+ def test_raises_after_finalize(self, backend):
+ poly = Poly1305(b"0" * 32)
+ poly.finalize()
+
+ with pytest.raises(AlreadyFinalized):
+ poly.update(b"foo")
+
+ with pytest.raises(AlreadyFinalized):
+ poly.finalize()
+
+ def test_reject_unicode(self, backend):
+ poly = Poly1305(b"0" * 32)
+ with pytest.raises(TypeError):
+ poly.update(u'')
+
+ with pytest.raises(TypeError):
+ Poly1305.generate_tag(b"0" * 32, u'')
+
+ def test_verify(self, backend):
+ poly = Poly1305(b"0" * 32)
+ poly.update(b"msg")
+ tag = poly.finalize()
+
+ with pytest.raises(AlreadyFinalized):
+ poly.verify(b"")
+
+ poly2 = Poly1305(b"0" * 32)
+ poly2.update(b"msg")
+ poly2.verify(tag)
+
+ Poly1305.verify_tag(b"0" * 32, b"msg", tag)
+
+ def test_invalid_verify(self, backend):
+ poly = Poly1305(b"0" * 32)
+ poly.update(b"msg")
+ with pytest.raises(InvalidSignature):
+ poly.verify(b"")
+
+ p2 = Poly1305(b"0" * 32)
+ p2.update(b"msg")
+ with pytest.raises(InvalidSignature):
+ p2.verify(b"\x00" * 16)
+
+ with pytest.raises(InvalidSignature):
+ Poly1305.verify_tag(b"0" * 32, b"msg", b"\x00" * 16)
+
+ def test_verify_reject_unicode(self, backend):
+ poly = Poly1305(b"0" * 32)
+ with pytest.raises(TypeError):
+ poly.verify(u'')
+
+ with pytest.raises(TypeError):
+ Poly1305.verify_tag(b"0" * 32, b"msg", u'')
+
+ def test_invalid_key_type(self, backend):
+ with pytest.raises(TypeError):
+ Poly1305(object())
+
+ with pytest.raises(TypeError):
+ Poly1305.generate_tag(object(), b"msg")
+
+ def test_invalid_key_length(self, backend):
+ with pytest.raises(ValueError):
+ Poly1305(b"0" * 31)
+
+ with pytest.raises(ValueError):
+ Poly1305.generate_tag(b"0" * 31, b"msg")
+
+ with pytest.raises(ValueError):
+ Poly1305(b"0" * 33)
+
+ with pytest.raises(ValueError):
+ Poly1305.generate_tag(b"0" * 33, b"msg")
+
+ def test_buffer_protocol(self, backend):
+ key = binascii.unhexlify(
+ b"1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cb"
+ b"c207075c0"
+ )
+ msg = binascii.unhexlify(
+ b"2754776173206272696c6c69672c20616e642074686520736c69746"
+ b"87920746f7665730a446964206779726520616e642067696d626c65"
+ b"20696e2074686520776162653a0a416c6c206d696d7379207765726"
+ b"52074686520626f726f676f7665732c0a416e6420746865206d6f6d"
+ b"65207261746873206f757467726162652e"
+ )
+ key = bytearray(key)
+ poly = Poly1305(key)
+ poly.update(bytearray(msg))
+ assert poly.finalize() == binascii.unhexlify(
+ b"4541669a7eaaee61e708dc7cbcc5eb62"
+ )
+
+ assert Poly1305.generate_tag(key, msg) == binascii.unhexlify(
+ b"4541669a7eaaee61e708dc7cbcc5eb62"
+ )
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index eb12df8d..e6482651 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -6,12 +6,10 @@ from __future__ import absolute_import, division, print_function
import binascii
import itertools
-import math
import os
import pytest
-from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, _Reasons
)
@@ -19,38 +17,43 @@ from cryptography.hazmat.backends.interfaces import (
PEMSerializationBackend, RSABackend
)
from cryptography.hazmat.primitives import hashes, serialization
-from cryptography.hazmat.primitives.asymmetric import padding, rsa
+from cryptography.hazmat.primitives.asymmetric import (
+ padding, rsa, utils as asym_utils
+)
from cryptography.hazmat.primitives.asymmetric.rsa import (
RSAPrivateNumbers, RSAPublicNumbers
)
+from cryptography.utils import CryptographyDeprecationWarning
from .fixtures_rsa import (
RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027, RSA_KEY_1028,
RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031, RSA_KEY_1536, RSA_KEY_2048,
- RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, RSA_KEY_745,
- RSA_KEY_768,
+ RSA_KEY_2048_ALT, RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599,
+ RSA_KEY_745, RSA_KEY_768,
)
from .utils import (
_check_rsa_private_numbers, generate_rsa_verification_test
)
+from ...doubles import (
+ DummyAsymmetricPadding, DummyHashAlgorithm, DummyKeySerializationEncryption
+)
from ...utils import (
- load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file,
- raises_unsupported_algorithm
+ load_nist_vectors, load_pkcs1_vectors, load_rsa_nist_vectors,
+ load_vectors_from_file, raises_unsupported_algorithm
)
-@utils.register_interface(padding.AsymmetricPadding)
-class DummyPadding(object):
- name = "UNSUPPORTED-PADDING"
-
-
class DummyMGF(object):
_salt_length = 0
-@utils.register_interface(serialization.KeySerializationEncryption)
-class DummyKeyEncryption(object):
- pass
+def _check_rsa_private_numbers_if_serializable(key):
+ if isinstance(key, rsa.RSAPrivateKeyWithSerialization):
+ _check_rsa_private_numbers(key.private_numbers())
+
+
+def test_check_rsa_private_numbers_if_serializable():
+ _check_rsa_private_numbers_if_serializable("notserializable")
def _flatten_pkcs1_examples(vectors):
@@ -64,6 +67,60 @@ def _flatten_pkcs1_examples(vectors):
return flattened_vectors
+def _build_oaep_sha2_vectors():
+ base_path = os.path.join("asymmetric", "RSA", "oaep-custom")
+ vectors = []
+ hashalgs = [
+ hashes.SHA1(),
+ hashes.SHA224(),
+ hashes.SHA256(),
+ hashes.SHA384(),
+ hashes.SHA512(),
+ ]
+ for mgf1alg, oaepalg in itertools.product(hashalgs, hashalgs):
+ if mgf1alg.name == "sha1" and oaepalg.name == "sha1":
+ # We need to generate the cartesian product of the permutations
+ # of all the SHAs above, but SHA1/SHA1 is something we already
+ # tested previously and thus did not generate custom vectors for.
+ continue
+
+ examples = _flatten_pkcs1_examples(
+ load_vectors_from_file(
+ os.path.join(
+ base_path,
+ "oaep-{}-{}.txt".format(
+ mgf1alg.name, oaepalg.name
+ )
+ ),
+ load_pkcs1_vectors
+ )
+ )
+ # We've loaded the files, but the loaders don't give us any information
+ # about the mgf1 or oaep hash algorithms. We know this info so we'll
+ # just add that to the end of the tuple
+ for private, public, vector in examples:
+ vectors.append((private, public, vector, mgf1alg, oaepalg))
+ return vectors
+
+
+def _skip_pss_hash_algorithm_unsupported(backend, hash_alg):
+ if not backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hash_alg),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ):
+ pytest.skip(
+ "Does not support {} in MGF1 using PSS.".format(hash_alg.name)
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+def test_skip_pss_hash_algorithm_unsupported(backend):
+ with pytest.raises(pytest.skip.Exception):
+ _skip_pss_hash_algorithm_unsupported(backend, DummyHashAlgorithm())
+
+
def test_modular_inverse():
p = int(
"d1f9f6c09fd3d38987f7970247b85a6da84907753d42ec52bc23b745093f4fff5cff3"
@@ -85,21 +142,6 @@ def test_modular_inverse():
)
-def _skip_if_no_serialization(key, backend):
- if not isinstance(
- key,
- (rsa.RSAPrivateKeyWithSerialization, rsa.RSAPublicKeyWithSerialization)
- ):
- pytest.skip(
- "{0} does not support RSA key serialization".format(backend)
- )
-
-
-def test_skip_if_no_serialization():
- with pytest.raises(pytest.skip.Exception):
- _skip_if_no_serialization("notakeywithserialization", "backend")
-
-
@pytest.mark.requires_backend_interface(interface=RSABackend)
class TestRSA(object):
@pytest.mark.parametrize(
@@ -113,10 +155,9 @@ class TestRSA(object):
skey = rsa.generate_private_key(public_exponent, key_size, backend)
assert skey.key_size == key_size
- if isinstance(skey, rsa.RSAPrivateKeyWithSerialization):
- _check_rsa_private_numbers(skey.private_numbers())
- pkey = skey.public_key()
- assert isinstance(pkey.public_numbers(), rsa.RSAPublicNumbers)
+ _check_rsa_private_numbers_if_serializable(skey)
+ pkey = skey.public_key()
+ assert isinstance(pkey.public_numbers(), rsa.RSAPublicNumbers)
def test_generate_bad_public_exponent(self, backend):
with pytest.raises(ValueError):
@@ -177,6 +218,133 @@ class TestRSA(object):
assert public_num.n == public_num2.n
assert public_num.e == public_num2.e
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "RSA", "oaep-label.txt"),
+ load_nist_vectors)
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=b"label"
+ )
+ ),
+ skip_message="Does not support RSA OAEP labels"
+ )
+ def test_oaep_label_decrypt(self, vector, backend):
+ private_key = serialization.load_der_private_key(
+ binascii.unhexlify(vector["key"]), None, backend
+ )
+ assert vector["oaepdigest"] == b"SHA512"
+ decrypted = private_key.decrypt(
+ binascii.unhexlify(vector["input"]),
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA512()),
+ algorithm=hashes.SHA512(),
+ label=binascii.unhexlify(vector["oaeplabel"])
+ )
+ )
+ assert vector["output"][1:-1] == decrypted
+
+ @pytest.mark.parametrize(
+ ("msg", "label"),
+ [
+ (b"amazing encrypted msg", b"some label"),
+ (b"amazing encrypted msg", b""),
+ ]
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=b"label"
+ )
+ ),
+ skip_message="Does not support RSA OAEP labels"
+ )
+ def test_oaep_label_roundtrip(self, msg, label, backend):
+ private_key = RSA_KEY_2048.private_key(backend)
+ ct = private_key.public_key().encrypt(
+ msg,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=label
+ )
+ )
+ pt = private_key.decrypt(
+ ct,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=label
+ )
+ )
+ assert pt == msg
+
+ @pytest.mark.parametrize(
+ ("enclabel", "declabel"),
+ [
+ (b"label1", b"label2"),
+ (b"label3", b""),
+ (b"", b"label4"),
+ ]
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=b"label"
+ )
+ ),
+ skip_message="Does not support RSA OAEP labels"
+ )
+ def test_oaep_wrong_label(self, enclabel, declabel, backend):
+ private_key = RSA_KEY_2048.private_key(backend)
+ msg = b"test"
+ ct = private_key.public_key().encrypt(
+ msg, padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=enclabel
+ )
+ )
+ with pytest.raises(ValueError):
+ private_key.decrypt(
+ ct, padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=declabel
+ )
+ )
+
+ @pytest.mark.supported(
+ only_if=lambda backend: not backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=b"label"
+ )
+ ),
+ skip_message="Requires backend without RSA OAEP label support"
+ )
+ def test_unsupported_oaep_label_decrypt(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
+ private_key.decrypt(
+ b"0" * 64,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=b"label"
+ )
+ )
+
def test_rsa_generate_invalid_backend():
pretend_backend = object()
@@ -215,9 +383,11 @@ class TestRSASignature(object):
n=private["modulus"]
)
).private_key(backend)
- signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
- signer.update(binascii.unhexlify(example["message"]))
- signature = signer.finalize()
+ signature = private_key.sign(
+ binascii.unhexlify(example["message"]),
+ padding.PKCS1v15(),
+ hashes.SHA1()
+ )
assert binascii.hexlify(signature) == example["signature"]
@pytest.mark.supported(
@@ -255,56 +425,43 @@ class TestRSASignature(object):
e=public["public_exponent"],
n=public["modulus"]
).public_key(backend)
- signer = private_key.signer(
+ signature = private_key.sign(
+ binascii.unhexlify(example["message"]),
padding.PSS(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA1()
)
- signer.update(binascii.unhexlify(example["message"]))
- signature = signer.finalize()
- assert len(signature) == math.ceil(private_key.key_size / 8.0)
+ assert len(signature) == (private_key.key_size + 7) // 8
# PSS signatures contain randomness so we can't do an exact
# signature check. Instead we'll verify that the signature created
# successfully verifies.
- verifier = public_key.verifier(
+ public_key.verify(
signature,
+ binascii.unhexlify(example["message"]),
padding.PSS(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA1(),
)
- verifier.update(binascii.unhexlify(example["message"]))
- verifier.verify()
@pytest.mark.parametrize(
"hash_alg",
[hashes.SHA224(), hashes.SHA256(), hashes.SHA384(), hashes.SHA512()]
)
def test_pss_signing_sha2(self, hash_alg, backend):
- if not backend.rsa_padding_supported(
- padding.PSS(
- mgf=padding.MGF1(hash_alg),
- salt_length=padding.PSS.MAX_LENGTH
- )
- ):
- pytest.skip(
- "Does not support {0} in MGF1 using PSS.".format(hash_alg.name)
- )
+ _skip_pss_hash_algorithm_unsupported(backend, hash_alg)
private_key = RSA_KEY_768.private_key(backend)
public_key = private_key.public_key()
pss = padding.PSS(
mgf=padding.MGF1(hash_alg),
salt_length=padding.PSS.MAX_LENGTH
)
- signer = private_key.signer(pss, hash_alg)
- signer.update(b"testing signature")
- signature = signer.finalize()
- verifier = public_key.verifier(signature, pss, hash_alg)
- verifier.update(b"testing signature")
- verifier.verify()
+ msg = b"testing signature"
+ signature = private_key.sign(msg, pss, hash_alg)
+ public_key.verify(signature, msg, pss, hash_alg)
@pytest.mark.supported(
only_if=lambda backend: (
@@ -320,15 +477,14 @@ class TestRSASignature(object):
)
def test_pss_minimum_key_size_for_digest(self, backend):
private_key = RSA_KEY_522.private_key(backend)
- signer = private_key.signer(
+ private_key.sign(
+ b"no failure",
padding.PSS(
mgf=padding.MGF1(hashes.SHA1()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA512()
)
- signer.update(b"no failure")
- signer.finalize()
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -346,7 +502,8 @@ class TestRSASignature(object):
def test_pss_signing_digest_too_large_for_key_size(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with pytest.raises(ValueError):
- private_key.signer(
+ private_key.sign(
+ b"msg",
padding.PSS(
mgf=padding.MGF1(hashes.SHA1()),
salt_length=padding.PSS.MAX_LENGTH
@@ -365,16 +522,15 @@ class TestRSASignature(object):
)
def test_pss_signing_salt_length_too_long(self, backend):
private_key = RSA_KEY_512.private_key(backend)
- signer = private_key.signer(
- padding.PSS(
- mgf=padding.MGF1(hashes.SHA1()),
- salt_length=1000000
- ),
- hashes.SHA1()
- )
- signer.update(b"failure coming")
with pytest.raises(ValueError):
- signer.finalize()
+ private_key.sign(
+ b"failure coming",
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=1000000
+ ),
+ hashes.SHA1()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -384,7 +540,8 @@ class TestRSASignature(object):
)
def test_use_after_finalize(self, backend):
private_key = RSA_KEY_512.private_key(backend)
- signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
+ with pytest.warns(CryptographyDeprecationWarning):
+ signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
signer.update(b"sign me")
signer.finalize()
with pytest.raises(AlreadyFinalized):
@@ -395,12 +552,12 @@ class TestRSASignature(object):
def test_unsupported_padding(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
- private_key.signer(DummyPadding(), hashes.SHA1())
+ private_key.sign(b"msg", DummyAsymmetricPadding(), hashes.SHA1())
def test_padding_incorrect_type(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with pytest.raises(TypeError):
- private_key.signer("notpadding", hashes.SHA1())
+ private_key.sign(b"msg", "notpadding", hashes.SHA1())
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -411,7 +568,8 @@ class TestRSASignature(object):
def test_unsupported_pss_mgf(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
- private_key.signer(
+ private_key.sign(
+ b"msg",
padding.PSS(
mgf=DummyMGF(),
salt_length=padding.PSS.MAX_LENGTH
@@ -427,13 +585,12 @@ class TestRSASignature(object):
)
def test_pkcs1_digest_too_large_for_key_size(self, backend):
private_key = RSA_KEY_599.private_key(backend)
- signer = private_key.signer(
- padding.PKCS1v15(),
- hashes.SHA512()
- )
- signer.update(b"failure coming")
with pytest.raises(ValueError):
- signer.finalize()
+ private_key.sign(
+ b"failure coming",
+ padding.PKCS1v15(),
+ hashes.SHA512()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -443,12 +600,104 @@ class TestRSASignature(object):
)
def test_pkcs1_minimum_key_size(self, backend):
private_key = RSA_KEY_745.private_key(backend)
- signer = private_key.signer(
+ private_key.sign(
+ b"no failure",
padding.PKCS1v15(),
hashes.SHA512()
)
- signer.update(b"no failure")
- signer.finalize()
+
+ def test_sign(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ message = b"one little message"
+ pkcs = padding.PKCS1v15()
+ algorithm = hashes.SHA1()
+ signature = private_key.sign(message, pkcs, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, pkcs, algorithm)
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ ),
+ skip_message="Does not support PSS."
+ )
+ def test_prehashed_sign(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ digest = h.finalize()
+ pss = padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ prehashed_alg = asym_utils.Prehashed(hashes.SHA1())
+ signature = private_key.sign(digest, pss, prehashed_alg)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, pss, hashes.SHA1())
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.hash_supported(
+ hashes.BLAKE2s(digest_size=32)),
+ skip_message="Does not support BLAKE2s",
+ )
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ ),
+ skip_message="Does not support PSS."
+ )
+ def test_unsupported_hash(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ message = b"one little message"
+ pss = padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=0)
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
+ private_key.sign(message, pss, hashes.BLAKE2s(32))
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ ),
+ skip_message="Does not support PSS."
+ )
+ def test_prehashed_digest_mismatch(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA512(), backend)
+ h.update(message)
+ digest = h.finalize()
+ pss = padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ prehashed_alg = asym_utils.Prehashed(hashes.SHA1())
+ with pytest.raises(ValueError):
+ private_key.sign(digest, pss, prehashed_alg)
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
+ def test_prehashed_unsupported_in_signer_ctx(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ private_key.signer(
+ padding.PKCS1v15(),
+ asym_utils.Prehashed(hashes.SHA1())
+ )
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
+ def test_prehashed_unsupported_in_verifier_ctx(self, backend):
+ public_key = RSA_KEY_512.private_key(backend).public_key()
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ public_key.verifier(
+ b"0" * 64,
+ padding.PKCS1v15(),
+ asym_utils.Prehashed(hashes.SHA1())
+ )
@pytest.mark.requires_backend_interface(interface=RSABackend)
@@ -473,13 +722,12 @@ class TestRSAVerification(object):
e=public["public_exponent"],
n=public["modulus"]
).public_key(backend)
- verifier = public_key.verifier(
+ public_key.verify(
binascii.unhexlify(example["signature"]),
+ binascii.unhexlify(example["message"]),
padding.PKCS1v15(),
hashes.SHA1()
)
- verifier.update(binascii.unhexlify(example["message"]))
- verifier.verify()
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -490,17 +738,51 @@ class TestRSAVerification(object):
def test_invalid_pkcs1v15_signature_wrong_data(self, backend):
private_key = RSA_KEY_512.private_key(backend)
public_key = private_key.public_key()
- signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
- signer.update(b"sign me")
- signature = signer.finalize()
- verifier = public_key.verifier(
- signature,
- padding.PKCS1v15(),
- hashes.SHA1()
+ signature = private_key.sign(
+ b"sign me", padding.PKCS1v15(), hashes.SHA1()
)
- verifier.update(b"incorrect data")
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature,
+ b"incorrect data",
+ padding.PKCS1v15(),
+ hashes.SHA1()
+ )
+
+ def test_invalid_signature_sequence_removed(self, backend):
+ """
+ This test comes from wycheproof
+ """
+ key_der = binascii.unhexlify(
+ b"30820122300d06092a864886f70d01010105000382010f003082010a02820101"
+ b"00a2b451a07d0aa5f96e455671513550514a8a5b462ebef717094fa1fee82224"
+ b"e637f9746d3f7cafd31878d80325b6ef5a1700f65903b469429e89d6eac88450"
+ b"97b5ab393189db92512ed8a7711a1253facd20f79c15e8247f3d3e42e46e48c9"
+ b"8e254a2fe9765313a03eff8f17e1a029397a1fa26a8dce26f490ed81299615d9"
+ b"814c22da610428e09c7d9658594266f5c021d0fceca08d945a12be82de4d1ece"
+ b"6b4c03145b5d3495d4ed5411eb878daf05fd7afc3e09ada0f1126422f590975a"
+ b"1969816f48698bcbba1b4d9cae79d460d8f9f85e7975005d9bc22c4e5ac0f7c1"
+ b"a45d12569a62807d3b9a02e5a530e773066f453d1f5b4c2e9cf7820283f742b9"
+ b"d50203010001"
+ )
+ sig = binascii.unhexlify(
+ b"498209f59a0679a1f926eccf3056da2cba553d7ab3064e7c41ad1d739f038249"
+ b"f02f5ad12ee246073d101bc3cdb563e8b6be61562056422b7e6c16ad53deb12a"
+ b"f5de744197753a35859833f41bb59c6597f3980132b7478fd0b95fd27dfad64a"
+ b"20fd5c25312bbd41a85286cd2a83c8df5efa0779158d01b0747ff165b055eb28"
+ b"80ea27095700a295593196d8c5922cf6aa9d7e29b5056db5ded5eb20aeb31b89"
+ b"42e26b15a5188a4934cd7e39cfe379a197f49a204343a493452deebca436ee61"
+ b"4f4daf989e355544489f7e69ffa8ccc6a1e81cf0ab33c3e6d7591091485a6a31"
+ b"bda3b33946490057b9a3003d3fd9daf7c4778b43fd46144d945d815f12628ff4"
+ )
+ public_key = serialization.load_der_public_key(key_der, backend)
+ with pytest.raises(InvalidSignature):
+ public_key.verify(
+ sig,
+ binascii.unhexlify(b"313233343030"),
+ padding.PKCS1v15(),
+ hashes.SHA256()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -512,17 +794,12 @@ class TestRSAVerification(object):
private_key = RSA_KEY_512.private_key(backend)
private_key2 = RSA_KEY_512_ALT.private_key(backend)
public_key = private_key2.public_key()
- signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
- signer.update(b"sign me")
- signature = signer.finalize()
- verifier = public_key.verifier(
- signature,
- padding.PKCS1v15(),
- hashes.SHA1()
- )
- verifier.update(b"sign me")
+ msg = b"sign me"
+ signature = private_key.sign(msg, padding.PKCS1v15(), hashes.SHA1())
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature, msg, padding.PKCS1v15(), hashes.SHA1()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -547,16 +824,15 @@ class TestRSAVerification(object):
e=public["public_exponent"],
n=public["modulus"]
).public_key(backend)
- verifier = public_key.verifier(
+ public_key.verify(
binascii.unhexlify(example["signature"]),
+ binascii.unhexlify(example["message"]),
padding.PSS(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
salt_length=20
),
hashes.SHA1()
)
- verifier.update(binascii.unhexlify(example["message"]))
- verifier.verify()
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -580,17 +856,16 @@ class TestRSAVerification(object):
b"0e68c3649df91c5bc3665f96e157efa75b71934aaa514d91e94ca8418d100f45"
b"6f05288e58525f99666bab052adcffdf7186eb40f583bd38d98c97d3d524808b"
)
- verifier = public_key.verifier(
- signature,
- padding.PSS(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- salt_length=padding.PSS.MAX_LENGTH
- ),
- hashes.SHA1()
- )
- verifier.update(b"incorrect data")
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature,
+ b"incorrect data",
+ padding.PSS(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ ),
+ hashes.SHA1()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -616,17 +891,16 @@ class TestRSAVerification(object):
),
e=65537
).public_key(backend)
- verifier = public_key.verifier(
- signature,
- padding.PSS(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- salt_length=padding.PSS.MAX_LENGTH
- ),
- hashes.SHA1()
- )
- verifier.update(b"sign me")
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature,
+ b"sign me",
+ padding.PSS(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ ),
+ hashes.SHA1()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -652,17 +926,16 @@ class TestRSAVerification(object):
),
e=65537
).public_key(backend)
- verifier = public_key.verifier(
- signature,
- padding.PSS(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- salt_length=padding.PSS.MAX_LENGTH
- ),
- hashes.SHA1()
- )
- verifier.update(b"sign me")
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature,
+ b"sign me",
+ padding.PSS(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ ),
+ hashes.SHA1()
+ )
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -673,15 +946,16 @@ class TestRSAVerification(object):
def test_use_after_finalize(self, backend):
private_key = RSA_KEY_512.private_key(backend)
public_key = private_key.public_key()
- signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
- signer.update(b"sign me")
- signature = signer.finalize()
-
- verifier = public_key.verifier(
- signature,
- padding.PKCS1v15(),
- hashes.SHA1()
+ signature = private_key.sign(
+ b"sign me", padding.PKCS1v15(), hashes.SHA1()
)
+
+ with pytest.warns(CryptographyDeprecationWarning):
+ verifier = public_key.verifier(
+ signature,
+ padding.PKCS1v15(),
+ hashes.SHA1()
+ )
verifier.update(b"sign me")
verifier.verify()
with pytest.raises(AlreadyFinalized):
@@ -693,13 +967,33 @@ class TestRSAVerification(object):
private_key = RSA_KEY_512.private_key(backend)
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
- public_key.verifier(b"sig", DummyPadding(), hashes.SHA1())
+ public_key.verify(
+ b"sig", b"msg", DummyAsymmetricPadding(), hashes.SHA1()
+ )
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
+ def test_signature_not_bytes(self, backend):
+ public_key = RSA_KEY_512.public_numbers.public_key(backend)
+ signature = 1234
+
+ with pytest.raises(TypeError), \
+ pytest.warns(CryptographyDeprecationWarning):
+ public_key.verifier(
+ signature,
+ padding.PKCS1v15(),
+ hashes.SHA1()
+ )
def test_padding_incorrect_type(self, backend):
private_key = RSA_KEY_512.private_key(backend)
public_key = private_key.public_key()
with pytest.raises(TypeError):
- public_key.verifier(b"sig", "notpadding", hashes.SHA1())
+ public_key.verify(b"sig", b"msg", "notpadding", hashes.SHA1())
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -711,8 +1005,9 @@ class TestRSAVerification(object):
private_key = RSA_KEY_512.private_key(backend)
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
- public_key.verifier(
+ public_key.verify(
b"sig",
+ b"msg",
padding.PSS(
mgf=DummyMGF(),
salt_length=padding.PSS.MAX_LENGTH
@@ -741,8 +1036,9 @@ class TestRSAVerification(object):
)
public_key = private_key.public_key()
with pytest.raises(ValueError):
- public_key.verifier(
+ public_key.verify(
signature,
+ b"msg doesn't matter",
padding.PSS(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
salt_length=padding.PSS.MAX_LENGTH
@@ -772,19 +1068,50 @@ class TestRSAVerification(object):
),
e=65537
).public_key(backend)
- verifier = public_key.verifier(
- signature,
- padding.PSS(
- mgf=padding.MGF1(
- algorithm=hashes.SHA1(),
- ),
- salt_length=1000000
- ),
- hashes.SHA1()
- )
- verifier.update(b"sign me")
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature,
+ b"sign me",
+ padding.PSS(
+ mgf=padding.MGF1(
+ algorithm=hashes.SHA1(),
+ ),
+ salt_length=1000000
+ ),
+ hashes.SHA1()
+ )
+
+ def test_verify(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ message = b"one little message"
+ pkcs = padding.PKCS1v15()
+ algorithm = hashes.SHA1()
+ signature = private_key.sign(message, pkcs, algorithm)
+ public_key = private_key.public_key()
+ public_key.verify(signature, message, pkcs, algorithm)
+
+ def test_prehashed_verify(self, backend):
+ private_key = RSA_KEY_512.private_key(backend)
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ digest = h.finalize()
+ prehashed_alg = asym_utils.Prehashed(hashes.SHA1())
+ pkcs = padding.PKCS1v15()
+ signature = private_key.sign(message, pkcs, hashes.SHA1())
+ public_key = private_key.public_key()
+ public_key.verify(signature, digest, pkcs, prehashed_alg)
+
+ def test_prehashed_digest_mismatch(self, backend):
+ public_key = RSA_KEY_512.private_key(backend).public_key()
+ message = b"one little message"
+ h = hashes.Hash(hashes.SHA1(), backend)
+ h.update(message)
+ data = h.finalize()
+ prehashed_alg = asym_utils.Prehashed(hashes.SHA512())
+ pkcs = padding.PKCS1v15()
+ with pytest.raises(ValueError):
+ public_key.verify(b"\x00" * 64, data, pkcs, prehashed_alg)
@pytest.mark.requires_backend_interface(interface=RSABackend)
@@ -1009,6 +1336,10 @@ class TestRSAPKCS1Verification(object):
class TestPSS(object):
+ def test_calculate_max_pss_salt_length(self):
+ with pytest.raises(TypeError):
+ padding.calculate_max_pss_salt_length(object(), hashes.SHA256())
+
def test_invalid_salt_length_not_integer(self):
with pytest.raises(TypeError):
padding.PSS(
@@ -1096,14 +1427,14 @@ class TestRSADecryption(object):
)
).private_key(backend)
ciphertext = binascii.unhexlify(example["encryption"])
- assert len(ciphertext) == math.ceil(skey.key_size / 8.0)
+ assert len(ciphertext) == (skey.key_size + 7) // 8
message = skey.decrypt(ciphertext, padding.PKCS1v15())
assert message == binascii.unhexlify(example["message"])
def test_unsupported_padding(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
- private_key.decrypt(b"0" * 64, DummyPadding())
+ private_key.decrypt(b"0" * 64, DummyAsymmetricPadding())
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
@@ -1193,6 +1524,119 @@ class TestRSADecryption(object):
)
assert message == binascii.unhexlify(example["message"])
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA224()),
+ algorithm=hashes.SHA224(),
+ label=None
+ )
+ ),
+ skip_message="Does not support OAEP using SHA224 MGF1 and SHA224 hash."
+ )
+ @pytest.mark.parametrize(
+ "vector",
+ _build_oaep_sha2_vectors()
+ )
+ def test_decrypt_oaep_sha2_vectors(self, vector, backend):
+ private, public, example, mgf1_alg, hash_alg = vector
+ skey = rsa.RSAPrivateNumbers(
+ p=private["p"],
+ q=private["q"],
+ d=private["private_exponent"],
+ dmp1=private["dmp1"],
+ dmq1=private["dmq1"],
+ iqmp=private["iqmp"],
+ public_numbers=rsa.RSAPublicNumbers(
+ e=private["public_exponent"],
+ n=private["modulus"]
+ )
+ ).private_key(backend)
+ message = skey.decrypt(
+ binascii.unhexlify(example["encryption"]),
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=mgf1_alg),
+ algorithm=hash_alg,
+ label=None
+ )
+ )
+ assert message == binascii.unhexlify(example["message"])
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ ),
+ skip_message="Does not support OAEP."
+ )
+ def test_invalid_oaep_decryption(self, backend):
+ # More recent versions of OpenSSL may raise RSA_R_OAEP_DECODING_ERROR
+ # This test triggers it and confirms that we properly handle it. Other
+ # backends should also return the proper ValueError.
+ private_key = RSA_KEY_512.private_key(backend)
+
+ ciphertext = private_key.public_key().encrypt(
+ b'secure data',
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ )
+
+ private_key_alt = RSA_KEY_512_ALT.private_key(backend)
+
+ with pytest.raises(ValueError):
+ private_key_alt.decrypt(
+ ciphertext,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ )
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ ),
+ skip_message="Does not support OAEP."
+ )
+ def test_invalid_oaep_decryption_data_to_large_for_modulus(self, backend):
+ key = RSA_KEY_2048_ALT.private_key(backend)
+
+ ciphertext = (
+ b'\xb1ph\xc0\x0b\x1a|\xe6\xda\xea\xb5\xd7%\x94\x07\xf96\xfb\x96'
+ b'\x11\x9b\xdc4\xea.-\x91\x80\x13S\x94\x04m\xe9\xc5/F\x1b\x9b:\\'
+ b'\x1d\x04\x16ML\xae\xb32J\x01yuA\xbb\x83\x1c\x8f\xf6\xa5\xdbp\xcd'
+ b'\nx\xc7\xf6\x15\xb2/\xdcH\xae\xe7\x13\x13by\r4t\x99\x0fc\x1f\xc1'
+ b'\x1c\xb1\xdd\xc5\x08\xd1\xee\xa1XQ\xb8H@L5v\xc3\xaf\xf2\r\x97'
+ b'\xed\xaa\xe7\xf1\xd4xai\xd3\x83\xd9\xaa9\xbfx\xe1\x87F \x01\xff'
+ b'L\xccv}ae\xb3\xfa\xf2B\xb8\xf9\x04H\x94\x85\xcb\x86\xbb\\ghx!W31'
+ b'\xc7;t\na_E\xc2\x16\xb0;\xa1\x18\t\x1b\xe1\xdb\x80>)\x15\xc6\x12'
+ b'\xcb\xeeg`\x8b\x9b\x1b\x05y4\xb0\x84M6\xcd\xa1\x827o\xfd\x96\xba'
+ b'Z#\x8d\xae\x01\xc9\xf2\xb6\xde\x89{8&eQ\x1e8\x03\x01#?\xb66\\'
+ b'\xad.\xe9\xfa!\x95 c{\xcaz\xe0*\tP\r\x91\x9a)B\xb5\xadN\xf4$\x83'
+ b'\t\xb5u\xab\x19\x99'
+ )
+
+ with pytest.raises(ValueError):
+ key.decrypt(
+ ciphertext,
+ padding.OAEP(
+ algorithm=hashes.SHA1(),
+ mgf=padding.MGF1(hashes.SHA1()),
+ label=None
+ )
+ )
+
def test_unsupported_oaep_mgf(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
@@ -1239,7 +1683,48 @@ class TestRSAEncryption(object):
public_key = private_key.public_key()
ct = public_key.encrypt(pt, pad)
assert ct != pt
- assert len(ct) == math.ceil(public_key.key_size / 8.0)
+ assert len(ct) == (public_key.key_size + 7) // 8
+ recovered_pt = private_key.decrypt(ct, pad)
+ assert recovered_pt == pt
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA512(),
+ label=None
+ )
+ ),
+ skip_message="Does not support OAEP using SHA256 MGF1 and SHA512 hash."
+ )
+ @pytest.mark.parametrize(
+ ("mgf1hash", "oaephash"),
+ itertools.product([
+ hashes.SHA1(),
+ hashes.SHA224(),
+ hashes.SHA256(),
+ hashes.SHA384(),
+ hashes.SHA512(),
+ ], [
+ hashes.SHA1(),
+ hashes.SHA224(),
+ hashes.SHA256(),
+ hashes.SHA384(),
+ hashes.SHA512(),
+ ])
+ )
+ def test_rsa_encrypt_oaep_sha2(self, mgf1hash, oaephash, backend):
+ pad = padding.OAEP(
+ mgf=padding.MGF1(algorithm=mgf1hash),
+ algorithm=oaephash,
+ label=None
+ )
+ private_key = RSA_KEY_2048.private_key(backend)
+ pt = b"encrypt me using sha2 hashes!"
+ public_key = private_key.public_key()
+ ct = public_key.encrypt(pt, pad)
+ assert ct != pt
+ assert len(ct) == (public_key.key_size + 7) // 8
recovered_pt = private_key.decrypt(ct, pad)
assert recovered_pt == pt
@@ -1264,7 +1749,7 @@ class TestRSAEncryption(object):
public_key = private_key.public_key()
ct = public_key.encrypt(pt, pad)
assert ct != pt
- assert len(ct) == math.ceil(public_key.key_size / 8.0)
+ assert len(ct) == (public_key.key_size + 7) // 8
recovered_pt = private_key.decrypt(ct, pad)
assert recovered_pt == pt
@@ -1306,7 +1791,7 @@ class TestRSAEncryption(object):
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
- public_key.encrypt(b"somedata", DummyPadding())
+ public_key.encrypt(b"somedata", DummyAsymmetricPadding())
with pytest.raises(TypeError):
public_key.encrypt(b"somedata", padding=object())
@@ -1367,304 +1852,81 @@ class TestRSANumbers(object):
with pytest.raises(TypeError):
rsa.RSAPublicNumbers(e=1, n=None)
- def test_private_numbers_invalid_types(self):
- public_numbers = rsa.RSAPublicNumbers(e=1, n=15)
-
- with pytest.raises(TypeError):
- rsa.RSAPrivateNumbers(
- p=None,
- q=5,
- d=1,
- dmp1=1,
- dmq1=1,
- iqmp=2,
- public_numbers=public_numbers
- )
-
- with pytest.raises(TypeError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=None,
- d=1,
- dmp1=1,
- dmq1=1,
- iqmp=2,
- public_numbers=public_numbers
- )
-
- with pytest.raises(TypeError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=5,
- d=None,
- dmp1=1,
- dmq1=1,
- iqmp=2,
- public_numbers=public_numbers
- )
-
- with pytest.raises(TypeError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=5,
- d=1,
- dmp1=None,
- dmq1=1,
- iqmp=2,
- public_numbers=public_numbers
- )
-
- with pytest.raises(TypeError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=5,
- d=1,
- dmp1=1,
- dmq1=None,
- iqmp=2,
- public_numbers=public_numbers
- )
-
+ @pytest.mark.parametrize(
+ ("p", "q", "d", "dmp1", "dmq1", "iqmp", "public_numbers"),
+ [
+ (None, 5, 1, 1, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)),
+ (3, None, 1, 1, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)),
+ (3, 5, None, 1, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)),
+ (3, 5, 1, None, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)),
+ (3, 5, 1, 1, None, 2, rsa.RSAPublicNumbers(e=1, n=15)),
+ (3, 5, 1, 1, 1, None, rsa.RSAPublicNumbers(e=1, n=15)),
+ (3, 5, 1, 1, 1, 2, None),
+ ]
+ )
+ def test_private_numbers_invalid_types(self, p, q, d, dmp1, dmq1, iqmp,
+ public_numbers):
with pytest.raises(TypeError):
rsa.RSAPrivateNumbers(
- p=3,
- q=5,
- d=1,
- dmp1=1,
- dmq1=1,
- iqmp=None,
+ p=p, q=q,
+ d=d,
+ dmp1=dmp1,
+ dmq1=dmq1,
+ iqmp=iqmp,
public_numbers=public_numbers
)
- with pytest.raises(TypeError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=5,
- d=1,
- dmp1=1,
- dmq1=1,
- iqmp=2,
- public_numbers=None
- )
-
- def test_invalid_public_numbers_argument_values(self, backend):
+ @pytest.mark.parametrize(
+ ("e", "n"),
+ [
+ (7, 2), # modulus < 3
+ (1, 15), # public_exponent < 3
+ (17, 15), # public_exponent > modulus
+ (14, 15), # public_exponent not odd
+ ]
+ )
+ def test_invalid_public_numbers_argument_values(self, e, n, backend):
# Start with public_exponent=7, modulus=15. Then change one value at a
# time to test the bounds.
- # Test a modulus < 3.
-
- with pytest.raises(ValueError):
- rsa.RSAPublicNumbers(e=7, n=2).public_key(backend)
-
- # Test a public_exponent < 3
with pytest.raises(ValueError):
- rsa.RSAPublicNumbers(e=1, n=15).public_key(backend)
+ rsa.RSAPublicNumbers(e=e, n=n).public_key(backend)
- # Test a public_exponent > modulus
- with pytest.raises(ValueError):
- rsa.RSAPublicNumbers(e=17, n=15).public_key(backend)
-
- # Test a public_exponent that is not odd.
- with pytest.raises(ValueError):
- rsa.RSAPublicNumbers(e=14, n=15).public_key(backend)
-
- def test_invalid_private_numbers_argument_values(self, backend):
+ @pytest.mark.parametrize(
+ ("p", "q", "d", "dmp1", "dmq1", "iqmp", "e", "n"),
+ [
+ (3, 11, 3, 1, 3, 2, 7, 2), # modulus < 3
+ (3, 11, 3, 1, 3, 2, 7, 35), # modulus != p * q
+ (37, 11, 3, 1, 3, 2, 7, 33), # p > modulus
+ (3, 37, 3, 1, 3, 2, 7, 33), # q > modulus
+ (3, 11, 3, 35, 3, 2, 7, 33), # dmp1 > modulus
+ (3, 11, 3, 1, 35, 2, 7, 33), # dmq1 > modulus
+ (3, 11, 3, 1, 3, 35, 7, 33), # iqmp > modulus
+ (3, 11, 37, 1, 3, 2, 7, 33), # d > modulus
+ (3, 11, 3, 1, 3, 2, 1, 33), # public_exponent < 3
+ (3, 11, 3, 1, 3, 35, 65537, 33), # public_exponent > modulus
+ (3, 11, 3, 1, 3, 2, 6, 33), # public_exponent is not odd
+ (3, 11, 3, 2, 3, 2, 7, 33), # dmp1 is not odd
+ (3, 11, 3, 1, 4, 2, 7, 33), # dmq1 is not odd
+ ]
+ )
+ def test_invalid_private_numbers_argument_values(self, p, q, d, dmp1, dmq1,
+ iqmp, e, n, backend):
# Start with p=3, q=11, private_exponent=3, public_exponent=7,
# modulus=33, dmp1=1, dmq1=3, iqmp=2. Then change one value at
# a time to test the bounds.
- # Test a modulus < 3.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=2
- )
- ).private_key(backend)
-
- # Test a modulus != p * q.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=35
- )
- ).private_key(backend)
-
- # Test a p > modulus.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=37,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test a q > modulus.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=37,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test a dmp1 > modulus.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=35,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test a dmq1 > modulus.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=35,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test an iqmp > modulus.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=35,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test a private_exponent > modulus
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=37,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test a public_exponent < 3
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=1,
- n=33
- )
- ).private_key(backend)
-
- # Test a public_exponent > modulus
with pytest.raises(ValueError):
rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=35,
+ p=p,
+ q=q,
+ d=d,
+ dmp1=dmp1,
+ dmq1=dmq1,
+ iqmp=iqmp,
public_numbers=rsa.RSAPublicNumbers(
- e=65537,
- n=33
- )
- ).private_key(backend)
-
- # Test a public_exponent that is not odd.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=6,
- n=33
- )
- ).private_key(backend)
-
- # Test a dmp1 that is not odd.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=2,
- dmq1=3,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
- )
- ).private_key(backend)
-
- # Test a dmq1 that is not odd.
- with pytest.raises(ValueError):
- rsa.RSAPrivateNumbers(
- p=3,
- q=11,
- d=3,
- dmp1=1,
- dmq1=4,
- iqmp=2,
- public_numbers=rsa.RSAPublicNumbers(
- e=7,
- n=33
+ e=e,
+ n=n
)
).private_key(backend)
@@ -1721,6 +1983,22 @@ class TestRSANumbersEquality(object):
)
assert num != object()
+ def test_public_numbers_hash(self):
+ pub1 = RSAPublicNumbers(3, 17)
+ pub2 = RSAPublicNumbers(3, 17)
+ pub3 = RSAPublicNumbers(7, 21)
+
+ assert hash(pub1) == hash(pub2)
+ assert hash(pub1) != hash(pub3)
+
+ def test_private_numbers_hash(self):
+ priv1 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2))
+ priv2 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2))
+ priv3 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 3))
+
+ assert hash(priv1) == hash(priv2)
+ assert hash(priv1) != hash(priv3)
+
class TestRSAPrimeFactorRecovery(object):
@pytest.mark.parametrize(
@@ -1739,10 +2017,11 @@ class TestRSAPrimeFactorRecovery(object):
private["private_exponent"]
)
# Unfortunately there is no convention on which prime should be p
- # and which one q. The function we use always makes p < q, but the
- # NIST vectors are not so consistent. Accordingly we verify we've
+ # and which one q. The function we use always makes p > q, but the
+ # NIST vectors are not so consistent. Accordingly, we verify we've
# recovered the proper (p, q) by sorting them and asserting on that.
assert sorted([p, q]) == sorted([private["p"], private["q"]])
+ assert p > q
def test_invalid_recover_prime_factors(self):
with pytest.raises(ValueError):
@@ -1769,7 +2048,6 @@ class TestRSAPrivateKeySerialization(object):
)
def test_private_bytes_encrypted_pem(self, backend, fmt, password):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
serialization.Encoding.PEM,
fmt,
@@ -1783,6 +2061,20 @@ class TestRSAPrivateKeySerialization(object):
assert loaded_priv_num == priv_num
@pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8),
+ (serialization.Encoding.DER, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.Raw, serialization.PrivateFormat.Raw),
+ (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8),
+ ]
+ )
+ def test_private_bytes_rejects_invalid(self, encoding, fmt, backend):
+ key = RSA_KEY_2048.private_key(backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(encoding, fmt, serialization.NoEncryption())
+
+ @pytest.mark.parametrize(
("fmt", "password"),
[
[serialization.PrivateFormat.PKCS8, b"s"],
@@ -1793,7 +2085,6 @@ class TestRSAPrivateKeySerialization(object):
)
def test_private_bytes_encrypted_der(self, backend, fmt, password):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
serialization.Encoding.DER,
fmt,
@@ -1834,7 +2125,6 @@ class TestRSAPrivateKeySerialization(object):
def test_private_bytes_unencrypted(self, backend, encoding, fmt,
loader_func):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
serialized = key.private_bytes(
encoding, fmt, serialization.NoEncryption()
)
@@ -1878,7 +2168,6 @@ class TestRSAPrivateKeySerialization(object):
def test_private_bytes_traditional_der_encrypted_invalid(self, backend):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.private_bytes(
serialization.Encoding.DER,
@@ -1888,7 +2177,6 @@ class TestRSAPrivateKeySerialization(object):
def test_private_bytes_invalid_encoding(self, backend):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
"notencoding",
@@ -1898,7 +2186,6 @@ class TestRSAPrivateKeySerialization(object):
def test_private_bytes_invalid_format(self, backend):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
serialization.Encoding.PEM,
@@ -1908,7 +2195,6 @@ class TestRSAPrivateKeySerialization(object):
def test_private_bytes_invalid_encryption_algorithm(self, backend):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.private_bytes(
serialization.Encoding.PEM,
@@ -1918,12 +2204,11 @@ class TestRSAPrivateKeySerialization(object):
def test_private_bytes_unsupported_encryption_type(self, backend):
key = RSA_KEY_2048.private_key(backend)
- _skip_if_no_serialization(key, backend)
with pytest.raises(ValueError):
key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
- DummyKeyEncryption()
+ DummyKeySerializationEncryption()
)
@@ -1966,18 +2251,78 @@ class TestRSAPEMPublicKeySerialization(object):
key_path, lambda pemfile: pemfile.read(), mode="rb"
)
key = loader_func(key_bytes, backend)
- _skip_if_no_serialization(key, backend)
serialized = key.public_bytes(encoding, format)
assert serialized == key_bytes
+ def test_public_bytes_openssh(self, backend):
+ key_bytes = load_vectors_from_file(
+ os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"),
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ key = serialization.load_pem_public_key(key_bytes, backend)
+
+ ssh_bytes = key.public_bytes(
+ serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH
+ )
+ assert ssh_bytes == (
+ b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7JHoJfg6yNzLMOWet8Z49a4KD"
+ b"0dCspMAYvo2YAMB7/wdEycocujbhJ2n/seONi+5XqTqqFkM5VBl8rmkkFPZk/7x0"
+ b"xmdsTPECSWnHK+HhoaNDFPR3j8jQhVo1laxiqcEhAHegi5cwtFosuJAvSKAFKEvy"
+ b"D43si00DQnXWrYHAEQ=="
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM, serialization.PublicFormat.OpenSSH
+ )
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.DER, serialization.PublicFormat.OpenSSH
+ )
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.OpenSSH,
+ serialization.PublicFormat.PKCS1,
+ )
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.OpenSSH,
+ serialization.PublicFormat.SubjectPublicKeyInfo,
+ )
+
def test_public_bytes_invalid_encoding(self, backend):
key = RSA_KEY_2048.private_key(backend).public_key()
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.public_bytes("notencoding", serialization.PublicFormat.PKCS1)
def test_public_bytes_invalid_format(self, backend):
key = RSA_KEY_2048.private_key(backend).public_key()
- _skip_if_no_serialization(key, backend)
with pytest.raises(TypeError):
key.public_bytes(serialization.Encoding.PEM, "invalidformat")
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt"),
+ [
+ (
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ ),
+ (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1),
+ ] + list(itertools.product(
+ [
+ serialization.Encoding.Raw,
+ serialization.Encoding.X962,
+ serialization.Encoding.PEM,
+ serialization.Encoding.DER
+ ],
+ [
+ serialization.PublicFormat.Raw,
+ serialization.PublicFormat.UncompressedPoint,
+ serialization.PublicFormat.CompressedPoint
+ ]
+ ))
+ )
+ def test_public_bytes_rejects_invalid(self, encoding, fmt, backend):
+ key = RSA_KEY_2048.private_key(backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(encoding, fmt)
diff --git a/tests/hazmat/primitives/test_scrypt.py b/tests/hazmat/primitives/test_scrypt.py
new file mode 100644
index 00000000..8f3a14ed
--- /dev/null
+++ b/tests/hazmat/primitives/test_scrypt.py
@@ -0,0 +1,183 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import (
+ AlreadyFinalized, InvalidKey, UnsupportedAlgorithm
+)
+from cryptography.hazmat.backends.interfaces import ScryptBackend
+from cryptography.hazmat.primitives.kdf.scrypt import Scrypt, _MEM_LIMIT
+
+from tests.utils import load_nist_vectors, load_vectors_from_file
+
+vectors = load_vectors_from_file(
+ os.path.join("KDF", "scrypt.txt"), load_nist_vectors)
+
+
+def _skip_if_memory_limited(memory_limit, params):
+ # Memory calc adapted from OpenSSL (URL split over 2 lines, thanks PEP8)
+ # https://github.com/openssl/openssl/blob/6286757141a8c6e14d647ec733634a
+ # e0c83d9887/crypto/evp/scrypt.c#L189-L221
+ blen = int(params["p"]) * 128 * int(params["r"])
+ vlen = 32 * int(params["r"]) * (int(params["n"]) + 2) * 4
+ memory_required = blen + vlen
+ if memory_limit < memory_required:
+ pytest.skip("Test exceeds Scrypt memory limit. "
+ "This is likely a 32-bit platform.")
+
+
+def test_memory_limit_skip():
+ with pytest.raises(pytest.skip.Exception):
+ _skip_if_memory_limited(1000, {"p": 16, "r": 64, "n": 1024})
+
+ _skip_if_memory_limited(2 ** 31, {"p": 16, "r": 64, "n": 1024})
+
+
+@pytest.mark.requires_backend_interface(interface=ScryptBackend)
+class TestScrypt(object):
+ @pytest.mark.parametrize("params", vectors)
+ def test_derive(self, backend, params):
+ _skip_if_memory_limited(_MEM_LIMIT, params)
+ password = params["password"]
+ work_factor = int(params["n"])
+ block_size = int(params["r"])
+ parallelization_factor = int(params["p"])
+ length = int(params["length"])
+ salt = params["salt"]
+ derived_key = params["derived_key"]
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+ assert binascii.hexlify(scrypt.derive(password)) == derived_key
+
+ def test_unsupported_backend(self):
+ work_factor = 1024
+ block_size = 8
+ parallelization_factor = 16
+ length = 64
+ salt = b"NaCl"
+ backend = object()
+
+ with pytest.raises(UnsupportedAlgorithm):
+ Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+
+ def test_salt_not_bytes(self, backend):
+ work_factor = 1024
+ block_size = 8
+ parallelization_factor = 16
+ length = 64
+ salt = 1
+
+ with pytest.raises(TypeError):
+ Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+
+ def test_scrypt_malloc_failure(self, backend):
+ password = b"NaCl"
+ work_factor = 1024 ** 3
+ block_size = 589824
+ parallelization_factor = 16
+ length = 64
+ salt = b"NaCl"
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+
+ with pytest.raises(MemoryError):
+ scrypt.derive(password)
+
+ def test_password_not_bytes(self, backend):
+ password = 1
+ work_factor = 1024
+ block_size = 8
+ parallelization_factor = 16
+ length = 64
+ salt = b"NaCl"
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+
+ with pytest.raises(TypeError):
+ scrypt.derive(password)
+
+ def test_buffer_protocol(self, backend):
+ password = bytearray(b"password")
+ work_factor = 256
+ block_size = 8
+ parallelization_factor = 16
+ length = 10
+ salt = b"NaCl"
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+
+ assert scrypt.derive(password) == b'\xf4\x92\x86\xb2\x06\x0c\x848W\x87'
+
+ @pytest.mark.parametrize("params", vectors)
+ def test_verify(self, backend, params):
+ _skip_if_memory_limited(_MEM_LIMIT, params)
+ password = params["password"]
+ work_factor = int(params["n"])
+ block_size = int(params["r"])
+ parallelization_factor = int(params["p"])
+ length = int(params["length"])
+ salt = params["salt"]
+ derived_key = params["derived_key"]
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+ assert scrypt.verify(password, binascii.unhexlify(derived_key)) is None
+
+ def test_invalid_verify(self, backend):
+ password = b"password"
+ work_factor = 1024
+ block_size = 8
+ parallelization_factor = 16
+ length = 64
+ salt = b"NaCl"
+ derived_key = b"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e773"
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+
+ with pytest.raises(InvalidKey):
+ scrypt.verify(password, binascii.unhexlify(derived_key))
+
+ def test_already_finalized(self, backend):
+ password = b"password"
+ work_factor = 1024
+ block_size = 8
+ parallelization_factor = 16
+ length = 64
+ salt = b"NaCl"
+
+ scrypt = Scrypt(salt, length, work_factor, block_size,
+ parallelization_factor, backend)
+ scrypt.derive(password)
+ with pytest.raises(AlreadyFinalized):
+ scrypt.derive(password)
+
+ def test_invalid_n(self, backend):
+ # n is less than 2
+ with pytest.raises(ValueError):
+ Scrypt(b"NaCl", 64, 1, 8, 16, backend)
+
+ # n is not a power of 2
+ with pytest.raises(ValueError):
+ Scrypt(b"NaCl", 64, 3, 8, 16, backend)
+
+ def test_invalid_r(self, backend):
+ with pytest.raises(ValueError):
+ Scrypt(b"NaCl", 64, 2, 0, 16, backend)
+
+ def test_invalid_p(self, backend):
+ with pytest.raises(ValueError):
+ Scrypt(b"NaCl", 64, 2, 8, 0, backend)
diff --git a/tests/hazmat/primitives/test_seed.py b/tests/hazmat/primitives/test_seed.py
index 7697bd8b..2d03d774 100644
--- a/tests/hazmat/primitives/test_seed.py
+++ b/tests/hazmat/primitives/test_seed.py
@@ -18,13 +18,13 @@ from ...utils import load_nist_vectors
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.SEED("\x00" * 16), modes.ECB()
+ algorithms.SEED(b"\x00" * 16), modes.ECB()
),
skip_message="Does not support SEED ECB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestSEEDModeECB(object):
- test_ECB = generate_encrypt_test(
+ test_ecb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "SEED"),
["rfc-4269.txt"],
@@ -35,13 +35,13 @@ class TestSEEDModeECB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.SEED("\x00" * 16), modes.CBC("\x00" * 16)
+ algorithms.SEED(b"\x00" * 16), modes.CBC(b"\x00" * 16)
),
skip_message="Does not support SEED CBC",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestSEEDModeCBC(object):
- test_CBC = generate_encrypt_test(
+ test_cbc = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "SEED"),
["rfc-4196.txt"],
@@ -52,13 +52,13 @@ class TestSEEDModeCBC(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.SEED("\x00" * 16), modes.OFB("\x00" * 16)
+ algorithms.SEED(b"\x00" * 16), modes.OFB(b"\x00" * 16)
),
skip_message="Does not support SEED OFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestSEEDModeOFB(object):
- test_OFB = generate_encrypt_test(
+ test_ofb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "SEED"),
["seed-ofb.txt"],
@@ -69,13 +69,13 @@ class TestSEEDModeOFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
- algorithms.SEED("\x00" * 16), modes.CFB("\x00" * 16)
+ algorithms.SEED(b"\x00" * 16), modes.CFB(b"\x00" * 16)
),
skip_message="Does not support SEED CFB",
)
@pytest.mark.requires_backend_interface(interface=CipherBackend)
class TestSEEDModeCFB(object):
- test_CFB = generate_encrypt_test(
+ test_cfb = generate_encrypt_test(
load_nist_vectors,
os.path.join("ciphers", "SEED"),
["seed-cfb.txt"],
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index 22c2145c..8e8e15af 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -16,10 +16,15 @@ from cryptography.hazmat.backends.interfaces import (
DERSerializationBackend, DSABackend, EllipticCurveBackend,
PEMSerializationBackend, RSABackend
)
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.hazmat.primitives.asymmetric import (
+ dsa, ec, ed25519, ed448, rsa, x25519, x448
+)
from cryptography.hazmat.primitives.serialization import (
- BestAvailableEncryption, load_der_private_key, load_der_public_key,
- load_pem_private_key, load_pem_public_key, load_ssh_public_key
+ BestAvailableEncryption, Encoding, NoEncryption,
+ PrivateFormat, PublicFormat,
+ load_der_parameters, load_der_private_key,
+ load_der_public_key, load_pem_parameters, load_pem_private_key,
+ load_pem_public_key, load_ssh_public_key
)
@@ -31,6 +36,55 @@ from .utils import (
from ...utils import raises_unsupported_algorithm
+class TestBufferProtocolSerialization(object):
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.parametrize(
+ ("key_path", "password"),
+ [
+ (["DER_Serialization", "enc-rsa-pkcs8.der"], bytearray(b"foobar")),
+ (["DER_Serialization", "enc2-rsa-pkcs8.der"], bytearray(b"baz")),
+ (["DER_Serialization", "unenc-rsa-pkcs8.der"], None),
+ (["DER_Serialization", "testrsa.der"], None),
+ ]
+ )
+ def test_load_der_rsa_private_key(self, key_path, password, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda derfile: derfile.read(), mode="rb"
+ )
+ key = load_der_private_key(bytearray(data), password, backend)
+ assert key
+ assert isinstance(key, rsa.RSAPrivateKey)
+ _check_rsa_private_numbers(key.private_numbers())
+
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.parametrize(
+ ("key_path", "password"),
+ [
+ (
+ ["PEM_Serialization", "rsa_private_key.pem"],
+ bytearray(b"123456")
+ ),
+ (["PKCS8", "unenc-rsa-pkcs8.pem"], None),
+ (["PKCS8", "enc-rsa-pkcs8.pem"], bytearray(b"foobar")),
+ (["PKCS8", "enc2-rsa-pkcs8.pem"], bytearray(b"baz")),
+ (
+ ["Traditional_OpenSSL_Serialization", "key1.pem"],
+ bytearray(b"123456")
+ ),
+ ]
+ )
+ def test_load_pem_rsa_private_key(self, key_path, password, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda pemfile: pemfile.read(), mode="rb"
+ )
+ key = load_pem_private_key(bytearray(data), password, backend)
+ assert key
+ assert isinstance(key, rsa.RSAPrivateKey)
+ _check_rsa_private_numbers(key.private_numbers())
+
+
@pytest.mark.requires_backend_interface(interface=DERSerializationBackend)
class TestDERSerialization(object):
@pytest.mark.requires_backend_interface(interface=RSABackend)
@@ -53,8 +107,7 @@ class TestDERSerialization(object):
)
assert key
assert isinstance(key, rsa.RSAPrivateKey)
- if isinstance(key, rsa.RSAPrivateKeyWithSerialization):
- _check_rsa_private_numbers(key.private_numbers())
+ _check_rsa_private_numbers(key.private_numbers())
@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.parametrize(
@@ -76,8 +129,27 @@ class TestDERSerialization(object):
)
assert key
assert isinstance(key, dsa.DSAPrivateKey)
- if isinstance(key, dsa.DSAPrivateKeyWithSerialization):
- _check_dsa_private_numbers(key.private_numbers())
+ _check_dsa_private_numbers(key.private_numbers())
+
+ @pytest.mark.parametrize(
+ "key_path",
+ [
+ ["DER_Serialization", "enc-rsa-pkcs8.der"],
+ ]
+ )
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ def test_password_not_bytes(self, key_path, backend):
+ key_file = os.path.join("asymmetric", *key_path)
+ password = u"this password is not bytes"
+
+ with pytest.raises(TypeError):
+ load_vectors_from_file(
+ key_file,
+ lambda derfile: load_der_private_key(
+ derfile.read(), password, backend
+ ),
+ mode="rb"
+ )
@pytest.mark.parametrize(
("key_path", "password"),
@@ -247,9 +319,8 @@ class TestDERSerialization(object):
)
assert key
assert isinstance(key, rsa.RSAPublicKey)
- if isinstance(key, rsa.RSAPublicKeyWithSerialization):
- numbers = key.public_numbers()
- assert numbers.e == 65537
+ numbers = key.public_numbers()
+ assert numbers.e == 65537
def test_load_der_invalid_public_key(self, backend):
with pytest.raises(ValueError):
@@ -293,6 +364,14 @@ class TestDERSerialization(object):
assert key.curve.name == "secp256r1"
assert key.curve.key_size == 256
+ def test_wrong_parameters_format(self, backend):
+ param_data = b"---- NOT A KEY ----\n"
+
+ with pytest.raises(ValueError):
+ load_der_parameters(
+ param_data, backend
+ )
+
@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
class TestPEMSerialization(object):
@@ -330,8 +409,7 @@ class TestPEMSerialization(object):
assert key
assert isinstance(key, rsa.RSAPrivateKey)
- if isinstance(key, rsa.RSAPrivateKeyWithSerialization):
- _check_rsa_private_numbers(key.private_numbers())
+ _check_rsa_private_numbers(key.private_numbers())
@pytest.mark.parametrize(
("key_path", "password"),
@@ -352,8 +430,7 @@ class TestPEMSerialization(object):
)
assert key
assert isinstance(key, dsa.DSAPrivateKey)
- if isinstance(key, dsa.DSAPrivateKeyWithSerialization):
- _check_dsa_private_numbers(key.private_numbers())
+ _check_dsa_private_numbers(key.private_numbers())
@pytest.mark.parametrize(
("key_path", "password"),
@@ -397,9 +474,8 @@ class TestPEMSerialization(object):
)
assert key
assert isinstance(key, rsa.RSAPublicKey)
- if isinstance(key, rsa.RSAPublicKeyWithSerialization):
- numbers = key.public_numbers()
- assert numbers.e == 65537
+ numbers = key.public_numbers()
+ assert numbers.e == 65537
@pytest.mark.parametrize(
("key_file"),
@@ -498,6 +574,43 @@ class TestPEMSerialization(object):
)
)
+ def test_invalid_encoding_with_traditional(self, backend):
+ key_file = os.path.join(
+ "asymmetric", "Traditional_OpenSSL_Serialization", "testrsa.pem"
+ )
+ key = load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_private_key(
+ pemfile.read(), None, backend
+ ),
+ mode="rb"
+ )
+
+ for enc in (Encoding.OpenSSH, Encoding.Raw, Encoding.X962):
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ enc, PrivateFormat.TraditionalOpenSSL, NoEncryption()
+ )
+
+ @pytest.mark.parametrize(
+ "key_path",
+ [
+ ["Traditional_OpenSSL_Serialization", "testrsa-encrypted.pem"],
+ ["PKCS8", "enc-rsa-pkcs8.pem"]
+ ]
+ )
+ def test_password_not_bytes(self, key_path, backend):
+ key_file = os.path.join("asymmetric", *key_path)
+ password = u"this password is not bytes"
+
+ with pytest.raises(TypeError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
@pytest.mark.parametrize(
"key_path",
[
@@ -558,6 +671,12 @@ class TestPEMSerialization(object):
with pytest.raises(ValueError):
load_pem_public_key(key_data, backend)
+ def test_wrong_parameters_format(self, backend):
+ param_data = b"---- NOT A KEY ----\n"
+
+ with pytest.raises(ValueError):
+ load_pem_parameters(param_data, backend)
+
def test_corrupt_traditional_format(self, backend):
# privkey.pem with a bunch of data missing.
key_data = textwrap.dedent("""\
@@ -770,41 +889,40 @@ class TestPEMSerialization(object):
params = key.parameters()
assert isinstance(params, dsa.DSAParameters)
- if isinstance(params, dsa.DSAParametersWithNumbers):
- num = key.private_numbers()
- pub = num.public_numbers
- parameter_numbers = pub.parameter_numbers
- assert num.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203",
- 16)
- assert pub.y == int(
- "2b260ea97dc6a12ae932c640e7df3d8ff04a8a05a0324f8d5f1b23f15fa1"
- "70ff3f42061124eff2586cb11b49a82dcdc1b90fc6a84fb10109cb67db5d"
- "2da971aeaf17be5e37284563e4c64d9e5fc8480258b319f0de29d54d8350"
- "70d9e287914d77df81491f4423b62da984eb3f45eb2a29fcea5dae525ac6"
- "ab6bcce04bfdf5b6",
- 16
- )
+ num = key.private_numbers()
+ pub = num.public_numbers
+ parameter_numbers = pub.parameter_numbers
+ assert num.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203",
+ 16)
+ assert pub.y == int(
+ "2b260ea97dc6a12ae932c640e7df3d8ff04a8a05a0324f8d5f1b23f15fa1"
+ "70ff3f42061124eff2586cb11b49a82dcdc1b90fc6a84fb10109cb67db5d"
+ "2da971aeaf17be5e37284563e4c64d9e5fc8480258b319f0de29d54d8350"
+ "70d9e287914d77df81491f4423b62da984eb3f45eb2a29fcea5dae525ac6"
+ "ab6bcce04bfdf5b6",
+ 16
+ )
- assert parameter_numbers.p == int(
- "00aa0930cc145825221caffa28ac2894196a27833de5ec21270791689420"
- "7774a2e7b238b0d36f1b2499a2c2585083eb01432924418d867faa212dd1"
- "071d4dceb2782794ad393cc08a4d4ada7f68d6e839a5fcd34b4e402d82cb"
- "8a8cb40fec31911bf9bd360b034caacb4c5e947992573c9e90099c1b0f05"
- "940cabe5d2de49a167",
- 16
- )
+ assert parameter_numbers.p == int(
+ "00aa0930cc145825221caffa28ac2894196a27833de5ec21270791689420"
+ "7774a2e7b238b0d36f1b2499a2c2585083eb01432924418d867faa212dd1"
+ "071d4dceb2782794ad393cc08a4d4ada7f68d6e839a5fcd34b4e402d82cb"
+ "8a8cb40fec31911bf9bd360b034caacb4c5e947992573c9e90099c1b0f05"
+ "940cabe5d2de49a167",
+ 16
+ )
- assert parameter_numbers.q == int(
- "00adc0e869b36f0ac013a681fdf4d4899d69820451", 16)
+ assert parameter_numbers.q == int(
+ "00adc0e869b36f0ac013a681fdf4d4899d69820451", 16)
- assert parameter_numbers.g == int(
- "008c6b4589afa53a4d1048bfc346d1f386ca75521ccf72ddaa251286880e"
- "e13201ff48890bbfc33d79bacaec71e7a778507bd5f1a66422e39415be03"
- "e71141ba324f5b93131929182c88a9fa4062836066cebe74b5c6690c7d10"
- "1106c240ab7ebd54e4e3301fd086ce6adac922fb2713a2b0887cba13b9bc"
- "68ce5cfff241cd3246",
- 16
- )
+ assert parameter_numbers.g == int(
+ "008c6b4589afa53a4d1048bfc346d1f386ca75521ccf72ddaa251286880e"
+ "e13201ff48890bbfc33d79bacaec71e7a778507bd5f1a66422e39415be03"
+ "e71141ba324f5b93131929182c88a9fa4062836066cebe74b5c6690c7d10"
+ "1106c240ab7ebd54e4e3301fd086ce6adac922fb2713a2b0887cba13b9bc"
+ "68ce5cfff241cd3246",
+ 16
+ )
@pytest.mark.parametrize(
("key_file", "password"),
@@ -813,9 +931,7 @@ class TestPEMSerialization(object):
]
)
def test_load_bad_oid_key(self, key_file, password, backend):
- with raises_unsupported_algorithm(
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
- ):
+ with pytest.raises(ValueError):
load_vectors_from_file(
os.path.join(
"asymmetric", "PKCS8", key_file),
@@ -861,7 +977,18 @@ class TestRSASSHSerialization(object):
with pytest.raises(ValueError):
load_ssh_public_key(ssh_key, backend)
- def test_load_ssh_public_key_rsa_extra_string_after_comment(self, backend):
+ def test_load_ssh_public_key_truncated_int(self, backend):
+ ssh_key = b'ssh-rsa AAAAB3NzaC1yc2EAAAA='
+
+ with pytest.raises(ValueError):
+ load_ssh_public_key(ssh_key, backend)
+
+ ssh_key = b'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAACKr+IHXo'
+
+ with pytest.raises(ValueError):
+ load_ssh_public_key(ssh_key, backend)
+
+ def test_load_ssh_public_key_rsa_comment_with_spaces(self, backend):
ssh_key = (
b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk"
b"FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll"
@@ -873,8 +1000,7 @@ class TestRSASSHSerialization(object):
b"2MzHvnbv testkey@localhost extra"
)
- with pytest.raises(ValueError):
- load_ssh_public_key(ssh_key, backend)
+ load_ssh_public_key(ssh_key, backend)
def test_load_ssh_public_key_rsa_extra_data_after_modulo(self, backend):
ssh_key = (
@@ -950,7 +1076,7 @@ class TestDSSSSHSerialization(object):
with pytest.raises(ValueError):
load_ssh_public_key(ssh_key, backend)
- def test_load_ssh_public_key_dss_extra_string_after_comment(self, backend):
+ def test_load_ssh_public_key_dss_comment_with_spaces(self, backend):
ssh_key = (
b"ssh-dss AAAAB3NzaC1kc3MAAACBALmwUtfwdjAUjU2Dixd5DvT0NDcjjr69UD"
b"LqSD/Xt5Al7D3GXr1WOrWGpjO0NE9qzRCvMTU7zykRH6XjuNXB6Hvv48Zfm4vm"
@@ -964,8 +1090,7 @@ class TestDSSSSHSerialization(object):
b"z53N7tPF/IhHTjBHb1Ol7IFu9p9A== testkey@localhost extra"
)
- with pytest.raises(ValueError):
- load_ssh_public_key(ssh_key, backend)
+ load_ssh_public_key(ssh_key, backend)
def test_load_ssh_public_key_dss_extra_data_after_modulo(self, backend):
ssh_key = (
@@ -1169,6 +1294,53 @@ class TestECDSASSHSerialization(object):
load_ssh_public_key(ssh_key, backend)
+@pytest.mark.supported(
+ only_if=lambda backend: backend.ed25519_supported(),
+ skip_message="Requires OpenSSL with Ed25519 support"
+)
+class TestEd25519SSHSerialization(object):
+ def test_load_ssh_public_key(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRaN/E4"
+ b"GR+xWvBmvxjxrB1vG user@chiron.local"
+ )
+ key = load_ssh_public_key(ssh_key, backend)
+ assert isinstance(key, ed25519.Ed25519PublicKey)
+ assert key.public_bytes(
+ Encoding.Raw, PublicFormat.Raw
+ ) == (
+ b"m\x9f\x82\x99\xa9`\xee\xb5\xa9\xe01\x19\xdd0\x81\x16\x8d\xfc"
+ b"N\x06G\xecV\xbc\x19\xaf\xc6<k\x07[\xc6"
+ )
+
+ def test_public_bytes_openssh(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRaN/E4"
+ b"GR+xWvBmvxjxrB1vG"
+ )
+ key = load_ssh_public_key(ssh_key, backend)
+ assert isinstance(key, ed25519.Ed25519PublicKey)
+ assert key.public_bytes(
+ Encoding.OpenSSH, PublicFormat.OpenSSH
+ ) == ssh_key
+
+ def test_load_ssh_public_key_not_32_bytes(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI22fgpmpYO61qeAxGd0wgRaN/E4"
+ b"GR+xWvBmvxjxrB1vGaGVs user@chiron.local"
+ )
+ with pytest.raises(ValueError):
+ load_ssh_public_key(ssh_key, backend)
+
+ def test_load_ssh_public_key_trailing_data(self, backend):
+ ssh_key = (
+ b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRa"
+ b"N/E4GR+xWvBmvxjxrB1vGdHJhaWxpbmdkYXRh user@chiron.local"
+ )
+ with pytest.raises(ValueError):
+ load_ssh_public_key(ssh_key, backend)
+
+
class TestKeySerializationEncryptionTypes(object):
def test_non_bytes_password(self):
with pytest.raises(ValueError):
@@ -1177,3 +1349,363 @@ class TestKeySerializationEncryptionTypes(object):
def test_encryption_with_zero_length_password(self):
with pytest.raises(ValueError):
BestAvailableEncryption(b"")
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.ed25519_supported(),
+ skip_message="Requires OpenSSL with Ed25519 support"
+)
+class TestEd25519Serialization(object):
+ def test_load_der_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8-enc.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ key = load_der_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.DER, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ def test_load_pem_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8-enc.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ key = load_pem_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ @pytest.mark.parametrize(
+ ("key_path", "encoding", "loader"),
+ [
+ (
+ ["Ed25519", "ed25519-pub.pem"],
+ Encoding.PEM,
+ load_pem_public_key
+ ),
+ (
+ ["Ed25519", "ed25519-pub.der"],
+ Encoding.DER,
+ load_der_public_key
+ ),
+ ]
+ )
+ def test_load_public_key(self, key_path, encoding, loader, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ public_key = loader(data, backend)
+ assert public_key.public_bytes(
+ encoding, PublicFormat.SubjectPublicKeyInfo
+ ) == data
+
+ def test_openssl_serialization_unsupported(self, backend):
+ key = ed25519.Ed25519PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.DER, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x448_supported(),
+ skip_message="Requires OpenSSL with X448 support"
+)
+class TestX448Serialization(object):
+ def test_load_der_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-pkcs8-enc.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-pkcs8.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ key = load_der_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.DER, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ def test_load_pem_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-pkcs8-enc.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "x448-pkcs8.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ key = load_pem_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ @pytest.mark.parametrize(
+ ("key_path", "encoding", "loader"),
+ [
+ (
+ ["X448", "x448-pub.pem"],
+ Encoding.PEM,
+ load_pem_public_key
+ ),
+ (
+ ["X448", "x448-pub.der"],
+ Encoding.DER,
+ load_der_public_key
+ ),
+ ]
+ )
+ def test_load_public_key(self, key_path, encoding, loader, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ public_key = loader(data, backend)
+ assert public_key.public_bytes(
+ encoding, PublicFormat.SubjectPublicKeyInfo
+ ) == data
+
+ def test_openssl_serialization_unsupported(self, backend):
+ key = x448.X448PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.DER, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x25519_supported(),
+ skip_message="Requires OpenSSL with X25519 support"
+)
+class TestX25519Serialization(object):
+ def test_load_der_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8-enc.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ key = load_der_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.DER, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ def test_load_pem_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8-enc.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "x25519-pkcs8.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ key = load_pem_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ @pytest.mark.parametrize(
+ ("key_path", "encoding", "loader"),
+ [
+ (
+ ["X25519", "x25519-pub.pem"],
+ Encoding.PEM,
+ load_pem_public_key
+ ),
+ (
+ ["X25519", "x25519-pub.der"],
+ Encoding.DER,
+ load_der_public_key
+ ),
+ ]
+ )
+ def test_load_public_key(self, key_path, encoding, loader, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ public_key = loader(data, backend)
+ assert public_key.public_bytes(
+ encoding, PublicFormat.SubjectPublicKeyInfo
+ ) == data
+
+ def test_openssl_serialization_unsupported(self, backend):
+ key = x25519.X25519PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.DER, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.ed448_supported(),
+ skip_message="Requires OpenSSL with Ed448 support"
+)
+class TestEd448Serialization(object):
+ def test_load_der_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed448", "ed448-pkcs8-enc.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed448", "ed448-pkcs8.der"),
+ lambda derfile: derfile.read(),
+ mode="rb"
+ )
+ key = load_der_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.DER, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ def test_load_pem_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed448", "ed448-pkcs8-enc.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ unencrypted = load_vectors_from_file(
+ os.path.join("asymmetric", "Ed448", "ed448-pkcs8.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ key = load_pem_private_key(data, b"password", backend)
+ assert key.private_bytes(
+ Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()
+ ) == unencrypted
+
+ @pytest.mark.parametrize(
+ ("key_path", "encoding", "loader"),
+ [
+ (
+ ["Ed448", "ed448-pub.pem"],
+ Encoding.PEM,
+ load_pem_public_key
+ ),
+ (
+ ["Ed448", "ed448-pub.der"],
+ Encoding.DER,
+ load_der_public_key
+ ),
+ ]
+ )
+ def test_load_public_key(self, key_path, encoding, loader, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", *key_path),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ public_key = loader(data, backend)
+ assert public_key.public_bytes(
+ encoding, PublicFormat.SubjectPublicKeyInfo
+ ) == data
+
+ def test_openssl_serialization_unsupported(self, backend):
+ key = ed448.Ed448PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ Encoding.DER, PrivateFormat.TraditionalOpenSSL, NoEncryption(),
+ )
+
+ def test_openssh_serialization_unsupported(self, backend):
+ key = ed448.Ed448PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.public_key().public_bytes(
+ Encoding.OpenSSH, PublicFormat.OpenSSH,
+ )
+
+
+class TestDHSerialization(object):
+ """Test all options with least-supported key type.
+ """
+ def test_dh_public_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "dhkey.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ public_key = load_pem_private_key(data, None, backend).public_key()
+ for enc in (
+ Encoding.PEM, Encoding.DER, Encoding.OpenSSH,
+ Encoding.Raw, Encoding.X962
+ ):
+ for fmt in (
+ PublicFormat.SubjectPublicKeyInfo, PublicFormat.PKCS1,
+ PublicFormat.OpenSSH, PublicFormat.Raw,
+ PublicFormat.CompressedPoint, PublicFormat.UncompressedPoint,
+ ):
+ if (
+ enc in (Encoding.PEM, Encoding.DER) and
+ fmt == PublicFormat.SubjectPublicKeyInfo
+ ):
+ # tested elsewhere
+ continue
+ with pytest.raises(ValueError):
+ public_key.public_bytes(enc, fmt)
+
+ def test_dh_private_key(self, backend):
+ data = load_vectors_from_file(
+ os.path.join("asymmetric", "DH", "dhkey.pem"),
+ lambda pemfile: pemfile.read(),
+ mode="rb"
+ )
+ private_key = load_pem_private_key(data, None, backend)
+ for enc in (
+ Encoding.PEM, Encoding.DER, Encoding.OpenSSH,
+ Encoding.Raw, Encoding.X962
+ ):
+ for fmt in (
+ PrivateFormat.PKCS8, PrivateFormat.TraditionalOpenSSL,
+ PrivateFormat.Raw
+ ):
+ if (
+ enc in (Encoding.PEM, Encoding.DER) and
+ fmt is PrivateFormat.PKCS8
+ ):
+ # tested elsewhere
+ continue
+ with pytest.raises(ValueError):
+ private_key.private_bytes(enc, fmt, NoEncryption())
diff --git a/tests/hazmat/primitives/test_x25519.py b/tests/hazmat/primitives/test_x25519.py
new file mode 100644
index 00000000..30dc2818
--- /dev/null
+++ b/tests/hazmat/primitives/test_x25519.py
@@ -0,0 +1,260 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import _Reasons
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.x25519 import (
+ X25519PrivateKey, X25519PublicKey
+)
+
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: not backend.x25519_supported(),
+ skip_message="Requires OpenSSL without X25519 support"
+)
+def test_x25519_unsupported(backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
+ X25519PublicKey.from_public_bytes(b"0" * 32)
+
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
+ X25519PrivateKey.from_private_bytes(b"0" * 32)
+
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
+ X25519PrivateKey.generate()
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x25519_supported(),
+ skip_message="Requires OpenSSL with X25519 support"
+)
+class TestX25519Exchange(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "X25519", "rfc7748.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_rfc7748(self, vector, backend):
+ private = binascii.unhexlify(vector["input_scalar"])
+ public = binascii.unhexlify(vector["input_u"])
+ shared_key = binascii.unhexlify(vector["output_u"])
+ private_key = X25519PrivateKey.from_private_bytes(private)
+ public_key = X25519PublicKey.from_public_bytes(public)
+ computed_shared_key = private_key.exchange(public_key)
+ assert computed_shared_key == shared_key
+
+ def test_rfc7748_1000_iteration(self, backend):
+ old_private = private = public = binascii.unhexlify(
+ b"090000000000000000000000000000000000000000000000000000000000"
+ b"0000"
+ )
+ shared_key = binascii.unhexlify(
+ b"684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d9953"
+ b"2c51"
+ )
+ private_key = X25519PrivateKey.from_private_bytes(private)
+ public_key = X25519PublicKey.from_public_bytes(public)
+ for _ in range(1000):
+ computed_shared_key = private_key.exchange(public_key)
+ private_key = X25519PrivateKey.from_private_bytes(
+ computed_shared_key
+ )
+ public_key = X25519PublicKey.from_public_bytes(old_private)
+ old_private = computed_shared_key
+
+ assert computed_shared_key == shared_key
+
+ def test_null_shared_key_raises_error(self, backend):
+ """
+ The vector used here is taken from wycheproof's x25519 test vectors
+ """
+ public = binascii.unhexlify(
+ "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157"
+ )
+ private = binascii.unhexlify(
+ "78f1e8edf14481b389448dac8f59c70b038e7cf92ef2c7eff57a72466e115296"
+ )
+ private_key = X25519PrivateKey.from_private_bytes(
+ private
+ )
+ public_key = X25519PublicKey.from_public_bytes(public)
+ with pytest.raises(ValueError):
+ private_key.exchange(public_key)
+
+ def test_public_bytes_bad_args(self, backend):
+ key = X25519PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(None, serialization.PublicFormat.Raw)
+ with pytest.raises(TypeError):
+ key.public_bytes(serialization.Encoding.Raw)
+
+ # These vectors are also from RFC 7748
+ # https://tools.ietf.org/html/rfc7748#section-6.1
+ @pytest.mark.parametrize(
+ ("private_bytes", "public_bytes"),
+ [
+ (
+ binascii.unhexlify(
+ b"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba"
+ b"51db92c2a"
+ ),
+ binascii.unhexlify(
+ b"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98"
+ b"eaa9b4e6a"
+ )
+ ),
+ (
+ binascii.unhexlify(
+ b"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b2"
+ b"7ff88e0eb"
+ ),
+ binascii.unhexlify(
+ b"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e1"
+ b"46f882b4f"
+ )
+ )
+ ]
+ )
+ def test_pub_priv_bytes_raw(self, private_bytes, public_bytes, backend):
+ private_key = X25519PrivateKey.from_private_bytes(private_bytes)
+ assert private_key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
+ assert private_key.public_key().public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == public_bytes
+ public_key = X25519PublicKey.from_public_bytes(public_bytes)
+ assert public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == public_bytes
+
+ def test_generate(self, backend):
+ key = X25519PrivateKey.generate()
+ assert key
+ assert key.public_key()
+
+ def test_invalid_type_exchange(self, backend):
+ key = X25519PrivateKey.generate()
+ with pytest.raises(TypeError):
+ key.exchange(object())
+
+ def test_invalid_length_from_public_bytes(self, backend):
+ with pytest.raises(ValueError):
+ X25519PublicKey.from_public_bytes(b"a" * 31)
+
+ with pytest.raises(ValueError):
+ X25519PublicKey.from_public_bytes(b"a" * 33)
+
+ def test_invalid_length_from_private_bytes(self, backend):
+ with pytest.raises(ValueError):
+ X25519PrivateKey.from_private_bytes(b"a" * 31)
+
+ with pytest.raises(ValueError):
+ X25519PrivateKey.from_private_bytes(b"a" * 33)
+
+ def test_invalid_private_bytes(self, backend):
+ key = X25519PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.PKCS8,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ )
+
+ def test_invalid_public_bytes(self, backend):
+ key = X25519PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.PKCS1
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.Raw
+ )
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt", "encryption", "passwd", "load_func"),
+ [
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_der_private_key
+ ),
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_der_private_key
+ ),
+ ]
+ )
+ def test_round_trip_private_serialization(self, encoding, fmt, encryption,
+ passwd, load_func, backend):
+ key = X25519PrivateKey.generate()
+ serialized = key.private_bytes(encoding, fmt, encryption)
+ loaded_key = load_func(serialized, passwd, backend)
+ assert isinstance(loaded_key, X25519PrivateKey)
+
+ def test_buffer_protocol(self, backend):
+ private_bytes = bytearray(os.urandom(32))
+ key = X25519PrivateKey.from_private_bytes(private_bytes)
+ assert key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
diff --git a/tests/hazmat/primitives/test_x448.py b/tests/hazmat/primitives/test_x448.py
new file mode 100644
index 00000000..472d49cd
--- /dev/null
+++ b/tests/hazmat/primitives/test_x448.py
@@ -0,0 +1,239 @@
+# 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 os
+
+import pytest
+
+from cryptography.exceptions import _Reasons
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.x448 import (
+ X448PrivateKey, X448PublicKey
+)
+
+from ...utils import (
+ load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: not backend.x448_supported(),
+ skip_message="Requires OpenSSL without X448 support"
+)
+def test_x448_unsupported(backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
+ X448PublicKey.from_public_bytes(b"0" * 56)
+
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
+ X448PrivateKey.from_private_bytes(b"0" * 56)
+
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
+ X448PrivateKey.generate()
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.x448_supported(),
+ skip_message="Requires OpenSSL with X448 support"
+)
+class TestX448Exchange(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join("asymmetric", "X448", "rfc7748.txt"),
+ load_nist_vectors
+ )
+ )
+ def test_rfc7748(self, vector, backend):
+ private = binascii.unhexlify(vector["input_scalar"])
+ public = binascii.unhexlify(vector["input_u"])
+ shared_key = binascii.unhexlify(vector["output_u"])
+ private_key = X448PrivateKey.from_private_bytes(private)
+ public_key = X448PublicKey.from_public_bytes(public)
+ computed_shared_key = private_key.exchange(public_key)
+ assert computed_shared_key == shared_key
+
+ def test_rfc7748_1000_iteration(self, backend):
+ old_private = private = public = binascii.unhexlify(
+ b"05000000000000000000000000000000000000000000000000000000"
+ b"00000000000000000000000000000000000000000000000000000000"
+ )
+ shared_key = binascii.unhexlify(
+ b"aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4"
+ b"af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38"
+ )
+ private_key = X448PrivateKey.from_private_bytes(private)
+ public_key = X448PublicKey.from_public_bytes(public)
+ for _ in range(1000):
+ computed_shared_key = private_key.exchange(public_key)
+ private_key = X448PrivateKey.from_private_bytes(
+ computed_shared_key
+ )
+ public_key = X448PublicKey.from_public_bytes(old_private)
+ old_private = computed_shared_key
+
+ assert computed_shared_key == shared_key
+
+ # These vectors are also from RFC 7748
+ # https://tools.ietf.org/html/rfc7748#section-6.2
+ @pytest.mark.parametrize(
+ ("private_bytes", "public_bytes"),
+ [
+ (
+ binascii.unhexlify(
+ b"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
+ b"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b"
+ ),
+ binascii.unhexlify(
+ b"9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c"
+ b"22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0"
+ )
+ ),
+ (
+ binascii.unhexlify(
+ b"1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d"
+ b"6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d"
+ ),
+ binascii.unhexlify(
+ b"3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430"
+ b"27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609"
+ )
+ )
+ ]
+ )
+ def test_pub_priv_bytes_raw(self, private_bytes, public_bytes, backend):
+ private_key = X448PrivateKey.from_private_bytes(private_bytes)
+ assert private_key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
+ assert private_key.public_key().public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == public_bytes
+ public_key = X448PublicKey.from_public_bytes(public_bytes)
+ assert public_key.public_bytes(
+ serialization.Encoding.Raw, serialization.PublicFormat.Raw
+ ) == public_bytes
+
+ @pytest.mark.parametrize(
+ ("encoding", "fmt", "encryption", "passwd", "load_func"),
+ [
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(b"password"),
+ b"password",
+ serialization.load_der_private_key
+ ),
+ (
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_pem_private_key
+ ),
+ (
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption(),
+ None,
+ serialization.load_der_private_key
+ ),
+ ]
+ )
+ def test_round_trip_private_serialization(self, encoding, fmt, encryption,
+ passwd, load_func, backend):
+ key = X448PrivateKey.generate()
+ serialized = key.private_bytes(encoding, fmt, encryption)
+ loaded_key = load_func(serialized, passwd, backend)
+ assert isinstance(loaded_key, X448PrivateKey)
+
+ def test_generate(self, backend):
+ key = X448PrivateKey.generate()
+ assert key
+ assert key.public_key()
+
+ def test_invalid_type_exchange(self, backend):
+ key = X448PrivateKey.generate()
+ with pytest.raises(TypeError):
+ key.exchange(object())
+
+ def test_invalid_length_from_public_bytes(self, backend):
+ with pytest.raises(ValueError):
+ X448PublicKey.from_public_bytes(b"a" * 55)
+
+ with pytest.raises(ValueError):
+ X448PublicKey.from_public_bytes(b"a" * 57)
+
+ def test_invalid_length_from_private_bytes(self, backend):
+ with pytest.raises(ValueError):
+ X448PrivateKey.from_private_bytes(b"a" * 55)
+
+ with pytest.raises(ValueError):
+ X448PrivateKey.from_private_bytes(b"a" * 57)
+
+ def test_invalid_private_bytes(self, backend):
+ key = X448PrivateKey.generate()
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.PKCS8,
+ None
+ )
+
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ )
+
+ def test_invalid_public_bytes(self, backend):
+ key = X448PrivateKey.generate().public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.Raw,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.PKCS1
+ )
+
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.Raw
+ )
+
+ def test_buffer_protocol(self, backend):
+ private_bytes = binascii.unhexlify(
+ b"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
+ b"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b"
+ )
+ key = X448PrivateKey.from_private_bytes(bytearray(private_bytes))
+ assert key.private_bytes(
+ serialization.Encoding.Raw,
+ serialization.PrivateFormat.Raw,
+ serialization.NoEncryption()
+ ) == private_bytes
diff --git a/tests/hazmat/primitives/test_x963_vectors.py b/tests/hazmat/primitives/test_x963_vectors.py
new file mode 100644
index 00000000..c75afa41
--- /dev/null
+++ b/tests/hazmat/primitives/test_x963_vectors.py
@@ -0,0 +1,66 @@
+# 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 os
+
+import pytest
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.backends.interfaces import HashBackend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF
+
+from ...doubles import DummyHashAlgorithm
+from ...utils import load_vectors_from_file, load_x963_vectors
+
+
+def _skip_hashfn_unsupported(backend, hashfn):
+ if not backend.hash_supported(hashfn):
+ pytest.skip(
+ "Hash {} is not supported by this backend {}".format(
+ hashfn.name, backend
+ )
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestX963(object):
+ _algorithms_dict = {
+ 'SHA-1': hashes.SHA1,
+ 'SHA-224': hashes.SHA224,
+ 'SHA-256': hashes.SHA256,
+ 'SHA-384': hashes.SHA384,
+ 'SHA-512': hashes.SHA512
+ }
+
+ @pytest.mark.parametrize(
+ ("vector"),
+ load_vectors_from_file(
+ os.path.join("KDF", "ansx963_2001.txt"),
+ load_x963_vectors
+ )
+ )
+ def test_x963(self, backend, vector):
+ hashfn = self._algorithms_dict[vector["hash"]]
+ _skip_hashfn_unsupported(backend, hashfn())
+
+ key = binascii.unhexlify(vector["Z"])
+ sharedinfo = None
+ if vector["sharedinfo_length"] != 0:
+ sharedinfo = binascii.unhexlify(vector["sharedinfo"])
+ key_data_len = vector["key_data_length"] // 8
+ key_data = binascii.unhexlify(vector["key_data"])
+
+ xkdf = X963KDF(algorithm=hashfn(),
+ length=key_data_len,
+ sharedinfo=sharedinfo,
+ backend=default_backend())
+ xkdf.verify(key, key_data)
+
+ def test_unsupported_hash(self, backend):
+ with pytest.raises(pytest.skip.Exception):
+ _skip_hashfn_unsupported(backend, DummyHashAlgorithm())
diff --git a/tests/hazmat/primitives/test_x963kdf.py b/tests/hazmat/primitives/test_x963kdf.py
new file mode 100644
index 00000000..c4dd8925
--- /dev/null
+++ b/tests/hazmat/primitives/test_x963kdf.py
@@ -0,0 +1,131 @@
+# 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 (
+ AlreadyFinalized, InvalidKey, _Reasons
+)
+from cryptography.hazmat.backends.interfaces import HashBackend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF
+
+from ...utils import raises_unsupported_algorithm
+
+
+@pytest.mark.requires_backend_interface(interface=HashBackend)
+class TestX963KDF(object):
+ def test_length_limit(self, backend):
+ big_length = hashes.SHA256().digest_size * (2 ** 32 - 1) + 1
+
+ with pytest.raises(ValueError):
+ X963KDF(hashes.SHA256(), big_length, None, backend)
+
+ def test_already_finalized(self, backend):
+ xkdf = X963KDF(hashes.SHA256(), 16, None, backend)
+
+ xkdf.derive(b"\x01" * 16)
+
+ with pytest.raises(AlreadyFinalized):
+ xkdf.derive(b"\x02" * 16)
+
+ def test_derive(self, backend):
+ key = binascii.unhexlify(
+ b"96c05619d56c328ab95fe84b18264b08725b85e33fd34f08"
+ )
+
+ derivedkey = binascii.unhexlify(b"443024c3dae66b95e6f5670601558f71")
+
+ xkdf = X963KDF(hashes.SHA256(), 16, None, backend)
+
+ assert xkdf.derive(key) == derivedkey
+
+ def test_buffer_protocol(self, backend):
+ key = bytearray(binascii.unhexlify(
+ b"96c05619d56c328ab95fe84b18264b08725b85e33fd34f08"
+ ))
+
+ derivedkey = binascii.unhexlify(b"443024c3dae66b95e6f5670601558f71")
+
+ xkdf = X963KDF(hashes.SHA256(), 16, None, backend)
+
+ assert xkdf.derive(key) == derivedkey
+
+ def test_verify(self, backend):
+ key = binascii.unhexlify(
+ b"22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d"
+ )
+
+ sharedinfo = binascii.unhexlify(b"75eef81aa3041e33b80971203d2c0c52")
+
+ derivedkey = binascii.unhexlify(
+ b"c498af77161cc59f2962b9a713e2b215152d139766ce34a776df11866a69bf2e"
+ b"52a13d9c7c6fc878c50c5ea0bc7b00e0da2447cfd874f6cf92f30d0097111485"
+ b"500c90c3af8b487872d04685d14c8d1dc8d7fa08beb0ce0ababc11f0bd496269"
+ b"142d43525a78e5bc79a17f59676a5706dc54d54d4d1f0bd7e386128ec26afc21"
+ )
+
+ xkdf = X963KDF(hashes.SHA256(), 128, sharedinfo, backend)
+
+ assert xkdf.verify(key, derivedkey) is None
+
+ def test_invalid_verify(self, backend):
+ key = binascii.unhexlify(
+ b"96c05619d56c328ab95fe84b18264b08725b85e33fd34f08"
+ )
+
+ xkdf = X963KDF(hashes.SHA256(), 16, None, backend)
+
+ with pytest.raises(InvalidKey):
+ xkdf.verify(key, b"wrong derived key")
+
+ def test_unicode_typeerror(self, backend):
+ with pytest.raises(TypeError):
+ X963KDF(
+ hashes.SHA256(),
+ 16,
+ sharedinfo=u"foo",
+ backend=backend
+ )
+
+ with pytest.raises(TypeError):
+ xkdf = X963KDF(
+ hashes.SHA256(),
+ 16,
+ sharedinfo=None,
+ backend=backend
+ )
+
+ xkdf.derive(u"foo")
+
+ with pytest.raises(TypeError):
+ xkdf = X963KDF(
+ hashes.SHA256(),
+ 16,
+ sharedinfo=None,
+ backend=backend
+ )
+
+ xkdf.verify(u"foo", b"bar")
+
+ with pytest.raises(TypeError):
+ xkdf = X963KDF(
+ hashes.SHA256(),
+ 16,
+ sharedinfo=None,
+ backend=backend
+ )
+
+ xkdf.verify(b"foo", u"bar")
+
+
+def test_invalid_backend():
+ pretend_backend = object()
+
+ with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+ X963KDF(hashes.SHA256(), 16, None, pretend_backend)
diff --git a/tests/hazmat/primitives/twofactor/test_hotp.py b/tests/hazmat/primitives/twofactor/test_hotp.py
index ab5f93c5..14cb08a8 100644
--- a/tests/hazmat/primitives/twofactor/test_hotp.py
+++ b/tests/hazmat/primitives/twofactor/test_hotp.py
@@ -35,6 +35,10 @@ class TestHOTP(object):
with pytest.raises(ValueError):
HOTP(secret, 6, SHA1(), backend)
+ def test_unenforced_invalid_kwy_length(self, backend):
+ secret = os.urandom(10)
+ HOTP(secret, 6, SHA1(), backend, enforce_key_length=False)
+
def test_invalid_hotp_length(self, backend):
secret = os.urandom(16)
@@ -105,6 +109,11 @@ class TestHOTP(object):
"GNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&issuer=Foo"
"&counter=1")
+ def test_buffer_protocol(self, backend):
+ key = bytearray(b"a long key with lots of entropy goes here")
+ hotp = HOTP(key, 6, SHA1(), backend)
+ assert hotp.generate(10) == b"559978"
+
def test_invalid_backend():
secret = b"12345678901234567890"
diff --git a/tests/hazmat/primitives/twofactor/test_totp.py b/tests/hazmat/primitives/twofactor/test_totp.py
index 95829713..59d875af 100644
--- a/tests/hazmat/primitives/twofactor/test_totp.py
+++ b/tests/hazmat/primitives/twofactor/test_totp.py
@@ -139,6 +139,12 @@ class TestTOTP(object):
"DGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&issuer=World"
"&period=30")
+ def test_buffer_protocol(self, backend):
+ key = bytearray(b"a long key with lots of entropy goes here")
+ totp = TOTP(key, 8, hashes.SHA512(), 30, backend)
+ time = 60
+ assert totp.generate(time) == b"53049576"
+
def test_invalid_backend():
secret = b"12345678901234567890"
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index e148bc63..4aa5ce71 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -18,6 +18,9 @@ from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
+from cryptography.hazmat.primitives.kdf.kbkdf import (
+ CounterLocation, KBKDFHMAC, Mode
+)
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from ...utils import load_vectors_from_file
@@ -44,6 +47,10 @@ def generate_encrypt_test(param_loader, path, file_names, cipher_factory,
def encrypt_test(backend, cipher_factory, mode_factory, params):
+ assert backend.cipher_supported(
+ cipher_factory(**params), mode_factory(**params)
+ )
+
plaintext = params["plaintext"]
ciphertext = params["ciphertext"]
cipher = Cipher(
@@ -161,16 +168,15 @@ def hash_test(backend, algorithm, params):
assert m.finalize() == binascii.unhexlify(expected_md)
-def generate_base_hash_test(algorithm, digest_size, block_size):
+def generate_base_hash_test(algorithm, digest_size):
def test_base_hash(self, backend):
- base_hash_test(backend, algorithm, digest_size, block_size)
+ base_hash_test(backend, algorithm, digest_size)
return test_base_hash
-def base_hash_test(backend, algorithm, digest_size, block_size):
+def base_hash_test(backend, algorithm, digest_size):
m = hashes.Hash(algorithm, backend=backend)
assert m.algorithm.digest_size == digest_size
- assert m.algorithm.block_size == block_size
m_copy = m.copy()
assert m != m_copy
assert m._ctx != m_copy._ctx
@@ -182,18 +188,6 @@ def base_hash_test(backend, algorithm, digest_size, block_size):
assert copy.finalize() == m.finalize()
-def generate_long_string_hash_test(hash_factory, md):
- def test_long_string_hash(self, backend):
- long_string_hash_test(backend, hash_factory, md)
- return test_long_string_hash
-
-
-def long_string_hash_test(backend, algorithm, md):
- m = hashes.Hash(algorithm, backend=backend)
- m.update(b"a" * 1000000)
- assert m.finalize() == binascii.unhexlify(md.lower().encode("ascii"))
-
-
def generate_base_hmac_test(hash_cls):
def test_base_hmac(self, backend):
base_hmac_test(backend, hash_cls)
@@ -296,8 +290,6 @@ def aead_tag_exception_test(backend, cipher_factory, mode_factory):
mode_factory(binascii.unhexlify(b"0" * 24)),
backend
)
- with pytest.raises(ValueError):
- cipher.decryptor()
with pytest.raises(ValueError):
mode_factory(binascii.unhexlify(b"0" * 24), b"000")
@@ -370,6 +362,57 @@ def generate_hkdf_test(param_loader, path, file_names, algorithm):
return test_hkdf
+def generate_kbkdf_counter_mode_test(param_loader, path, file_names):
+ all_params = _load_all_params(path, file_names, param_loader)
+
+ @pytest.mark.parametrize("params", all_params)
+ def test_kbkdf(self, backend, params):
+ kbkdf_counter_mode_test(backend, params)
+ return test_kbkdf
+
+
+def kbkdf_counter_mode_test(backend, params):
+ supported_algorithms = {
+ 'hmac_sha1': hashes.SHA1,
+ 'hmac_sha224': hashes.SHA224,
+ 'hmac_sha256': hashes.SHA256,
+ 'hmac_sha384': hashes.SHA384,
+ 'hmac_sha512': hashes.SHA512,
+ }
+
+ supported_counter_locations = {
+ "before_fixed": CounterLocation.BeforeFixed,
+ "after_fixed": CounterLocation.AfterFixed,
+ }
+
+ algorithm = supported_algorithms.get(params.get('prf'))
+ if algorithm is None or not backend.hmac_supported(algorithm()):
+ pytest.skip("KBKDF does not support algorithm: {}".format(
+ params.get('prf')
+ ))
+
+ ctr_loc = supported_counter_locations.get(params.get("ctrlocation"))
+ if ctr_loc is None or not isinstance(ctr_loc, CounterLocation):
+ pytest.skip("Does not support counter location: {}".format(
+ params.get('ctrlocation')
+ ))
+
+ ctrkdf = KBKDFHMAC(
+ algorithm(),
+ Mode.CounterMode,
+ params['l'] // 8,
+ params['rlen'] // 8,
+ None,
+ ctr_loc,
+ None,
+ None,
+ binascii.unhexlify(params['fixedinputdata']),
+ backend=backend)
+
+ ko = ctrkdf.derive(binascii.unhexlify(params['ki']))
+ assert binascii.hexlify(ko) == params["ko"]
+
+
def generate_rsa_verification_test(param_loader, path, file_names, hash_alg,
pad_factory):
all_params = _load_all_params(path, file_names, param_loader)
@@ -390,17 +433,23 @@ def rsa_verification_test(backend, params, hash_alg, pad_factory):
)
public_key = public_numbers.public_key(backend)
pad = pad_factory(params, hash_alg)
- verifier = public_key.verifier(
- binascii.unhexlify(params["s"]),
- pad,
- hash_alg
- )
- verifier.update(binascii.unhexlify(params["msg"]))
+ signature = binascii.unhexlify(params["s"])
+ msg = binascii.unhexlify(params["msg"])
if params["fail"]:
with pytest.raises(InvalidSignature):
- verifier.verify()
+ public_key.verify(
+ signature,
+ msg,
+ pad,
+ hash_alg
+ )
else:
- verifier.verify()
+ public_key.verify(
+ signature,
+ msg,
+ pad,
+ hash_alg
+ )
def _check_rsa_private_numbers(skey):
diff --git a/tests/hazmat/test_der.py b/tests/hazmat/test_der.py
new file mode 100644
index 00000000..d052802c
--- /dev/null
+++ b/tests/hazmat/test_der.py
@@ -0,0 +1,225 @@
+# 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 cryptography.hazmat._der import (
+ DERReader, INTEGER, NULL, OCTET_STRING, SEQUENCE, encode_der,
+ encode_der_integer
+)
+
+
+def test_der_reader_basic():
+ reader = DERReader(b"123456789")
+ assert reader.read_byte() == ord(b"1")
+ assert reader.read_bytes(1).tobytes() == b"2"
+ assert reader.read_bytes(4).tobytes() == b"3456"
+
+ with pytest.raises(ValueError):
+ reader.read_bytes(4)
+
+ assert reader.read_bytes(3).tobytes() == b"789"
+
+ # The input is now empty.
+ with pytest.raises(ValueError):
+ reader.read_bytes(1)
+ with pytest.raises(ValueError):
+ reader.read_byte()
+
+
+def test_der():
+ # This input is the following structure, using
+ # https://github.com/google/der-ascii
+ #
+ # SEQUENCE {
+ # SEQUENCE {
+ # NULL {}
+ # INTEGER { 42 }
+ # OCTET_STRING { "hello" }
+ # }
+ # }
+ der = b"\x30\x0e\x30\x0c\x05\x00\x02\x01\x2a\x04\x05\x68\x65\x6c\x6c\x6f"
+ reader = DERReader(der)
+ with pytest.raises(ValueError):
+ reader.check_empty()
+
+ with pytest.raises(ValueError):
+ with reader:
+ pass
+
+ with pytest.raises(ZeroDivisionError):
+ with DERReader(der):
+ raise ZeroDivisionError
+
+ # Parse the outer element.
+ outer = reader.read_element(SEQUENCE)
+ reader.check_empty()
+ assert outer.data.tobytes() == der[2:]
+
+ # Parse the outer element with read_any_element.
+ reader = DERReader(der)
+ tag, outer2 = reader.read_any_element()
+ reader.check_empty()
+ assert tag == SEQUENCE
+ assert outer2.data.tobytes() == der[2:]
+
+ # Parse the outer element with read_single_element.
+ outer3 = DERReader(der).read_single_element(SEQUENCE)
+ assert outer3.data.tobytes() == der[2:]
+
+ # read_single_element rejects trailing data.
+ with pytest.raises(ValueError):
+ DERReader(der + der).read_single_element(SEQUENCE)
+
+ # Continue parsing the structure.
+ inner = outer.read_element(SEQUENCE)
+ outer.check_empty()
+
+ # Parsing a missing optional element should work.
+ assert inner.read_optional_element(INTEGER) is None
+
+ null = inner.read_element(NULL)
+ null.check_empty()
+
+ # Parsing a present optional element should work.
+ integer = inner.read_optional_element(INTEGER)
+ assert integer.as_integer() == 42
+
+ octet_string = inner.read_element(OCTET_STRING)
+ assert octet_string.data.tobytes() == b"hello"
+
+ # Parsing a missing optional element should work when the input is empty.
+ inner.check_empty()
+ assert inner.read_optional_element(INTEGER) is None
+
+ # Re-encode the same structure.
+ der2 = encode_der(
+ SEQUENCE,
+ encode_der(
+ SEQUENCE,
+ encode_der(NULL),
+ encode_der(INTEGER, encode_der_integer(42)),
+ encode_der(OCTET_STRING, b"hello"),
+ )
+ )
+ assert der2 == der
+
+
+@pytest.mark.parametrize(
+ "length,header",
+ [
+ # Single-byte lengths.
+ (0, b"\x04\x00"),
+ (1, b"\x04\x01"),
+ (2, b"\x04\x02"),
+ (127, b"\x04\x7f"),
+ # Long-form lengths.
+ (128, b"\x04\x81\x80"),
+ (129, b"\x04\x81\x81"),
+ (255, b"\x04\x81\xff"),
+ (0x100, b"\x04\x82\x01\x00"),
+ (0x101, b"\x04\x82\x01\x01"),
+ (0xffff, b"\x04\x82\xff\xff"),
+ (0x10000, b"\x04\x83\x01\x00\x00"),
+ ]
+)
+def test_der_lengths(length, header):
+ body = length * b"a"
+ der = header + body
+
+ reader = DERReader(der)
+ element = reader.read_element(OCTET_STRING)
+ reader.check_empty()
+ assert element.data.tobytes() == body
+
+ assert encode_der(OCTET_STRING, body) == der
+
+
+@pytest.mark.parametrize(
+ "bad_input",
+ [
+ # The input ended before the tag.
+ b"",
+ # The input ended before the length.
+ b"\x30",
+ # The input ended before the second byte of the length.
+ b"\x30\x81",
+ # The input ended before the body.
+ b"\x30\x01",
+ # The length used long form when it should be short form.
+ b"\x30\x81\x01\x00",
+ # The length was not minimally-encoded.
+ b"\x30\x82\x00\x80" + (0x80 * b"a"),
+ # Indefinite-length encoding is not valid DER.
+ b"\x30\x80\x00\x00"
+ # Tag number (the bottom 5 bits) 31 indicates long form tags, which we
+ # do not support.
+ b"\x1f\x00",
+ b"\x9f\x00",
+ b"\xbf\x00",
+ b"\xff\x00",
+ ]
+)
+def test_der_reader_bad_input(bad_input):
+ reader = DERReader(bad_input)
+ with pytest.raises(ValueError):
+ reader.read_any_element()
+
+
+def test_der_reader_wrong_tag():
+ reader = DERReader(b"\x04\x00")
+ with pytest.raises(ValueError):
+ reader.read_element(SEQUENCE)
+
+
+@pytest.mark.parametrize(
+ "value,der",
+ [
+ (0, b'\x00'),
+ (1, b'\x01'),
+ (2, b'\x02'),
+ (3, b'\x03'),
+ (127, b'\x7f'),
+ (128, b'\x00\x80'),
+ (0x112233445566778899aabbccddeeff,
+ b'\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff'),
+ ]
+)
+def test_integer(value, der):
+ assert encode_der_integer(value) == der
+ assert DERReader(der).as_integer() == value
+
+
+@pytest.mark.parametrize(
+ "bad_input",
+ [
+ # Zero is encoded as b"\x00", not the empty string.
+ b"",
+ # Too many leading zeros.
+ b"\x00\x00",
+ b"\x00\x7f",
+ # Too many leading ones.
+ b"\xff\xff",
+ b"\xff\x80",
+ # Negative integers are not supported.
+ b"\x80",
+ b"\x81",
+ b"\x80\x00\x00",
+ b"\xff",
+ ]
+)
+def test_invalid_integer(bad_input):
+ reader = DERReader(bad_input)
+ with pytest.raises(ValueError):
+ reader.as_integer()
+
+
+def test_invalid_integer_encode():
+ with pytest.raises(ValueError):
+ encode_der_integer(-1)
+
+ with pytest.raises(ValueError):
+ encode_der_integer("not an integer")
diff --git a/tests/hazmat/test_oid.py b/tests/hazmat/test_oid.py
new file mode 100644
index 00000000..d1a34f8e
--- /dev/null
+++ b/tests/hazmat/test_oid.py
@@ -0,0 +1,39 @@
+# 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 cryptography.hazmat._oid import ObjectIdentifier
+
+
+def test_basic_oid():
+ assert ObjectIdentifier('1.2.3.4').dotted_string == '1.2.3.4'
+
+
+def test_oid_constraint():
+ # Too short
+ with pytest.raises(ValueError):
+ ObjectIdentifier('1')
+
+ # First node too big
+ with pytest.raises(ValueError):
+ ObjectIdentifier('3.2.1')
+
+ # Outside range
+ with pytest.raises(ValueError):
+ ObjectIdentifier('1.40')
+ with pytest.raises(ValueError):
+ ObjectIdentifier('0.42')
+
+ # non-decimal oid
+ with pytest.raises(ValueError):
+ ObjectIdentifier('1.2.foo.bar')
+ with pytest.raises(ValueError):
+ ObjectIdentifier('1.2.0xf00.0xba4')
+
+ # negative oid
+ with pytest.raises(ValueError):
+ ObjectIdentifier('1.2.-3.-4')