Merge branch 'master' into triple-des

Conflicts:
	tests/primitives/test_nist.py
diff --git a/cryptography/primitives/block/ciphers.py b/cryptography/primitives/block/ciphers.py
index cf54aa3..f204dbe 100644
--- a/cryptography/primitives/block/ciphers.py
+++ b/cryptography/primitives/block/ciphers.py
@@ -32,3 +32,15 @@
     @property
     def key_size(self):
         return len(self.key) * 8
+
+
+class TripleDES(object):
+    name = "3DES"
+    block_size = 64
+    # TODO: is there a better way to represent the fact that the effective key
+    # size is 56 bits?
+    key_sizes = set([64, 128, 192])
+
+    def __init__(self, key):
+        super(TripleDES, self).__init__()
+        self.key = key
diff --git a/docs/primitives/symmetric-encryption.rst b/docs/primitives/symmetric-encryption.rst
index 1b8d1d7..ce3b13e 100644
--- a/docs/primitives/symmetric-encryption.rst
+++ b/docs/primitives/symmetric-encryption.rst
@@ -52,6 +52,25 @@
                       This must be kept secret.
 
 
+Insecure Ciphers
+----------------
+
+.. class:: cryptography.primitives.block.ciphers.TripleDES(key)
+
+    Triple DES (Data Encryption Standard), sometimes refered to as 3DES, is a
+    block cipher standardized by NIST. Triple DES should be considered to be
+    cryptographically broken and should not be used for new applications, old
+    applications should strongly consider moving away from it.
+
+    :param bytes key: The secret key, either ``64``, ``128``, or ``192`` bits
+                      (note that DES functionally uses ``56``, ``112``, or
+                      ``168`` bits of the key, there is a parity byte in each
+                      component of the key), in some materials these are
+                      referred to as being up to three separate keys (each
+                      ``56`` bits long), they can simply be concatenated to
+                      produce the full key. This must be kept secret.
+
+
 Modes
 ~~~~~
 
diff --git a/tests/primitives/test_nist.py b/tests/primitives/test_nist.py
index 8bef118..e736d4d 100644
--- a/tests/primitives/test_nist.py
+++ b/tests/primitives/test_nist.py
@@ -86,3 +86,96 @@
         actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
         actual_ciphertext += cipher.finalize()
         assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+
+class TestTripleDES_CBC(object):
+    @parameterize_encrypt_test(
+        "3DES", "KAT",
+        ["keys", "iv", "plaintext", "ciphertext"],
+        [
+            "TCBCinvperm.rsp",
+            "TCBCpermop.rsp",
+            "TCBCsubtab.rsp",
+            "TCBCvarkey.rsp",
+            "TCBCvartext.rsp",
+        ]
+    )
+    def test_KAT_1(self, keys, iv, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.TripleDES(binascii.unhexlify(keys)),
+            modes.CBC(binascii.unhexlify(iv)),
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+    @parameterize_encrypt_test(
+        "3DES", "KAT",
+        ["keys", "iv1", "iv2", "iv3", "plaintext", "ciphertext3"],
+        [
+            "TCBCIpermop.rsp",
+            "TCBCIsubtab.rsp",
+            "TCBCIvarkey.rsp",
+            "TCBCIvartext.rsp",
+        ]
+    )
+    def test_KAT_2(self, keys, iv1, iv2, iv3, plaintext, ciphertext3):
+        cipher = BlockCipher(
+            ciphers.TripleDES(binascii.unhexlify(keys)),
+            modes.CBC(binascii.unhexlify(iv1 + iv2 + iv3)),
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext3
+
+    @parameterize_encrypt_test(
+        "3DES", "KAT",
+        ["keys", "iv1", "iv2", "iv3", "plaintext1", "ciphertext3"],
+        [
+            "TCBCIinvperm.rsp",
+        ]
+    )
+    def test_KAT_3(self, keys, iv1, iv2, iv3, plaintext1, ciphertext3):
+        cipher = BlockCipher(
+            ciphers.TripleDES(binascii.unhexlify(keys)),
+            modes.CBC(binascii.unhexlify(iv1 + iv2 + iv3)),
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext1))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext3
+
+    @parameterize_encrypt_test(
+        "3DES", "MMT",
+        ["key1", "key2", "key3", "iv1", "iv2", "iv3", "plaintext", "ciphertext"],
+        [
+            "TCBCIMMT1.rsp",
+            "TCBCIMMT2.rsp",
+            "TCBCIMMT3.rsp",
+        ]
+    )
+    def test_MMT_1(self, key1, key2, key3, iv1, iv2, iv3, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.TripleDES(binascii.unhexlify(key1 + key2 + key3)),
+            modes.CBC(binascii.unhexlify(iv1 + iv2 + iv3)),
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+    @parameterize_encrypt_test(
+        "3DES", "MMT",
+        ["key1", "key2", "key3", "iv", "plaintext", "ciphertext"],
+        [
+            "TCBCMMT1.rsp",
+            "TCBCMMT2.rsp",
+            "TCBCMMT3.rsp",
+        ]
+    )
+    def test_MMT_2(self, key1, key2, key3, iv, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.TripleDES(binascii.unhexlify(key1 + key2 + key3)),
+            modes.CBC(binascii.unhexlify(iv)),
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext