Skip to content

Commit

Permalink
Update examples
Browse files Browse the repository at this point in the history
  • Loading branch information
Legrandin committed Jan 9, 2024
1 parent 03a66b2 commit ebfef94
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 52 deletions.
2 changes: 2 additions & 0 deletions Doc/src/cipher/classic.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _classic_cipher_modes:

Classic modes of operation for symmetric block ciphers
======================================================

Expand Down
4 changes: 3 additions & 1 deletion Doc/src/cipher/modern.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _modern_cipher_modes:

Modern modes of operation for symmetric block ciphers
=====================================================

Expand Down Expand Up @@ -447,7 +449,7 @@ a new OCB cipher object for the relevant base algorithm.
:param bytes key: the cryptographic key
:param mode: the constant ``Crypto.Cipher.<algorithm>.MODE_OCB``
:param bytes nonce: the value of the fixed nonce,
wuth length between 1 and 15 bytes.
with length between 1 and 15 bytes.
It must be unique for the combination message/key.
If not present, the library creates a 15 bytes random nonce.
:param integer mac_len: the desired length of the
Expand Down
180 changes: 129 additions & 51 deletions Doc/src/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,129 @@ Examples
Encrypt data with AES
~~~~~~~~~~~~~~~~~~~~~

The following code generates a new AES128 key and encrypts a piece of data into a file.
We use the `EAX mode`_ because it allows the receiver to detect any
unauthorized modification (similarly, we could have used other `authenticated
encryption modes`_ like `GCM`_, `CCM`_ or `SIV`_).
The following code generates a new AES-128 key and encrypts a piece of data into a file.
We use the :ref:`CTR mode <ctr_mode>` (which is a :ref:`classic mode of operation <classic_cipher_modes>`,
simple but not erecommended anymore).

With CTR alone, the receiver is not able to detect if the *ciphertext* (i.e., the encrypted
data) was modified while in transit. To address that risk, we also attach
a MAC authentication tag (HMAC with SHA256), made with a second key.

.. code-block:: python
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
from Crypto.Random import get_random_bytes
data = b'secret data'
data = 'secret data to transmit'.encode()
key = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(data)
file_out = open("encrypted.bin", "wb")
[ file_out.write(x) for x in (cipher.nonce, tag, ciphertext) ]
file_out.close()
aes_key = get_random_bytes(16)
hmac_key = get_random_bytes(16)
cipher = AES.new(aes_key, AES.MODE_CTR)
ciphertext = cipher.encrypt(data)
hmac = HMAC.new(hmac_key, digestmod=SHA256)
tag = hmac.update(cipher.nonce + ciphertext).digest()
At the other end, the receiver can securely load the piece of data back (if they know the key!).
for open("encrypted.bin", "wb") as f:
f.write(tag)
f.write(nonce)
f.write(ciphertext)
# Share securely aes_key and hmac_key with the receiver
# encrypted.bin can be sent over an unsecure channel
At the other end, the receiver can securely load the piece of data back (if they know the two keys!).
Note that the code generates a ``ValueError`` exception when tampering is detected.
.. code-block:: python
import sys
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
# Somehow, the receiver securely get aes_key and hmac_key
# encrypted.bin can be sent over an unsecure channel
with open("encrypted.bin", "rb") as f:
tag = f.read(32)
nonce = f.read(8)
ciphertext = f.read()
try:
hmac = HMAC.new(hmac_key, digestmod=SHA256)
tag = hmac.update(nonce + ciphertext).verify(tag)
except ValueError:
print("The message was modified!")
sys.exit(1)
cipher = AES.new(aes_key, AES.MODE_CTR, nonce=nonce)
message = cipher.decrypt(ciphertext)
print("Message:", message.decode())
Encrypt and authenticate data in one step
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The code in the previous section contains three subtle but important design decisions:
the *nonce* of the cipher is authenticated, the authentication is performed
after encryption, and encryption and authentication use two uncorrelated keys.
It is not easy to securely combine cryptographic primitives,
so more modern cryptographic cipher
modes have been created such as, the :ref:`OCB mode <ocb_mode>`
(see also other :ref:`authenticated encryption modes <modern_cipher_modes>`
like :ref:`EAX <eax_mode>`, :ref:`GCM <gcm_mode>`, :ref:`CCM <ccm_mode>`, :ref:`SIV <siv_mode>`).

file_in = open("encrypted.bin", "rb")
nonce, tag, ciphertext = [ file_in.read(x) for x in (16, 16, -1) ]
file_in.close()
# let's assume that the key is somehow available again
cipher = AES.new(key, AES.MODE_EAX, nonce)
data = cipher.decrypt_and_verify(ciphertext, tag)
.. code-block:: python
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
data = 'secret data to transmit'.encode()
aes_key = get_random_bytes(16)
cipher = AES.new(aes_key, AES.MODE_OCB)
ciphertext, tag = cipher.encrypt_and_digest(data)
assert len(cipher.nonce) == 15
with open("encrypted.bin", "wb") as f:
f.write(tag)
f.write(cipher.nonce)
f.write(ciphertext)
# Share securely aes_key and hmac_key with the receiver
# encrypted.bin can be sent over an unsecure channel
Decryption is also simpler:

