diff options
| author | Hynek Schlawack <hs@ox.cx> | 2013-09-07 12:14:31 -0700 |
|---|---|---|
| committer | Hynek Schlawack <hs@ox.cx> | 2013-09-07 12:14:31 -0700 |
| commit | 5e00e96f20f8c70614b463015e64a277a4348576 (patch) | |
| tree | 416db58071e8bc6ad51f613e7c01edb9cfb6ae64 /cryptography | |
| parent | a53f81dbb49a8504559e9d8ef7e2dd386c15ed41 (diff) | |
| parent | aaa22171618e18888d0a0487d4564bcdae5c2015 (diff) | |
| download | cryptography-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.py | 75 | ||||
| -rw-r--r-- | cryptography/primitives/__init__.py | 0 | ||||
| -rw-r--r-- | cryptography/primitives/block/__init__.py | 21 | ||||
| -rw-r--r-- | cryptography/primitives/block/base.py | 64 | ||||
| -rw-r--r-- | cryptography/primitives/block/ciphers.py | 34 | ||||
| -rw-r--r-- | cryptography/primitives/block/modes.py | 22 |
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 |
