aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cryptography/bindings/openssl/api.py8
-rw-r--r--cryptography/primitives/block/ciphers.py24
-rw-r--r--cryptography/primitives/hashes.py6
-rw-r--r--docs/primitives/cryptographic-hashes.rst4
-rw-r--r--docs/primitives/symmetric-encryption.rst17
-rw-r--r--tests/primitives/test_ciphers.py17
-rw-r--r--tests/primitives/test_hashes.py11
-rw-r--r--tests/primitives/test_nist.py90
-rw-r--r--tests/primitives/test_utils.py5
-rw-r--r--tests/primitives/utils.py4
-rw-r--r--tests/test_utils.py10
-rw-r--r--tox.ini6
12 files changed, 189 insertions, 13 deletions
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index 67d73afb..fedaf9cc 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -19,7 +19,7 @@ import sys
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 @@ class API(object):
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_encrypt_context(self, cipher, mode):
ctx, evp, iv_nonce = self._create_block_cipher_context(cipher, mode)
diff --git a/cryptography/primitives/block/ciphers.py b/cryptography/primitives/block/ciphers.py
index 4ac150a4..4143b89d 100644
--- a/cryptography/primitives/block/ciphers.py
+++ b/cryptography/primitives/block/ciphers.py
@@ -52,3 +52,27 @@ class Camellia(object):
@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/cryptography/primitives/hashes.py b/cryptography/primitives/hashes.py
index e8c1f929..c4bd8ad0 100644
--- a/cryptography/primitives/hashes.py
+++ b/cryptography/primitives/hashes.py
@@ -23,13 +23,17 @@ from cryptography.bindings import _default_api
class BaseHash(six.with_metaclass(abc.ABCMeta)):
- def __init__(self, api=None, ctx=None):
+ def __init__(self, data=None, api=None, ctx=None):
if api is None:
api = _default_api
self._api = api
self._ctx = self._api.create_hash_context(self) if ctx is None else ctx
+ if data is not None:
+ self.update(data)
def update(self, data):
+ if isinstance(data, six.text_type):
+ raise TypeError("Unicode-objects must be encoded before hashing")
self._api.update_hash_context(self._ctx, data)
def copy(self):
diff --git a/docs/primitives/cryptographic-hashes.rst b/docs/primitives/cryptographic-hashes.rst
index d4dde042..aeb30f40 100644
--- a/docs/primitives/cryptographic-hashes.rst
+++ b/docs/primitives/cryptographic-hashes.rst
@@ -1,11 +1,13 @@
Message Digests
===============
-.. class:: cryptography.primitives.hashes.BaseHash
+.. class:: cryptography.primitives.hashes.BaseHash(data=None)
Abstract base class that implements a common interface for all hash
algorithms that follow here.
+ If ``data`` is provided ``update(data)`` is called upon construction.
+
.. method:: update(data)
:param bytes data: The bytes you wish to hash.
diff --git a/docs/primitives/symmetric-encryption.rst b/docs/primitives/symmetric-encryption.rst
index 79d712e5..73d8ad37 100644
--- a/docs/primitives/symmetric-encryption.rst
+++ b/docs/primitives/symmetric-encryption.rst
@@ -79,6 +79,23 @@ Ciphers
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 27d35850..17fcdbaf 100644
--- a/tests/primitives/test_ciphers.py
+++ b/tests/primitives/test_ciphers.py
@@ -17,7 +17,7 @@ import binascii
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 @@ class TestCamellia(object):
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_hashes.py b/tests/primitives/test_hashes.py
index 901ddabb..805d992b 100644
--- a/tests/primitives/test_hashes.py
+++ b/tests/primitives/test_hashes.py
@@ -13,11 +13,22 @@
from __future__ import absolute_import, division, print_function
+import pytest
+
+import six
+
from cryptography.primitives import hashes
from .utils import generate_base_hash_test
+class TestBaseHash(object):
+ def test_base_hash_reject_unicode(self, api):
+ m = hashes.SHA1(api=api)
+ with pytest.raises(TypeError):
+ m.update(six.u("\u00FC"))
+
+
class TestSHA1(object):
test_SHA1 = generate_base_hash_test(
hashes.SHA1,
diff --git a/tests/primitives/test_nist.py b/tests/primitives/test_nist.py
index d97b207b..2a32d1bc 100644
--- a/tests/primitives/test_nist.py
+++ b/tests/primitives/test_nist.py
@@ -164,3 +164,93 @@ class TestAES_CFB(object):
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)),
+ )
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/primitives/utils.py b/tests/primitives/utils.py
index 70ece52a..91ca36d8 100644
--- a/tests/primitives/utils.py
+++ b/tests/primitives/utils.py
@@ -72,6 +72,8 @@ def hash_test(api, hash_cls, params, only_if, skip_message):
m = hash_cls(api=api)
m.update(binascii.unhexlify(msg))
assert m.hexdigest() == md.replace(" ", "").lower()
+ digest = hash_cls(api=api, data=binascii.unhexlify(msg)).hexdigest()
+ assert digest == md.replace(" ", "").lower()
def generate_base_hash_test(hash_cls, digest_size, block_size,
@@ -120,6 +122,6 @@ def generate_long_string_hash_test(hash_factory, md, only_if=None,
def long_string_hash_test(api, hash_factory, md, only_if, skip_message):
if only_if is not None and not only_if(api):
pytest.skip(skip_message)
- m = hash_factory(api)
+ m = hash_factory(api=api)
m.update(b"a" * 1000000)
assert m.hexdigest() == md.lower()
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():
diff --git a/tox.ini b/tox.ini
index e72eb58b..b437a7a9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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