.. code-block:: python
import sys
from Crypto.Cipher import AES
# Somehow, the receiver securely get aes_key and hmac_key
# encrypted.bin can be sent over an unsecure channel
with open("encrypted.bin", "rb") as f:
tag = f.read(16)
nonce = f.read(15)
ciphertext = f.read()
cipher = AES.new(aes_key, AES.MODE_OCB, nonce=nonce)
try:
message = cipher.decrypt_and_verify(ciphertext, tag)
except ValueError:
print("The message was modified!")
sys.exit(1)
print("Message:", message.decode())
Generate an RSA key
~~~~~~~~~~~~~~~~~~~

The following code generates a new RSA key pair (secret) and saves it into a file, protected by a password.
We use the `scrypt`_ key derivation function to thwart dictionary attacks.
We use the :ref:`scrypt <scrypt_func>` key derivation function to thwart dictionary attacks.
At the end, the code prints our the RSA public key in ASCII/PEM format:

.. code-block:: python
Expand All @@ -53,11 +136,11 @@ At the end, the code prints our the RSA public key in ASCII/PEM format:
secret_code = "Unguessable"
key = RSA.generate(2048)
encrypted_key = key.export_key(passphrase=secret_code, pkcs=8,
protection="scryptAndAES128-CBC")
file_out = open("rsa_key.bin", "wb")
file_out.write(encrypted_key)
file_out.close()
protection="scryptAndAES128-CBC",
prot_params={'iteration_count':131072})
with open("rsa_key.bin", "wb") as f:
f.write(encrypted_key)
print(key.publickey().export_key())
Expand Down Expand Up @@ -85,14 +168,12 @@ The following code generates public key stored in ``receiver.pem`` and private k
key = RSA.generate(2048)
private_key = key.export_key()
file_out = open("private.pem", "wb")
file_out.write(private_key)
file_out.close()
with open("private.pem", "wb") as f:
f.write(private_key)
public_key = key.publickey().export_key()
file_out = open("receiver.pem", "wb")
file_out.write(public_key)
file_out.close()
with open("receiver.pem", "wb") as f:
f.write(public_key)
Encrypt data with RSA
~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -101,7 +182,7 @@ The following code encrypts a piece of data for a receiver we have the RSA publi
The RSA public key is stored in a file called ``receiver.pem``.

Since we want to be able to encrypt an arbitrary amount of data, we use a hybrid encryption scheme.
We use RSA with PKCS#1 `OAEP`_ for asymmetric encryption of an AES session key.
We use RSA with PKCS#1 :ref:`OAEP <rsa_oaep>` for asymmetric encryption of an AES session key.
The session key can then be used to encrypt all the actual data.

As in the first example, we use the EAX mode to allow detection of unauthorized modifications.
Expand All @@ -113,20 +194,25 @@ As in the first example, we use the EAX mode to allow detection of unauthorized
from Crypto.Cipher import AES, PKCS1_OAEP
data = "I met aliens in UFO. Here is the map.".encode("utf-8")
file_out = open("encrypted_data.bin", "wb")
recipient_key = RSA.import_key(open("receiver.pem").read())
session_key = get_random_bytes(16)
# Encrypt the session key with the public RSA key
# Encrypt the session key with the public RSA key
cipher_rsa = PKCS1_OAEP.new(recipient_key)
enc_session_key = cipher_rsa.encrypt(session_key)
# Encrypt the data with the AES session key
# Encrypt the data with the AES session key
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(data)
[ file_out.write(x) for x in (enc_session_key, cipher_aes.nonce, tag, ciphertext) ]
file_out.close()
with open("encrypted_data.bin", "wb") as f:
f.write(enc_session_key)
f.write(cipher_aes.nonce)
f.write(tag)
f.write(ciphertext)
The receiver has the private RSA key. They will use it to decrypt the session key
first, and with that the rest of the file:
Expand All @@ -136,13 +222,13 @@ first, and with that the rest of the file:
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
file_in = open("encrypted_data.bin", "rb")
private_key = RSA.import_key(open("private.pem").read())
enc_session_key, nonce, tag, ciphertext = \
[ file_in.read(x) for x in (private_key.size_in_bytes(), 16, 16, -1) ]
file_in.close()
with open("encrypted_data.bin", "rb") as f:
enc_session_key = f.read(private_key.size_in_bytes())
nonce = f.read(16)
tag = f.read(16)
ciphertext = f.read()
# Decrypt the session key with the private RSA key
cipher_rsa = PKCS1_OAEP.new(private_key)
Expand All @@ -152,11 +238,3 @@ first, and with that the rest of the file:
cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
data = cipher_aes.decrypt_and_verify(ciphertext, tag)
print(data.decode("utf-8"))
.. _EAX mode: http://en.wikipedia.org/wiki/EAX_mode
.. _CCM: http://en.wikipedia.org/wiki/CCM_mode
.. _GCM: http://en.wikipedia.org/wiki/GCM_mode
.. _SIV: http://tools.ietf.org/html/rfc5297
.. _scrypt: http://it.wikipedia.org/wiki/Scrypt
.. _OAEP: http://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding
.. _authenticated encryption modes: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html

0 comments on commit ebfef94

Please sign in to comment.