diff options
-rw-r--r-- | cryptography/bindings/openssl/api.py | 10 | ||||
-rw-r--r-- | cryptography/bindings/openssl/evp.py | 2 | ||||
-rw-r--r-- | cryptography/primitives/hashes.py | 22 | ||||
-rw-r--r-- | docs/primitives/cryptographic-hashes.rst | 88 | ||||
-rw-r--r-- | docs/primitives/index.rst | 1 | ||||
-rw-r--r-- | tests/primitives/test_hash_vectors.py | 35 | ||||
-rw-r--r-- | tests/primitives/test_hashes.py | 20 |
7 files changed, 170 insertions, 8 deletions
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py index 3c2cf2e2..f5e042e7 100644 --- a/cryptography/bindings/openssl/api.py +++ b/cryptography/bindings/openssl/api.py @@ -99,10 +99,8 @@ class API(object): self.lib.EVP_get_cipherbyname(ciphername.encode("ascii"))) def create_block_cipher_context(self, cipher, mode): - ctx = self.ffi.new("EVP_CIPHER_CTX *") - res = self.lib.EVP_CIPHER_CTX_init(ctx) - assert res != 0 - ctx = self.ffi.gc(ctx, self.lib.EVP_CIPHER_CTX_cleanup) + ctx = self.lib.EVP_CIPHER_CTX_new() + ctx = self.ffi.gc(ctx, self.lib.EVP_CIPHER_CTX_free) # TODO: compute name using a better algorithm ciphername = "{0}-{1}-{2}".format( cipher.name, cipher.key_size, mode.name @@ -144,7 +142,7 @@ class API(object): res = self.lib.EVP_EncryptFinal_ex(ctx, buf, outlen) assert res != 0 res = self.lib.EVP_CIPHER_CTX_cleanup(ctx) - assert res != 0 + assert res == 1 return self.ffi.buffer(buf)[:outlen[0]] def supports_hash(self, hash_cls): @@ -168,6 +166,8 @@ class API(object): buf = self.ffi.new("unsigned char[]", digest_size) res = self.lib.EVP_DigestFinal_ex(ctx, buf, self.ffi.NULL) assert res != 0 + res = self.lib.EVP_MD_CTX_cleanup(ctx) + assert res == 1 return self.ffi.buffer(buf)[:digest_size] def copy_hash_context(self, ctx): diff --git a/cryptography/bindings/openssl/evp.py b/cryptography/bindings/openssl/evp.py index 20159906..2bb5b0f7 100644 --- a/cryptography/bindings/openssl/evp.py +++ b/cryptography/bindings/openssl/evp.py @@ -45,6 +45,8 @@ int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *); const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *); int EVP_CIPHER_block_size(const EVP_CIPHER *); void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *); +EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(); +void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); EVP_MD_CTX *EVP_MD_CTX_create(); int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *); diff --git a/cryptography/primitives/hashes.py b/cryptography/primitives/hashes.py index affca564..e8c1f929 100644 --- a/cryptography/primitives/hashes.py +++ b/cryptography/primitives/hashes.py @@ -13,20 +13,24 @@ from __future__ import absolute_import, division, print_function +import abc + import binascii +import six + from cryptography.bindings import _default_api -class BaseHash(object): +class BaseHash(six.with_metaclass(abc.ABCMeta)): def __init__(self, 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 - def update(self, string): - self._api.update_hash_context(self._ctx, string) + def update(self, data): + self._api.update_hash_context(self._ctx, data) def copy(self): return self.__class__(ctx=self._copy_ctx()) @@ -76,3 +80,15 @@ class RIPEMD160(BaseHash): name = "ripemd160" digest_size = 20 block_size = 64 + + +class Whirlpool(BaseHash): + name = "whirlpool" + digest_size = 64 + block_size = 64 + + +class MD5(BaseHash): + name = "md5" + digest_size = 16 + block_size = 64 diff --git a/docs/primitives/cryptographic-hashes.rst b/docs/primitives/cryptographic-hashes.rst new file mode 100644 index 00000000..d4dde042 --- /dev/null +++ b/docs/primitives/cryptographic-hashes.rst @@ -0,0 +1,88 @@ +Message Digests +=============== + +.. class:: cryptography.primitives.hashes.BaseHash + + Abstract base class that implements a common interface for all hash + algorithms that follow here. + + .. method:: update(data) + + :param bytes data: The bytes you wish to hash. + + .. method:: copy() + + :return: a new instance of this object with a copied internal state. + + .. method:: digest() + + :return bytes: The message digest as bytes. + + .. method:: hexdigest() + + :return str: The message digest as hex. + +SHA-1 +~~~~~ + +.. attention:: + + NIST has deprecated SHA-1 in favor of the SHA-2 variants. New applications + are strongly suggested to use SHA-2 over SHA-1. + +.. class:: cryptography.primitives.hashes.SHA1() + + SHA-1 is a cryptographic hash function standardized by NIST. It has a + 160-bit message digest. + +SHA-2 Family +~~~~~~~~~~~~ + +.. class:: cryptography.primitives.hashes.SHA224() + + SHA-224 is a cryptographic hash function from the SHA-2 family and + standardized by NIST. It has a 224-bit message digest. + +.. class:: cryptography.primitives.hashes.SHA256() + + SHA-256 is a cryptographic hash function from the SHA-2 family and + standardized by NIST. It has a 256-bit message digest. + +.. class:: cryptography.primitives.hashes.SHA384() + + SHA-384 is a cryptographic hash function from the SHA-2 family and + standardized by NIST. It has a 384-bit message digest. + +.. class:: cryptography.primitives.hashes.SHA512() + + SHA-512 is a cryptographic hash function from the SHA-2 family and + standardized by NIST. It has a 512-bit message digest. + +RIPEMD160 +~~~~~~~~~ + +.. class:: cryptography.primitives.hashes.RIPEMD160() + + RIPEMD160 is a cryptographic hash function that is part of ISO/IEC + 10118-3:2004. It has a 160-bit message digest. + +Whirlpool +~~~~~~~~~ + +.. class:: cryptography.primitives.hashes.Whirlpool() + + Whirlpool is a cryptographic hash function that is part of ISO/IEC + 10118-3:2004. It has a 512-bit message digest. + +MD5 +~~~ + +.. warning:: + + MD5 is a deprecated hash algorithm that has practical known collision + attacks. You are strongly discouraged from using it. + +.. class:: cryptography.primitives.hashes.MD5() + + MD5 is a deprecated cryptographic hash function. It has a 160-bit message + digest and has practical known collision attacks. diff --git a/docs/primitives/index.rst b/docs/primitives/index.rst index 1066e30e..c18c62ca 100644 --- a/docs/primitives/index.rst +++ b/docs/primitives/index.rst @@ -4,4 +4,5 @@ Primitives .. toctree:: :maxdepth: 1 + cryptographic-hashes symmetric-encryption diff --git a/tests/primitives/test_hash_vectors.py b/tests/primitives/test_hash_vectors.py index 51c4b85d..02ef4dbb 100644 --- a/tests/primitives/test_hash_vectors.py +++ b/tests/primitives/test_hash_vectors.py @@ -109,3 +109,38 @@ class TestRIPEMD160(object): only_if=lambda api: api.supports_hash(hashes.RIPEMD160), skip_message="Does not support RIPEMD160", ) + + +class TestWhirlpool(object): + test_whirlpool = generate_hash_test( + load_hash_vectors_from_file, + os.path.join("ISO", "whirlpool"), + [ + "iso-test-vectors.txt", + ], + hashes.Whirlpool, + only_if=lambda api: api.supports_hash(hashes.Whirlpool), + skip_message="Does not support Whirlpool", + ) + + test_whirlpool_long_string = generate_long_string_hash_test( + hashes.Whirlpool, + ("0c99005beb57eff50a7cf005560ddf5d29057fd86b2" + "0bfd62deca0f1ccea4af51fc15490eddc47af32bb2b" + "66c34ff9ad8c6008ad677f77126953b226e4ed8b01"), + only_if=lambda api: api.supports_hash(hashes.Whirlpool), + skip_message="Does not support Whirlpool", + ) + + +class TestMD5(object): + test_md5 = generate_hash_test( + load_hash_vectors_from_file, + os.path.join("RFC", "MD5"), + [ + "rfc-1321.txt", + ], + hashes.MD5, + only_if=lambda api: api.supports_hash(hashes.MD5), + skip_message="Does not support MD5", + ) diff --git a/tests/primitives/test_hashes.py b/tests/primitives/test_hashes.py index bfb45037..901ddabb 100644 --- a/tests/primitives/test_hashes.py +++ b/tests/primitives/test_hashes.py @@ -76,3 +76,23 @@ class TestRIPEMD160(object): only_if=lambda api: api.supports_hash(hashes.RIPEMD160), skip_message="Does not support RIPEMD160", ) + + +class TestWhirlpool(object): + test_Whirlpool = generate_base_hash_test( + hashes.Whirlpool, + digest_size=64, + block_size=64, + only_if=lambda api: api.supports_hash(hashes.Whirlpool), + skip_message="Does not support Whirlpool", + ) + + +class TestMD5(object): + test_MD5 = generate_base_hash_test( + hashes.MD5, + digest_size=16, + block_size=64, + only_if=lambda api: api.supports_hash(hashes.MD5), + skip_message="Does not support MD5", + ) |