| Fernet (symmetric encryption) |
| ============================= |
| |
| .. currentmodule:: cryptography.fernet |
| |
| Fernet guarantees that a message encrypted using it cannot be |
| manipulated or read without the key. `Fernet`_ is an implementation of |
| symmetric (also known as "secret key") authenticated cryptography. Fernet also |
| has support for implementing key rotation via :class:`MultiFernet`. |
| |
| .. class:: Fernet(key) |
| |
| This class provides both encryption and decryption facilities. |
| |
| .. doctest:: |
| |
| >>> from cryptography.fernet import Fernet |
| >>> key = Fernet.generate_key() |
| >>> f = Fernet(key) |
| >>> token = f.encrypt(b"my deep dark secret") |
| >>> token |
| b'...' |
| >>> f.decrypt(token) |
| b'my deep dark secret' |
| |
| :param bytes key: A URL-safe base64-encoded 32-byte key. This **must** be |
| kept secret. Anyone with this key is able to create and |
| read messages. |
| |
| .. classmethod:: generate_key() |
| |
| Generates a fresh fernet key. Keep this some place safe! If you lose it |
| you'll no longer be able to decrypt messages; if anyone else gains |
| access to it, they'll be able to decrypt all of your messages, and |
| they'll also be able forge arbitrary messages that will be |
| authenticated and decrypted. |
| |
| .. method:: encrypt(data) |
| |
| Encrypts data passed. The result of this encryption is known as a |
| "Fernet token" and has strong privacy and authenticity guarantees. |
| |
| :param bytes data: The message you would like to encrypt. |
| :returns bytes: A secure message that cannot be read or altered |
| without the key. It is URL-safe base64-encoded. This is |
| referred to as a "Fernet token". |
| :raises TypeError: This exception is raised if ``data`` is not |
| ``bytes``. |
| |
| .. note:: |
| |
| The encrypted message contains the current time when it was |
| generated in *plaintext*, the time a message was created will |
| therefore be visible to a possible attacker. |
| |
| .. method:: decrypt(token, ttl=None) |
| |
| Decrypts a Fernet token. If successfully decrypted you will receive the |
| original plaintext as the result, otherwise an exception will be |
| raised. It is safe to use this data immediately as Fernet verifies |
| that the data has not been tampered with prior to returning it. |
| |
| :param bytes token: The Fernet token. This is the result of calling |
| :meth:`encrypt`. |
| :param int ttl: Optionally, the number of seconds old a message may be |
| for it to be valid. If the message is older than |
| ``ttl`` seconds (from the time it was originally |
| created) an exception will be raised. If ``ttl`` is not |
| provided (or is ``None``), the age of the message is |
| not considered. |
| :returns bytes: The original plaintext. |
| :raises cryptography.fernet.InvalidToken: If the ``token`` is in any |
| way invalid, this exception |
| is raised. A token may be |
| invalid for a number of |
| reasons: it is older than the |
| ``ttl``, it is malformed, or |
| it does not have a valid |
| signature. |
| :raises TypeError: This exception is raised if ``token`` is not |
| ``bytes``. |
| |
| .. method:: extract_timestamp(token) |
| |
| .. versionadded:: 2.3 |
| |
| Returns the timestamp for the token. The caller can then decide if |
| the token is about to expire and, for example, issue a new token. |
| |
| :param bytes token: The Fernet token. This is the result of calling |
| :meth:`encrypt`. |
| :returns int: The UNIX timestamp of the token. |
| :raises cryptography.fernet.InvalidToken: If the ``token``'s signature |
| is invalid this exception |
| is raised. |
| :raises TypeError: This exception is raised if ``token`` is not |
| ``bytes``. |
| |
| |
| .. class:: MultiFernet(fernets) |
| |
| .. versionadded:: 0.7 |
| |
| This class implements key rotation for Fernet. It takes a ``list`` of |
| :class:`Fernet` instances and implements the same API with the exception |
| of one additional method: :meth:`MultiFernet.rotate`: |
| |
| .. doctest:: |
| |
| >>> from cryptography.fernet import Fernet, MultiFernet |
| >>> key1 = Fernet(Fernet.generate_key()) |
| >>> key2 = Fernet(Fernet.generate_key()) |
| >>> f = MultiFernet([key1, key2]) |
| >>> token = f.encrypt(b"Secret message!") |
| >>> token |
| b'...' |
| >>> f.decrypt(token) |
| b'Secret message!' |
| |
| MultiFernet performs all encryption options using the *first* key in the |
| ``list`` provided. MultiFernet attempts to decrypt tokens with each key in |
| turn. A :class:`cryptography.fernet.InvalidToken` exception is raised if |
| the correct key is not found in the ``list`` provided. |
| |
| Key rotation makes it easy to replace old keys. You can add your new key at |
| the front of the list to start encrypting new messages, and remove old keys |
| as they are no longer needed. |
| |
| Token rotation as offered by :meth:`MultiFernet.rotate` is a best practice |
| and manner of cryptographic hygiene designed to limit damage in the event of |
| an undetected event and to increase the difficulty of attacks. For example, |
| if an employee who had access to your company's fernet keys leaves, you'll |
| want to generate new fernet key, rotate all of the tokens currently deployed |
| using that new key, and then retire the old fernet key(s) to which the |
| employee had access. |
| |
| .. method:: rotate(msg) |
| |
| .. versionadded:: 2.2 |
| |
| Rotates a token by re-encrypting it under the :class:`MultiFernet` |
| instance's primary key. This preserves the timestamp that was originally |
| saved with the token. If a token has successfully been rotated then the |
| rotated token will be returned. If rotation fails this will raise an |
| exception. |
| |
| .. doctest:: |
| |
| >>> from cryptography.fernet import Fernet, MultiFernet |
| >>> key1 = Fernet(Fernet.generate_key()) |
| >>> key2 = Fernet(Fernet.generate_key()) |
| >>> f = MultiFernet([key1, key2]) |
| >>> token = f.encrypt(b"Secret message!") |
| >>> token |
| b'...' |
| >>> f.decrypt(token) |
| b'Secret message!' |
| >>> key3 = Fernet(Fernet.generate_key()) |
| >>> f2 = MultiFernet([key3, key1, key2]) |
| >>> rotated = f2.rotate(token) |
| >>> f2.decrypt(rotated) |
| b'Secret message!' |
| |
| :param bytes msg: The token to re-encrypt. |
| :returns bytes: A secure message that cannot be read or altered without |
| the key. This is URL-safe base64-encoded. This is referred to as a |
| "Fernet token". |
| :raises cryptography.fernet.InvalidToken: If a ``token`` is in any |
| way invalid this exception is raised. |
| :raises TypeError: This exception is raised if the ``msg`` is not |
| ``bytes``. |
| |
| |
| .. class:: InvalidToken |
| |
| See :meth:`Fernet.decrypt` for more information. |
| |
| |
| Using passwords with Fernet |
| --------------------------- |
| |
| It is possible to use passwords with Fernet. To do this, you need to run the |
| password through a key derivation function such as |
| :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`, bcrypt or |
| :class:`~cryptography.hazmat.primitives.kdf.scrypt.Scrypt`. |
| |
| .. doctest:: |
| |
| >>> import base64 |
| >>> import os |
| >>> from cryptography.fernet import Fernet |
| >>> from cryptography.hazmat.backends import default_backend |
| >>> from cryptography.hazmat.primitives import hashes |
| >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC |
| >>> password = b"password" |
| >>> salt = os.urandom(16) |
| >>> kdf = PBKDF2HMAC( |
| ... algorithm=hashes.SHA256(), |
| ... length=32, |
| ... salt=salt, |
| ... iterations=100000, |
| ... backend=default_backend() |
| ... ) |
| >>> key = base64.urlsafe_b64encode(kdf.derive(password)) |
| >>> f = Fernet(key) |
| >>> token = f.encrypt(b"Secret message!") |
| >>> token |
| b'...' |
| >>> f.decrypt(token) |
| b'Secret message!' |
| |
| In this scheme, the salt has to be stored in a retrievable location in order |
| to derive the same key from the password in the future. |
| |
| The iteration count used should be adjusted to be as high as your server can |
| tolerate. A good default is at least 100,000 iterations which is what Django |
| recommended in 2014. |
| |
| Implementation |
| -------------- |
| |
| Fernet is built on top of a number of standard cryptographic primitives. |
| Specifically it uses: |
| |
| * :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` in |
| :class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode with a |
| 128-bit key for encryption; using |
| :class:`~cryptography.hazmat.primitives.padding.PKCS7` padding. |
| * :class:`~cryptography.hazmat.primitives.hmac.HMAC` using |
| :class:`~cryptography.hazmat.primitives.hashes.SHA256` for authentication. |
| * Initialization vectors are generated using ``os.urandom()``. |
| |
| For complete details consult the `specification`_. |
| |
| Limitations |
| ----------- |
| |
| Fernet is ideal for encrypting data that easily fits in memory. As a design |
| feature it does not expose unauthenticated bytes. Unfortunately, this makes it |
| generally unsuitable for very large files at this time. |
| |
| |
| .. _`Fernet`: https://github.com/fernet/spec/ |
| .. _`specification`: https://github.com/fernet/spec/blob/master/Spec.md |