### Api Stability API stability ============= From its first release, ``cryptography`` has had a strong API stability policy. What does this policy cover? ---------------------------- This policy includes any API or behavior that is documented in this documentation. What does "stable" mean? ------------------------ * Public APIs will not be removed or renamed without providing a compatibility alias. * The behavior of existing APIs will not change. What doesn't this policy cover? ------------------------------- * We may add new features, things like the result of ``dir(obj))`` or the contents of ``obj.__dict__`` may change. * Objects are not guaranteed to be pickleable, and pickled objects from one version of ``cryptography`` may not be loadable in future versions. * Unless otherwise documented, types in ``cryptography`` are not intended to be sub-classed, and we do not guarantee that behavior with respect to sub-classes will be stable. * Development versions of ``cryptography``. Before a feature is in a release, it is not covered by this policy and may change. Security ~~~~~~~~ One exception to our API stability policy is for security. We will violate this policy as necessary in order to resolve a security issue or harden ``cryptography`` against a possible attack. Versioning ---------- Version 35.0.0+ ~~~~~~~~~~~~~~~ Beginning with release 35.0.0 ``cryptography`` uses a Firefox-inspired version scheme. Given a version ``cryptography X.Y.Z``, * ``X`` indicates the major version number. This is incremented on any feature release. * ``Y`` is always ``0``. * ``Z`` is an integer that is incremented for minor backward-compatible releases (such as fixing security issues or correcting regressions in a major release). This scheme is compatible with `SemVer`_, though many major releases will **not** include any backwards-incompatible changes. Deprecation ~~~~~~~~~~~ From time to time we will want to change the behavior of an API or remove it entirely. In that case, here's how the process will work: * In ``cryptography X.0.0`` the feature exists. * In ``cryptography (X + 1).0.0`` using that feature will emit a ``CryptographyDeprecationWarning`` (base class ``UserWarning``). * In ``cryptography (X + 2).0.0`` using that feature will emit a ``CryptographyDeprecationWarning``. * In ``cryptography (X + 3).0.0`` the feature will be removed or changed. In short, code that runs without warnings will always continue to work for a period of two major releases. From time to time, we may decide to deprecate an API that is particularly widely used. In these cases, we may decide to provide an extended deprecation period, at our discretion. Previous Scheme ~~~~~~~~~~~~~~~ Before version 35.0.0 this project uses a custom versioning scheme as described below. Given a version ``cryptography X.Y.Z``, * ``X.Y`` is a decimal number that is incremented for potentially-backwards-incompatible releases. * This increases like a standard decimal. In other words, 0.9 is the ninth release, and 1.0 is the tenth (not 0.10). The dividing decimal point can effectively be ignored. * ``Z`` is an integer that is incremented for backward-compatible releases. .. _`SemVer`: https://semver.org/ --- ### Cobblestone Cobblestone (streaming symmetric encryption) ============================================= .. currentmodule:: cryptography.cobblestone Cobblestone provides authenticated symmetric encryption of large messages — up to 4 PiB — as a stream, without ever holding the whole message in memory. It is an implementation of the `C2SP chunked-encryption specification`_'s two named instantiations: **Cobblestone-128** (SHA-512 and AES-128-GCM, the recommended choice) and **Cobblestone-256** (SHA-512 and AES-256-GCM, for environments that mandate 256-bit keys). .. doctest:: >>> from cryptography.cobblestone import ( ... Cobblestone128Decryptor, Cobblestone128Encryptor ... ) >>> key = Cobblestone128Encryptor.generate_key() >>> encryptor = Cobblestone128Encryptor( ... key, context=b"example-app file encryption" ... ) >>> ciphertext = encryptor.update(b"a secret message") >>> ciphertext += encryptor.finalize() >>> decryptor = Cobblestone128Decryptor( ... key, context=b"example-app file encryption" ... ) >>> decryptor.update(ciphertext) + decryptor.finalize() b'a secret message' .. class:: Cobblestone128Encryptor(key, context) .. versionadded:: 50.0.0 Encrypts a single message under ``key`` with Cobblestone-128. Each instance must be used for exactly one message: call :meth:`update` (or :meth:`update_into`) any number of times, then call :meth:`finalize` exactly once. The concatenation of the returned bytes is the ciphertext. :param key: A 16-byte key. This **must** be kept secret, and **must** be uniformly random (e.g. the output of :meth:`generate_key` — never a password). A single key may be used to encrypt a practically unlimited number of messages. :type key: :term:`bytes-like` :param context: Application-provided context, bound to the ciphertext. Decryption fails unless the same value is passed to :class:`Cobblestone128Decryptor`. It is not secret, may be empty, and is not part of the ciphertext, so it must be available to the decrypting party independently. It can be used for domain separation, e.g. ``b"myapp v2 backup encryption"``. :type context: :term:`bytes-like` :raises ValueError: If ``key`` is not 16 bytes. .. staticmethod:: generate_key() Generates a fresh 16-byte key. :return bytes: A new key. .. method:: update(data) Encrypts ``data``. Data is internally buffered into 16 KiB chunks, so between 0 and ``len(data) + 16 KiB`` bytes of ciphertext are returned. :param data: The data to encrypt. :type data: :term:`bytes-like` :return bytes: The next portion of the ciphertext. .. method:: update_into(data, buf) Encrypts ``data``, writing the resulting ciphertext into ``buf``, and returns the number of bytes written. This avoids allocating a new buffer for each call. :param data: The data to encrypt. :type data: :term:`bytes-like` :param buf: A writable buffer to write the ciphertext into. A buffer of ``len(data) + len(data) // 1024 + 16456`` bytes is always large enough. :type buf: :term:`bytes-like` :return int: The number of bytes written to ``buf``. :raises ValueError: If ``buf`` is too small. .. method:: finalize() Encrypts the final chunk and returns the last portion of the ciphertext. This must always be called, and the instance cannot be used afterwards. :return bytes: The remainder of the ciphertext. :raises cryptography.exceptions.AlreadyFinalized: If ``finalize`` has already been called. .. class:: Cobblestone128Decryptor(key, context) .. versionadded:: 50.0.0 Decrypts a single message encrypted by :class:`Cobblestone128Encryptor` with the same ``key`` and ``context``. Call :meth:`update` (or :meth:`update_into`) with the ciphertext any number of times, then call :meth:`finalize` exactly once. The concatenation of the returned bytes is the plaintext. Any returned plaintext is authenticated, but until :meth:`finalize` returns successfully the message could still turn out to be truncated: an application acting on streamed plaintext before that point must be prepared to discard its work if a later call raises :class:`~cryptography.exceptions.InvalidTag`. Once any method raises :class:`~cryptography.exceptions.InvalidTag`, the instance is permanently unusable and all further calls raise :class:`~cryptography.exceptions.AlreadyFinalized`. :param key: The 16-byte key the message was encrypted with. :type key: :term:`bytes-like` :param context: The context value the message was encrypted with. :type context: :term:`bytes-like` :raises ValueError: If ``key`` is not 16 bytes. .. method:: update(data) Processes ``data``, which need not be aligned to any boundary, and returns the plaintext of all complete chunks that have been authenticated so far. :param data: The next portion of the ciphertext. :type data: :term:`bytes-like` :return bytes: The next portion of the plaintext. :raises cryptography.exceptions.InvalidTag: If the ciphertext was encrypted with a different key or context, or has been modified. .. method:: update_into(data, buf) Like ``update``, but writes the plaintext into ``buf`` and returns the number of bytes written. :param data: The next portion of the ciphertext. :type data: :term:`bytes-like` :param buf: A writable buffer to write the plaintext into. A buffer of ``len(data) + 16400`` bytes is always large enough. :type buf: :term:`bytes-like` :return int: The number of bytes written to ``buf``. :raises ValueError: If ``buf`` is too small. :raises cryptography.exceptions.InvalidTag: If the ciphertext was encrypted with a different key or context, or has been modified. Note that in this case unauthenticated data may have been written to ``buf`` and must not be used. .. method:: finalize() Decrypts and authenticates the final chunk, verifying that the entire message has been processed, and returns the final portion of the plaintext. This must always be called: a successful return is what guarantees the complete message was authentic and not truncated. :return bytes: The remainder of the plaintext. :raises cryptography.exceptions.InvalidTag: If the ciphertext was truncated or otherwise modified. :raises cryptography.exceptions.AlreadyFinalized: If ``finalize`` has already been called. .. class:: Cobblestone256Encryptor(key, context) .. versionadded:: 50.0.0 Exactly like :class:`Cobblestone128Encryptor`, but implements Cobblestone-256: the ``key`` is 32 bytes and messages are encrypted with AES-256-GCM. Use this when a 256-bit key is mandated; otherwise Cobblestone-128 is recommended. .. class:: Cobblestone256Decryptor(key, context) .. versionadded:: 50.0.0 Exactly like :class:`Cobblestone128Decryptor`, but decrypts messages produced by :class:`Cobblestone256Encryptor` with a 32-byte key. .. _`C2SP chunked-encryption specification`: https://c2sp.org/chunked-encryption --- ### Community Community ========= You can find ``cryptography`` all over the web: * `Mailing list`_ * `Source code`_ * `Issue tracker`_ * `Documentation`_ * IRC: ``#pyca`` on ``irc.libera.chat`` Wherever we interact, we adhere to the `Python Community Code of Conduct`_. .. _`Mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev .. _`Source code`: https://github.com/pyca/cryptography .. _`Issue tracker`: https://github.com/pyca/cryptography/issues .. _`Documentation`: https://cryptography.io/ .. _`Python Community Code of Conduct`: https://www.python.org/psf/codeofconduct/ --- ### Doing A Release Doing a release =============== Doing a release of ``cryptography`` requires a few steps. Security Releases ----------------- In addition to the other steps described below, for a release which fixes a security vulnerability, you should also include the following steps: * Request a `CVE from MITRE`_. Once you have received the CVE, it should be included in the :doc:`changelog`. Ideally you should request the CVE before starting the release process so that the CVE is available at the time of the release. * Document the CVE in the git commit that fixes the issue. * Ensure that the :doc:`changelog` entry credits whoever reported the issue and contains the assigned CVE. * Publish a GitHub Security Advisory on the repository with all relevant information. * The release should be announced on the `oss-security`_ mailing list, in addition to the regular announcement lists. Verifying OpenSSL version ------------------------- The release process creates wheels bundling OpenSSL for Windows, macOS, and Linux. Check that the Windows, macOS, and Linux builders (the ``manylinux`` containers) have the latest OpenSSL. If anything is out of date follow the instructions for upgrading OpenSSL. Upgrading OpenSSL ----------------- Use the `upgrading OpenSSL issue template`_. Bumping the version number -------------------------- The next step in doing a release is bumping the version number in the software. * Run ``python release.py bump-version {new_version}`` * Set the release date in the :doc:`/changelog`. * Do a commit indicating this. * Send a pull request with this. * Wait for it to be merged. Performing the release ---------------------- The commit that merged the version number bump is now the official release commit for this release. You will need to have ``git`` configured to perform signed tags. Once this has happened: * Run ``python release.py release``. The release should now be available on PyPI and a tag should be available in the repository. Verifying the release --------------------- You should verify that ``pip install cryptography`` works correctly: .. code-block:: pycon >>> import cryptography >>> cryptography.__version__ '...' >>> import cryptography_vectors >>> cryptography_vectors.__version__ '...' Verify that this is the version you just released. For the Windows wheels check the builds for the ``cryptography-wheel-builder`` job and verify that the final output for each build shows it loaded and linked the expected OpenSSL version. Post-release tasks ------------------ * Send an email to the `mailing list`_ and `python-announce`_ announcing the release. * Close the `milestone`_ for the previous release on GitHub. * For major version releases, send a pull request to pyOpenSSL increasing the maximum ``cryptography`` version pin and perform a pyOpenSSL release. * Update the version number to the next major (e.g. ``0.5.dev1``) with ``python release.py bump-version {new_version}``. * Add new :doc:`/changelog` entry with next version and note that it is under active development * Send a pull request with these items * Check for any outstanding code undergoing a deprecation cycle by looking in ``cryptography.utils`` for ``DeprecatedIn**`` definitions. If any exist open a ticket to increment them for the next release. .. _`CVE from MITRE`: https://cveform.mitre.org/ .. _`oss-security`: https://www.openwall.com/lists/oss-security/ .. _`upgrading OpenSSL issue template`: https://github.com/pyca/cryptography/issues/new?template=openssl-release.md .. _`milestone`: https://github.com/pyca/cryptography/milestones .. _`mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev .. _`python-announce`: https://mail.python.org/mailman3/lists/python-announce-list.python.org/ --- ### Exceptions Exceptions ========== .. currentmodule:: cryptography.exceptions .. class:: UnsupportedAlgorithm Raised when the requested algorithm, or combination of algorithms is not supported. .. class:: AlreadyFinalized This is raised when a context is used after being finalized. .. class:: InvalidSignature This is raised when signature verification fails. This can occur with HMAC or asymmetric key signature validation. .. class:: NotYetFinalized This is raised when the AEAD tag property is accessed on a context before it is finalized. .. class:: AlreadyUpdated This is raised when additional data is added to a context after update has already been called. .. class:: InvalidKey This is raised when the verify method of a key derivation function's computed key does not match the expected key. --- ### Faq Frequently asked questions ========================== What issues can you help with in your issue tracker? ---------------------------------------------------- The primary purpose of our issue tracker is to enable us to identify and resolve bugs and feature requests in ``cryptography``, so any time a user files a bug, we start by asking: Is this a ``cryptography`` bug, or is it a bug somewhere else? That said, we do our best to help users to debug issues that are in their code or environments. Please note, however, that there's a limit to our ability to assist users in resolving problems that are specific to their environments, particularly when we have no way to reproduce the issue. Lastly, we're not able to provide support for general Python or Python packaging issues. .. _faq-howto-handle-deprecation-warning: I cannot suppress the deprecation warning that ``cryptography`` emits on import ------------------------------------------------------------------------------- .. hint:: The deprecation warning emitted on import does not inherit :py:exc:`DeprecationWarning` but inherits :py:exc:`UserWarning` instead. If your pytest setup follows the best practices of failing on emitted warnings (``filterwarnings = error``), you may ignore it by adding the following line at the end of the list:: ignore:Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.:UserWarning **Note:** Using ``cryptography.utils.CryptographyDeprecationWarning`` is not possible here because specifying it triggers ``import cryptography`` internally that emits the warning before the ignore rule even kicks in. Ref: https://github.com/pytest-dev/pytest/issues/7524 The same applies when you use :py:func:`~warnings.filterwarnings` in your code or invoke CPython with :std:option:`-W` command line option. ``cryptography`` failed to install! ----------------------------------- If you are having issues installing ``cryptography`` the first troubleshooting step is to upgrade ``pip`` and then try to install again. For most users this will take the form of ``pip install -U pip``, but on Windows you should do ``python -m pip install -U pip``. If you are still seeing errors after upgrading and trying ``pip install cryptography`` again, please see the :doc:`/installation` documentation. How does ``cryptography`` compare to NaCl (Networking and Cryptography Library)? -------------------------------------------------------------------------------- While ``cryptography`` and `NaCl`_ both share the goal of making cryptography easier, and safer, to use for developers, ``cryptography`` is designed to be a general purpose library, interoperable with existing systems, while NaCl features a collection of hand selected algorithms. ``cryptography``'s :ref:`recipes ` layer has similar goals to NaCl. If you prefer NaCl's design, we highly recommend `PyNaCl`_, which is also maintained by the PyCA team. Why use ``cryptography``? ------------------------- If you've done cryptographic work in Python before you have likely encountered other libraries in Python such as *M2Crypto*, *PyCrypto*, or *PyOpenSSL*. In building ``cryptography`` we wanted to address a few issues we observed in the legacy libraries: * Extremely error prone APIs and insecure defaults. * Use of poor implementations of algorithms (i.e. ones with known side-channel attacks). * Lack of maintenance. * Lack of high level APIs. * Lack of PyPy and Python 3 support. * Absence of algorithms such as :class:`AES-GCM ` and :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF`. Why does ``cryptography`` require Rust? --------------------------------------- ``cryptography`` uses OpenSSL (see: :doc:`/openssl`) for its cryptographic operations. OpenSSL is the de facto standard for cryptographic libraries and provides high performance along with various certifications that may be relevant to developers. However, it is written in C and lacks `memory safety`_. We want ``cryptography`` to be as secure as possible while retaining the advantages of OpenSSL, so we've chosen to rewrite non-cryptographic operations (such as ASN.1 parsing) in a high performance memory safe language: Rust. ``cryptography`` raised an ``InternalError`` and I'm not sure what to do? ------------------------------------------------------------------------- Frequently ``InternalError`` is raised when there are errors on the OpenSSL error stack that were placed there by other libraries that are also using OpenSSL. Try removing the other libraries and see if the problem persists. If you have no other libraries using OpenSSL in your process, or they do not appear to be at fault, it's possible that this is a bug in ``cryptography``. Please file an `issue`_ with instructions on how to reproduce it. Installing cryptography with OpenSSL older than 3.0.0 fails ------------------------------------------------------------ The OpenSSL project has dropped support for the 0.9.8, 1.0.0, 1.0.1, 1.0.2, 1.1.0, and 1.1.1 release series. Since they are no longer receiving security patches from upstream, ``cryptography`` is also dropping support for them. To fix this issue you should upgrade to OpenSSL 3.0.0 or later. This may require you to upgrade to a newer operating system. Installing ``cryptography`` fails with ``error: Can not find Rust compiler`` ---------------------------------------------------------------------------- Building ``cryptography`` from source requires you have :ref:`Rust installed and available` on your ``PATH``. You may be able to fix this by upgrading to a newer version of ``pip`` which will install a pre-compiled ``cryptography`` wheel. If not, you'll need to install Rust. Follow the :ref:`instructions` to ensure you install a recent Rust version. Rust is only required during the build phase of ``cryptography``, you do not need to have Rust installed after you've built ``cryptography``. This is the same as the C compiler toolchain which is also required to build ``cryptography``, but not afterwards. I'm getting errors installing or importing ``cryptography`` on AWS Lambda ------------------------------------------------------------------------- Make sure you're following AWS's documentation either for `building .zip archives for Lambda`_ or `building container images for Lambda`_. Why are there no wheels for my Python3.x version? ------------------------------------------------- Our Python3 wheels are ``abi3`` wheels. This means they support multiple versions of Python. The ``abi3`` wheel can be used with any version of Python greater than or equal to the version it specifies. Recent versions of ``pip`` will automatically install ``abi3`` wheels. Why can't I import my PEM file? ------------------------------- PEM is a format (defined by several RFCs, but originally :rfc:`1421`) for encoding keys, certificates, and others cryptographic data into a regular form. The data is encoded as base64 and wrapped with a header and footer. If you are having trouble importing PEM files, make sure your file fits the following rules: * has a one-line header like this: ``-----BEGIN [FILE TYPE]-----`` (where ``[FILE TYPE]`` is ``CERTIFICATE``, ``PUBLIC KEY``, ``PRIVATE KEY``, etc.) * has a one-line footer like this: ``-----END [FILE TYPE]-----`` * all lines, except for the final one, must consist of exactly 64 characters. For example, this is a PEM file for a RSA Public Key: :: -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7CsKFSzq20NLb2VQDXma 9DsDXtKADv0ziI5hT1KG6Bex5seE9pUoEcUxNv4uXo2jzAUgyRweRl/DLU8SoN8+ WWd6YWik4GZvNv7j0z28h9Q5jRySxy4dmElFtIRHGiKhqd1Z06z4AzrmKEzgxkOk LJjY9cvwD+iXjpK2oJwNNyavvjb5YZq6V60RhpyNtKpMh2+zRLgIk9sROEPQeYfK 22zj2CnGBMg5Gm2uPOsGDltl/I/Fdh1aO3X4i1GXwCuPf1kSAg6lPJD0batftkSG v0X0heUaV0j1HSNlBWamT4IR9+iJfKJHekOqvHQBcaCu7Ja4kXzx6GZ3M2j/Ja3A 2QIDAQAB -----END PUBLIC KEY----- .. _faq-missing-backend: What happened to the backend argument? -------------------------------------- ``cryptography`` stopped requiring the use of ``backend`` arguments in version 3.1 and deprecated their use in version 36.0. If you are on an older version that requires these arguments please view the appropriate documentation version or upgrade to the latest release. Note that for forward compatibility ``backend`` is still silently accepted by functions that previously required it, but it is ignored and no longer documented. Will you upload wheels for my non-x86 non-ARM64 CPU architecture? ----------------------------------------------------------------- Maybe! But there's some pre-requisites. For us to build wheels and upload them to PyPI, we consider it necessary to run our tests for that architecture as a part of our CI (i.e. for every commit). If we don't run the tests, it's hard to have confidence that everything works -- particularly with cryptography, which frequently employs per-architecture assembly code. For us to add something to CI we need a provider which offers builds on that architecture, which integrate into our workflows, has sufficient capacity, and performs well enough not to regress the contributor experience. We don't think this is an insurmountable bar, but it's also not one that can be cleared lightly. If you are interested in helping support a new CPU architecture, we encourage you to reach out, discuss, and contribute that support. We will attempt to be supportive, but we cannot commit to doing the work ourselves. .. _`NaCl`: https://nacl.cr.yp.to/ .. _`PyNaCl`: https://pynacl.readthedocs.io .. _`issue`: https://github.com/pyca/cryptography/issues .. _`memory safety`: https://alexgaynor.net/2019/aug/12/introduction-to-memory-unsafety-for-vps-of-engineering/ .. _`building .zip archives for Lambda`: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html .. _`building container images for Lambda`: https://docs.aws.amazon.com/lambda/latest/dg/python-image.html --- ### Fernet 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. This class exhibits :term:`thread safety`. .. 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 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. :type key: bytes or str .. 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 to 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:: encrypt_at_time(data, current_time) .. versionadded:: 3.0 Encrypts data passed using explicitly passed current time. See :meth:`encrypt` for the documentation of the ``data`` parameter, the return type and the exceptions raised. The motivation behind this method is for the client code to be able to test token expiration. Since this method can be used in an insecure manner one should make sure the correct time (``int(time.time())``) is passed as ``current_time`` outside testing. :param int current_time: The current time. .. note:: Similarly to :meth:`encrypt` the encrypted message contains the timestamp in *plaintext*, in this case the timestamp is the value of the ``current_time`` parameter. .. 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 or str 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`` or ``str``. .. method:: decrypt_at_time(token, ttl, current_time) .. versionadded:: 3.0 Decrypts a token using explicitly passed current time. See :meth:`decrypt` for the documentation of the ``token`` and ``ttl`` parameters (``ttl`` is required here), the return type and the exceptions raised. The motivation behind this method is for the client code to be able to test token expiration. Since this method can be used in an insecure manner one should make sure the correct time (``int(time.time())``) is passed as ``current_time`` outside testing. :param int current_time: The current time. .. 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 or str 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`` or ``str``. .. 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 or str 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`` or ``str``. .. 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. ``cryptography`` provides several such functions; it is generally recommended to use :class:`~cryptography.hazmat.primitives.kdf.argon2.Argon2id`. .. doctest:: >>> import base64 >>> import os >>> from cryptography.fernet import Fernet >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.argon2 import Argon2id >>> password = b"password" >>> salt = os.urandom(16) >>> kdf = Argon2id( ... salt=salt, ... length=32, ... iterations=1, ... lanes=4, ... memory_cost=2**21 ... ) >>> 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 :class:`~cryptography.hazmat.primitives.kdf.argon2.Argon2id` parameters in the above code example are based on the recommendations of `IRTF RFC 9106`_ for general applications. For memory-constrained applications, the RFC recommends ``iterations=3`` and ``memory_cost=2**16``. See that document for more information. 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. This means that the complete message contents must be available in memory, making Fernet 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 .. _`IRTF RFC 9106`: https://datatracker.ietf.org/doc/html/rfc9106#name-parameter-choice --- ### Glossary Glossary ======== .. glossary:: :sorted: plaintext User-readable data you care about. ciphertext The encoded data, it's not user readable. Potential attackers are able to see this. encryption The process of converting plaintext to ciphertext. decryption The process of converting ciphertext to plaintext. key Secret data is encoded with a function using this key. Sometimes multiple keys are used. These **must** be kept secret, if a key is exposed to an attacker, any data encrypted with it will be exposed. symmetric cryptography Cryptographic operations where encryption and decryption use the same key. public-key cryptography asymmetric cryptography Cryptographic operations where encryption and decryption use different keys. There are separate encryption and decryption keys. Typically encryption is performed using a :term:`public key`, and it can then be decrypted using a :term:`private key`. Asymmetric cryptography can also be used to create signatures, which can be generated with a :term:`private key` and verified with a :term:`public key`. public key This is one of two keys involved in :term:`public-key cryptography`. It can be used to encrypt messages for someone possessing the corresponding :term:`private key` and to verify signatures created with the corresponding :term:`private key`. This can be distributed publicly, hence the name. private key This is one of two keys involved in :term:`public-key cryptography`. It can be used to decrypt messages which were encrypted with the corresponding :term:`public key`, as well as to create signatures, which can be verified with the corresponding :term:`public key`. These **must** be kept secret, if they are exposed, all encrypted messages are compromised, and an attacker will be able to forge signatures. authentication The process of verifying that a message was created by a specific individual (or program). Like encryption, authentication can be either symmetric or asymmetric. Authentication is necessary for effective encryption. ciphertext indistinguishability This is a property of encryption systems whereby two encrypted messages aren't distinguishable without knowing the encryption key. This is considered a basic, necessary property for a working encryption system. nonce A nonce is a **n**\ umber used **once**. Nonces are used in many cryptographic protocols. Generally, a nonce does not have to be secret or unpredictable, but it must be unique. A nonce is often a random or pseudo-random number (see :doc:`Random number generation `). Since a nonce does not have to be unpredictable, it can also take a form of a counter. opaque key An opaque key is a type of key that allows you to perform cryptographic operations such as encryption, decryption, signing, and verification, but does not allow access to the key itself. Typically an opaque key is loaded from a `hardware security module`_ (HSM). A-label The ASCII compatible encoded (ACE) representation of an internationalized (unicode) domain name. A-labels begin with the prefix ``xn--``. To create an A-label from a unicode domain string use a library like `idna`_. bits A bit is binary value -- a value that has only two possible states. Typically binary values are represented visually as 0 or 1, but remember that their actual value is not a printable character. A byte on modern computers is 8 bits and represents 256 possible values. In cryptographic applications when you see something say it requires a 128 bit key, you can calculate the number of bytes by dividing by 8. 128 divided by 8 is 16, so a 128 bit key is a 16 byte key. bytes-like A bytes-like object contains binary data and supports the `buffer protocol`_. This includes ``bytes``, ``bytearray``, and ``memoryview`` objects. It is :term:`unsafe` to pass a mutable object (e.g., a ``bytearray`` or other implementer of the buffer protocol) and to `mutate it concurrently`_ with the operation it has been provided for. U-label The presentational unicode form of an internationalized domain name. U-labels use unicode characters outside the ASCII range and are encoded as A-labels when stored in certificates. unsafe This is a term used to describe an operation where the user must ensure that the input is correct. Failure to do so can result in crashes, hangs, and other security issues. thread safety All immutable objects in ``cryptography`` are safe to use in multi-threaded environments. This means they can be shared across threads without requiring additional synchronization. Mutable objects, such as hash contexts, can also be shared, but concurrent modification may lead to exceptions or incorrect results. When working with cryptographic operations in a multi-threaded application, ensure that any mutable objects are used in a thread-safe manner. .. _`hardware security module`: https://en.wikipedia.org/wiki/Hardware_security_module .. _`idna`: https://pypi.org/project/idna/ .. _`buffer protocol`: https://docs.python.org/3/c-api/buffer.html .. _`mutate it concurrently`: https://alexgaynor.net/2022/oct/23/buffers-on-the-edge/ --- ### Index Welcome to ``pyca/cryptography`` ================================ ``cryptography`` includes both high level recipes and low level interfaces to common cryptographic algorithms such as symmetric ciphers, message digests, and key derivation functions. For example, to encrypt something with ``cryptography``'s high level symmetric encryption recipe: .. code-block:: pycon >>> from cryptography.fernet import Fernet >>> # Put this somewhere safe! >>> key = Fernet.generate_key() >>> f = Fernet(key) >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") >>> token b'...' >>> f.decrypt(token) b'A really secret message. Not for prying eyes.' If you are interested in learning more about the field of cryptography, we recommend `Crypto 101, by Laurens Van Houtven`_ and `The Cryptopals Crypto Challenges`_. Installation ------------ To install ``cryptography``: .. tab:: ``pip`` .. code-block:: console $ pip install cryptography .. tab:: ``uv`` .. code-block:: console $ uv add cryptography See :doc:`Installation ` for more information. .. _cryptography-layout: Layout ------ ``cryptography`` is broadly divided into two levels. One with safe cryptographic recipes that require little to no configuration choices. These are safe and easy to use and don't require developers to make many decisions. The other level is low-level cryptographic primitives. These are often dangerous and can be used incorrectly. They require making decisions and having an in-depth knowledge of the cryptographic concepts at work. Because of the potential danger in working at this level, this is referred to as the "hazardous materials" or "hazmat" layer. These live in the ``cryptography.hazmat`` package, and their documentation will always contain an admonition at the top. We recommend using the recipes layer whenever possible, and falling back to the hazmat layer only when necessary. .. toctree:: :maxdepth: 2 :caption: The recipes layer fernet cobblestone x509/index .. toctree:: :maxdepth: 2 :caption: The hazardous materials layer hazmat/primitives/index exceptions random-numbers hazmat/asn1/index hazmat/decrepit/index .. toctree:: :maxdepth: 2 :caption: The cryptography open source project installation changelog faq development/index openssl security limitations api-stability doing-a-release community glossary .. toctree:: :maxdepth: 1 statements/index .. note:: ``cryptography`` has not been subjected to an external audit of its code or documentation. If you're interested in discussing an audit please :doc:`get in touch `. .. _`Crypto 101, by Laurens Van Houtven`: https://www.crypto101.io/ .. _`The Cryptopals Crypto Challenges`: https://cryptopals.com/ --- ### Installation Installation ============ You can install ``cryptography``: .. tab:: ``pip`` .. code-block:: console $ pip install cryptography .. tab:: ``uv`` .. code-block:: console $ uv add cryptography If this does not work please **upgrade your pip** first, as that is the single most common cause of installation problems. Supported platforms ------------------- Currently we test ``cryptography`` on Python 3.9+ and PyPy3.11 on these operating systems. * x86-64 CentOS Stream 9, 10 * x86-64 Fedora (latest) * ARM64 macOS 26 Tahoe * x86-64 Ubuntu 22.04, 24.04, 26.04, and rolling * ARM64, ARMv7l, and ``ppc64le`` Ubuntu rolling * x86-64 Debian Bookworm (12.x), Trixie (13.x), and Sid (unstable) * x86-64 and ARM64 Alpine (latest) * 64-bit Python on 64-bit Windows Server 2025 We test compiling with ``clang`` as well as ``gcc`` and use the following OpenSSL releases in addition to distribution provided releases from the above supported platforms: * ``OpenSSL 3.0-latest`` * ``OpenSSL 3.4-latest`` * ``OpenSSL 3.5-latest`` * ``OpenSSL 3.6-latest`` * ``OpenSSL 4.0-latest`` We also test against the latest commit of BoringSSL, the latest ``aws-lc`` release, and versions of LibreSSL that are receiving security support at the time of a given ``cryptography`` release. Building cryptography on Windows -------------------------------- The wheel package on Windows is a statically linked build (as of 0.5) so all dependencies are included. To install ``cryptography``, you will typically just run .. code-block:: console $ pip install cryptography If you prefer to compile it yourself you'll need to have OpenSSL installed. You can compile OpenSSL yourself as well or use `a binary distribution`_. Be sure to download the proper version for your architecture and Python (VC2015 is required for 3.9 and above). Wherever you place your copy of OpenSSL you'll need to set the ``OPENSSL_DIR`` environment variable to include the proper location. For example: .. code-block:: console C:\> \path\to\vcvarsall.bat x86_amd64 C:\> set OPENSSL_DIR=C:\OpenSSL-win64 C:\> pip install cryptography You will also need to have :ref:`Rust installed and available`. If you need to rebuild ``cryptography`` for any reason be sure to clear the local `wheel cache`_. .. _build-on-linux: Building cryptography on Linux ------------------------------ .. note:: You should **upgrade pip** and attempt to install ``cryptography`` again before following the instructions to compile it below. Most Linux platforms will receive a binary wheel and require no compiler if you have an updated ``pip``! ``cryptography`` ships ``manylinux`` wheels (as of 2.0) so all dependencies are included. For users on **pip 19.3** or above running on a ``manylinux2014`` (or greater) compatible distribution (or **pip 21.2.4** for ``musllinux``) all you should need to do is: .. code-block:: console $ pip install cryptography If you want to compile ``cryptography`` yourself you'll need a C compiler, a Rust compiler, headers for Python (if you're not using ``pypy``), and headers for the OpenSSL and ``libffi`` libraries available on your system. On all Linux distributions you will need to have :ref:`Rust installed and available`. .. tab:: Alpine .. warning:: The Rust available by default in Alpine < 3.21 is older than the minimum supported version. See the :ref:`Rust installation instructions ` for information about installing a newer Rust. .. code-block:: console $ sudo apk add gcc musl-dev python3-dev libffi-dev openssl-dev cargo pkgconfig If you get an error with ``openssl-dev`` you may have to use ``libressl-dev``. .. tab:: Debian/Ubuntu .. warning:: The Rust available in Debian versions prior to Trixie are older than the minimum supported version. See the :ref:`Rust installation instructions ` for information about installing a newer Rust. .. code-block:: console $ sudo apt-get install build-essential libssl-dev libffi-dev \ python3-dev cargo pkg-config .. tab:: Fedora/RHEL/CentOS .. warning:: For RHEL and CentOS you must be on version 9.6 or newer for the command below to install a sufficiently new Rust. If your Rust is less than 1.83.0 please see the :ref:`Rust installation instructions ` for information about installing a newer Rust. .. code-block:: console $ sudo dnf install redhat-rpm-config gcc libffi-devel python3-devel \ openssl-devel cargo pkg-config Building ~~~~~~~~ You should now be able to build and install cryptography. To avoid getting the pre-built wheel on ``manylinux`` compatible distributions you'll need to use ``--no-binary``. .. code-block:: console $ pip install cryptography --no-binary cryptography Using your own OpenSSL on Linux ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python links to OpenSSL for its own purposes and this can sometimes cause problems when you wish to use a different version of OpenSSL with cryptography. If you want to use cryptography with your own build of OpenSSL you will need to make sure that the build is configured correctly so that your version of OpenSSL doesn't conflict with Python's. The options you need to add allow the linker to identify every symbol correctly even when multiple versions of the library are linked into the same program. If you are using your distribution's source packages these will probably be patched in for you already, otherwise you'll need to use options something like this when configuring OpenSSL: .. code-block:: console $ ./config -Wl,-Bsymbolic-functions -fPIC shared Static Wheels ~~~~~~~~~~~~~ Cryptography ships statically-linked wheels for macOS, Windows, and Linux (via ``manylinux`` and ``musllinux``). This allows compatible environments to use the most recent OpenSSL, regardless of what is shipped by default on those platforms. If you are using a platform not covered by our wheels, you can build your own statically-linked wheels that will work on your own systems. This will allow you to continue to use relatively old Linux distributions (such as LTS releases), while making sure you have the most recent OpenSSL available to your Python programs. To do so, you should find yourself a machine that is as similar as possible to your target environment (e.g. your production environment): for example, spin up a new cloud server running your target Linux distribution. On this machine, install the Cryptography dependencies as mentioned in :ref:`build-on-linux`. Please also make sure you have `virtualenv`_ installed: this should be available from your system package manager. Then, paste the following into a shell script. You'll need to populate the ``OPENSSL_VERSION`` variable. To do that, visit `openssl.org`_ and find the latest non-FIPS release version number, then set the string appropriately. For example, for OpenSSL 3.0.9, use ``OPENSSL_VERSION="3.0.9"``. When this shell script is complete, you'll find a collection of wheel files in a directory called ``wheelhouse``. These wheels can be installed by a sufficiently-recent version of ``pip``. The Cryptography wheel in this directory contains a statically-linked OpenSSL binding, which ensures that you have access to the most-recent OpenSSL releases without corrupting your system dependencies. .. code-block:: console set -e OPENSSL_VERSION="VERSIONGOESHERE" CWD=$(pwd) virtualenv env . env/bin/activate pip install -U setuptools pip install -U wheel pip curl -LO https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz tar xzvf openssl-${OPENSSL_VERSION}.tar.gz cd openssl-${OPENSSL_VERSION} ./config no-shared no-ssl2 no-ssl3 -fPIC --prefix=${CWD}/openssl make && make install cd .. OPENSSL_DIR="${CWD}/openssl" pip wheel --no-cache-dir --no-binary cryptography cryptography Building cryptography on macOS ------------------------------ .. note:: If installation gives a ``fatal error: 'openssl/aes.h' file not found`` see the :doc:`FAQ ` for information about how to fix this issue. The wheel package on macOS is a statically linked build (as of 1.0.1) so for users with pip 8 or above you only need one step: .. code-block:: console $ pip install cryptography If you want to build cryptography yourself or are on an older macOS version, cryptography requires the presence of a C compiler, development headers, and the proper libraries. On macOS much of this is provided by Apple's Xcode development tools. To install the Xcode command line tools (on macOS 10.10+) open a terminal window and run: .. code-block:: console $ xcode-select --install This will install a compiler (clang) along with (most of) the required development headers. You will also need to have :ref:`Rust installed and available`, which can be obtained from `Homebrew`_, `MacPorts`_, or directly from the Rust website. Finally you need OpenSSL, which you can obtain from `Homebrew`_ or `MacPorts`_. Cryptography does **not** support the OpenSSL/LibreSSL libraries Apple ships in its base operating system. To build cryptography and dynamically link it: `Homebrew`_ .. code-block:: console $ brew install openssl@3 rust $ pip install --no-binary cryptography cryptography `MacPorts`_: .. code-block:: console $ sudo port install openssl rust $ env OPENSSL_DIR="-L/opt/local" pip install --no-binary cryptography cryptography You can also build cryptography statically: `Homebrew`_ .. code-block:: console $ brew install openssl@3 rust $ env OPENSSL_STATIC=1 pip install --no-binary cryptography cryptography `MacPorts`_: .. code-block:: console $ sudo port install openssl rust $ env OPENSSL_STATIC=1 OPENSSL_DIR="/opt/local" pip install --no-binary cryptography cryptography If you need to rebuild ``cryptography`` for any reason be sure to clear the local `wheel cache`_. Building with BoringSSL, LibreSSL, or AWS-LC -------------------------------------------- To build against BoringSSL, LibreSSL, or AWS-LC instead of OpenSSL, you can set the ``OPENSSL_DIR`` environment variable to point to your BoringSSL, LibreSSL, or AWS-LC installation directory. Rust ---- .. note:: If you are using Linux, then you should **upgrade pip** (in a virtual environment!) and attempt to install ``cryptography`` again before trying to install the Rust toolchain. On most Linux distributions, the latest version of ``pip`` will be able to install a binary wheel, so you won't need a Rust toolchain. Building ``cryptography`` requires having a working Rust toolchain. The current minimum supported Rust version is 1.83.0. **This is newer than the Rust some package managers ship**, so users may need to install with the instructions below. Instructions for installing Rust can be found on `the Rust Project's website`_. We recommend installing Rust with ``rustup`` (as documented by the Rust Project) in order to ensure you have a recent version. Rust is only required when building ``cryptography``, meaning that you may install it for the duration of your ``pip install`` command and then remove it from a system. A Rust toolchain is not required to **use** ``cryptography``. In deployments such as ``docker``, you may use a multi-stage ``Dockerfile`` where you install Rust during the build phase but do not install it in the runtime image. This is the same as the C compiler toolchain which is also required to build ``cryptography``, but not afterwards. .. _`Homebrew`: https://brew.sh .. _`MacPorts`: https://www.macports.org .. _`a binary distribution`: https://wiki.openssl.org/index.php/Binaries .. _virtualenv: https://virtualenv.pypa.io/en/latest/ .. _openssl.org: https://www.openssl.org/source/ .. _`wheel cache`: https://pip.pypa.io/en/stable/cli/pip_install/#caching .. _`the Rust Project's website`: https://www.rust-lang.org/tools/install --- ### Limitations Known security limitations ========================== Secure memory wiping -------------------- `Memory wiping`_ is used to protect secret data or key material from attackers with access to deallocated memory. This is a defense-in-depth measure against vulnerabilities that leak application memory. Many ``cryptography`` APIs which accept ``bytes`` also accept types which implement the buffer interface. Thus, users wishing to do so can pass ``memoryview`` or another mutable type to ``cryptography`` APIs, and overwrite the contents once the data is no longer needed. However, ``cryptography`` does not clear memory by default, as there is no way to clear immutable structures such as ``bytes``. As a result, ``cryptography``, like almost all software in Python is potentially vulnerable to this attack. The `CERT secure coding guidelines`_ assesses this issue as "Severity: medium, Likelihood: unlikely, Remediation Cost: expensive to repair" and we do not consider this a high risk for most users. RSA PKCS1 v1.5 constant time decryption --------------------------------------- RSA decryption has several different modes, one of which is PKCS1 v1.5. When used in **online contexts**, a secure protocol implementation requires that peers not be able to tell whether RSA PKCS1 v1.5 decryption failed or succeeded, even by timing variability. ``cryptography`` does not provide an API that makes this possible, due to the fact that RSA decryption raises an exception on failure, which takes a different amount of time than returning a value in the success case. In OpenSSL 3.2.0 and newer, this is automatically mitigated by OpenSSL (by returning a random value and never raising an exception). If you are using cryptography with an older version of OpenSSL, such attacks are still possible. Regardless of OpenSSL version, we recommend not implementing or using online protocols that use RSA PKCS1 v1.5 decryption, as such protocols generally have poor security properties due to their lack of forward security. .. _`Memory wiping`: https://devblogs.microsoft.com/oldnewthing/?p=4223 .. _`CERT secure coding guidelines`: https://wiki.sei.cmu.edu/confluence/display/c/MEM03-C.+Clear+sensitive+information+stored+in+reusable+resources --- ### Openssl Use of OpenSSL ============== ``cryptography`` depends on the `OpenSSL`_ C library for all cryptographic operation. OpenSSL is the de facto standard for cryptographic libraries and provides high performance along with various certifications that may be relevant to developers. A list of supported versions can be found in our :doc:`/installation` documentation. In general the backend should be considered an internal implementation detail of the project, but there are some public methods available for debugging purposes. .. data:: cryptography.hazmat.backends.openssl.backend .. method:: openssl_version_text() :return text: The friendly string name of the loaded OpenSSL library. This is not necessarily the same version as it was compiled against. .. method:: openssl_version_number() .. versionadded:: 1.8 :return int: The integer version of the loaded OpenSSL library. This is defined in ``opensslv.h`` as ``OPENSSL_VERSION_NUMBER`` and is typically shown in hexadecimal (e.g. ``0x1010003f``). This is not necessarily the same version as it was compiled against. .. _legacy-provider: Legacy provider in OpenSSL 3.x ------------------------------ .. versionadded:: 39.0.0 Users can set ``CRYPTOGRAPHY_OPENSSL_NO_LEGACY`` environment variable to disable the legacy provider in OpenSSL 3.x. This will disable legacy cryptographic algorithms, including ``Blowfish``, ``CAST5``, ``SEED``, ``ARC4``, and ``RC2`` (which is used by some encrypted serialization formats). Additionally, the ``CRYPTOGRAPHY_BUILD_OPENSSL_NO_LEGACY`` environment variable can be set during the build process to prevent the library from ever attempting to load the legacy provider. If loading the legacy provider is not disabled and the legacy provider fails to load, a warning is emitted. .. _`OpenSSL`: https://www.openssl.org/ --- ### Random Numbers Random number generation ======================== When generating random data for use in cryptographic operations, such as an initialization vector for encryption in :class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode, you do not want to use the standard :mod:`random` module APIs. This is because they do not provide a cryptographically secure random number generator, which can result in major security issues depending on the algorithms in use. Therefore, it is our recommendation to `always use your operating system's provided random number generator`_, which is available as :func:`os.urandom`. For example, if you need 16 bytes of random data for an initialization vector, you can obtain them with: .. doctest:: >>> import os >>> iv = os.urandom(16) If you need your random number as an big integer, you can use ``int.from_bytes`` to convert the result of ``os.urandom``: .. code-block:: pycon >>> serial = int.from_bytes(os.urandom(16), byteorder="big") In addition, the `Python standard library`_ includes the ``secrets`` module, which can be used for generating cryptographically secure random numbers, with specific helpers for text-based formats. .. _`always use your operating system's provided random number generator`: https://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/ .. _`Python standard library`: https://docs.python.org/3/library/secrets.html --- ### Security Security ======== We take the security of ``cryptography`` seriously. The following are a set of policies we have adopted to ensure that security issues are addressed in a timely fashion. Known vulnerabilities --------------------- A list of all known vulnerabilities in ``cryptography`` can be found on `osv.dev`_, as well as other ecosystem vulnerability databases. They can automatically be scanned for using tools such as `pip-audit`_ or `osv-scan`_. Infrastructure -------------- In addition to ``cryptography``'s code, we're also concerned with the security of the infrastructure we run (primarily ``cryptography.io``). If you discover a security vulnerability in our infrastructure, we ask you to report it using the same procedure. What is a security issue? ------------------------- Anytime it's possible to write code using ``cryptography``'s public API which does not provide the guarantees that a reasonable developer would expect it to based on our documentation. That's a bit academic, but basically it means the scope of what we consider a vulnerability is broad, and we do not require a proof of concept or even a specific exploit, merely a reasonable threat model under which ``cryptography`` could be attacked. To give a few examples of things we would consider security issues: * If a recipe, such as Fernet, made it easy for a user to bypass confidentiality or integrity with the public API (e.g. if the API let a user reuse nonces). * If, under any circumstances, we used a CSPRNG which wasn't fork-safe. * If ``cryptography`` used an API in an underlying C library and failed to handle error conditions safely. Examples of things we wouldn't consider security issues: * Offering ECB mode for symmetric encryption in the *Hazmat* layer. Though ECB is critically weak, it is documented as being weak in our documentation. * Using a variable time comparison somewhere, if it's not possible to articulate any particular program in which this would result in problematic information disclosure. * An uncaught ``PanicException`` from ``pyo3``. In general, these represent bugs that should be filed as regular issues, but they are not security vulnerabilities because they do not cause memory unsafety or undefined behavior -- the process remains in a well-defined state. In general, if you're unsure, we request that you to default to treating things as security issues and handling them sensitively, the worst thing that can happen is that we'll ask you to file a public issue. Reporting a security issue -------------------------- We ask that you do not report security issues to our normal GitHub issue tracker. If you believe you've identified a security issue with ``cryptography``, please report it via our `security advisory page`_. Once you've submitted an issue, you should receive an acknowledgment within 48 hours, and depending on the action to be taken, you may receive further follow-up. Supported Versions ------------------ At any given time, we will provide security support for the `main`_ branch as well as the most recent release. New releases for OpenSSL updates -------------------------------- As of versions 0.5, 1.0.1, and 2.0.0, ``cryptography`` statically links OpenSSL in binary distributions for Windows, macOS, and Linux respectively, to ease installation. Due to this, ``cryptography`` will release a new version whenever OpenSSL has a security or bug fix release to avoid shipping insecure software. Like all our other releases, this will be announced on the mailing list and we strongly recommend that you upgrade as soon as possible. Disclosure Process ------------------ When we become aware of a security bug in ``cryptography``, we will endeavor to fix it and issue a release as quickly as possible. We will generally issue a new release for any security issue. The steps for issuing a security release are described in our :doc:`/doing-a-release` documentation. .. _`osv.dev`: https://osv.dev/list?ecosystem=PyPI&q=cryptography .. _`pip-audit`: https://pypi.org/project/pip-audit/ .. _`osv-scan`: https://google.github.io/osv-scanner/ .. _`security advisory page`: https://github.com/pyca/cryptography/security/advisories/new .. _`main`: https://github.com/pyca/cryptography ---