ChaCha20 is a stream cipher by Daniel Bernstein that turns a 256-bit key, a 96-bit nonce and a 32-bit counter into a keystream XORed with your data. RFC 8439 specifies it together with Poly1305 as the ChaCha20-Poly1305 authenticated cipher. In TLS 1.3 it appears as TLS_CHACHA20_POLY1305_SHA256, which RFC 8446 says implementations should support. It is a refinement of Salsa20.
ChaCha20 builds a 4 by 4 grid of 32-bit words, called the state, and scrambles a copy of it to make one 64-byte keystream block. The layout from RFC 8439 is:
The scrambling uses only addition modulo 2^32, XOR and bit rotations by 16, 12, 8 and 7 positions, applied through the quarter round. Twenty rounds run as ten pairs, alternating column and diagonal quarter rounds. The result is added back to the starting state and serialized little-endian. The cipher then XORs the keystream with the plaintext, so encryption and decryption are the same operation, and the last block may be partial.
Because it uses no lookup tables, ChaCha20 runs in constant time in plain software, which is why it is common on phones and chips without AES instructions.
The example runs the RFC 8439 section 2.4.2 test vector with Python. The library takes a 16-byte value made of the 4-byte little-endian counter followed by the 12-byte nonce.
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
key = bytes(range(32))
nonce = bytes.fromhex("000000000000004a00000000")
pt = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
iv = (1).to_bytes(4, "little") + nonce
ct = Cipher(algorithms.ChaCha20(key, iv), mode=None).encryptor().update(pt)
print(len(pt), len(ct))
print(ct[:16].hex())
114 114
6e2e359a2568f98041ba0728dd0d6981
The first 16 bytes equal the RFC value 6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81. Ciphertext length always equals plaintext length.
RFC 8439 ChaCha20 uses a 32-byte key and a 12-byte nonce, and each message can hold at most 2^32 blocks of 64 bytes, which is 256 GiB (about 275 GB). The original design by Bernstein used a 64-bit nonce and a 64-bit counter, which is why a few libraries still show 8-byte nonces. Python's cryptography package rejects a bare 12-byte nonce with "nonce must be 128-bits (16 bytes)" and rejects a 16-byte key to ChaCha20Poly1305 with "ChaCha20Poly1305 key must be 32 bytes."
No. ChaCha20 alone only hides data, and a flipped ciphertext bit flips the same plaintext bit. Pair it with Poly1305, as RFC 8439 does, so the receiver rejects tampered messages. ChaCha20Poly1305 in Python returns the ciphertext plus a 16-byte tag, so "hello" encrypted to 21 bytes in the test run.