Merge branch 'master' into triple-des
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index 7d189d6..3d92c14 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -19,7 +19,7 @@
 import cffi
 
 from cryptography.primitives import interfaces
-from cryptography.primitives.block.ciphers import AES, Camellia
+from cryptography.primitives.block.ciphers import AES, Camellia, TripleDES
 from cryptography.primitives.block.modes import CBC, CTR, ECB, OFB, CFB
 
 
@@ -135,6 +135,12 @@
                 mode_cls,
                 GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
             )
+        for mode_cls in [CBC, CFB, OFB]:
+            self.register_cipher_adapter(
+                TripleDES,
+                mode_cls,
+                GetCipherByName("des-ede3-{mode.name}")
+            )
 
     def create_block_cipher_context(self, cipher, mode):
         ctx = self.lib.EVP_CIPHER_CTX_new()
diff --git a/cryptography/primitives/block/ciphers.py b/cryptography/primitives/block/ciphers.py
index 4ac150a..4143b89 100644
--- a/cryptography/primitives/block/ciphers.py
+++ b/cryptography/primitives/block/ciphers.py
@@ -52,3 +52,27 @@
     @property
     def key_size(self):
         return len(self.key) * 8
+
+
+class TripleDES(object):
+    name = "3DES"
+    block_size = 64
+    key_sizes = frozenset([64, 128, 192])
+
+    def __init__(self, key):
+        super(TripleDES, self).__init__()
+        if len(key) == 8:
+            key += key + key
+        elif len(key) == 16:
+            key += key[:8]
+        self.key = key
+
+        # Verify that the key size matches the expected key size
+        if self.key_size not in self.key_sizes:
+            raise ValueError("Invalid key size ({0}) for {1}".format(
+                self.key_size, self.name
+            ))
+
+    @property
+    def key_size(self):
+        return len(self.key) * 8
diff --git a/docs/primitives/symmetric-encryption.rst b/docs/primitives/symmetric-encryption.rst
index 7899e67..cf251c9 100644
--- a/docs/primitives/symmetric-encryption.rst
+++ b/docs/primitives/symmetric-encryption.rst
@@ -61,6 +61,23 @@
                       This must be kept secret.
 
 
+.. 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 has known cryptoanalytic
+    flaws, however none of them currently enable a practical attack.
+    Nonetheless, Triples DES is not reccomended for new applications because it
+    is incredibly slow, old applications should 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_ciphers.py b/tests/primitives/test_ciphers.py
index 27d3585..17fcdba 100644
--- a/tests/primitives/test_ciphers.py
+++ b/tests/primitives/test_ciphers.py
@@ -17,7 +17,7 @@
 
 import pytest
 
-from cryptography.primitives.block.ciphers import AES, Camellia
+from cryptography.primitives.block.ciphers import AES, Camellia, TripleDES
 
 
 class TestAES(object):
@@ -48,3 +48,18 @@
     def test_invalid_key_size(self):
         with pytest.raises(ValueError):
             Camellia(binascii.unhexlify(b"0" * 12))
+
+
+class TestTripleDES(object):
+    @pytest.mark.parametrize("key", [
+        b"0" * 16,
+        b"0" * 32,
+        b"0" * 48,
+    ])
+    def test_key_size(self, key):
+        cipher = TripleDES(binascii.unhexlify(key))
+        assert cipher.key_size == 192
+
+    def test_invalid_key_size(self):
+        with pytest.raises(ValueError):
+            TripleDES(binascii.unhexlify(b"0" * 12))
diff --git a/tests/primitives/test_nist.py b/tests/primitives/test_nist.py
index d97b207..2a32d1b 100644
--- a/tests/primitives/test_nist.py
+++ b/tests/primitives/test_nist.py
@@ -164,3 +164,93 @@
         lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
         lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
     )
+
+
+class TestTripleDES_CBC(object):
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("3DES", "KAT"),
+        [
+            "TCBCinvperm.rsp",
+            "TCBCpermop.rsp",
+            "TCBCsubtab.rsp",
+            "TCBCvarkey.rsp",
+            "TCBCvartext.rsp",
+        ],
+        lambda keys, iv: ciphers.TripleDES(binascii.unhexlify(keys)),
+        lambda keys, iv: modes.CBC(binascii.unhexlify(iv)),
+    )
+
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("3DES", "MMT"),
+        [
+            "TCBCMMT1.rsp",
+            "TCBCMMT2.rsp",
+            "TCBCMMT3.rsp",
+        ],
+        lambda key1, key2, key3, iv: (
+            ciphers.TripleDES(binascii.unhexlify(key1 + key2 + key3))
+        ),
+        lambda key1, key2, key3, iv: modes.CBC(binascii.unhexlify(iv)),
+    )
+
+
+class TestTripleDES_OFB(object):
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("3DES", "KAT"),
+        [
+            "TOFBpermop.rsp",
+            "TOFBsubtab.rsp",
+            "TOFBvarkey.rsp",
+            "TOFBvartext.rsp",
+            "TOFBinvperm.rsp",
+        ],
+        lambda keys, iv: ciphers.TripleDES(binascii.unhexlify(keys)),
+        lambda keys, iv: modes.OFB(binascii.unhexlify(iv)),
+    )
+
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("3DES", "MMT"),
+        [
+            "TOFBMMT1.rsp",
+            "TOFBMMT2.rsp",
+            "TOFBMMT3.rsp",
+        ],
+        lambda key1, key2, key3, iv: (
+            ciphers.TripleDES(binascii.unhexlify(key1 + key2 + key3))
+        ),
+        lambda key1, key2, key3, iv: modes.OFB(binascii.unhexlify(iv)),
+    )
+
+
+class TestTripleDES_CFB(object):
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("3DES", "KAT"),
+        [
+            "TCFB64invperm.rsp",
+            "TCFB64permop.rsp",
+            "TCFB64subtab.rsp",
+            "TCFB64varkey.rsp",
+            "TCFB64vartext.rsp",
+        ],
+        lambda keys, iv: ciphers.TripleDES(binascii.unhexlify(keys)),
+        lambda keys, iv: modes.CFB(binascii.unhexlify(iv)),
+    )
+
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("3DES", "MMT"),
+        [
+            "TCFB64MMT1.rsp",
+            "TCFB64MMT2.rsp",
+            "TCFB64MMT3.rsp",
+        ],
+        lambda key1, key2, key3, iv: (
+            ciphers.TripleDES(binascii.unhexlify(key1 + key2 + key3))
+        ),
+        lambda key1, key2, key3, iv: modes.CFB(binascii.unhexlify(iv)),
+    )