fernet.rst 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. Fernet (symmetric encryption)
  2. =============================
  3. .. currentmodule:: cryptography.fernet
  4. Fernet guarantees that a message encrypted using it cannot be
  5. manipulated or read without the key. `Fernet`_ is an implementation of
  6. symmetric (also known as "secret key") authenticated cryptography. Fernet also
  7. has support for implementing key rotation via :class:`MultiFernet`.
  8. .. class:: Fernet(key)
  9. This class provides both encryption and decryption facilities.
  10. .. doctest::
  11. >>> from cryptography.fernet import Fernet
  12. >>> key = Fernet.generate_key()
  13. >>> f = Fernet(key)
  14. >>> token = f.encrypt(b"my deep dark secret")
  15. >>> token
  16. b'...'
  17. >>> f.decrypt(token)
  18. b'my deep dark secret'
  19. :param bytes key: A URL-safe base64-encoded 32-byte key. This **must** be
  20. kept secret. Anyone with this key is able to create and
  21. read messages.
  22. .. classmethod:: generate_key()
  23. Generates a fresh fernet key. Keep this some place safe! If you lose it
  24. you'll no longer be able to decrypt messages; if anyone else gains
  25. access to it, they'll be able to decrypt all of your messages, and
  26. they'll also be able forge arbitrary messages that will be
  27. authenticated and decrypted.
  28. .. method:: encrypt(data)
  29. Encrypts data passed. The result of this encryption is known as a
  30. "Fernet token" and has strong privacy and authenticity guarantees.
  31. :param bytes data: The message you would like to encrypt.
  32. :returns bytes: A secure message that cannot be read or altered
  33. without the key. It is URL-safe base64-encoded. This is
  34. referred to as a "Fernet token".
  35. :raises TypeError: This exception is raised if ``data`` is not
  36. ``bytes``.
  37. .. note::
  38. The encrypted message contains the current time when it was
  39. generated in *plaintext*, the time a message was created will
  40. therefore be visible to a possible attacker.
  41. .. method:: decrypt(token, ttl=None)
  42. Decrypts a Fernet token. If successfully decrypted you will receive the
  43. original plaintext as the result, otherwise an exception will be
  44. raised. It is safe to use this data immediately as Fernet verifies
  45. that the data has not been tampered with prior to returning it.
  46. :param bytes token: The Fernet token. This is the result of calling
  47. :meth:`encrypt`.
  48. :param int ttl: Optionally, the number of seconds old a message may be
  49. for it to be valid. If the message is older than
  50. ``ttl`` seconds (from the time it was originally
  51. created) an exception will be raised. If ``ttl`` is not
  52. provided (or is ``None``), the age of the message is
  53. not considered.
  54. :returns bytes: The original plaintext.
  55. :raises cryptography.fernet.InvalidToken: If the ``token`` is in any
  56. way invalid, this exception
  57. is raised. A token may be
  58. invalid for a number of
  59. reasons: it is older than the
  60. ``ttl``, it is malformed, or
  61. it does not have a valid
  62. signature.
  63. :raises TypeError: This exception is raised if ``token`` is not
  64. ``bytes``.
  65. .. method:: extract_timestamp(token)
  66. .. versionadded:: 2.3
  67. Returns the timestamp for the token. The caller can then decide if
  68. the token is about to expire and, for example, issue a new token.
  69. :param bytes token: The Fernet token. This is the result of calling
  70. :meth:`encrypt`.
  71. :returns int: The UNIX timestamp of the token.
  72. :raises cryptography.fernet.InvalidToken: If the ``token``'s signature
  73. is invalid this exception
  74. is raised.
  75. :raises TypeError: This exception is raised if ``token`` is not
  76. ``bytes``.
  77. .. class:: MultiFernet(fernets)
  78. .. versionadded:: 0.7
  79. This class implements key rotation for Fernet. It takes a ``list`` of
  80. :class:`Fernet` instances and implements the same API with the exception
  81. of one additional method: :meth:`MultiFernet.rotate`:
  82. .. doctest::
  83. >>> from cryptography.fernet import Fernet, MultiFernet
  84. >>> key1 = Fernet(Fernet.generate_key())
  85. >>> key2 = Fernet(Fernet.generate_key())
  86. >>> f = MultiFernet([key1, key2])
  87. >>> token = f.encrypt(b"Secret message!")
  88. >>> token
  89. b'...'
  90. >>> f.decrypt(token)
  91. b'Secret message!'
  92. MultiFernet performs all encryption options using the *first* key in the
  93. ``list`` provided. MultiFernet attempts to decrypt tokens with each key in
  94. turn. A :class:`cryptography.fernet.InvalidToken` exception is raised if
  95. the correct key is not found in the ``list`` provided.
  96. Key rotation makes it easy to replace old keys. You can add your new key at
  97. the front of the list to start encrypting new messages, and remove old keys
  98. as they are no longer needed.
  99. Token rotation as offered by :meth:`MultiFernet.rotate` is a best practice
  100. and manner of cryptographic hygiene designed to limit damage in the event of
  101. an undetected event and to increase the difficulty of attacks. For example,
  102. if an employee who had access to your company's fernet keys leaves, you'll
  103. want to generate new fernet key, rotate all of the tokens currently deployed
  104. using that new key, and then retire the old fernet key(s) to which the
  105. employee had access.
  106. .. method:: rotate(msg)
  107. .. versionadded:: 2.2
  108. Rotates a token by re-encrypting it under the :class:`MultiFernet`
  109. instance's primary key. This preserves the timestamp that was originally
  110. saved with the token. If a token has successfully been rotated then the
  111. rotated token will be returned. If rotation fails this will raise an
  112. exception.
  113. .. doctest::
  114. >>> from cryptography.fernet import Fernet, MultiFernet
  115. >>> key1 = Fernet(Fernet.generate_key())
  116. >>> key2 = Fernet(Fernet.generate_key())
  117. >>> f = MultiFernet([key1, key2])
  118. >>> token = f.encrypt(b"Secret message!")
  119. >>> token
  120. b'...'
  121. >>> f.decrypt(token)
  122. b'Secret message!'
  123. >>> key3 = Fernet(Fernet.generate_key())
  124. >>> f2 = MultiFernet([key3, key1, key2])
  125. >>> rotated = f2.rotate(token)
  126. >>> f2.decrypt(rotated)
  127. b'Secret message!'
  128. :param bytes msg: The token to re-encrypt.
  129. :returns bytes: A secure message that cannot be read or altered without
  130. the key. This is URL-safe base64-encoded. This is referred to as a
  131. "Fernet token".
  132. :raises cryptography.fernet.InvalidToken: If a ``token`` is in any
  133. way invalid this exception is raised.
  134. :raises TypeError: This exception is raised if the ``msg`` is not
  135. ``bytes``.
  136. .. class:: InvalidToken
  137. See :meth:`Fernet.decrypt` for more information.
  138. Using passwords with Fernet
  139. ---------------------------
  140. It is possible to use passwords with Fernet. To do this, you need to run the
  141. password through a key derivation function such as
  142. :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`, bcrypt or
  143. :class:`~cryptography.hazmat.primitives.kdf.scrypt.Scrypt`.
  144. .. doctest::
  145. >>> import base64
  146. >>> import os
  147. >>> from cryptography.fernet import Fernet
  148. >>> from cryptography.hazmat.backends import default_backend
  149. >>> from cryptography.hazmat.primitives import hashes
  150. >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
  151. >>> password = b"password"
  152. >>> salt = os.urandom(16)
  153. >>> kdf = PBKDF2HMAC(
  154. ... algorithm=hashes.SHA256(),
  155. ... length=32,
  156. ... salt=salt,
  157. ... iterations=100000,
  158. ... backend=default_backend()
  159. ... )
  160. >>> key = base64.urlsafe_b64encode(kdf.derive(password))
  161. >>> f = Fernet(key)
  162. >>> token = f.encrypt(b"Secret message!")
  163. >>> token
  164. b'...'
  165. >>> f.decrypt(token)
  166. b'Secret message!'
  167. In this scheme, the salt has to be stored in a retrievable location in order
  168. to derive the same key from the password in the future.
  169. The iteration count used should be adjusted to be as high as your server can
  170. tolerate. A good default is at least 100,000 iterations which is what Django
  171. recommended in 2014.
  172. Implementation
  173. --------------
  174. Fernet is built on top of a number of standard cryptographic primitives.
  175. Specifically it uses:
  176. * :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` in
  177. :class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode with a
  178. 128-bit key for encryption; using
  179. :class:`~cryptography.hazmat.primitives.padding.PKCS7` padding.
  180. * :class:`~cryptography.hazmat.primitives.hmac.HMAC` using
  181. :class:`~cryptography.hazmat.primitives.hashes.SHA256` for authentication.
  182. * Initialization vectors are generated using ``os.urandom()``.
  183. For complete details consult the `specification`_.
  184. Limitations
  185. -----------
  186. Fernet is ideal for encrypting data that easily fits in memory. As a design
  187. feature it does not expose unauthenticated bytes. Unfortunately, this makes it
  188. generally unsuitable for very large files at this time.
  189. .. _`Fernet`: https://github.com/fernet/spec/
  190. .. _`specification`: https://github.com/fernet/spec/blob/master/Spec.md