fernet.rst 11 KB

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