Merge pull request #1046 from reaperhulk/add-cms-macro

add a CMS macro
diff --git a/.travis/install.sh b/.travis/install.sh
index 7979005..e5cdbba 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -42,8 +42,8 @@
             sudo pip install virtualenv
             ;;
         pypy)
-            pyenv install pypy-2.2.1
-            pyenv global pypy-2.2.1
+            pyenv install pypy-2.3
+            pyenv global pypy-2.3
             pip install virtualenv
             ;;
         py32)
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 8a2635e..1801f33 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,6 +7,10 @@
 .. note:: This version is not yet released and is under active development.
 
 * Added :class:`~cryptography.hazmat.primitives.HKDFExpand`.
+* Added :class:`~cryptography.hazmat.primitives.ciphers.modes.CFB8` support
+  for :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` and
+  :class:`~cryptography.hazmat.primitives.ciphers.algorithms.TripleDES` on
+  :doc:`/hazmat/backends/commoncrypto` and :doc:`/hazmat/backends/openssl`.
 
 0.4 - 2014-05-03
 ~~~~~~~~~~~~~~~~
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
index 4faca73..91c0721 100644
--- a/cryptography/hazmat/backends/commoncrypto/backend.py
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -28,7 +28,7 @@
     AES, ARC4, Blowfish, CAST5, TripleDES
 )
 from cryptography.hazmat.primitives.ciphers.modes import (
-    CBC, CFB, CTR, ECB, GCM, OFB
+    CBC, CFB, CFB8, CTR, ECB, GCM, OFB
 )
 
 
@@ -165,6 +165,7 @@
             (CBC, self._lib.kCCModeCBC),
             (ECB, self._lib.kCCModeECB),
             (CFB, self._lib.kCCModeCFB),
+            (CFB8, self._lib.kCCModeCFB8),
             (OFB, self._lib.kCCModeOFB),
             (CTR, self._lib.kCCModeCTR),
             (GCM, self._lib.kCCModeGCM),
@@ -178,6 +179,7 @@
         for mode_cls, mode_const in [
             (CBC, self._lib.kCCModeCBC),
             (CFB, self._lib.kCCModeCFB),
+            (CFB8, self._lib.kCCModeCFB8),
             (OFB, self._lib.kCCModeOFB),
         ]:
             self._register_cipher_adapter(
@@ -264,7 +266,7 @@
         # This bug has been filed as rdar://15589470
         self._bytes_processed = 0
         if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not
-                isinstance(mode, (OFB, CFB, CTR))):
+                isinstance(mode, (OFB, CFB, CFB8, CTR))):
             self._byte_block_size = cipher.block_size // 8
         else:
             self._byte_block_size = 1
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index e00be92..af8fc75 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -38,7 +38,7 @@
     AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES
 )
 from cryptography.hazmat.primitives.ciphers.modes import (
-    CBC, CFB, CTR, ECB, GCM, OFB
+    CBC, CFB, CFB8, CTR, ECB, GCM, OFB
 )
 
 
@@ -147,16 +147,19 @@
         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],
-        ):
+        for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]:
             self.register_cipher_adapter(
-                cipher_cls,
+                AES,
                 mode_cls,
                 GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
             )
-        for mode_cls in [CBC, CFB, OFB]:
+        for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
+            self.register_cipher_adapter(
+                Camellia,
+                mode_cls,
+                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+            )
+        for mode_cls in [CBC, CFB, CFB8, OFB]:
             self.register_cipher_adapter(
                 TripleDES,
                 mode_cls,
diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py
index 739f23d..20dab8a 100644
--- a/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/cryptography/hazmat/primitives/ciphers/modes.py
@@ -17,6 +17,13 @@
 from cryptography.hazmat.primitives import interfaces
 
 
+def _check_iv_length(mode, algorithm):
+    if len(mode.initialization_vector) * 8 != algorithm.block_size:
+        raise ValueError("Invalid IV size ({0}) for {1}".format(
+            len(mode.initialization_vector), mode.name
+        ))
+
+
 @utils.register_interface(interfaces.Mode)
 @utils.register_interface(interfaces.ModeWithInitializationVector)
 class CBC(object):
@@ -25,11 +32,7 @@
     def __init__(self, initialization_vector):
         self.initialization_vector = initialization_vector
 
-    def validate_for_algorithm(self, algorithm):
-        if len(self.initialization_vector) * 8 != algorithm.block_size:
-            raise ValueError("Invalid iv size ({0}) for {1}".format(
-                len(self.initialization_vector), self.name
-            ))
+    validate_for_algorithm = _check_iv_length
 
 
 @utils.register_interface(interfaces.Mode)
@@ -48,11 +51,7 @@
     def __init__(self, initialization_vector):
         self.initialization_vector = initialization_vector
 
-    def validate_for_algorithm(self, algorithm):
-        if len(self.initialization_vector) * 8 != algorithm.block_size:
-            raise ValueError("Invalid iv size ({0}) for {1}".format(
-                len(self.initialization_vector), self.name
-            ))
+    validate_for_algorithm = _check_iv_length
 
 
 @utils.register_interface(interfaces.Mode)
@@ -63,11 +62,18 @@
     def __init__(self, initialization_vector):
         self.initialization_vector = initialization_vector
 
-    def validate_for_algorithm(self, algorithm):
-        if len(self.initialization_vector) * 8 != algorithm.block_size:
-            raise ValueError("Invalid iv size ({0}) for {1}".format(
-                len(self.initialization_vector), self.name
-            ))
+    validate_for_algorithm = _check_iv_length
+
+
+@utils.register_interface(interfaces.Mode)
+@utils.register_interface(interfaces.ModeWithInitializationVector)
+class CFB8(object):
+    name = "CFB8"
+
+    def __init__(self, initialization_vector):
+        self.initialization_vector = initialization_vector
+
+    validate_for_algorithm = _check_iv_length
 
 
 @utils.register_interface(interfaces.Mode)
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index e5d8c65..0e3a451 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -275,6 +275,19 @@
         Must be the same number of bytes as the ``block_size`` of the cipher.
         Do not reuse an ``initialization_vector`` with a given ``key``.
 
+.. class:: CFB8(initialization_vector)
+
+    CFB (Cipher Feedback) is a mode of operation for block ciphers. It
+    transforms a block cipher into a stream cipher. The CFB8 variant uses an
+    8-bit shift register.
+
+    **This mode does not require padding.**
+
+    :param bytes initialization_vector: Must be random bytes. They do not need
+        to be kept secret and they can be included in a transmitted message.
+        Must be the same number of bytes as the ``block_size`` of the cipher.
+        Do not reuse an ``initialization_vector`` with a given ``key``.
+
 .. class:: GCM(initialization_vector, tag=None)
 
     .. danger::
diff --git a/tests/hazmat/primitives/test_3des.py b/tests/hazmat/primitives/test_3des.py
index a4d696c..b9354f0 100644
--- a/tests/hazmat/primitives/test_3des.py
+++ b/tests/hazmat/primitives/test_3des.py
@@ -137,3 +137,40 @@
         ),
         lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)),
     )
+
+
+@pytest.mark.supported(
+    only_if=lambda backend: backend.cipher_supported(
+        algorithms.TripleDES("\x00" * 8), modes.CFB8("\x00" * 8)
+    ),
+    skip_message="Does not support TripleDES CFB8",
+)
+@pytest.mark.cipher
+class TestTripleDESModeCFB8(object):
+    test_KAT = generate_encrypt_test(
+        load_nist_vectors,
+        os.path.join("ciphers", "3DES", "CFB"),
+        [
+            "TCFB8invperm.rsp",
+            "TCFB8permop.rsp",
+            "TCFB8subtab.rsp",
+            "TCFB8varkey.rsp",
+            "TCFB8vartext.rsp",
+        ],
+        lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)),
+        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+    )
+
+    test_MMT = generate_encrypt_test(
+        load_nist_vectors,
+        os.path.join("ciphers", "3DES", "CFB"),
+        [
+            "TCFB8MMT1.rsp",
+            "TCFB8MMT2.rsp",
+            "TCFB8MMT3.rsp",
+        ],
+        lambda key1, key2, key3, **kwargs: algorithms.TripleDES(
+            binascii.unhexlify(key1 + key2 + key3)
+        ),
+        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+    )
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 03be268..173075d 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -158,6 +158,39 @@
 
 @pytest.mark.supported(
     only_if=lambda backend: backend.cipher_supported(
+        algorithms.AES("\x00" * 16), modes.CFB8("\x00" * 16)
+    ),
+    skip_message="Does not support AES CFB8",
+)
+@pytest.mark.cipher
+class TestAESModeCFB8(object):
+    test_CFB8 = generate_encrypt_test(
+        load_nist_vectors,
+        os.path.join("ciphers", "AES", "CFB"),
+        [
+            "CFB8GFSbox128.rsp",
+            "CFB8GFSbox192.rsp",
+            "CFB8GFSbox256.rsp",
+            "CFB8KeySbox128.rsp",
+            "CFB8KeySbox192.rsp",
+            "CFB8KeySbox256.rsp",
+            "CFB8VarKey128.rsp",
+            "CFB8VarKey192.rsp",
+            "CFB8VarKey256.rsp",
+            "CFB8VarTxt128.rsp",
+            "CFB8VarTxt192.rsp",
+            "CFB8VarTxt256.rsp",
+            "CFB8MMT128.rsp",
+            "CFB8MMT192.rsp",
+            "CFB8MMT256.rsp",
+        ],
+        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
+        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+    )
+
+
+@pytest.mark.supported(
+    only_if=lambda backend: backend.cipher_supported(
         algorithms.AES("\x00" * 16), modes.CTR("\x00" * 16)
     ),
     skip_message="Does not support AES CTR",
diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py
index acfd947..022e3af 100644
--- a/tests/hazmat/primitives/test_block.py
+++ b/tests/hazmat/primitives/test_block.py
@@ -184,6 +184,14 @@
                 backend,
             )
 
+    def test_cfb8(self, backend):
+        with pytest.raises(ValueError):
+            Cipher(
+                algorithms.AES(b"\x00" * 16),
+                modes.CFB8(b"abc"),
+                backend,
+            )
+
     def test_ctr(self, backend):
         with pytest.raises(ValueError):
             Cipher(