fernet.rst 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. '...'
  17. >>> f.decrypt(token)
  18. '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. :param bytes data: The message you would like to encrypt.
  30. :returns bytes: A secure message that cannot be read or altered
  31. without the key. It is URL-safe base64-encoded. This is
  32. referred to as a "Fernet token".
  33. :raises TypeError: This exception is raised if ``data`` is not
  34. ``bytes``.
  35. .. note::
  36. The encrypted message contains the current time when it was
  37. generated in *plaintext*, the time a message was created will
  38. therefore be visible to a possible attacker.
  39. .. method:: decrypt(token, ttl=None)
  40. :param bytes token: The Fernet token. This is the result of calling
  41. :meth:`encrypt`.
  42. :param int ttl: Optionally, the number of seconds old a message may be
  43. for it to be valid. If the message is older than
  44. ``ttl`` seconds (from the time it was originally
  45. created) an exception will be raised. If ``ttl`` is not
  46. provided (or is ``None``), the age of the message is
  47. not considered.
  48. :returns bytes: The original plaintext.
  49. :raises cryptography.fernet.InvalidToken: If the ``token`` is in any
  50. way invalid, this exception
  51. is raised. A token may be
  52. invalid for a number of
  53. reasons: it is older than the
  54. ``ttl``, it is malformed, or
  55. it does not have a valid
  56. signature.
  57. :raises TypeError: This exception is raised if ``token`` is not
  58. ``bytes``.
  59. .. class:: MultiFernet(fernets)
  60. .. versionadded:: 0.7
  61. This class implements key rotation for Fernet. It takes a ``list`` of
  62. :class:`Fernet` instances, and implements the same API:
  63. .. doctest::
  64. >>> from cryptography.fernet import Fernet, MultiFernet
  65. >>> key1 = Fernet(Fernet.generate_key())
  66. >>> key2 = Fernet(Fernet.generate_key())
  67. >>> f = MultiFernet([key1, key2])
  68. >>> token = f.encrypt(b"Secret message!")
  69. >>> token
  70. '...'
  71. >>> f.decrypt(token)
  72. 'Secret message!'
  73. MultiFernet performs all encryption options using the *first* key in the
  74. ``list`` provided. MultiFernet attempts to decrypt tokens with each key in
  75. turn. A :class:`cryptography.fernet.InvalidToken` exception is raised if
  76. the correct key is not found in the ``list`` provided.
  77. Key rotation makes it easy to replace old keys. You can add your new key at
  78. the front of the list to start encrypting new messages, and remove old keys
  79. as they are no longer needed.
  80. .. class:: InvalidToken
  81. See :meth:`Fernet.decrypt` for more information.
  82. Using passwords with Fernet
  83. ---------------------------
  84. It is possible to use passwords with Fernet. To do this, you need to run the
  85. password through a key derivation function such as
  86. :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`, bcrypt or
  87. scrypt.
  88. .. doctest::
  89. >>> import base64
  90. >>> import os
  91. >>> from cryptography.fernet import Fernet
  92. >>> from cryptography.hazmat.backends import default_backend
  93. >>> from cryptography.hazmat.primitives import hashes
  94. >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
  95. >>> password = b"password"
  96. >>> salt = os.urandom(16)
  97. >>> kdf = PBKDF2HMAC(
  98. ... algorithm=hashes.SHA256(),
  99. ... length=32,
  100. ... salt=salt,
  101. ... iterations=100000,
  102. ... backend=default_backend()
  103. ... )
  104. >>> key = base64.urlsafe_b64encode(kdf.derive(password))
  105. >>> f = Fernet(key)
  106. >>> token = f.encrypt(b"Secret message!")
  107. >>> token
  108. '...'
  109. >>> f.decrypt(token)
  110. 'Secret message!'
  111. In this scheme, the salt has to be stored in a retrievable location in order
  112. to derive the same key from the password in the future.
  113. The iteration count used should be adjusted to be as high as your server can
  114. tolerate. A good default is at least 100,000 iterations which is what Django
  115. `recommends`_ in 2014.
  116. Implementation
  117. --------------
  118. Fernet is built on top of a number of standard cryptographic primitives.
  119. Specifically it uses:
  120. * :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` in
  121. :class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode with a
  122. 128-bit key for encryption; using
  123. :class:`~cryptography.hazmat.primitives.padding.PKCS7` padding.
  124. * :class:`~cryptography.hazmat.primitives.hmac.HMAC` using
  125. :class:`~cryptography.hazmat.primitives.hashes.SHA256` for authentication.
  126. * Initialization vectors are generated using ``os.urandom()``.
  127. For complete details consult the `specification`_.
  128. .. _`Fernet`: https://github.com/fernet/spec/
  129. .. _`specification`: https://github.com/fernet/spec/blob/master/Spec.md
  130. .. _`recommends`: https://github.com/django/django/blob/master/django/utils/crypto.py#L148