aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography
diff options
context:
space:
mode:
authorHynek Schlawack <hs@ox.cx>2013-09-07 12:14:31 -0700
committerHynek Schlawack <hs@ox.cx>2013-09-07 12:14:31 -0700
commit5e00e96f20f8c70614b463015e64a277a4348576 (patch)
tree416db58071e8bc6ad51f613e7c01edb9cfb6ae64 /cryptography
parenta53f81dbb49a8504559e9d8ef7e2dd386c15ed41 (diff)
parentaaa22171618e18888d0a0487d4564bcdae5c2015 (diff)
downloadcryptography-5e00e96f20f8c70614b463015e64a277a4348576.tar.gz
cryptography-5e00e96f20f8c70614b463015e64a277a4348576.tar.bz2
cryptography-5e00e96f20f8c70614b463015e64a277a4348576.zip
Merge pull request #28 from alex/simple-symmetric-encryption
[WIP] initial implementation of symmetric encryption
Diffstat (limited to 'cryptography')
-rw-r--r--cryptography/bindings/openssl/api.py75
-rw-r--r--cryptography/primitives/__init__.py0
-rw-r--r--cryptography/primitives/block/__init__.py21
-rw-r--r--cryptography/primitives/block/base.py64
-rw-r--r--cryptography/primitives/block/ciphers.py34
-rw-r--r--cryptography/primitives/block/modes.py22
6 files changed, 216 insertions, 0 deletions
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index 3cc6a0e9..202595bf 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -13,11 +13,86 @@
from __future__ import absolute_import, division, print_function
+import cffi
+
class API(object):
"""
OpenSSL API wrapper.
"""
+ def __init__(self):
+ ffi = cffi.FFI()
+ self._populate_ffi(ffi)
+ self._ffi = ffi
+ self._lib = ffi.verify("""
+ #include <openssl/evp.h>
+ """)
+ self._lib.OpenSSL_add_all_algorithms()
+
+ def _populate_ffi(self, ffi):
+ ffi.cdef("""
+ typedef struct {
+ ...;
+ } EVP_CIPHER_CTX;
+ typedef ... EVP_CIPHER;
+ typedef ... ENGINE;
+
+ void OpenSSL_add_all_algorithms();
+
+ const EVP_CIPHER *EVP_get_cipherbyname(const char *);
+ int EVP_EncryptInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *,
+ ENGINE *, unsigned char *, unsigned char *);
+ int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *, int);
+ int EVP_EncryptUpdate(EVP_CIPHER_CTX *, unsigned char *, int *,
+ unsigned char *, int);
+ int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *);
+ int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *);
+ const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *);
+ int EVP_CIPHER_block_size(const EVP_CIPHER *);
+ """)
+
+ def create_block_cipher_context(self, cipher, mode):
+ ctx = self._ffi.new("EVP_CIPHER_CTX *")
+ ctx = self._ffi.gc(ctx, self._lib.EVP_CIPHER_CTX_cleanup)
+ # TODO: compute name using a better algorithm
+ ciphername = "{0}-{1}-{2}".format(
+ cipher.name, cipher.key_size, mode.name
+ )
+ evp_cipher = self._lib.EVP_get_cipherbyname(ciphername.encode("ascii"))
+ assert evp_cipher != self._ffi.NULL
+ # TODO: only use the key and initialization_vector as needed. Sometimes
+ # this needs to be a DecryptInit, when?
+ res = self._lib.EVP_EncryptInit_ex(
+ ctx, evp_cipher, self._ffi.NULL, cipher.key,
+ mode.initialization_vector
+ )
+ assert res != 0
+
+ # We purposely disable padding here as it's handled higher up in the
+ # API.
+ self._lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
+ return ctx
+
+ def update_encrypt_context(self, ctx, plaintext):
+ buf = self._ffi.new("unsigned char[]", len(plaintext))
+ outlen = self._ffi.new("int *")
+ res = self._lib.EVP_EncryptUpdate(
+ ctx, buf, outlen, plaintext, len(plaintext)
+ )
+ assert res != 0
+ return self._ffi.buffer(buf)[:outlen[0]]
+
+ def finalize_encrypt_context(self, ctx):
+ cipher = self._lib.EVP_CIPHER_CTX_cipher(ctx)
+ block_size = self._lib.EVP_CIPHER_block_size(cipher)
+ buf = self._ffi.new("unsigned char[]", block_size)
+ outlen = self._ffi.new("int *")
+ res = self._lib.EVP_EncryptFinal_ex(ctx, buf, outlen)
+ assert res != 0
+ res = self._lib.EVP_CIPHER_CTX_cleanup(ctx)
+ assert res != 0
+ return self._ffi.buffer(buf)[:outlen[0]]
+
api = API()
diff --git a/cryptography/primitives/__init__.py b/cryptography/primitives/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/cryptography/primitives/__init__.py
diff --git a/cryptography/primitives/block/__init__.py b/cryptography/primitives/block/__init__.py
new file mode 100644
index 00000000..a0c6d03f
--- /dev/null
+++ b/cryptography/primitives/block/__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.primitives.block.base import BlockCipher
+
+
+__all__ = [
+ "BlockCipher",
+]
diff --git a/cryptography/primitives/block/base.py b/cryptography/primitives/block/base.py
new file mode 100644
index 00000000..b4137fd4
--- /dev/null
+++ b/cryptography/primitives/block/base.py
@@ -0,0 +1,64 @@
+# 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 enum import Enum
+
+# TODO: which binding is used should be an option somewhere
+from cryptography.bindings.openssl import api
+
+
+class _Operation(Enum):
+ encrypt = 0
+ decrypt = 1
+
+
+class BlockCipher(object):
+ def __init__(self, cipher, mode):
+ super(BlockCipher, self).__init__()
+ self.cipher = cipher
+ self.mode = mode
+ self._ctx = api.create_block_cipher_context(cipher, mode)
+ self._operation = None
+
+ @property
+ def name(self):
+ return "{0}-{1}-{2}".format(
+ self.cipher.name, self.cipher.key_size, self.mode.name,
+ )
+
+ def encrypt(self, plaintext):
+ if self._ctx is None:
+ raise ValueError("BlockCipher was already finalized")
+
+ if self._operation is None:
+ self._operation = _Operation.encrypt
+ elif self._operation is not _Operation.encrypt:
+ raise ValueError("BlockCipher cannot encrypt when the operation is"
+ " set to %s" % self._operation.name)
+
+ return api.update_encrypt_context(self._ctx, plaintext)
+
+ def finalize(self):
+ if self._ctx is None:
+ raise ValueError("BlockCipher was already finalized")
+
+ if self._operation is _Operation.encrypt:
+ result = api.finalize_encrypt_context(self._ctx)
+ else:
+ raise ValueError("BlockCipher cannot finalize the unknown "
+ "operation %s" % self._operation.name)
+
+ self._ctx = None
+ return result
diff --git a/cryptography/primitives/block/ciphers.py b/cryptography/primitives/block/ciphers.py
new file mode 100644
index 00000000..2e478705
--- /dev/null
+++ b/cryptography/primitives/block/ciphers.py
@@ -0,0 +1,34 @@
+# 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
+
+
+class AES(object):
+ name = "AES"
+ block_size = 128
+ key_sizes = set([128, 192, 256])
+
+ def __init__(self, key):
+ super(AES, self).__init__()
+ self.key = key
+
+ # Verify that the key size matches the expected key size
+ if self.key_size not in self.key_sizes:
+ raise ValueError("Invalid key size (%s) for %s".format(
+ self.key_size, self.name
+ ))
+
+ @property
+ def key_size(self):
+ return len(self.key) * 8
diff --git a/cryptography/primitives/block/modes.py b/cryptography/primitives/block/modes.py
new file mode 100644
index 00000000..de31f086
--- /dev/null
+++ b/cryptography/primitives/block/modes.py
@@ -0,0 +1,22 @@
+# 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
+
+
+class CBC(object):
+ name = "CBC"
+
+ def __init__(self, initialization_vector):
+ super(CBC, self).__init__()
+ self.initialization_vector = initialization_vector