From 0a394df31c4165d0230843ebea2717b3cd3caafa Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 15 Nov 2013 16:19:50 -0800 Subject: Implement and document an interface for cipher algorithms --- .../hazmat/primitives/ciphers/algorithms.py | 8 +++++++ cryptography/hazmat/primitives/ciphers/base.py | 3 +++ cryptography/hazmat/primitives/interfaces.py | 14 ++++++++++++ docs/hazmat/primitives/interfaces.rst | 26 ++++++++++++++++++++-- tests/hazmat/bindings/test_openssl.py | 12 +++++----- tests/hazmat/primitives/test_block.py | 12 +++++++++- 6 files changed, 67 insertions(+), 8 deletions(-) diff --git a/cryptography/hazmat/primitives/ciphers/algorithms.py b/cryptography/hazmat/primitives/ciphers/algorithms.py index c135f563..1ed487b6 100644 --- a/cryptography/hazmat/primitives/ciphers/algorithms.py +++ b/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -13,7 +13,10 @@ from __future__ import absolute_import, division, print_function +from cryptography.hazmat.primitives import interfaces + +@interfaces.register(interfaces.CipherAlgorithm) class AES(object): name = "AES" block_size = 128 @@ -33,6 +36,7 @@ class AES(object): return len(self.key) * 8 +@interfaces.register(interfaces.CipherAlgorithm) class Camellia(object): name = "camellia" block_size = 128 @@ -52,6 +56,7 @@ class Camellia(object): return len(self.key) * 8 +@interfaces.register(interfaces.CipherAlgorithm) class TripleDES(object): name = "3DES" block_size = 64 @@ -75,6 +80,7 @@ class TripleDES(object): return len(self.key) * 8 +@interfaces.register(interfaces.CipherAlgorithm) class Blowfish(object): name = "Blowfish" block_size = 64 @@ -94,6 +100,7 @@ class Blowfish(object): return len(self.key) * 8 +@interfaces.register(interfaces.CipherAlgorithm) class CAST5(object): name = "CAST5" block_size = 64 @@ -113,6 +120,7 @@ class CAST5(object): return len(self.key) * 8 +@interfaces.register(interfaces.CipherAlgorithm) class ARC4(object): name = "RC4" block_size = 1 diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py index 0fe11996..78bf7e0c 100644 --- a/cryptography/hazmat/primitives/ciphers/base.py +++ b/cryptography/hazmat/primitives/ciphers/base.py @@ -24,6 +24,9 @@ class Cipher(object): _default_backend as backend, ) + if not isinstance(algorithm, interfaces.CipherAlgorithm): + raise TypeError("Expected interface of interfaces.CipherAlgorithm") + self.algorithm = algorithm self.mode = mode self._backend = backend diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py index 67dbe6fa..bbbb266c 100644 --- a/cryptography/hazmat/primitives/interfaces.py +++ b/cryptography/hazmat/primitives/interfaces.py @@ -25,6 +25,20 @@ def register(iface): return register_decorator +class CipherAlgorithm(six.with_metaclass(abc.ABCMeta)): + @abc.abstractproperty + def name(self): + """ + A string naming this mode. (e.g. AES, Camellia) + """ + + @abc.abstractproperty + def key_size(self): + """ + The size of the key being used as an integer in bits. (e.g. 128, 256) + """ + + class Mode(six.with_metaclass(abc.ABCMeta)): @abc.abstractproperty def name(self): diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst index 7068316e..11cff51a 100644 --- a/docs/hazmat/primitives/interfaces.rst +++ b/docs/hazmat/primitives/interfaces.rst @@ -12,11 +12,33 @@ to document argument and return types. .. _`Abstract Base Classes`: http://docs.python.org/3.2/library/abc.html -Cipher Modes -~~~~~~~~~~~~ +Symmetric Ciphers +~~~~~~~~~~~~~~~~~ .. currentmodule:: cryptography.hazmat.primitives.interfaces + +.. class:: CipherAlgorithm + + A named symmetric encryption algorithm. + + .. attribute:: name + + :type: str + + The standard name for the mode, for example, "AES", "Camellia", or + "Blowfish". + + .. attribute:: key_size + + :type: int + + The number of bits in the key being used. + + +Cipher Modes +------------ + Interfaces used by the symmetric cipher modes described in :ref:`Symmetric Encryption Modes `. diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index 241c6411..e4f8dd8b 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -15,16 +15,18 @@ import pytest from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.bindings.openssl.backend import backend, Backend +from cryptography.hazmat.primitives import interfaces from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC -class FakeMode(object): +class DummyMode(object): pass -class FakeCipher(object): +@interfaces.register(interfaces.CipherAlgorithm) +class DummyCipher(object): pass @@ -58,12 +60,12 @@ class TestOpenSSL(object): def test_nonexistent_cipher(self): b = Backend() b.register_cipher_adapter( - FakeCipher, - FakeMode, + DummyCipher, + DummyMode, lambda backend, cipher, mode: backend.ffi.NULL ) cipher = Cipher( - FakeCipher(), FakeMode(), backend=b, + DummyCipher(), DummyMode(), backend=b, ) with pytest.raises(UnsupportedAlgorithm): cipher.encryptor() diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py index 938cff36..963136b9 100644 --- a/tests/hazmat/primitives/test_block.py +++ b/tests/hazmat/primitives/test_block.py @@ -24,6 +24,11 @@ from cryptography.hazmat.primitives.ciphers import ( ) +@interfaces.register(interfaces.CipherAlgorithm) +class DummyCipher(object): + pass + + class TestCipher(object): def test_instantiate_without_backend(self): Cipher( @@ -45,6 +50,11 @@ class TestCipher(object): ) assert isinstance(cipher.decryptor(), interfaces.CipherContext) + def test_instantiate_with_non_algorithm(self): + algorithm = object() + with pytest.raises(TypeError): + Cipher(algorithm, mode=None) + class TestCipherContext(object): def test_use_after_finalize(self, backend): @@ -90,7 +100,7 @@ class TestCipherContext(object): def test_nonexistent_cipher(self, backend): cipher = Cipher( - object(), object(), backend + DummyCipher(), object(), backend ) with pytest.raises(UnsupportedAlgorithm): cipher.encryptor() -- cgit v1.2.3