From d53de818ac5abaa5c251d8d585b143555224bdb0 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 16 Apr 2014 21:39:26 +0800 Subject: OpenSSL backend's CMAC implementation --- cryptography/hazmat/backends/openssl/backend.py | 78 ++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 5e13bfc1..4054e56c 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -25,8 +25,8 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - RSABackend + CipherBackend, CMACBackend, DSABackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, RSABackend ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, interfaces @@ -47,6 +47,7 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", @utils.register_interface(CipherBackend) +@utils.register_interface(CMACBackend) @utils.register_interface(DSABackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @@ -554,6 +555,12 @@ class Backend(object): return self._ffi.buffer(buf)[:res] + def cmac_supported(self): + return backend._lib.Cryptography_HAS_CMAC == 1 + + def create_cmac_ctx(self, algorithm): + return _CMACContext(self, algorithm) + class GetCipherByName(object): def __init__(self, fmt): @@ -1240,4 +1247,71 @@ class _RSAVerificationContext(object): raise InvalidSignature +@utils.register_interface(interfaces.CMACContext) +class _CMACContext(object): + def __init__(self, backend, algorithm, ctx=None): + + if not backend.cmac_supported(): + raise UnsupportedAlgorithm("This backend does not support CMAC") + + self._backend = backend + self._key = algorithm.key + self._algorithm = algorithm + self._output_length = algorithm.block_size // 8 + + if ctx is None: + registry = self._backend._cipher_registry + try: + adapter = registry[type(algorithm), CBC] + except KeyError: + raise UnsupportedAlgorithm( + "cipher {0} is not supported by this backend".format( + algorithm.name), _Reasons.UNSUPPORTED_CIPHER + ) + + evp_cipher = adapter(self._backend, algorithm, CBC) + if evp_cipher == self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "cipher {0} is not supported by this backend".format( + algorithm.name), _Reasons.UNSUPPORTED_CIPHER + ) + + ctx = self._backend._lib.CMAC_CTX_new() + self._backend._lib.CMAC_Init( + ctx, self._key, len(self._key), + evp_cipher, self._backend._ffi.NULL + ) + + self._ctx = ctx + + def update(self, data): + res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) + assert res == 1 + + def finalize(self): + buf = self._backend._ffi.new("unsigned char[]", self._output_length) + length = self._backend._ffi.new("size_t *", self._output_length) + res = self._backend._lib.CMAC_Final( + self._ctx, buf, length + ) + assert res == 1 + + self._backend._lib.CMAC_CTX_free(self._ctx) + return self._backend._ffi.buffer(buf)[:] + + def copy(self): + copied_ctx = self._backend._lib.CMAC_CTX_new() + self._backend._lib.CMAC_CTX_init(copied_ctx) + copied_ctx = self._backend._ffi.gc( + copied_ctx, self._backend._lib.CMAC_CTX_free() + ) + res = self._backend._lib.CMAC_CTX_copy( + copied_ctx, self._ctx + ) + assert res != 0 + return _CMACContext( + self._backend, self.algorithm, ctx=copied_ctx + ) + + backend = Backend() -- cgit v1.2.3 From 3080127116dc298271a2768c16173cf591d614ce Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 16 Apr 2014 21:41:14 +0800 Subject: CMAC primitive implementation --- cryptography/hazmat/primitives/cmac.py | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 cryptography/hazmat/primitives/cmac.py diff --git a/cryptography/hazmat/primitives/cmac.py b/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 00000000..bbf0bf9c --- /dev/null +++ b/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,75 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import six + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import CMACBackend +from cryptography.hazmat.primitives import constant_time, interfaces + + +@utils.register_interface(interfaces.CMACContext) +class CMAC(object): + def __init__(self, algorithm, backend, ctx=None): + if not isinstance(backend, CMACBackend): + raise UnsupportedAlgorithm( + "Backend object does not implement CMACBackend", + _Reasons.BACKEND_MISSING_INTERFACE + ) + + if not isinstance(algorithm, interfaces.BlockCipherAlgorithm): + raise TypeError( + "Expected instance of interfaces.BlockCipherAlgorithm" + ) + self.algorithm = algorithm + + self._backend = backend + if ctx is None: + self._ctx = self._backend.create_cmac_ctx(self.algorithm) + else: + self._ctx = ctx + + def update(self, data): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + if isinstance(data, six.text_type): + raise TypeError("Unicode-objects must be encoded before hashing") + self._ctx.update(data) + + def finalize(self): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + digest = self._ctx.finalize() + self._ctx = None + return digest + + def verify(self, signature): + if isinstance(signature, six.text_type): + raise TypeError("Unicode-objects must be encoded before verifying") + digest = self.finalize() + if not constant_time.bytes_eq(digest, signature): + raise InvalidSignature("Signature did not match digest.") + + def copy(self): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized") + return CMAC( + self.algorithm, + backend=self._backend, + ctx=self._ctx.copy() + ) -- cgit v1.2.3 From b5bb0653b934bdf5fbf93dc1e5491e78f5c71467 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 16 Apr 2014 21:42:11 +0800 Subject: Added CMAC tests --- pytest.ini | 1 + tests/conftest.py | 6 +- tests/hazmat/primitives/test_cmac.py | 172 +++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 tests/hazmat/primitives/test_cmac.py diff --git a/pytest.ini b/pytest.ini index b590d0be..cb6a80a8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,6 +2,7 @@ addopts = -r s markers = cipher: this test requires a backend providing CipherBackend + cmac: this test requires a backend providing CMACBackend dsa: this test requires a backend providing DSABackend hash: this test requires a backend providing HashBackend hmac: this test requires a backend providing HMACBackend diff --git a/tests/conftest.py b/tests/conftest.py index 1ee2a993..6ba8ae0a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,10 +17,9 @@ import pytest from cryptography.hazmat.backends import _available_backends from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - RSABackend + CipherBackend, CMACBackend, DSABackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, RSABackend ) - from .utils import check_backend_support, check_for_iface, select_backends @@ -36,6 +35,7 @@ def pytest_generate_tests(metafunc): def pytest_runtest_setup(item): check_for_iface("hmac", HMACBackend, item) check_for_iface("cipher", CipherBackend, item) + check_for_iface("cmac", CMACBackend, item) check_for_iface("hash", HashBackend, item) check_for_iface("pbkdf2hmac", PBKDF2HMACBackend, item) check_for_iface("dsa", DSABackend, item) diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py new file mode 100644 index 00000000..d61ce5a3 --- /dev/null +++ b/tests/hazmat/primitives/test_cmac.py @@ -0,0 +1,172 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import binascii + +import pretend + +import pytest + +import six + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidSignature, _Reasons +) +from cryptography.hazmat.backends.interfaces import CMACBackend +from cryptography.hazmat.primitives.ciphers.algorithms import ( + AES, ARC4, TripleDES +) +from cryptography.hazmat.primitives.cmac import CMAC + +from tests.utils import ( + load_vectors_from_file, load_nist_vectors, raises_unsupported_algorithm +) + +vectors_aes128 = load_vectors_from_file( + "CMAC/nist-800-38b-aes128.txt", load_nist_vectors) + +vectors_aes192 = load_vectors_from_file( + "CMAC/nist-800-38b-aes192.txt", load_nist_vectors) + +vectors_aes256 = load_vectors_from_file( + "CMAC/nist-800-38b-aes256.txt", load_nist_vectors) + +vectors_aes = vectors_aes128 + vectors_aes192 + vectors_aes256 + +vectors_3des = load_vectors_from_file( + "CMAC/nist-800-38b-3des.txt", load_nist_vectors) + + +@pytest.mark.supported( + only_if=lambda backend: backend.cmac_supported(), + skip_message="Does not support CMAC." +) +@pytest.mark.cmac +class TestCMAC(object): + @pytest.mark.parametrize("params", vectors_aes) + def test_aes_generate(self, backend, params): + key = params["key"] + message = params["message"] + output = params["output"] + + cmac = CMAC(AES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert binascii.hexlify(cmac.finalize()) == output + + @pytest.mark.parametrize("params", vectors_aes) + def test_aes_verify(self, backend, params): + key = params["key"] + message = params["message"] + output = params["output"] + + cmac = CMAC(AES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert cmac.verify(binascii.unhexlify(output)) is None + + @pytest.mark.parametrize("params", vectors_3des) + def test_3des_generate(self, backend, params): + key1 = params["key1"] + key2 = params["key2"] + key3 = params["key3"] + + if key1 == key3: + key = key1 + key2 + else: + key = key1 + key2 + key3 + + message = params["message"] + output = params["output"] + + cmac = CMAC(TripleDES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert binascii.hexlify(cmac.finalize()) == output + + @pytest.mark.parametrize("params", vectors_3des) + def test_3des_verify(self, backend, params): + key1 = params["key1"] + key2 = params["key2"] + key3 = params["key3"] + + if key1 == key3: + key = key1 + key2 + else: + key = key1 + key2 + key3 + + message = params["message"] + output = params["output"] + + cmac = CMAC(TripleDES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert cmac.verify(binascii.unhexlify(output)) is None + + def test_invalid_verify(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + cmac.update(b"6bc1bee22e409f96e93d7e117393172a") + + with pytest.raises(InvalidSignature): + cmac.verify(b"foobar") + + def test_invalid_algorithm(self, backend): + key = b"0102030405" + with pytest.raises(TypeError): + CMAC(ARC4(key), backend) + + def test_raises_after_finalize(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + cmac.finalize() + + with pytest.raises(AlreadyFinalized): + cmac.update(b"foo") + + with pytest.raises(AlreadyFinalized): + cmac.copy() + + with pytest.raises(AlreadyFinalized): + cmac.finalize() + + def test_verify_reject_unicode(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + + with pytest.raises(TypeError): + cmac.update(six.u('')) + + with pytest.raises(TypeError): + cmac.verify(six.u('')) + + def test_copy(self, backend): + @utils.register_interface(CMACBackend) + class PretendBackend(object): + pass + + pretend_backend = PretendBackend() + copied_ctx = pretend.stub() + pretend_ctx = pretend.stub(copy=lambda: copied_ctx) + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend=pretend_backend, ctx=pretend_ctx) + + assert cmac._backend is pretend_backend + assert cmac.copy()._backend is pretend_backend + + +def test_invalid_backend(): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + pretend_backend = object() + + with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): + CMAC(AES(key), pretend_backend) -- cgit v1.2.3 From fa4a6b2f5f536ada8115b49373da768f297dc256 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 16 Apr 2014 23:03:14 +0800 Subject: Added CMAC docs --- docs/hazmat/backends/interfaces.rst | 2 +- docs/hazmat/primitives/hmac.rst | 100 ------------------------ docs/hazmat/primitives/index.rst | 3 +- docs/hazmat/primitives/mac/cmac.rst | 100 ++++++++++++++++++++++++ docs/hazmat/primitives/mac/hmac.rst | 100 ++++++++++++++++++++++++ docs/hazmat/primitives/symmetric-encryption.rst | 4 +- 6 files changed, 205 insertions(+), 104 deletions(-) delete mode 100644 docs/hazmat/primitives/hmac.rst create mode 100644 docs/hazmat/primitives/mac/cmac.rst create mode 100644 docs/hazmat/primitives/mac/hmac.rst diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst index 71cd4564..3ed4cfb6 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -9,7 +9,7 @@ Backend interfaces Backend implementations may provide a number of interfaces to support operations such as :doc:`/hazmat/primitives/symmetric-encryption`, :doc:`/hazmat/primitives/cryptographic-hashes`, and -:doc:`/hazmat/primitives/hmac`. +:doc:`/hazmat/primitives/mac/hmac`. A specific ``backend`` may provide one or more of these interfaces. diff --git a/docs/hazmat/primitives/hmac.rst b/docs/hazmat/primitives/hmac.rst deleted file mode 100644 index 11b10735..00000000 --- a/docs/hazmat/primitives/hmac.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. hazmat:: - -Hash-based message authentication codes -======================================= - -.. currentmodule:: cryptography.hazmat.primitives.hmac - -.. testsetup:: - - import binascii - key = binascii.unhexlify(b"0" * 32) - -Hash-based message authentication codes (or HMACs) are a tool for calculating -message authentication codes using a cryptographic hash function coupled with a -secret key. You can use an HMAC to verify both the integrity and authenticity -of a message. - -.. class:: HMAC(key, algorithm, backend) - - HMAC objects take a ``key`` and a - :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` provider. - The ``key`` should be randomly generated bytes and is recommended to be - equal in length to the ``digest_size`` of the hash function chosen. - You must keep the ``key`` secret. - - This is an implementation of :rfc:`2104`. - - .. doctest:: - - >>> from cryptography.hazmat.backends import default_backend - >>> from cryptography.hazmat.primitives import hashes, hmac - >>> h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) - >>> h.update(b"message to hash") - >>> h.finalize() - '#F\xdaI\x8b"e\xc4\xf1\xbb\x9a\x8fc\xff\xf5\xdex.\xbc\xcd/+\x8a\x86\x1d\x84\'\xc3\xa6\x1d\xd8J' - - If the backend doesn't support the requested ``algorithm`` an - :class:`~cryptography.exceptions.UnsupportedAlgorithm` exception will be - raised. - - To check that a given signature is correct use the :meth:`verify` method. - You will receive an exception if the signature is wrong: - - .. code-block:: pycon - - >>> h.verify(b"an incorrect signature") - Traceback (most recent call last): - ... - cryptography.exceptions.InvalidSignature: Signature did not match digest. - - :param bytes key: Secret key as ``bytes``. - :param algorithm: An - :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` - provider such as those described in - :ref:`Cryptographic Hashes `. - :param backend: An - :class:`~cryptography.hazmat.backends.interfaces.HMACBackend` - provider. - - :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the - provided ``backend`` does not implement - :class:`~cryptography.hazmat.backends.interfaces.HMACBackend` - - .. method:: update(msg) - - :param bytes msg: The bytes to hash and authenticate. - :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` - - .. method:: copy() - - Copy this :class:`HMAC` instance, usually so that we may call - :meth:`finalize` to get an intermediate digest value while we continue - to call :meth:`update` on the original instance. - - :return: A new instance of :class:`HMAC` that can be updated - and finalized independently of the original instance. - :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` - - .. method:: verify(signature) - - Finalize the current context and securely compare digest to - ``signature``. - - :param bytes signature: The bytes to compare the current digest - against. - :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` - :raises cryptography.exceptions.InvalidSignature: If signature does not - match digest - - .. method:: finalize() - - Finalize the current context and return the message digest as bytes. - - After ``finalize`` has been called this object can no longer be used - and :meth:`update`, :meth:`copy`, :meth:`verify` and :meth:`finalize` - will raise an :class:`~cryptography.exceptions.AlreadyFinalized` - exception. - - :return bytes: The message digest as bytes. - :raises cryptography.exceptions.AlreadyFinalized: diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst index 90deec8b..2b92c43e 100644 --- a/docs/hazmat/primitives/index.rst +++ b/docs/hazmat/primitives/index.rst @@ -7,7 +7,8 @@ Primitives :maxdepth: 1 cryptographic-hashes - hmac + mac/hmac + mac/cmac symmetric-encryption padding key-derivation-functions diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst new file mode 100644 index 00000000..90d94ed8 --- /dev/null +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -0,0 +1,100 @@ +.. hazmat:: + +Cipher-based message authentication code +======================================== + +.. currentmodule:: cryptography.hazmat.primitives.cmac + +.. testsetup:: + + import binascii + key = binascii.unhexlify(b"0" * 32) + +Cipher-based message authentication codes (or CMACs) are a tool for calculating +message authentication codes using a block cipher coupled with a +secret key. You can use an CMAC to verify both the integrity and authenticity +of a message. + +.. class:: CMAC(algorithm, backend) + + CMAC objects take a + :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` provider. + + .. doctest:: + + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives import cmac + >>> from cryptography.hazmat.primitives.ciphers import algorithms + >>> c = cmac.CMAC(algorithms.AES(key), backend=default_backend()) + >>> c.update(b"message to authenticate") + >>> c.finalize() + 'CT\x1d\xc8\x0e\x15\xbe4e\xdb\xb6\x84\xca\xd9Xk' + + If the backend doesn't support the requested ``algorithm`` an + :class:`~cryptography.exceptions.UnsupportedAlgorithm` exception will be + raised. + + If the `algorithm`` isn't a + :class:`~cryptography.primitives.interfaces.BlockCipherAlgorithm` provider, + ``TypeError`` will be raised. + + To check that a given signature is correct use the :meth:`verify` method. + You will receive an exception if the signature is wrong: + + .. code-block:: pycon + + >>> c.verify(b"an incorrect signature") + Traceback (most recent call last): + ... + cryptography.exceptions.InvalidSignature: Signature did not match digest. + + :param algorithm: An + :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` + provider. + :param backend: An + :class:`~cryptography.hazmat.backends.interfaces.CMACBackend` + provider. + :raises TypeError: This is raised if the provided ``algorithm`` is not an instance of + :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` + :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the + provided ``backend`` does not implement + :class:`~cryptography.hazmat.backends.interfaces.CMACBackend` + + .. method:: update(data) + + :param bytes data: The bytes to hash and authenticate. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + .. method:: copy() + + Copy this :class:`CMAC` instance, usually so that we may call + :meth:`finalize` to get an intermediate value while we continue + to call :meth:`update` on the original instance. + + :return: A new instance of :class:`CMAC` that can be updated + and finalized independently of the original instance. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + .. method:: verify(signature) + + Finalize the current context and securely compare the MAC to + ``signature``. + + :param bytes signature: The bytes to compare the current CMAC + against. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + :raises cryptography.exceptions.InvalidSignature: If signature does not + match digest + + .. method:: finalize() + + Finalize the current context and return the message authentication code + as bytes. + + After ``finalize`` has been called this object can no longer be used + and :meth:`update`, :meth:`copy`, :meth:`verify` and :meth:`finalize` + will raise an :class:`~cryptography.exceptions.AlreadyFinalized` + exception. + + :return bytes: The message authentication code as bytes. + :raises cryptography.exceptions.AlreadyFinalized: diff --git a/docs/hazmat/primitives/mac/hmac.rst b/docs/hazmat/primitives/mac/hmac.rst new file mode 100644 index 00000000..11b10735 --- /dev/null +++ b/docs/hazmat/primitives/mac/hmac.rst @@ -0,0 +1,100 @@ +.. hazmat:: + +Hash-based message authentication codes +======================================= + +.. currentmodule:: cryptography.hazmat.primitives.hmac + +.. testsetup:: + + import binascii + key = binascii.unhexlify(b"0" * 32) + +Hash-based message authentication codes (or HMACs) are a tool for calculating +message authentication codes using a cryptographic hash function coupled with a +secret key. You can use an HMAC to verify both the integrity and authenticity +of a message. + +.. class:: HMAC(key, algorithm, backend) + + HMAC objects take a ``key`` and a + :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` provider. + The ``key`` should be randomly generated bytes and is recommended to be + equal in length to the ``digest_size`` of the hash function chosen. + You must keep the ``key`` secret. + + This is an implementation of :rfc:`2104`. + + .. doctest:: + + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives import hashes, hmac + >>> h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) + >>> h.update(b"message to hash") + >>> h.finalize() + '#F\xdaI\x8b"e\xc4\xf1\xbb\x9a\x8fc\xff\xf5\xdex.\xbc\xcd/+\x8a\x86\x1d\x84\'\xc3\xa6\x1d\xd8J' + + If the backend doesn't support the requested ``algorithm`` an + :class:`~cryptography.exceptions.UnsupportedAlgorithm` exception will be + raised. + + To check that a given signature is correct use the :meth:`verify` method. + You will receive an exception if the signature is wrong: + + .. code-block:: pycon + + >>> h.verify(b"an incorrect signature") + Traceback (most recent call last): + ... + cryptography.exceptions.InvalidSignature: Signature did not match digest. + + :param bytes key: Secret key as ``bytes``. + :param algorithm: An + :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` + provider such as those described in + :ref:`Cryptographic Hashes `. + :param backend: An + :class:`~cryptography.hazmat.backends.interfaces.HMACBackend` + provider. + + :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the + provided ``backend`` does not implement + :class:`~cryptography.hazmat.backends.interfaces.HMACBackend` + + .. method:: update(msg) + + :param bytes msg: The bytes to hash and authenticate. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + .. method:: copy() + + Copy this :class:`HMAC` instance, usually so that we may call + :meth:`finalize` to get an intermediate digest value while we continue + to call :meth:`update` on the original instance. + + :return: A new instance of :class:`HMAC` that can be updated + and finalized independently of the original instance. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + .. method:: verify(signature) + + Finalize the current context and securely compare digest to + ``signature``. + + :param bytes signature: The bytes to compare the current digest + against. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + :raises cryptography.exceptions.InvalidSignature: If signature does not + match digest + + .. method:: finalize() + + Finalize the current context and return the message digest as bytes. + + After ``finalize`` has been called this object can no longer be used + and :meth:`update`, :meth:`copy`, :meth:`verify` and :meth:`finalize` + will raise an :class:`~cryptography.exceptions.AlreadyFinalized` + exception. + + :return bytes: The message digest as bytes. + :raises cryptography.exceptions.AlreadyFinalized: diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 1a4df222..c2692ae2 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -21,7 +21,7 @@ message but an attacker can create bogus messages and force the application to decrypt them. For this reason it is *strongly* recommended to combine encryption with a -message authentication code, such as :doc:`HMAC `, in +message authentication code, such as :doc:`HMAC `, in an "encrypt-then-MAC" formulation as `described by Colin Percival`_. .. class:: Cipher(algorithm, mode, backend) @@ -289,7 +289,7 @@ Modes block cipher mode that simultaneously encrypts the message as well as authenticating it. Additional unencrypted data may also be authenticated. Additional means of verifying integrity such as - :doc:`HMAC ` are not necessary. + :doc:`HMAC ` are not necessary. **This mode does not require padding.** -- cgit v1.2.3 From 999db72bd88f801871588f16a42aeb0003bb5b9d Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 16 Apr 2014 23:03:32 +0800 Subject: Added CMACBackend to MultiBackend --- cryptography/hazmat/backends/multibackend.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 86cded85..23385682 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -16,12 +16,13 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - RSABackend + CipherBackend, CMACBackend, DSABackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, RSABackend ) @utils.register_interface(CipherBackend) +@utils.register_interface(CMACBackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) @@ -156,3 +157,11 @@ class MultiBackend(object): return b.generate_dsa_private_key(parameters) raise UnsupportedAlgorithm("DSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def cmac_supported(self): + for b in self._filtered_backends(CMACBackend): + return b.cmac_supported() + + def create_cmac_ctx(self, algorithm): + for b in self._filtered_backends(CMACBackend): + return b.create_cmac_ctx(algorithm) -- cgit v1.2.3 From 2f2aa5f1647dac2ff8403e25deb38e7839bb2729 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 16 Apr 2014 23:20:15 +0800 Subject: Updated CMAC documentation --- docs/hazmat/primitives/index.rst | 3 +-- docs/hazmat/primitives/mac/index.rst | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 docs/hazmat/primitives/mac/index.rst diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst index 2b92c43e..a9ab38a0 100644 --- a/docs/hazmat/primitives/index.rst +++ b/docs/hazmat/primitives/index.rst @@ -7,8 +7,7 @@ Primitives :maxdepth: 1 cryptographic-hashes - mac/hmac - mac/cmac + mac/index symmetric-encryption padding key-derivation-functions diff --git a/docs/hazmat/primitives/mac/index.rst b/docs/hazmat/primitives/mac/index.rst new file mode 100644 index 00000000..59fb8da2 --- /dev/null +++ b/docs/hazmat/primitives/mac/index.rst @@ -0,0 +1,10 @@ +.. hazmat:: + +Message Authentication Codes +============================ + +.. toctree:: + :maxdepth: 1 + + cmac + hmac -- cgit v1.2.3 From 78f6f333742883f334cb4ff859e6f0633babc242 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Thu, 17 Apr 2014 17:15:02 +0800 Subject: Updated tests --- cryptography/hazmat/backends/openssl/backend.py | 7 ++- tests/hazmat/primitives/test_cmac.py | 74 +++++++++++++++++-------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 4054e56c..ae4b4a85 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -555,8 +555,9 @@ class Backend(object): return self._ffi.buffer(buf)[:res] - def cmac_supported(self): - return backend._lib.Cryptography_HAS_CMAC == 1 + def cmac_algorithm_supported(self, algorithm): + return backend._lib.Cryptography_HAS_CMAC == 1 \ + and backend.cipher_supported(algorithm, CBC(0)) def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) @@ -1251,7 +1252,7 @@ class _RSAVerificationContext(object): class _CMACContext(object): def __init__(self, backend, algorithm, ctx=None): - if not backend.cmac_supported(): + if not backend.cmac_algorithm_supported(algorithm): raise UnsupportedAlgorithm("This backend does not support CMAC") self._backend = backend diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index d61ce5a3..80a9e7a6 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -49,13 +49,14 @@ vectors_aes = vectors_aes128 + vectors_aes192 + vectors_aes256 vectors_3des = load_vectors_from_file( "CMAC/nist-800-38b-3des.txt", load_nist_vectors) +fake_key = "AAAAAAAAAAAAAAAA" -@pytest.mark.supported( - only_if=lambda backend: backend.cmac_supported(), - skip_message="Does not support CMAC." -) @pytest.mark.cmac class TestCMAC(object): + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + skip_message="Does not support CMAC." + ) @pytest.mark.parametrize("params", vectors_aes) def test_aes_generate(self, backend, params): key = params["key"] @@ -66,6 +67,10 @@ class TestCMAC(object): cmac.update(binascii.unhexlify(message)) assert binascii.hexlify(cmac.finalize()) == output + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + skip_message="Does not support CMAC." + ) @pytest.mark.parametrize("params", vectors_aes) def test_aes_verify(self, backend, params): key = params["key"] @@ -76,16 +81,18 @@ class TestCMAC(object): cmac.update(binascii.unhexlify(message)) assert cmac.verify(binascii.unhexlify(output)) is None + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + TripleDES(fake_key)), + skip_message="Does not support CMAC." + ) @pytest.mark.parametrize("params", vectors_3des) def test_3des_generate(self, backend, params): key1 = params["key1"] key2 = params["key2"] key3 = params["key3"] - if key1 == key3: - key = key1 + key2 - else: - key = key1 + key2 + key3 + key = key1 + key2 + key3 message = params["message"] output = params["output"] @@ -94,16 +101,18 @@ class TestCMAC(object): cmac.update(binascii.unhexlify(message)) assert binascii.hexlify(cmac.finalize()) == output + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + TripleDES(fake_key)), + skip_message="Does not support CMAC." + ) @pytest.mark.parametrize("params", vectors_3des) def test_3des_verify(self, backend, params): key1 = params["key1"] key2 = params["key2"] key3 = params["key3"] - if key1 == key3: - key = key1 + key2 - else: - key = key1 + key2 + key3 + key = key1 + key2 + key3 message = params["message"] output = params["output"] @@ -112,6 +121,11 @@ class TestCMAC(object): cmac.update(binascii.unhexlify(message)) assert cmac.verify(binascii.unhexlify(output)) is None + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) def test_invalid_verify(self, backend): key = b"2b7e151628aed2a6abf7158809cf4f3c" cmac = CMAC(AES(key), backend) @@ -120,11 +134,20 @@ class TestCMAC(object): with pytest.raises(InvalidSignature): cmac.verify(b"foobar") + @pytest.mark.supported( + only_if=lambda backend: backend.cipher_supported( + ARC4(fake_key), None), + skip_message="Does not support CMAC." + ) def test_invalid_algorithm(self, backend): key = b"0102030405" with pytest.raises(TypeError): CMAC(ARC4(key), backend) + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + skip_message="Does not support CMAC." + ) def test_raises_after_finalize(self, backend): key = b"2b7e151628aed2a6abf7158809cf4f3c" cmac = CMAC(AES(key), backend) @@ -139,6 +162,10 @@ class TestCMAC(object): with pytest.raises(AlreadyFinalized): cmac.finalize() + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + skip_message="Does not support CMAC." + ) def test_verify_reject_unicode(self, backend): key = b"2b7e151628aed2a6abf7158809cf4f3c" cmac = CMAC(AES(key), backend) @@ -149,19 +176,20 @@ class TestCMAC(object): with pytest.raises(TypeError): cmac.verify(six.u('')) - def test_copy(self, backend): - @utils.register_interface(CMACBackend) - class PretendBackend(object): - pass - pretend_backend = PretendBackend() - copied_ctx = pretend.stub() - pretend_ctx = pretend.stub(copy=lambda: copied_ctx) - key = b"2b7e151628aed2a6abf7158809cf4f3c" - cmac = CMAC(AES(key), backend=pretend_backend, ctx=pretend_ctx) +def test_copy(): + @utils.register_interface(CMACBackend) + class PretendBackend(object): + pass + + pretend_backend = PretendBackend() + copied_ctx = pretend.stub() + pretend_ctx = pretend.stub(copy=lambda: copied_ctx) + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend=pretend_backend, ctx=pretend_ctx) - assert cmac._backend is pretend_backend - assert cmac.copy()._backend is pretend_backend + assert cmac._backend is pretend_backend + assert cmac.copy()._backend is pretend_backend def test_invalid_backend(): -- cgit v1.2.3 From 2124324a4aa678dc885ac20c5ac203663ac5f966 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Thu, 17 Apr 2014 17:36:31 +0800 Subject: Fixed pep8 error --- tests/hazmat/primitives/test_cmac.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index 80a9e7a6..a1c24ada 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -51,10 +51,12 @@ vectors_3des = load_vectors_from_file( fake_key = "AAAAAAAAAAAAAAAA" + @pytest.mark.cmac class TestCMAC(object): @pytest.mark.supported( - only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), skip_message="Does not support CMAC." ) @pytest.mark.parametrize("params", vectors_aes) @@ -68,7 +70,8 @@ class TestCMAC(object): assert binascii.hexlify(cmac.finalize()) == output @pytest.mark.supported( - only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), skip_message="Does not support CMAC." ) @pytest.mark.parametrize("params", vectors_aes) @@ -145,7 +148,8 @@ class TestCMAC(object): CMAC(ARC4(key), backend) @pytest.mark.supported( - only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), skip_message="Does not support CMAC." ) def test_raises_after_finalize(self, backend): @@ -163,7 +167,8 @@ class TestCMAC(object): cmac.finalize() @pytest.mark.supported( - only_if=lambda backend: backend.cmac_algorithm_supported(AES(fake_key)), + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), skip_message="Does not support CMAC." ) def test_verify_reject_unicode(self, backend): -- cgit v1.2.3 From a536085949f5bd1b479eb313ea1c0b114294c0fd Mon Sep 17 00:00:00 2001 From: Ayrx Date: Fri, 18 Apr 2014 20:20:05 +0800 Subject: Various fixes --- cryptography/hazmat/backends/openssl/backend.py | 13 +++++++++---- cryptography/hazmat/primitives/cmac.py | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index ae4b4a85..7cfdd284 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -556,8 +556,8 @@ class Backend(object): return self._ffi.buffer(buf)[:res] def cmac_algorithm_supported(self, algorithm): - return backend._lib.Cryptography_HAS_CMAC == 1 \ - and backend.cipher_supported(algorithm, CBC(0)) + return (backend._lib.Cryptography_HAS_CMAC == 1 + and backend.cipher_supported(algorithm, CBC(0))) def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) @@ -1278,6 +1278,10 @@ class _CMACContext(object): ) ctx = self._backend._lib.CMAC_CTX_new() + + assert ctx != self._backend._ffi.NULL + ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) + self._backend._lib.CMAC_Init( ctx, self._key, len(self._key), evp_cipher, self._backend._ffi.NULL @@ -1297,14 +1301,15 @@ class _CMACContext(object): ) assert res == 1 - self._backend._lib.CMAC_CTX_free(self._ctx) + self._ctx = None + return self._backend._ffi.buffer(buf)[:] def copy(self): copied_ctx = self._backend._lib.CMAC_CTX_new() self._backend._lib.CMAC_CTX_init(copied_ctx) copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.CMAC_CTX_free() + copied_ctx, self._backend._lib.CMAC_CTX_free ) res = self._backend._lib.CMAC_CTX_copy( copied_ctx, self._ctx diff --git a/cryptography/hazmat/primitives/cmac.py b/cryptography/hazmat/primitives/cmac.py index bbf0bf9c..7e7f65ab 100644 --- a/cryptography/hazmat/primitives/cmac.py +++ b/cryptography/hazmat/primitives/cmac.py @@ -36,11 +36,11 @@ class CMAC(object): raise TypeError( "Expected instance of interfaces.BlockCipherAlgorithm" ) - self.algorithm = algorithm + self._algorithm = algorithm self._backend = backend if ctx is None: - self._ctx = self._backend.create_cmac_ctx(self.algorithm) + self._ctx = self._backend.create_cmac_ctx(self._algorithm) else: self._ctx = ctx @@ -69,7 +69,7 @@ class CMAC(object): if self._ctx is None: raise AlreadyFinalized("Context was already finalized") return CMAC( - self.algorithm, + self._algorithm, backend=self._backend, ctx=self._ctx.copy() ) -- cgit v1.2.3 From 4b8628aa626413d41ebc976c1a097ca3f4e13959 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Fri, 18 Apr 2014 20:40:05 +0800 Subject: Updated multibackend --- cryptography/hazmat/backends/multibackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 23385682..2d75fd11 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -158,9 +158,9 @@ class MultiBackend(object): raise UnsupportedAlgorithm("DSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) - def cmac_supported(self): + def cmac_algorithm_supported(self, algorithm): for b in self._filtered_backends(CMACBackend): - return b.cmac_supported() + return b.cmac_algorithm_supported(algorithm) def create_cmac_ctx(self, algorithm): for b in self._filtered_backends(CMACBackend): -- cgit v1.2.3 From 458cfd5df6902f4ee427fa0ca0eb67a8031b204c Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 21 Apr 2014 11:40:47 +0800 Subject: Changed stub keys and ivs to use null bytes --- cryptography/hazmat/backends/openssl/backend.py | 7 +++++-- tests/hazmat/primitives/test_cmac.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 7cfdd284..11731133 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -556,8 +556,11 @@ class Backend(object): return self._ffi.buffer(buf)[:res] def cmac_algorithm_supported(self, algorithm): - return (backend._lib.Cryptography_HAS_CMAC == 1 - and backend.cipher_supported(algorithm, CBC(0))) + return ( + backend._lib.Cryptography_HAS_CMAC == 1 + and backend.cipher_supported(algorithm, CBC( + b"\x00" * algorithm.block_size)) + ) def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index a1c24ada..dd7f9df4 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -49,7 +49,7 @@ vectors_aes = vectors_aes128 + vectors_aes192 + vectors_aes256 vectors_3des = load_vectors_from_file( "CMAC/nist-800-38b-3des.txt", load_nist_vectors) -fake_key = "AAAAAAAAAAAAAAAA" +fake_key = b"\x00" * 16 @pytest.mark.cmac -- cgit v1.2.3 From 9eff2e55e216e10a3a60e664a14e14f6a24551c6 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 21 Apr 2014 11:49:58 +0800 Subject: Removed multibackend changes for another PR --- cryptography/hazmat/backends/multibackend.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 2d75fd11..903bcceb 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -16,13 +16,12 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CipherBackend, CMACBackend, DSABackend, HMACBackend, HashBackend, + CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend ) @utils.register_interface(CipherBackend) -@utils.register_interface(CMACBackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) @@ -157,11 +156,3 @@ class MultiBackend(object): return b.generate_dsa_private_key(parameters) raise UnsupportedAlgorithm("DSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) - - def cmac_algorithm_supported(self, algorithm): - for b in self._filtered_backends(CMACBackend): - return b.cmac_algorithm_supported(algorithm) - - def create_cmac_ctx(self, algorithm): - for b in self._filtered_backends(CMACBackend): - return b.create_cmac_ctx(algorithm) -- cgit v1.2.3 From dbefd58fb00b4de02a0f6803f767d10561478459 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 21 Apr 2014 18:43:16 +0800 Subject: Changed doctests to code-block --- docs/hazmat/primitives/mac/cmac.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst index 90d94ed8..c2a50cdd 100644 --- a/docs/hazmat/primitives/mac/cmac.rst +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -20,7 +20,7 @@ of a message. CMAC objects take a :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` provider. - .. doctest:: + .. code-block:: pycon >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import cmac -- cgit v1.2.3 From 3f695110b27afc05f0a9e9e6dc0bfd6e67501418 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 21 Apr 2014 19:12:03 +0800 Subject: Fix pep8 errors --- cryptography/hazmat/backends/openssl/backend.py | 2 +- tests/conftest.py | 2 +- tests/hazmat/primitives/test_cmac.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 11731133..0692492b 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -25,7 +25,7 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, CMACBackend, DSABackend, HMACBackend, HashBackend, + CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend ) from cryptography.hazmat.bindings.openssl.binding import Binding diff --git a/tests/conftest.py b/tests/conftest.py index 6ba8ae0a..d55e6cf6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,7 +17,7 @@ import pytest from cryptography.hazmat.backends import _available_backends from cryptography.hazmat.backends.interfaces import ( - CipherBackend, CMACBackend, DSABackend, HMACBackend, HashBackend, + CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend ) from .utils import check_backend_support, check_for_iface, select_backends diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index dd7f9df4..25568114 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -32,7 +32,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( from cryptography.hazmat.primitives.cmac import CMAC from tests.utils import ( - load_vectors_from_file, load_nist_vectors, raises_unsupported_algorithm + load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm ) vectors_aes128 = load_vectors_from_file( -- cgit v1.2.3 From 001637bec0e7a2442c2694dfe9c98c6efdc97d16 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 21 Apr 2014 22:32:43 +0800 Subject: Added wikipedia link --- cryptography/hazmat/backends/multibackend.py | 4 ++-- docs/hazmat/primitives/mac/cmac.rst | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 903bcceb..86cded85 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -16,8 +16,8 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, - PBKDF2HMACBackend, RSABackend + CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, + RSABackend ) diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst index c2a50cdd..be35014f 100644 --- a/docs/hazmat/primitives/mac/cmac.rst +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -10,7 +10,7 @@ Cipher-based message authentication code import binascii key = binascii.unhexlify(b"0" * 32) -Cipher-based message authentication codes (or CMACs) are a tool for calculating +`Cipher-based message authentication codes`_ (or CMACs) are a tool for calculating message authentication codes using a block cipher coupled with a secret key. You can use an CMAC to verify both the integrity and authenticity of a message. @@ -98,3 +98,6 @@ of a message. :return bytes: The message authentication code as bytes. :raises cryptography.exceptions.AlreadyFinalized: + + +.. _`Cipher-based message authentication codes`: https://en.wikipedia.org/wiki/CMAC -- cgit v1.2.3 From a93abae0a62f4da67fb51a77dc0acafe2d04f99c Mon Sep 17 00:00:00 2001 From: Ayrx Date: Tue, 22 Apr 2014 11:10:55 +0800 Subject: Added documentation to note rfc 4493 --- cryptography/hazmat/backends/openssl/backend.py | 3 +-- docs/hazmat/primitives/mac/cmac.rst | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 0692492b..4137ad10 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -1254,7 +1254,6 @@ class _RSAVerificationContext(object): @utils.register_interface(interfaces.CMACContext) class _CMACContext(object): def __init__(self, backend, algorithm, ctx=None): - if not backend.cmac_algorithm_supported(algorithm): raise UnsupportedAlgorithm("This backend does not support CMAC") @@ -1317,7 +1316,7 @@ class _CMACContext(object): res = self._backend._lib.CMAC_CTX_copy( copied_ctx, self._ctx ) - assert res != 0 + assert res == 0 return _CMACContext( self._backend, self.algorithm, ctx=copied_ctx ) diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst index be35014f..8b88a3ce 100644 --- a/docs/hazmat/primitives/mac/cmac.rst +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -15,6 +15,8 @@ message authentication codes using a block cipher coupled with a secret key. You can use an CMAC to verify both the integrity and authenticity of a message. +A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`. + .. class:: CMAC(algorithm, backend) CMAC objects take a -- cgit v1.2.3 From 3881917b59c49ab1616b61a696a74aa195c710e0 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Tue, 22 Apr 2014 12:00:43 +0800 Subject: Added more copy() tests --- cryptography/hazmat/backends/openssl/backend.py | 5 ++--- tests/hazmat/primitives/test_cmac.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 4137ad10..7d73c413 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -1309,16 +1309,15 @@ class _CMACContext(object): def copy(self): copied_ctx = self._backend._lib.CMAC_CTX_new() - self._backend._lib.CMAC_CTX_init(copied_ctx) copied_ctx = self._backend._ffi.gc( copied_ctx, self._backend._lib.CMAC_CTX_free ) res = self._backend._lib.CMAC_CTX_copy( copied_ctx, self._ctx ) - assert res == 0 + assert res == 1 return _CMACContext( - self._backend, self.algorithm, ctx=copied_ctx + self._backend, self._algorithm, ctx=copied_ctx ) diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index 25568114..f47f2c13 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -181,6 +181,13 @@ class TestCMAC(object): with pytest.raises(TypeError): cmac.verify(six.u('')) + def test_copy_with_backend(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + cmac.update(b"6bc1bee22e409f96e93d7e117393172a") + copy_cmac = cmac.copy() + assert cmac.finalize() == copy_cmac.finalize() + def test_copy(): @utils.register_interface(CMACBackend) -- cgit v1.2.3 From 9a97cb93205b5785423bac9cae9288310f3b0c5a Mon Sep 17 00:00:00 2001 From: Ayrx Date: Tue, 22 Apr 2014 12:55:48 +0800 Subject: Added test skip for test_copy_with_backend() --- tests/hazmat/primitives/test_cmac.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index f47f2c13..7ec4af68 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -181,6 +181,11 @@ class TestCMAC(object): with pytest.raises(TypeError): cmac.verify(six.u('')) + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) def test_copy_with_backend(self, backend): key = b"2b7e151628aed2a6abf7158809cf4f3c" cmac = CMAC(AES(key), backend) -- cgit v1.2.3