yigityalim
projectshandbookslabshireshare
xgithub
siteprojectshandbookslabschangelog
aboutusesnowhireshare
elsewherexgithublinkedinemail
metarssllms.txtsitemap
© 2026 Yiğit Yalım. All rights reserved.
/
Back to Labs
May 10, 2026·crypto

ChaCha20 Inner State Stepper

Step through the 4×4 state matrix of ChaCha20 quarter-round by quarter-round. Set key and nonce, watch which cells change in each of the 320 sub-ops.

chacha20 · stream-cipher · rfc-7539

PreviousBase64 Encoder / DecoderNextCIDR Subnet Calculator

ChaCha20's beauty is in its simplicity: 16 32-bit words, ARX operations (add–rotate–xor), 20 rounds. The entire mystery lives in a 4-line quarter round and a column → diagonal swap.

This lab decomposes the algorithm at sub-op granularity. Each click applies exactly one line of ARX; you can randomize the key/nonce and auto-play at 1× / 4× / 16×. 320 sub-ops = 80 quarter rounds = 10 double rounds = full ChaCha20.

ChaCha20 — inner state stepper · 0/320 sub-op

Educational only. RFC 7539 inner permutation görselleştirmek için TS'te yeniden yazıldı — WebCrypto round state vermiyor. Üretimde crypto.subtle veya @noble/ciphers kullan.

key (32 bayt)
nonce (12 bayt)
counter (u32)
round 1/10 (col)QR 1/4sub-op —
—
00 · const61707865
01 · const3320646e
02 · const79622d32
03 · const6b206574
04 · key03020100
05 · key07060504
06 · key0b0a0908
07 · key0f0e0d0c
08 · key13121110
09 · key17161514
10 · key1b1a1918
11 · key1f1e1d1c
12 · ctr00000000
13 · nonce09000000
14 · nonce4a000000
15 · nonce00000000
constantskeycounternoncesky ring = active QR · amber = changed this step
encrypt · plaintext ⊕ keystream = ciphertext
plaintext · 15 bayt
48656c6c6f20436861436861323021
keystream · ChaCha20(key, nonce, counter)
8adc91fd9ff4f0f51b0fad50ff15d637
ciphertext
c2b9fd91f0d4b39d7a4cc531cd25f7
decrypt · ciphertext ⊕ keystream
= Hello ChaCha20!
48656c6c6f20436861436861323021

Counter'ı değiştir — keystream tamamen değişir, ciphertext da değişir. Aynı key/nonce çifti ile farklı counter her zaman farklı bir 64-byte blok üretir. Aynı XOR hem encrypt hem decrypt yapar.

State structure

Each cell of the 4×4 grid represents a different role, color-coded:

  • Constants (gray, row 1): 4 words from the ASCII of "expand 32-byte k" — 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574. Never change (at the start), identical across every ChaCha20 instance.
  • Key (blue, rows 2 + 3): 8 words of the 256-bit key, little-endian.
  • Counter (violet, cell 12): 32-bit block counter. ChaCha20 runs once per 64-byte keystream block; the counter increments 0, 1, 2, ... to distinguish blocks.
  • Nonce (amber, cells 13-15): 96-bit nonce, little-endian.

The quarter round

Four sub-ops in one line:

QR(a, b, c, d) — quarter round
a += b; d ^= a; d = ROTL(d, 16)
c += d; b ^= c; b = ROTL(b, 12)
a += b; d ^= a; d = ROTL(d, 8)
c += d; b ^= c; b = ROTL(b, 7)

Only add, XOR, left-rotate — constant-time, branchless, hardware-friendly.

Column → Diagonal

In one double round:

  1. Column round: apply QR to the four vertical columns → QR(0,4,8,12), QR(1,5,9,13), QR(2,6,10,14), QR(3,7,11,15)
  2. Diagonal round: apply QR to four diagonal quartets → QR(0,5,10,15), QR(1,6,11,12), QR(2,7,8,13), QR(3,4,9,14)

10 double rounds = 20 rounds. At the end, the working state is added to the initial state and serialized little-endian → 64-byte keystream block.

What it teaches

  • ChaCha20 has no black box — every primitive operation is visible; a math student can follow it on a chalkboard in half an hour
  • The diagonal round does what the column round cannot: diffusion. It mixes information from one column across all the others.
  • Constant-time behavior comes from code structure, not from hardware — only add/xor/rotate, no branches, no table lookups
  • Unlike AES it's software-friendly (no S-box, no fast CPU implementation needed); better on mobile and embedded

Production use

This implementation is for visualisation only — it conforms to RFC 7539 but is not timing-attack resistant or side-channel hardened.

For real encryption:

  • Browser: crypto.subtle (Node 19+ included) — ChaCha20-Poly1305 may not be supported; use AES-GCM
  • Pure JS: @noble/ciphers or @stablelib/chacha20poly1305
  • Node: crypto.createCipheriv('chacha20-poly1305', ...) (OpenSSL underneath)