aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cryptography/hazmat/bindings/openssl/binding.py77
-rw-r--r--cryptography/hazmat/bindings/utils.py88
-rw-r--r--cryptography/hazmat/primitives/ciphers/base.py3
-rw-r--r--cryptography/hazmat/primitives/ciphers/modes.py33
-rw-r--r--cryptography/hazmat/primitives/interfaces.py7
-rw-r--r--docs/hazmat/primitives/interfaces.rst12
-rw-r--r--tests/hazmat/backends/test_openssl.py3
-rw-r--r--tests/hazmat/primitives/test_block.py37
-rw-r--r--tests/hazmat/primitives/test_hash_vectors.py16
-rw-r--r--tests/hazmat/primitives/test_hashes.py16
-rw-r--r--tests/hazmat/primitives/test_hmac.py2
-rw-r--r--tests/hazmat/primitives/test_hmac_vectors.py14
12 files changed, 211 insertions, 97 deletions
diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py
index ccda368e..4f6b99ae 100644
--- a/cryptography/hazmat/bindings/openssl/binding.py
+++ b/cryptography/hazmat/bindings/openssl/binding.py
@@ -13,9 +13,7 @@
from __future__ import absolute_import, division, print_function
-import sys
-
-import cffi
+from cryptography.hazmat.bindings.utils import build_ffi
_OSX_PRE_INCLUDE = """
#ifdef __APPLE__
@@ -39,20 +37,6 @@ _OSX_POST_INCLUDE = """
class Binding(object):
"""
OpenSSL API wrapper.
-
- Modules listed in the ``_modules`` listed should have the following
- attributes:
-
- * ``INCLUDES``: A string containg C includes.
- * ``TYPES``: A string containing C declarations for types.
- * ``FUNCTIONS``: A string containing C declarations for functions.
- * ``MACROS``: A string containing C declarations for any macros.
- * ``CUSTOMIZATIONS``: A string containing arbitrary top-level C code, this
- can be used to do things like test for a define and provide an
- alternate implementation based on that.
- * ``CONDITIONAL_NAMES``: A dict mapping strings of condition names from the
- library to a list of names which will not be present without the
- condition.
"""
_module_prefix = "cryptography.hazmat.bindings.openssl."
_modules = [
@@ -92,59 +76,6 @@ class Binding(object):
if cls.ffi is not None and cls.lib is not None:
return
- ffi = cffi.FFI()
- includes = []
- functions = []
- macros = []
- customizations = []
- for name in cls._modules:
- module_name = cls._module_prefix + name
- __import__(module_name)
- module = sys.modules[module_name]
-
- ffi.cdef(module.TYPES)
-
- macros.append(module.MACROS)
- functions.append(module.FUNCTIONS)
- includes.append(module.INCLUDES)
- customizations.append(module.CUSTOMIZATIONS)
-
- # loop over the functions & macros after declaring all the types
- # so we can set interdependent types in different files and still
- # have them all defined before we parse the funcs & macros
- for func in functions:
- ffi.cdef(func)
- for macro in macros:
- ffi.cdef(macro)
-
- # We include functions here so that if we got any of their definitions
- # wrong, the underlying C compiler will explode. In C you are allowed
- # to re-declare a function if it has the same signature. That is:
- # int foo(int);
- # int foo(int);
- # is legal, but the following will fail to compile:
- # int foo(int);
- # int foo(short);
-
- lib = ffi.verify(
- source="\n".join(
- [_OSX_PRE_INCLUDE] +
- includes +
- [_OSX_POST_INCLUDE] +
- functions +
- customizations
- ),
- libraries=["crypto", "ssl"],
- ext_package="cryptography",
- )
-
- for name in cls._modules:
- module_name = cls._module_prefix + name
- module = sys.modules[module_name]
- for condition, names in module.CONDITIONAL_NAMES.items():
- if not getattr(lib, condition):
- for name in names:
- delattr(lib, name)
-
- cls.ffi = ffi
- cls.lib = lib
+ cls.ffi, cls.lib = build_ffi(cls._module_prefix, cls._modules,
+ _OSX_PRE_INCLUDE, _OSX_POST_INCLUDE,
+ ["crypto", "ssl"])
diff --git a/cryptography/hazmat/bindings/utils.py b/cryptography/hazmat/bindings/utils.py
new file mode 100644
index 00000000..9e1d3937
--- /dev/null
+++ b/cryptography/hazmat/bindings/utils.py
@@ -0,0 +1,88 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+import sys
+
+import cffi
+
+
+def build_ffi(module_prefix, modules, pre_include, post_include, libraries):
+ """
+ Modules listed in ``modules`` should have the following attributes:
+
+ * ``INCLUDES``: A string containing C includes.
+ * ``TYPES``: A string containing C declarations for types.
+ * ``FUNCTIONS``: A string containing C declarations for functions.
+ * ``MACROS``: A string containing C declarations for any macros.
+ * ``CUSTOMIZATIONS``: A string containing arbitrary top-level C code, this
+ can be used to do things like test for a define and provide an
+ alternate implementation based on that.
+ * ``CONDITIONAL_NAMES``: A dict mapping strings of condition names from the
+ library to a list of names which will not be present without the
+ condition.
+ """
+ ffi = cffi.FFI()
+ includes = []
+ functions = []
+ macros = []
+ customizations = []
+ for name in modules:
+ module_name = module_prefix + name
+ __import__(module_name)
+ module = sys.modules[module_name]
+
+ ffi.cdef(module.TYPES)
+
+ macros.append(module.MACROS)
+ functions.append(module.FUNCTIONS)
+ includes.append(module.INCLUDES)
+ customizations.append(module.CUSTOMIZATIONS)
+
+ # loop over the functions & macros after declaring all the types
+ # so we can set interdependent types in different files and still
+ # have them all defined before we parse the funcs & macros
+ for func in functions:
+ ffi.cdef(func)
+ for macro in macros:
+ ffi.cdef(macro)
+
+ # We include functions here so that if we got any of their definitions
+ # wrong, the underlying C compiler will explode. In C you are allowed
+ # to re-declare a function if it has the same signature. That is:
+ # int foo(int);
+ # int foo(int);
+ # is legal, but the following will fail to compile:
+ # int foo(int);
+ # int foo(short);
+ lib = ffi.verify(
+ source="\n".join(
+ [pre_include] +
+ includes +
+ [post_include] +
+ functions +
+ customizations
+ ),
+ libraries=libraries
+ )
+
+ for name in modules:
+ module_name = module_prefix + name
+ module = sys.modules[module_name]
+ for condition, names in module.CONDITIONAL_NAMES.items():
+ if not getattr(lib, condition):
+ for name in names:
+ delattr(lib, name)
+
+ return ffi, lib
diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py
index 1da0802c..d366e4cf 100644
--- a/cryptography/hazmat/primitives/ciphers/base.py
+++ b/cryptography/hazmat/primitives/ciphers/base.py
@@ -25,6 +25,9 @@ class Cipher(object):
if not isinstance(algorithm, interfaces.CipherAlgorithm):
raise TypeError("Expected interface of interfaces.CipherAlgorithm")
+ if mode is not None:
+ mode.validate_for_algorithm(algorithm)
+
self.algorithm = algorithm
self.mode = mode
self._backend = backend
diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py
index ab8501c6..739f23dd 100644
--- a/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/cryptography/hazmat/primitives/ciphers/modes.py
@@ -25,11 +25,20 @@ class CBC(object):
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
+ def validate_for_algorithm(self, algorithm):
+ if len(self.initialization_vector) * 8 != algorithm.block_size:
+ raise ValueError("Invalid iv size ({0}) for {1}".format(
+ len(self.initialization_vector), self.name
+ ))
+
@utils.register_interface(interfaces.Mode)
class ECB(object):
name = "ECB"
+ def validate_for_algorithm(self, algorithm):
+ pass
+
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
@@ -39,6 +48,12 @@ class OFB(object):
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
+ def validate_for_algorithm(self, algorithm):
+ if len(self.initialization_vector) * 8 != algorithm.block_size:
+ raise ValueError("Invalid iv size ({0}) for {1}".format(
+ len(self.initialization_vector), self.name
+ ))
+
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
@@ -48,6 +63,12 @@ class CFB(object):
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
+ def validate_for_algorithm(self, algorithm):
+ if len(self.initialization_vector) * 8 != algorithm.block_size:
+ raise ValueError("Invalid iv size ({0}) for {1}".format(
+ len(self.initialization_vector), self.name
+ ))
+
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithNonce)
@@ -57,6 +78,12 @@ class CTR(object):
def __init__(self, nonce):
self.nonce = nonce
+ def validate_for_algorithm(self, algorithm):
+ if len(self.nonce) * 8 != algorithm.block_size:
+ raise ValueError("Invalid nonce size ({0}) for {1}".format(
+ len(self.nonce), self.name
+ ))
+
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
@@ -65,6 +92,9 @@ class GCM(object):
name = "GCM"
def __init__(self, initialization_vector, tag=None):
+ # len(initialization_vector) must in [1, 2 ** 64), but it's impossible
+ # to actually construct a bytes object that large, so we don't check
+ # for it
if tag is not None and len(tag) < 4:
raise ValueError(
"Authentication tag must be 4 bytes or longer"
@@ -72,3 +102,6 @@ class GCM(object):
self.initialization_vector = initialization_vector
self.tag = tag
+
+ def validate_for_algorithm(self, algorithm):
+ pass
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index e87c9ca9..7a6bf3e2 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -47,6 +47,13 @@ class Mode(six.with_metaclass(abc.ABCMeta)):
A string naming this mode (e.g. "ECB", "CBC").
"""
+ @abc.abstractmethod
+ def validate_for_algorithm(self, algorithm):
+ """
+ Checks that all the necessary invariants of this (mode, algorithm)
+ combination are met.
+ """
+
class ModeWithInitializationVector(six.with_metaclass(abc.ABCMeta)):
@abc.abstractproperty
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index 361b723e..edb24cd9 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -67,6 +67,18 @@ Interfaces used by the symmetric cipher modes described in
The name may be used by a backend to influence the operation of a
cipher in conjunction with the algorithm's name.
+ .. method:: validate_for_algorithm(algorithm)
+
+ :param CipherAlgorithm algorithm:
+
+ Checks that the combination of this mode with the provided algorithm
+ meets any necessary invariants. This should raise an exception if they
+ are not met.
+
+ For example, the :class:`~cryptography.hazmat.primitives.modes.CBC`
+ mode uses this method to check that the provided initialization
+ vector's length matches the block size of the algorithm.
+
.. class:: ModeWithInitializationVector
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 22cfbe71..ad399594 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -27,6 +27,9 @@ from cryptography.hazmat.primitives.ciphers.modes import CBC
class DummyMode(object):
name = "dummy-mode"
+ def validate_for_algorithm(self, algorithm):
+ pass
+
@utils.register_interface(interfaces.CipherAlgorithm)
class DummyCipher(object):
diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py
index 30cf1d60..f758ffaa 100644
--- a/tests/hazmat/primitives/test_block.py
+++ b/tests/hazmat/primitives/test_block.py
@@ -35,6 +35,9 @@ from .utils import (
class DummyMode(object):
name = "dummy-mode"
+ def validate_for_algorithm(self, algorithm):
+ pass
+
@utils.register_interface(interfaces.CipherAlgorithm)
class DummyCipher(object):
@@ -152,3 +155,37 @@ class TestAEADCipherContext(object):
algorithms.AES,
modes.GCM,
)
+
+
+class TestModeValidation(object):
+ def test_cbc(self, backend):
+ with pytest.raises(ValueError):
+ Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.CBC(b"abc"),
+ backend,
+ )
+
+ def test_ofb(self, backend):
+ with pytest.raises(ValueError):
+ Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.OFB(b"abc"),
+ backend,
+ )
+
+ def test_cfb(self, backend):
+ with pytest.raises(ValueError):
+ Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.CFB(b"abc"),
+ backend,
+ )
+
+ def test_ctr(self, backend):
+ with pytest.raises(ValueError):
+ Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.CTR(b"abc"),
+ backend,
+ )
diff --git a/tests/hazmat/primitives/test_hash_vectors.py b/tests/hazmat/primitives/test_hash_vectors.py
index 13ffc3fd..ca97fc11 100644
--- a/tests/hazmat/primitives/test_hash_vectors.py
+++ b/tests/hazmat/primitives/test_hash_vectors.py
@@ -24,7 +24,7 @@ from ...utils import load_hash_vectors
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA1),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA1()),
skip_message="Does not support SHA1",
)
@pytest.mark.hash
@@ -41,7 +41,7 @@ class TestSHA1(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA224),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA224()),
skip_message="Does not support SHA224",
)
@pytest.mark.hash
@@ -58,7 +58,7 @@ class TestSHA224(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA256),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA256()),
skip_message="Does not support SHA256",
)
@pytest.mark.hash
@@ -75,7 +75,7 @@ class TestSHA256(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA384),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA384()),
skip_message="Does not support SHA384",
)
@pytest.mark.hash
@@ -92,7 +92,7 @@ class TestSHA384(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA512),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA512()),
skip_message="Does not support SHA512",
)
@pytest.mark.hash
@@ -109,7 +109,7 @@ class TestSHA512(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160),
+ only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160()),
skip_message="Does not support RIPEMD160",
)
@pytest.mark.hash
@@ -130,7 +130,7 @@ class TestRIPEMD160(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.Whirlpool),
+ only_if=lambda backend: backend.hash_supported(hashes.Whirlpool()),
skip_message="Does not support Whirlpool",
)
@pytest.mark.hash
@@ -153,7 +153,7 @@ class TestWhirlpool(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.MD5),
+ only_if=lambda backend: backend.hash_supported(hashes.MD5()),
skip_message="Does not support MD5",
)
@pytest.mark.hash
diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py
index c907ef61..9ca2feee 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -70,7 +70,7 @@ class TestHashContext(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA1),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA1()),
skip_message="Does not support SHA1",
)
@pytest.mark.hash
@@ -83,7 +83,7 @@ class TestSHA1(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA224),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA224()),
skip_message="Does not support SHA224",
)
@pytest.mark.hash
@@ -96,7 +96,7 @@ class TestSHA224(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA256),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA256()),
skip_message="Does not support SHA256",
)
@pytest.mark.hash
@@ -109,7 +109,7 @@ class TestSHA256(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA384),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA384()),
skip_message="Does not support SHA384",
)
@pytest.mark.hash
@@ -122,7 +122,7 @@ class TestSHA384(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA512),
+ only_if=lambda backend: backend.hash_supported(hashes.SHA512()),
skip_message="Does not support SHA512",
)
@pytest.mark.hash
@@ -135,7 +135,7 @@ class TestSHA512(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160),
+ only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160()),
skip_message="Does not support RIPEMD160",
)
@pytest.mark.hash
@@ -148,7 +148,7 @@ class TestRIPEMD160(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.Whirlpool),
+ only_if=lambda backend: backend.hash_supported(hashes.Whirlpool()),
skip_message="Does not support Whirlpool",
)
@pytest.mark.hash
@@ -161,7 +161,7 @@ class TestWhirlpool(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.MD5),
+ only_if=lambda backend: backend.hash_supported(hashes.MD5()),
skip_message="Does not support MD5",
)
@pytest.mark.hash
diff --git a/tests/hazmat/primitives/test_hmac.py b/tests/hazmat/primitives/test_hmac.py
index 04913af6..dd9cdaab 100644
--- a/tests/hazmat/primitives/test_hmac.py
+++ b/tests/hazmat/primitives/test_hmac.py
@@ -34,7 +34,7 @@ class UnsupportedDummyHash(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.MD5),
+ only_if=lambda backend: backend.hmac_supported(hashes.MD5()),
skip_message="Does not support MD5",
)
@pytest.mark.hmac
diff --git a/tests/hazmat/primitives/test_hmac_vectors.py b/tests/hazmat/primitives/test_hmac_vectors.py
index c5644459..0792080b 100644
--- a/tests/hazmat/primitives/test_hmac_vectors.py
+++ b/tests/hazmat/primitives/test_hmac_vectors.py
@@ -22,7 +22,7 @@ from ...utils import load_hash_vectors
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.MD5),
+ only_if=lambda backend: backend.hmac_supported(hashes.MD5()),
skip_message="Does not support MD5",
)
@pytest.mark.hmac
@@ -38,7 +38,7 @@ class TestHMAC_MD5(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.SHA1),
+ only_if=lambda backend: backend.hmac_supported(hashes.SHA1()),
skip_message="Does not support SHA1",
)
@pytest.mark.hmac
@@ -54,7 +54,7 @@ class TestHMAC_SHA1(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.SHA224),
+ only_if=lambda backend: backend.hmac_supported(hashes.SHA224()),
skip_message="Does not support SHA224",
)
@pytest.mark.hmac
@@ -70,7 +70,7 @@ class TestHMAC_SHA224(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.SHA256),
+ only_if=lambda backend: backend.hmac_supported(hashes.SHA256()),
skip_message="Does not support SHA256",
)
@pytest.mark.hmac
@@ -86,7 +86,7 @@ class TestHMAC_SHA256(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.SHA384),
+ only_if=lambda backend: backend.hmac_supported(hashes.SHA384()),
skip_message="Does not support SHA384",
)
@pytest.mark.hmac
@@ -102,7 +102,7 @@ class TestHMAC_SHA384(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.SHA512),
+ only_if=lambda backend: backend.hmac_supported(hashes.SHA512()),
skip_message="Does not support SHA512",
)
@pytest.mark.hmac
@@ -118,7 +118,7 @@ class TestHMAC_SHA512(object):
@pytest.mark.supported(
- only_if=lambda backend: backend.hmac_supported(hashes.RIPEMD160),
+ only_if=lambda backend: backend.hmac_supported(hashes.RIPEMD160()),
skip_message="Does not support RIPEMD160",
)
@pytest.mark.hmac