aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2013-10-21 22:06:41 -0500
committerPaul Kehrer <paul.l.kehrer@gmail.com>2013-10-21 22:06:41 -0500
commit9f7ea6e232e5e319a2bfc6bab8d88455947b4901 (patch)
treecddf176070c0a7589c4203eb2a88c47d0b99ea7f
parent653463f0e133def71425a26fdd80bfe7c8ad5961 (diff)
parentc160079df8dd021b6b1e8091025ba27ddc6cd6c0 (diff)
downloadcryptography-9f7ea6e232e5e319a2bfc6bab8d88455947b4901.tar.gz
cryptography-9f7ea6e232e5e319a2bfc6bab8d88455947b4901.tar.bz2
cryptography-9f7ea6e232e5e319a2bfc6bab8d88455947b4901.zip
Merge branch 'master' into block-cipher-decrypt
-rw-r--r--cryptography/bindings/openssl/api.py58
-rw-r--r--cryptography/bindings/openssl/hmac.py32
-rw-r--r--cryptography/primitives/block/base.py4
-rw-r--r--tests/bindings/test_openssl.py10
-rw-r--r--tests/primitives/test_cryptrec.py4
-rw-r--r--tests/primitives/test_openssl_vectors.py16
6 files changed, 103 insertions, 21 deletions
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index a3198c1a..67d73afb 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -13,11 +13,23 @@
from __future__ import absolute_import, division, print_function
+import itertools
import sys
import cffi
from cryptography.primitives import interfaces
+from cryptography.primitives.block.ciphers import AES, Camellia
+from cryptography.primitives.block.modes import CBC, CTR, ECB, OFB, CFB
+
+
+class GetCipherByName(object):
+ def __init__(self, fmt):
+ self._fmt = fmt
+
+ def __call__(self, api, cipher, mode):
+ cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()
+ return api.lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
class API(object):
@@ -35,6 +47,7 @@ class API(object):
"engine",
"err",
"evp",
+ "hmac",
"nid",
"opensslv",
"pem",
@@ -86,6 +99,9 @@ class API(object):
self.lib.OpenSSL_add_all_algorithms()
self.lib.SSL_load_error_strings()
+ self._cipher_registry = {}
+ self._register_default_ciphers()
+
def openssl_version_text(self):
"""
Friendly string name of linked OpenSSL.
@@ -94,9 +110,31 @@ class API(object):
"""
return self.ffi.string(self.lib.OPENSSL_VERSION_TEXT).decode("ascii")
- def supports_cipher(self, ciphername):
- return (self.ffi.NULL !=
- self.lib.EVP_get_cipherbyname(ciphername.encode("ascii")))
+ def supports_cipher(self, cipher, mode):
+ try:
+ adapter = self._cipher_registry[type(cipher), type(mode)]
+ except KeyError:
+ return False
+ evp_cipher = adapter(self, cipher, mode)
+ return self.ffi.NULL != evp_cipher
+
+ def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
+ if (cipher_cls, mode_cls) in self._cipher_registry:
+ raise ValueError("Duplicate registration for: {0} {1}".format(
+ cipher_cls, mode_cls)
+ )
+ self._cipher_registry[cipher_cls, mode_cls] = adapter
+
+ def _register_default_ciphers(self):
+ for cipher_cls, mode_cls in itertools.product(
+ [AES, Camellia],
+ [CBC, CTR, ECB, OFB, CFB],
+ ):
+ self.register_cipher_adapter(
+ cipher_cls,
+ mode_cls,
+ GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+ )
def create_block_cipher_encrypt_context(self, cipher, mode):
ctx, evp, iv_nonce = self._create_block_cipher_context(cipher, mode)
@@ -119,15 +157,11 @@ class API(object):
return ctx
def _create_block_cipher_context(self, cipher, mode):
- ctx = self.ffi.new("EVP_CIPHER_CTX *")
- res = self.lib.EVP_CIPHER_CTX_init(ctx)
- assert res != 0
- 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
- ).lower()
- evp_cipher = self.lib.EVP_get_cipherbyname(ciphername.encode("ascii"))
+ ctx = self.lib.EVP_CIPHER_CTX_new()
+ ctx = self.ffi.gc(ctx, self.lib.EVP_CIPHER_CTX_free)
+ evp_cipher = self._cipher_registry[type(cipher), type(mode)](
+ self, cipher, mode
+ )
assert evp_cipher != self.ffi.NULL
if isinstance(mode, interfaces.ModeWithInitializationVector):
iv_nonce = mode.initialization_vector
diff --git a/cryptography/bindings/openssl/hmac.py b/cryptography/bindings/openssl/hmac.py
new file mode 100644
index 00000000..e97ac35e
--- /dev/null
+++ b/cryptography/bindings/openssl/hmac.py
@@ -0,0 +1,32 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/hmac.h>
+"""
+
+TYPES = """
+typedef struct { ...; } HMAC_CTX;
+"""
+
+FUNCTIONS = """
+void HMAC_CTX_init(HMAC_CTX *);
+void HMAC_CTX_cleanup(HMAC_CTX *);
+int HMAC_Init_ex(HMAC_CTX *, const void *, int, const EVP_MD *, ENGINE *);
+int HMAC_Update(HMAC_CTX *, const unsigned char *, size_t);
+int HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *);
+int HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *);
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/primitives/block/base.py b/cryptography/primitives/block/base.py
index 14704ffe..e625dc7c 100644
--- a/cryptography/primitives/block/base.py
+++ b/cryptography/primitives/block/base.py
@@ -13,15 +13,13 @@
from __future__ import absolute_import, division, print_function
-from cryptography.bindings import _default_api
-
class BlockCipher(object):
def __init__(self, cipher, mode, api=None):
super(BlockCipher, self).__init__()
if api is None:
- api = _default_api
+ from cryptography.bindings import _default_api as api
self.cipher = cipher
self.mode = mode
diff --git a/tests/bindings/test_openssl.py b/tests/bindings/test_openssl.py
index e5b78d18..bf201e4d 100644
--- a/tests/bindings/test_openssl.py
+++ b/tests/bindings/test_openssl.py
@@ -11,7 +11,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import pytest
+
from cryptography.bindings.openssl.api import api
+from cryptography.primitives.block.ciphers import AES
+from cryptography.primitives.block.modes import CBC
class TestOpenSSL(object):
@@ -30,4 +34,8 @@ class TestOpenSSL(object):
assert api.openssl_version_text().startswith("OpenSSL")
def test_supports_cipher(self):
- assert api.supports_cipher("not-a-real-cipher") is False
+ assert api.supports_cipher(None, None) is False
+
+ def test_register_duplicate_cipher_adapter(self):
+ with pytest.raises(ValueError):
+ api.register_cipher_adapter(AES, CBC, None)
diff --git a/tests/primitives/test_cryptrec.py b/tests/primitives/test_cryptrec.py
index edf97652..02a04473 100644
--- a/tests/primitives/test_cryptrec.py
+++ b/tests/primitives/test_cryptrec.py
@@ -37,6 +37,8 @@ class TestCamelliaECB(object):
],
lambda key: ciphers.Camellia(binascii.unhexlify((key))),
lambda key: modes.ECB(),
- only_if=lambda api: api.supports_cipher("camellia-128-ecb"),
+ only_if=lambda api: api.supports_cipher(
+ ciphers.Camellia("\x00" * 16), modes.ECB()
+ ),
skip_message="Does not support Camellia ECB",
)
diff --git a/tests/primitives/test_openssl_vectors.py b/tests/primitives/test_openssl_vectors.py
index 5b2be784..86ff7cad 100644
--- a/tests/primitives/test_openssl_vectors.py
+++ b/tests/primitives/test_openssl_vectors.py
@@ -32,7 +32,9 @@ class TestCamelliaCBC(object):
["camellia-cbc.txt"],
lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
- only_if=lambda api: api.supports_cipher("camellia-128-cbc"),
+ only_if=lambda api: api.supports_cipher(
+ ciphers.Camellia("\x00" * 16), modes.CBC("\x00" * 16)
+ ),
skip_message="Does not support Camellia CBC",
)
@@ -44,7 +46,9 @@ class TestCamelliaOFB(object):
["camellia-ofb.txt"],
lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
- only_if=lambda api: api.supports_cipher("camellia-128-ofb"),
+ only_if=lambda api: api.supports_cipher(
+ ciphers.Camellia("\x00" * 16), modes.OFB("\x00" * 16)
+ ),
skip_message="Does not support Camellia OFB",
)
@@ -56,7 +60,9 @@ class TestCamelliaCFB(object):
["camellia-cfb.txt"],
lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
- only_if=lambda api: api.supports_cipher("camellia-128-cfb"),
+ only_if=lambda api: api.supports_cipher(
+ ciphers.Camellia("\x00" * 16), modes.CFB("\x00" * 16)
+ ),
skip_message="Does not support Camellia CFB",
)
@@ -68,6 +74,8 @@ class TestAESCTR(object):
["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
lambda key, iv: modes.CTR(binascii.unhexlify(iv)),
- only_if=lambda api: api.supports_cipher("aes-128-ctr"),
+ only_if=lambda api: api.supports_cipher(
+ ciphers.AES("\x00" * 16), modes.CTR("\x00" * 16)
+ ),
skip_message="Does not support AES CTR",
)