aboutsummaryrefslogtreecommitdiffstats
path: root/src/cryptography/hazmat/primitives/ciphers
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-11-07 19:17:08 -0500
committerDonald Stufft <donald@stufft.io>2014-11-13 07:56:31 -0500
commitc62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc (patch)
tree55482d6f2e98ff65f2174294f64ad96b7be68717 /src/cryptography/hazmat/primitives/ciphers
parentd9f137db78d451ecb6ef7925b7dec0139ca59898 (diff)
downloadcryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.tar.gz
cryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.tar.bz2
cryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.zip
Move the cryptography package into a src/ subdirectory
Due to differences in how py.test determines which module to ``import`` the test suite actually runs against the cryptography which is in the *current* directory instead of the cryptography which is installed. The problem essentially boils down to when there is a tests/__init__.py then py.test adds the current directory to the front of the sys.path, causing it to take precedence over the installed location. This means that running the tests relies on the implicit compile that CFFI does instead of testing against what people will actually be runnning, which is the module compiled by setup.py.
Diffstat (limited to 'src/cryptography/hazmat/primitives/ciphers')
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/__init__.py21
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/algorithms.py147
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/base.py132
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/modes.py125
4 files changed, 425 insertions, 0 deletions
diff --git a/src/cryptography/hazmat/primitives/ciphers/__init__.py b/src/cryptography/hazmat/primitives/ciphers/__init__.py
new file mode 100644
index 00000000..e5a8ca52
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/ciphers/__init__.py
@@ -0,0 +1,21 @@
+# 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
+
+from cryptography.hazmat.primitives.ciphers.base import Cipher
+
+
+__all__ = [
+ "Cipher",
+]
diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
new file mode 100644
index 00000000..bd8437c2
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -0,0 +1,147 @@
+# 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
+
+from cryptography import utils
+from cryptography.hazmat.primitives import interfaces
+
+
+def _verify_key_size(algorithm, key):
+ # Verify that the key size matches the expected key size
+ if len(key) * 8 not in algorithm.key_sizes:
+ raise ValueError("Invalid key size ({0}) for {1}.".format(
+ len(key) * 8, algorithm.name
+ ))
+ return key
+
+
+@utils.register_interface(interfaces.BlockCipherAlgorithm)
+@utils.register_interface(interfaces.CipherAlgorithm)
+class AES(object):
+ name = "AES"
+ block_size = 128
+ key_sizes = frozenset([128, 192, 256])
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.BlockCipherAlgorithm)
+@utils.register_interface(interfaces.CipherAlgorithm)
+class Camellia(object):
+ name = "camellia"
+ block_size = 128
+ key_sizes = frozenset([128, 192, 256])
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.BlockCipherAlgorithm)
+@utils.register_interface(interfaces.CipherAlgorithm)
+class TripleDES(object):
+ name = "3DES"
+ block_size = 64
+ key_sizes = frozenset([64, 128, 192])
+
+ def __init__(self, key):
+ if len(key) == 8:
+ key += key + key
+ elif len(key) == 16:
+ key += key[:8]
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.BlockCipherAlgorithm)
+@utils.register_interface(interfaces.CipherAlgorithm)
+class Blowfish(object):
+ name = "Blowfish"
+ block_size = 64
+ key_sizes = frozenset(range(32, 449, 8))
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.BlockCipherAlgorithm)
+@utils.register_interface(interfaces.CipherAlgorithm)
+class CAST5(object):
+ name = "CAST5"
+ block_size = 64
+ key_sizes = frozenset(range(40, 129, 8))
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.CipherAlgorithm)
+class ARC4(object):
+ name = "RC4"
+ key_sizes = frozenset([40, 56, 64, 80, 128, 192, 256])
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.CipherAlgorithm)
+class IDEA(object):
+ name = "IDEA"
+ block_size = 64
+ key_sizes = frozenset([128])
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
+
+
+@utils.register_interface(interfaces.BlockCipherAlgorithm)
+@utils.register_interface(interfaces.CipherAlgorithm)
+class SEED(object):
+ name = "SEED"
+ block_size = 128
+ key_sizes = frozenset([128])
+
+ def __init__(self, key):
+ self.key = _verify_key_size(self, key)
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py
new file mode 100644
index 00000000..e3fe5adc
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/ciphers/base.py
@@ -0,0 +1,132 @@
+# 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
+
+from cryptography import utils
+from cryptography.exceptions import (
+ AlreadyFinalized, AlreadyUpdated, NotYetFinalized, UnsupportedAlgorithm,
+ _Reasons
+)
+from cryptography.hazmat.backends.interfaces import CipherBackend
+from cryptography.hazmat.primitives import interfaces
+
+
+class Cipher(object):
+ def __init__(self, algorithm, mode, backend):
+ if not isinstance(backend, CipherBackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement CipherBackend.",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ 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
+
+ def encryptor(self):
+ if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
+ if self.mode.tag is not None:
+ raise ValueError(
+ "Authentication tag must be None when encrypting."
+ )
+ ctx = self._backend.create_symmetric_encryption_ctx(
+ self.algorithm, self.mode
+ )
+ return self._wrap_ctx(ctx, encrypt=True)
+
+ def decryptor(self):
+ if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
+ if self.mode.tag is None:
+ raise ValueError(
+ "Authentication tag must be provided when decrypting."
+ )
+ ctx = self._backend.create_symmetric_decryption_ctx(
+ self.algorithm, self.mode
+ )
+ return self._wrap_ctx(ctx, encrypt=False)
+
+ def _wrap_ctx(self, ctx, encrypt):
+ if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
+ if encrypt:
+ return _AEADEncryptionContext(ctx)
+ else:
+ return _AEADCipherContext(ctx)
+ else:
+ return _CipherContext(ctx)
+
+
+@utils.register_interface(interfaces.CipherContext)
+class _CipherContext(object):
+ def __init__(self, ctx):
+ self._ctx = ctx
+
+ def update(self, data):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ return self._ctx.update(data)
+
+ def finalize(self):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ data = self._ctx.finalize()
+ self._ctx = None
+ return data
+
+
+@utils.register_interface(interfaces.AEADCipherContext)
+@utils.register_interface(interfaces.CipherContext)
+class _AEADCipherContext(object):
+ def __init__(self, ctx):
+ self._ctx = ctx
+ self._tag = None
+ self._updated = False
+
+ def update(self, data):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ self._updated = True
+ return self._ctx.update(data)
+
+ def finalize(self):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ data = self._ctx.finalize()
+ self._tag = self._ctx.tag
+ self._ctx = None
+ return data
+
+ def authenticate_additional_data(self, data):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ if self._updated:
+ raise AlreadyUpdated("Update has been called on this context.")
+ self._ctx.authenticate_additional_data(data)
+
+
+@utils.register_interface(interfaces.AEADEncryptionContext)
+class _AEADEncryptionContext(_AEADCipherContext):
+ @property
+ def tag(self):
+ if self._ctx is not None:
+ raise NotYetFinalized("You must finalize encryption before "
+ "getting the tag.")
+ return self._tag
diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py
new file mode 100644
index 00000000..d995b876
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/ciphers/modes.py
@@ -0,0 +1,125 @@
+# 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
+
+from cryptography import utils
+from cryptography.hazmat.primitives import interfaces
+
+
+def _check_iv_length(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)
+class CBC(object):
+ name = "CBC"
+
+ def __init__(self, initialization_vector):
+ self._initialization_vector = initialization_vector
+
+ initialization_vector = utils.read_only_property("_initialization_vector")
+ validate_for_algorithm = _check_iv_length
+
+
+@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)
+class OFB(object):
+ name = "OFB"
+
+ def __init__(self, initialization_vector):
+ self._initialization_vector = initialization_vector
+
+ initialization_vector = utils.read_only_property("_initialization_vector")
+ validate_for_algorithm = _check_iv_length
+
+
+@utils.register_interface(interfaces.Mode)
+@utils.register_interface(interfaces.ModeWithInitializationVector)
+class CFB(object):
+ name = "CFB"
+
+ def __init__(self, initialization_vector):
+ self._initialization_vector = initialization_vector
+
+ initialization_vector = utils.read_only_property("_initialization_vector")
+ validate_for_algorithm = _check_iv_length
+
+
+@utils.register_interface(interfaces.Mode)
+@utils.register_interface(interfaces.ModeWithInitializationVector)
+class CFB8(object):
+ name = "CFB8"
+
+ def __init__(self, initialization_vector):
+ self._initialization_vector = initialization_vector
+
+ initialization_vector = utils.read_only_property("_initialization_vector")
+ validate_for_algorithm = _check_iv_length
+
+
+@utils.register_interface(interfaces.Mode)
+@utils.register_interface(interfaces.ModeWithNonce)
+class CTR(object):
+ name = "CTR"
+
+ def __init__(self, nonce):
+ self._nonce = nonce
+
+ nonce = utils.read_only_property("_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)
+@utils.register_interface(interfaces.ModeWithAuthenticationTag)
+class GCM(object):
+ name = "GCM"
+
+ def __init__(self, initialization_vector, tag=None, min_tag_length=16):
+ # 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 min_tag_length < 4:
+ raise ValueError("min_tag_length must be >= 4")
+ if tag is not None and len(tag) < min_tag_length:
+ raise ValueError(
+ "Authentication tag must be {0} bytes or longer.".format(
+ min_tag_length)
+ )
+
+ self._initialization_vector = initialization_vector
+ self._tag = tag
+
+ tag = utils.read_only_property("_tag")
+ initialization_vector = utils.read_only_property("_initialization_vector")
+
+ def validate_for_algorithm(self, algorithm):
+ pass