diff options
-rw-r--r-- | CONTRIBUTING.rst | 2 | ||||
-rw-r--r-- | README.rst | 5 | ||||
-rw-r--r-- | cryptography/bindings/openssl/api.py | 62 | ||||
-rw-r--r-- | cryptography/bindings/openssl/evp.py | 2 | ||||
-rw-r--r-- | cryptography/bindings/openssl/hmac.py | 32 | ||||
-rw-r--r-- | cryptography/primitives/block/base.py | 4 | ||||
-rw-r--r-- | docs/community.rst | 2 | ||||
-rw-r--r-- | docs/primitives/cryptographic-hashes.rst | 4 | ||||
-rw-r--r-- | tests/bindings/test_openssl.py | 10 | ||||
-rw-r--r-- | tests/primitives/test_cryptrec.py | 4 | ||||
-rw-r--r-- | tests/primitives/test_openssl_vectors.py | 16 | ||||
-rw-r--r-- | tests/primitives/test_utils.py | 5 | ||||
-rw-r--r-- | tests/test_utils.py | 10 | ||||
-rw-r--r-- | tox.ini | 6 |
14 files changed, 129 insertions, 35 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9575e845..9f63250f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -12,4 +12,4 @@ Examples of contributions include: Extensive contribution guidelines are available in the repository at ``docs/contributing.rst``, or online at: -https://cryptography.readthedocs.org/en/latest/contributing/ +https://cryptography.io/en/latest/contributing/ @@ -15,7 +15,10 @@ yet. It targets Python 2.6-2.7, Python 3.2+, as well as PyPy. You can find more documentation at `Read The Docs`_. -.. _`Read The Docs`: https://cryptography.readthedocs.org/ +You can find more information in the `documentation`_. + +.. _`documentation`: https://cryptography.io/ + Discussion ~~~~~~~~~~ diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py index 3c2cf2e2..7d189d62 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,20 +110,38 @@ 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_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 @@ -144,7 +178,7 @@ class API(object): res = self.lib.EVP_EncryptFinal_ex(ctx, buf, outlen) assert res != 0 res = self.lib.EVP_CIPHER_CTX_cleanup(ctx) - assert res != 0 + assert res == 1 return self.ffi.buffer(buf)[:outlen[0]] def supports_hash(self, hash_cls): @@ -168,6 +202,8 @@ class API(object): buf = self.ffi.new("unsigned char[]", digest_size) res = self.lib.EVP_DigestFinal_ex(ctx, buf, self.ffi.NULL) assert res != 0 + res = self.lib.EVP_MD_CTX_cleanup(ctx) + assert res == 1 return self.ffi.buffer(buf)[:digest_size] def copy_hash_context(self, ctx): diff --git a/cryptography/bindings/openssl/evp.py b/cryptography/bindings/openssl/evp.py index 20159906..2bb5b0f7 100644 --- a/cryptography/bindings/openssl/evp.py +++ b/cryptography/bindings/openssl/evp.py @@ -45,6 +45,8 @@ 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 *); void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *); +EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(); +void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); EVP_MD_CTX *EVP_MD_CTX_create(); int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *); 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 50e9e9e5..42c1f799 100644 --- a/cryptography/primitives/block/base.py +++ b/cryptography/primitives/block/base.py @@ -15,8 +15,6 @@ from __future__ import absolute_import, division, print_function from enum import Enum -from cryptography.bindings import _default_api - class _Operation(Enum): encrypt = 0 @@ -28,7 +26,7 @@ class BlockCipher(object): 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/docs/community.rst b/docs/community.rst index 86ba5055..552318da 100644 --- a/docs/community.rst +++ b/docs/community.rst @@ -12,4 +12,4 @@ You can find ``cryptography`` all over the web: .. _`Mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev .. _`Source code`: https://github.com/pyca/cryptography .. _`Issue tracker`: https://github.com/pyca/cryptography/issues -.. _`Documentation`: https://cryptography.readthedocs.org/ +.. _`Documentation`: https://cryptography.io/ diff --git a/docs/primitives/cryptographic-hashes.rst b/docs/primitives/cryptographic-hashes.rst index 397e50d7..d4dde042 100644 --- a/docs/primitives/cryptographic-hashes.rst +++ b/docs/primitives/cryptographic-hashes.rst @@ -1,5 +1,5 @@ Message Digests -==================== +=============== .. class:: cryptography.primitives.hashes.BaseHash @@ -8,7 +8,7 @@ Message Digests .. method:: update(data) - :param bytes data The bytes you wish to hash. + :param bytes data: The bytes you wish to hash. .. method:: copy() 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", ) diff --git a/tests/primitives/test_utils.py b/tests/primitives/test_utils.py index 9888309e..6e197923 100644 --- a/tests/primitives/test_utils.py +++ b/tests/primitives/test_utils.py @@ -1,7 +1,8 @@ import pytest -from .utils import (base_hash_test, encrypt_test, hash_test, - long_string_hash_test) +from .utils import ( + base_hash_test, encrypt_test, hash_test, long_string_hash_test +) class TestEncryptTest(object): diff --git a/tests/test_utils.py b/tests/test_utils.py index 3fe9e570..f96cf004 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -15,10 +15,12 @@ import textwrap import pytest -from .utils import (load_nist_vectors, load_nist_vectors_from_file, - load_cryptrec_vectors, load_cryptrec_vectors_from_file, - load_openssl_vectors, load_openssl_vectors_from_file, load_hash_vectors, - load_hash_vectors_from_file) +from .utils import ( + load_nist_vectors, load_nist_vectors_from_file, load_cryptrec_vectors, + load_cryptrec_vectors_from_file, load_openssl_vectors, + load_openssl_vectors_from_file, load_hash_vectors, + load_hash_vectors_from_file +) def test_load_nist_vectors_encrypt(): @@ -19,5 +19,7 @@ commands = [testenv:pep8] deps = flake8 -# E128 continuation line under-indented for visual indent -commands = flake8 --ignore="E128" cryptography/ tests/ docs/ +commands = flake8 . + +[flake8] +exclude = .tox,*.egg |