From 2724ff6af8ba5f8dfd1f0f511ed95fab5cd8abd8 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 20 Dec 2013 13:51:42 -0800 Subject: Link from symmetric encryption to fernet --- docs/hazmat/primitives/symmetric-encryption.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index f4d0457a..7b012975 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -1,4 +1,4 @@ -.. hazmat:: +.. hazmat:: /fernet Symmetric Encryption -- cgit v1.2.3 From bb996d7e06fe539cbddee880a1af22df334cd5db Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 7 Jan 2014 17:34:48 -0600 Subject: also remove CAST5 docs --- docs/hazmat/primitives/symmetric-encryption.rst | 9 --------- 1 file changed, 9 deletions(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index e05248ff..a1a3ec0d 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -189,15 +189,6 @@ Algorithms ``56`` bits long), they can simply be concatenated to produce the full key. This must be kept secret. -.. class:: CAST5(key) - - CAST5 (also known as CAST-128) is a block cipher approved for use in the - Canadian government by their Communications Security Establishment. It is a - variable key length cipher and supports keys from 40-128 bits in length. - - :param bytes key: The secret key, 40-128 bits in length (in increments of - 8). This must be kept secret. - Weak Ciphers ------------ -- cgit v1.2.3 From 78569d68de24bc56dd799c262f3dd2d522bcdcd1 Mon Sep 17 00:00:00 2001 From: David Reid Date: Tue, 7 Jan 2014 15:42:17 -0800 Subject: Try making the AEAD examples less dense. --- docs/hazmat/primitives/symmetric-encryption.rst | 51 ++++++++++++++++++------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index e05248ff..d3ba731a 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -365,20 +365,45 @@ Modes :param bytes tag: The tag bytes to verify during decryption. When encrypting this must be None. - .. doctest:: + .. code-block:: python - >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend - >>> cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend()) - >>> encryptor = cipher.encryptor() - >>> encryptor.authenticate_additional_data(b"authenticated but not encrypted payload") - >>> ct = encryptor.update(b"a secret message") + encryptor.finalize() - >>> tag = encryptor.tag - >>> cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag), backend) - >>> decryptor = cipher.decryptor() - >>> decryptor.authenticate_additional_data(b"authenticated but not encrypted payload") - >>> decryptor.update(ct) + decryptor.finalize() - 'a secret message' + def encrypt(key, plaintext, associated_data): + iv = os.urandom(12) + cipher = Cipher( + algorithms.AES(key), + modes.GCM(iv), + backend=default_backend() + ) + + encryptor = cipher.encryptor() + encryptor.authenticate_additional_data(associated_data) + ciphertext = encryptor.update(plaintext) + encryptor.finalize() + + return (associated_data, iv, ciphertext, encryptor.tag) + + def decrypt(key, associated_data, iv, ciphertext, tag): + cipher = Cipher( + algorithms.AES(key), + modes.GCM(iv, tag), + backend=default_backend() + ) + + decryptor = cipher.decryptor() + decryptor.authenticate_additional_data(associated_data) + + return decryptor.update(ciphertext) + decryptor.finalize() + + associated_data, iv, ciphertext, tag = encrypt( + key, + b"a secret message", + b"authenticated but not encrypted payload" + ) + + print(decrypt(key, associated_data, iv, ciphertext, tag)) + + .. testoutput:: + + a secret message Insecure Modes -- cgit v1.2.3 From abb72d23118fb63a8601d2036b7c2cef2598f408 Mon Sep 17 00:00:00 2001 From: David Reid Date: Tue, 7 Jan 2014 16:06:18 -0800 Subject: Make the example more complete and add some comments walking the user through some stuff. --- docs/hazmat/primitives/symmetric-encryption.rst | 63 +++++++++++++++++++------ 1 file changed, 49 insertions(+), 14 deletions(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index d3ba731a..59aff99b 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -365,45 +365,80 @@ Modes :param bytes tag: The tag bytes to verify during decryption. When encrypting this must be None. - .. code-block:: python + .. testcode:: + + import os + + from cryptography.hazmat.primitives.ciphers import ( + Cipher, algorithms, modes + ) + + from cryptography.hazmat.primitives.padding import PKCS7 def encrypt(key, plaintext, associated_data): + # Generate a random 96-bit IV. iv = os.urandom(12) - cipher = Cipher( + + # Construct a AES-GCM Cipher object with the given and our randomly + # generated IV. + encryptor = Cipher( algorithms.AES(key), modes.GCM(iv), backend=default_backend() - ) + ).encryptor() - encryptor = cipher.encryptor() + # We have to pad our plaintext because it may not be a + # multiple of the block size. + padder = PKCS7(algorithms.AES.block_size).padder() + padded_plaintext = padder.update(plaintext) + padder.finalize() + + # associated_data will be authenticated but not encrypted, + # it must also be passed in on decryption. encryptor.authenticate_additional_data(associated_data) - ciphertext = encryptor.update(plaintext) + encryptor.finalize() - return (associated_data, iv, ciphertext, encryptor.tag) + # Encrypt the plaintext and get the associated ciphertext. + ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize() + + return (iv, ciphertext, encryptor.tag) def decrypt(key, associated_data, iv, ciphertext, tag): - cipher = Cipher( + # Construct a Cipher object, with the key, iv, and additionally the + # GCM tag used for authenticating the message. + decryptor = Cipher( algorithms.AES(key), modes.GCM(iv, tag), backend=default_backend() - ) + ).decryptor() + + # We will need to unpad the plaintext. + unpadder = PKCS7(algorithms.AES.block_size).unpadder() - decryptor = cipher.decryptor() + # We put associated_data back in or the tag will fail to verify + # when we finalize the decryptor. decryptor.authenticate_additional_data(associated_data) - return decryptor.update(ciphertext) + decryptor.finalize() + # Decryption gets us the authenticated padded plaintext. + padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize() - associated_data, iv, ciphertext, tag = encrypt( + return unpadder.update(padded_plaintext) + unpadder.finalize() + + iv, ciphertext, tag = encrypt( key, - b"a secret message", + b"a secret message!", b"authenticated but not encrypted payload" ) - print(decrypt(key, associated_data, iv, ciphertext, tag)) + print(decrypt( + key, + b"authenticated but not encrypted payload", + iv, + ciphertext, + tag + )) .. testoutput:: - a secret message + a secret message! Insecure Modes -- cgit v1.2.3 From ad6d164a93352a9f6ddb57fd98152ba929e35d34 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 7 Jan 2014 19:10:12 -0600 Subject: move ciphercontext/aeadciphercontext to bottom of symmetric encryption Add a bit of additional text to try to make the convoluted AEAD explanation better. --- docs/hazmat/primitives/symmetric-encryption.rst | 156 +++++++++++++----------- 1 file changed, 83 insertions(+), 73 deletions(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index a683bb98..c1f7bb64 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -74,79 +74,6 @@ an "encrypt-then-MAC" formulation as `described by Colin Percival`_. and ``mode`` an :class:`cryptography.exceptions.UnsupportedAlgorithm` will be raised. - -.. currentmodule:: cryptography.hazmat.primitives.interfaces - -.. class:: CipherContext - - When calling ``encryptor()`` or ``decryptor()`` on a ``Cipher`` object - you will receive a return object conforming to the ``CipherContext`` - interface. You can then call ``update(data)`` with data until you have fed - everything into the context. Once that is done call ``finalize()`` to - finish the operation and obtain the remainder of the data. - - Block ciphers require that plaintext or ciphertext always be a multiple of - their block size, because of that **padding** is often required to make a - message the correct size. ``CipherContext`` will not automatically apply - any padding; you'll need to add your own. For block ciphers the recommended - padding is :class:`cryptography.hazmat.primitives.padding.PKCS7`. If you - are using a stream cipher mode (such as - :class:`cryptography.hazmat.primitives.modes.CTR`) you don't have to worry - about this. - - .. method:: update(data) - - :param bytes data: The data you wish to pass into the context. - :return bytes: Returns the data that was encrypted or decrypted. - :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` - - When the ``Cipher`` was constructed in a mode that turns it into a - stream cipher (e.g. - :class:`cryptography.hazmat.primitives.ciphers.modes.CTR`), this will - return bytes immediately, however in other modes it will return chunks, - whose size is determined by the cipher's block size. - - .. method:: finalize() - - :return bytes: Returns the remainder of the data. - :raises ValueError: This is raised when the data provided isn't - correctly padded to be a multiple of the - algorithm's block size. - - Once ``finalize`` is called this object can no longer be used and - :meth:`update` and :meth:`finalize` will raise - :class:`~cryptography.exceptions.AlreadyFinalized`. - -.. class:: AEADCipherContext - - When calling ``encryptor()`` or ``decryptor()`` on a ``Cipher`` object - with an AEAD mode you will receive a return object conforming to the - ``AEADCipherContext`` interface (in addition to the ``CipherContext`` - interface). If it is an encryption context it will additionally be an - ``AEADEncryptionContext`` interface. ``AEADCipherContext`` contains an - additional method ``authenticate_additional_data`` for adding additional - authenticated but unencrypted data. You should call this before calls to - ``update``. When you are done call ``finalize()`` to finish the operation. - - .. method:: authenticate_additional_data(data) - - :param bytes data: The data you wish to authenticate but not encrypt. - :raises: :class:`~cryptography.exceptions.AlreadyFinalized` - -.. class:: AEADEncryptionContext - - When creating an encryption context using ``encryptor()`` on a ``Cipher`` - object with an AEAD mode you will receive a return object conforming to the - ``AEADEncryptionContext`` interface (as well as ``AEADCipherContext``). - This interface provides one additional attribute ``tag``. ``tag`` can only - be obtained after ``finalize()``. - - .. attribute:: tag - - :return bytes: Returns the tag value as bytes. - :raises: :class:`~cryptography.exceptions.NotYetFinalized` if called - before the context is finalized. - .. _symmetric-encryption-algorithms: Algorithms @@ -448,6 +375,89 @@ Insecure Modes identical plaintext blocks will always result in identical ciphertext blocks, and thus result in information leakage +Interfaces +---------- + +.. class:: CipherContext + + When calling ``encryptor()`` or ``decryptor()`` on a ``Cipher`` object + you will receive a return object conforming to the ``CipherContext`` + interface. You can then call ``update(data)`` with data until you have fed + everything into the context. Once that is done call ``finalize()`` to + finish the operation and obtain the remainder of the data. + + Block ciphers require that plaintext or ciphertext always be a multiple of + their block size, because of that **padding** is often required to make a + message the correct size. ``CipherContext`` will not automatically apply + any padding; you'll need to add your own. For block ciphers the recommended + padding is :class:`cryptography.hazmat.primitives.padding.PKCS7`. If you + are using a stream cipher mode (such as + :class:`cryptography.hazmat.primitives.modes.CTR`) you don't have to worry + about this. + + .. method:: update(data) + + :param bytes data: The data you wish to pass into the context. + :return bytes: Returns the data that was encrypted or decrypted. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + When the ``Cipher`` was constructed in a mode that turns it into a + stream cipher (e.g. + :class:`cryptography.hazmat.primitives.ciphers.modes.CTR`), this will + return bytes immediately, however in other modes it will return chunks, + whose size is determined by the cipher's block size. + + .. method:: finalize() + + :return bytes: Returns the remainder of the data. + :raises ValueError: This is raised when the data provided isn't + correctly padded to be a multiple of the + algorithm's block size. + + Once ``finalize`` is called this object can no longer be used and + :meth:`update` and :meth:`finalize` will raise + :class:`~cryptography.exceptions.AlreadyFinalized`. + +.. class:: AEADCipherContext + + When calling ``encryptor()`` or ``decryptor()`` on a ``Cipher`` object + with an AEAD mode (e.g. + :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM`) you will receive + a return object conforming to the ``AEADCipherContext`` and + ``CipherContext`` interfaces. If it is an encryption context it will + additionally be an ``AEADEncryptionContext`` interface. + ``AEADCipherContext`` contains an additional method + ``authenticate_additional_data`` for adding additional authenticated but + unencrypted data (see note below). You should call this before calls to + ``update``. When you are done call ``finalize()`` to finish the operation. + + .. note:: + + In AEAD modes all data passed to ``update()`` will be both encrypted + and authenticated. Do not pass encrypted data to the + ``authenticate_additional_data()`` method. It is meant solely for + additional data you may want to authenticate but leave unencrypted. + + .. method:: authenticate_additional_data(data) + + :param bytes data: Any data you wish to authenticate but not encrypt. + :raises: :class:`~cryptography.exceptions.AlreadyFinalized` + +.. class:: AEADEncryptionContext + + When creating an encryption context using ``encryptor()`` on a ``Cipher`` + object with an AEAD mode (e.g. + :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM`) you will receive + a return object conforming to the ``AEADEncryptionContext`` interface (as + well as ``AEADCipherContext``). This interface provides one additional + attribute ``tag``. ``tag`` can only be obtained after ``finalize()``. + + .. attribute:: tag + + :return bytes: Returns the tag value as bytes. + :raises: :class:`~cryptography.exceptions.NotYetFinalized` if called + before the context is finalized. + .. _`described by Colin Percival`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html .. _`recommends 96-bit IV length`: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf -- cgit v1.2.3 From af0b9f56e761353593a0b33b1f4797169a716dec Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 7 Jan 2014 19:21:49 -0600 Subject: GCM does not require padding (removing from docs example) --- docs/hazmat/primitives/symmetric-encryption.rst | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index a683bb98..86267a25 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -364,8 +364,6 @@ Modes Cipher, algorithms, modes ) - from cryptography.hazmat.primitives.padding import PKCS7 - def encrypt(key, plaintext, associated_data): # Generate a random 96-bit IV. iv = os.urandom(12) @@ -378,17 +376,13 @@ Modes backend=default_backend() ).encryptor() - # We have to pad our plaintext because it may not be a - # multiple of the block size. - padder = PKCS7(algorithms.AES.block_size).padder() - padded_plaintext = padder.update(plaintext) + padder.finalize() - # associated_data will be authenticated but not encrypted, # it must also be passed in on decryption. encryptor.authenticate_additional_data(associated_data) # Encrypt the plaintext and get the associated ciphertext. - ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize() + # GCM does not require padding. + ciphertext = encryptor.update(plaintext) + encryptor.finalize() return (iv, ciphertext, encryptor.tag) @@ -401,17 +395,13 @@ Modes backend=default_backend() ).decryptor() - # We will need to unpad the plaintext. - unpadder = PKCS7(algorithms.AES.block_size).unpadder() - # We put associated_data back in or the tag will fail to verify # when we finalize the decryptor. decryptor.authenticate_additional_data(associated_data) - # Decryption gets us the authenticated padded plaintext. - padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize() - - return unpadder.update(padded_plaintext) + unpadder.finalize() + # Decryption gets us the authenticated plaintext. + # If the tag does not match an InvalidTag exception will be raised. + return decryptor.update(ciphertext) + decryptor.finalize() iv, ciphertext, tag = encrypt( key, -- cgit v1.2.3 From fe2e3c2827f2776e8e4116b3aec50d4409476cd9 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 7 Jan 2014 20:55:20 -0600 Subject: add padding info to docs --- docs/hazmat/primitives/symmetric-encryption.rst | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'docs/hazmat/primitives/symmetric-encryption.rst') diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 2233d525..83165690 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -169,6 +169,8 @@ Modes CBC (Cipher block chaining) is a mode of operation for block ciphers. It is considered cryptographically strong. + **Padding is required when using this mode.** + :param bytes initialization_vector: Must be random bytes. They do not need to be kept secret (they can be included in a transmitted message). Must be the @@ -211,6 +213,8 @@ Modes cryptographically strong. It transforms a block cipher into a stream cipher. + **This mode does not require padding.** + :param bytes nonce: Should be random bytes. It is critical to never reuse a ``nonce`` with a given key. Any reuse of a nonce with the same key compromises the security of every @@ -224,6 +228,8 @@ Modes OFB (Output Feedback) is a mode of operation for block ciphers. It transforms a block cipher into a stream cipher. + **This mode does not require padding.** + :param bytes initialization_vector: Must be random bytes. They do not need to be kept secret (they can be included in a transmitted message). Must be the @@ -237,6 +243,8 @@ Modes CFB (Cipher Feedback) is a mode of operation for block ciphers. It transforms a block cipher into a stream cipher. + **This mode does not require padding.** + :param bytes initialization_vector: Must be random bytes. They do not need to be kept secret (they can be included in a transmitted message). Must be the @@ -261,6 +269,8 @@ Modes Additional means of verifying integrity (like :doc:`HMAC `) are not necessary. + **This mode does not require padding.** + :param bytes initialization_vector: Must be random bytes. They do not need to be kept secret (they can be included in a transmitted message). NIST @@ -365,6 +375,8 @@ Insecure Modes identical plaintext blocks will always result in identical ciphertext blocks, and thus result in information leakage + **Padding is required when using this mode.** + Interfaces ---------- @@ -377,8 +389,8 @@ Interfaces finish the operation and obtain the remainder of the data. Block ciphers require that plaintext or ciphertext always be a multiple of - their block size, because of that **padding** is often required to make a - message the correct size. ``CipherContext`` will not automatically apply + their block size, because of that **padding** is sometimes required to make + a message the correct size. ``CipherContext`` will not automatically apply any padding; you'll need to add your own. For block ciphers the recommended padding is :class:`cryptography.hazmat.primitives.padding.PKCS7`. If you are using a stream cipher mode (such as -- cgit v1.2.3