ChaCha20-Poly1305 and XChaCha20-Poly1305 ======================================== ChaCha20-Poly1305 is an authenticated cipher with associated data (AEAD). It works with a 32 byte secret key and a nonce which **must never** be reused across encryptions performed under the same key. The cipher produces a 16 byte tag that the receiver must use to validate the message. There are three variants of the algorithm, defined by the length of the nonce: .. csv-table:: :header: Nonce length, Description, Max plaintext, If random nonce and same key :widths: 5, 50, 20, 20 "8 bytes", "Based on Bernstein's original ChaCha20.", "No limitations", "Max 200 000 messages" "12 bytes (default)", "Version used in TLS and specified in `RFC7539`_.", "256 GB", "Max 13 billion messages" "24 bytes", "XChaCha20-Poly1305, still in `draft stage `_.", "256 GB", "No limitations" The API of the cipher and its finite state machine are the same as for the :doc:`modern modes of operation of block ciphers `. You create a new cipher by calling :meth:`Crypto.Cipher.ChaCha20_Poly1305.new`. This is an example of how ChaCha20-Poly1305 (TLS version) can encrypt and authenticate data:: >>> import json >>> from base64 import b64encode >>> from Crypto.Cipher import ChaCha20_Poly1305 >>> from Crypto.Random import get_random_bytes >>> >>> header = b"header" >>> plaintext = b'Attack at dawn' >>> key = get_random_bytes(32) >>> cipher = ChaCha20_Poly1305.new(key=key) >>> cipher.update(header) >>> ciphertext, tag = cipher.encrypt_and_digest(plaintext) >>> >>> jk = [ 'nonce', 'header', 'ciphertext', 'tag' ] >>> jv = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ] >>> result = json.dumps(dict(zip(jk, jv))) >>> print(result) {"nonce": "4EE/9uqhoZ3mQXmm", "header": "aGVhZGVy", "ciphertext": "Wmmo4Vzn+eS3tUPv2a8=", "tag": "/FgVbM8qhzssPRY80T0iVA=="} In the example above, a 96 bit (12 byte) nonce is automatically created. It can be accessed as the ``nonce`` member in the ``cipher`` object. This is how you decrypt the data and check its authenticity:: >>> import json >>> from base64 import b64decode >>> from Crypto.Cipher import ChaCha20_Poly1305 >>> >>> # We assume that the key was securely shared beforehand >>> try: >>> b64 = json.loads(json_input) >>> jk = [ 'nonce', 'header', 'ciphertext', 'tag' ] >>> jv = {k:b64decode(b64[k]) for k in jk} >>> >>> cipher = ChaCha20_Poly1305.new(key=key, nonce=jv['nonce']) >>> cipher.update(jv['header']) >>> plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag']) >>> print("The message was: " + plaintext.decode()) >>> except (ValueError, KeyError): >>> print("Incorrect decryption") .. _RFC7539: https://tools.ietf.org/html/rfc7539 .. automodule:: Crypto.Cipher.ChaCha20_Poly1305 :members: