[{"data":1,"prerenderedAt":580},["ShallowReactive",2],{"navigation_docs":3,"-ciphers-chacha20-poly1305":276,"-ciphers-chacha20-poly1305-surround":577},[4,36],{"title":5,"path":6,"stem":7,"children":8,"icon":35},"Guide","\u002Fguide","1.guide\u002F01.index",[9,11,15,19,23,27,31],{"title":10,"path":6,"stem":7},"Getting Started",{"title":12,"path":13,"stem":14},"Encode and decode","\u002Fguide\u002Ftransform","1.guide\u002F02.transform",{"title":16,"path":17,"stem":18},"Analysis","\u002Fguide\u002Fanalysis","1.guide\u002F03.analysis",{"title":20,"path":21,"stem":22},"CLI","\u002Fguide\u002Fcli","1.guide\u002F04.cli",{"title":24,"path":25,"stem":26},"Agents","\u002Fguide\u002Fagents","1.guide\u002F05.agents",{"title":28,"path":29,"stem":30},"Custom ciphers","\u002Fguide\u002Fcustom","1.guide\u002F06.custom",{"title":32,"path":33,"stem":34},"Playground","\u002Fguide\u002Fplayground","1.guide\u002F07.playground","i-lucide-book-open",{"title":37,"path":38,"stem":39,"children":40,"icon":275},"Ciphers","\u002Fciphers","2.ciphers\u002F00.index",[41,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167,171,175,179,183,187,191,195,199,203,207,211,215,219,223,227,231,235,239,243,247,251,255,259,263,267,271],{"title":42,"path":38,"stem":39},"Overview",{"title":44,"path":45,"stem":46},"Classical","\u002Fciphers\u002Fclassical","2.ciphers\u002F01.classical",{"title":48,"path":49,"stem":50},"Caesar","\u002Fciphers\u002Fcaesar","2.ciphers\u002F02.caesar",{"title":52,"path":53,"stem":54},"ROT-13","\u002Fciphers\u002Frot13","2.ciphers\u002F03.rot13",{"title":56,"path":57,"stem":58},"ROT-47","\u002Fciphers\u002Frot47","2.ciphers\u002F04.rot47",{"title":60,"path":61,"stem":62},"Atbash","\u002Fciphers\u002Fatbash","2.ciphers\u002F05.atbash",{"title":64,"path":65,"stem":66},"Affine","\u002Fciphers\u002Faffine","2.ciphers\u002F06.affine",{"title":68,"path":69,"stem":70},"Vigenère","\u002Fciphers\u002Fvigenere","2.ciphers\u002F07.vigenere",{"title":72,"path":73,"stem":74},"Beaufort","\u002Fciphers\u002Fbeaufort","2.ciphers\u002F08.beaufort",{"title":76,"path":77,"stem":78},"Autokey","\u002Fciphers\u002Fautokey","2.ciphers\u002F09.autokey",{"title":80,"path":81,"stem":82},"Trithemius","\u002Fciphers\u002Ftrithemius","2.ciphers\u002F10.trithemius",{"title":84,"path":85,"stem":86},"Alberti","\u002Fciphers\u002Falberti","2.ciphers\u002F11.alberti",{"title":88,"path":89,"stem":90},"Playfair","\u002Fciphers\u002Fplayfair","2.ciphers\u002F12.playfair",{"title":92,"path":93,"stem":94},"Polybius square","\u002Fciphers\u002Fpolybius","2.ciphers\u002F13.polybius",{"title":96,"path":97,"stem":98},"Morse code","\u002Fciphers\u002Fmorse","2.ciphers\u002F14.morse",{"title":100,"path":101,"stem":102},"Bacon's cipher","\u002Fciphers\u002Fbacon","2.ciphers\u002F15.bacon",{"title":104,"path":105,"stem":106},"Tap code","\u002Fciphers\u002Ftap-code","2.ciphers\u002F16.tap-code",{"title":108,"path":109,"stem":110},"A1Z26","\u002Fciphers\u002Fa1z26","2.ciphers\u002F17.a1z26",{"title":112,"path":113,"stem":114},"Book cipher","\u002Fciphers\u002Fbook","2.ciphers\u002F18.book",{"title":116,"path":117,"stem":118},"ADFGVX","\u002Fciphers\u002Fadfgvx","2.ciphers\u002F19.adfgvx",{"title":120,"path":121,"stem":122},"Bifid","\u002Fciphers\u002Fbifid","2.ciphers\u002F20.bifid",{"title":124,"path":125,"stem":126},"Straddling checkerboard","\u002Fciphers\u002Fstraddling-checkerboard","2.ciphers\u002F21.straddling-checkerboard",{"title":128,"path":129,"stem":130},"Rail fence","\u002Fciphers\u002Frail-fence","2.ciphers\u002F22.rail-fence",{"title":132,"path":133,"stem":134},"Columnar transposition","\u002Fciphers\u002Fcolumnar","2.ciphers\u002F23.columnar",{"title":136,"path":137,"stem":138},"Route transposition","\u002Fciphers\u002Froute","2.ciphers\u002F24.route",{"title":140,"path":141,"stem":142},"Enigma M3","\u002Fciphers\u002Fenigma","2.ciphers\u002F25.enigma",{"title":144,"path":145,"stem":146},"Block","\u002Fciphers\u002Fblock","2.ciphers\u002F26.block",{"title":148,"path":149,"stem":150},"AES (ECB)","\u002Fciphers\u002Faes","2.ciphers\u002F27.aes",{"title":152,"path":153,"stem":154},"AES (CBC)","\u002Fciphers\u002Faes-cbc","2.ciphers\u002F28.aes-cbc",{"title":156,"path":157,"stem":158},"AES (CFB)","\u002Fciphers\u002Faes-cfb","2.ciphers\u002F29.aes-cfb",{"title":160,"path":161,"stem":162},"AES (OFB)","\u002Fciphers\u002Faes-ofb","2.ciphers\u002F30.aes-ofb",{"title":164,"path":165,"stem":166},"AES (CTR)","\u002Fciphers\u002Faes-ctr","2.ciphers\u002F31.aes-ctr",{"title":168,"path":169,"stem":170},"AES (CCM)","\u002Fciphers\u002Faes-ccm","2.ciphers\u002F32.aes-ccm",{"title":172,"path":173,"stem":174},"AES (OCB)","\u002Fciphers\u002Faes-ocb","2.ciphers\u002F33.aes-ocb",{"title":176,"path":177,"stem":178},"AES (LRW)","\u002Fciphers\u002Faes-lrw","2.ciphers\u002F34.aes-lrw",{"title":180,"path":181,"stem":182},"AES (XTS)","\u002Fciphers\u002Faes-xts","2.ciphers\u002F35.aes-xts",{"title":184,"path":185,"stem":186},"AES (CBC-MAC)","\u002Fciphers\u002Faes-cbc-mac","2.ciphers\u002F36.aes-cbc-mac",{"title":188,"path":189,"stem":190},"AES (passphrase)","\u002Fciphers\u002Faes-passphrase","2.ciphers\u002F37.aes-passphrase",{"title":192,"path":193,"stem":194},"Rijndael (ECB)","\u002Fciphers\u002Frijndael","2.ciphers\u002F38.rijndael",{"title":196,"path":197,"stem":198},"DES (ECB)","\u002Fciphers\u002Fdes","2.ciphers\u002F39.des",{"title":200,"path":201,"stem":202},"DESX (ECB)","\u002Fciphers\u002Fdesx","2.ciphers\u002F40.desx",{"title":204,"path":205,"stem":206},"Triple DES (ECB)","\u002Fciphers\u002Ftriple-des","2.ciphers\u002F41.triple-des",{"title":208,"path":209,"stem":210},"Triple DES (CBC)","\u002Fciphers\u002Ftriple-des-cbc","2.ciphers\u002F42.triple-des-cbc",{"title":212,"path":213,"stem":214},"Blowfish (ECB)","\u002Fciphers\u002Fblowfish","2.ciphers\u002F43.blowfish",{"title":216,"path":217,"stem":218},"IDEA (ECB)","\u002Fciphers\u002Fidea","2.ciphers\u002F44.idea",{"title":220,"path":221,"stem":222},"Lucifer (ECB)","\u002Fciphers\u002Flucifer","2.ciphers\u002F45.lucifer",{"title":224,"path":225,"stem":226},"MARS (ECB)","\u002Fciphers\u002Fmars","2.ciphers\u002F46.mars",{"title":228,"path":229,"stem":230},"Serpent (ECB)","\u002Fciphers\u002Fserpent","2.ciphers\u002F47.serpent",{"title":232,"path":233,"stem":234},"CAST5 (ECB)","\u002Fciphers\u002Fcast5","2.ciphers\u002F48.cast5",{"title":236,"path":237,"stem":238},"OpenPGP (passphrase)","\u002Fciphers\u002Fopenpgp","2.ciphers\u002F49.openpgp",{"title":240,"path":241,"stem":242},"Stream","\u002Fciphers\u002Fstream","2.ciphers\u002F50.stream",{"title":244,"path":245,"stem":246},"Rabbit","\u002Fciphers\u002Frabbit","2.ciphers\u002F51.rabbit",{"title":248,"path":249,"stem":250},"RC4","\u002Fciphers\u002Frc4","2.ciphers\u002F52.rc4",{"title":252,"path":253,"stem":254},"XOR","\u002Fciphers\u002Fxor","2.ciphers\u002F53.xor",{"title":256,"path":257,"stem":258},"Salsa20","\u002Fciphers\u002Fsalsa20","2.ciphers\u002F54.salsa20",{"title":260,"path":261,"stem":262},"XSalsa20","\u002Fciphers\u002Fxsalsa20","2.ciphers\u002F55.xsalsa20",{"title":264,"path":265,"stem":266},"ChaCha20","\u002Fciphers\u002Fchacha20","2.ciphers\u002F56.chacha20",{"title":268,"path":269,"stem":270},"XChaCha20","\u002Fciphers\u002Fxchacha20","2.ciphers\u002F57.xchacha20",{"title":272,"path":273,"stem":274},"ChaCha20-Poly1305","\u002Fciphers\u002Fchacha20-poly1305","2.ciphers\u002F58.chacha20-poly1305","i-lucide-library",{"id":277,"title":272,"body":278,"description":570,"extension":571,"links":572,"meta":573,"navigation":574,"path":273,"seo":575,"stem":274,"__hash__":576},"docs\u002F2.ciphers\u002F58.chacha20-poly1305.md",{"type":279,"value":280,"toc":563},"minimark",[281,285,293,298,301,304,311,315,330,443,460,464,478,502,518,522,525,529,532,552,559],[282,283],"cipher-facts",{"name":284},"chacha20-poly1305",[286,287,288,289,292],"p",{},"Plain ",[290,291,264],"a",{"href":265}," decrypts whatever you give it. Flip a bit, get a flipped bit, nobody complains. This one complains. It's ChaCha20 with Poly1305 on top, both by Bernstein, put together in RFC 8439. TLS 1.3 has it as a cipher suite, and WireGuard runs every packet through it.",[294,295,297],"h2",{"id":296},"how-it-runs","How it runs",[286,299,300],{},"Block 0 of the ChaCha20 keystream isn't used for the text. Its first 32 bytes are a one-time Poly1305 key, fresh for every nonce. The text gets encrypted from block 1.",[286,302,303],{},"Then Poly1305 reads the associated data, padded to 16 bytes, then the ciphertext, padded the same way, then both lengths. It's a polynomial evaluated modulo 2^130 - 5, which is where the name comes from. Out comes a 16-byte tag, glued to the end of the ciphertext.",[286,305,306,310],{},[307,308,309],"code",{},"decode"," recomputes the tag first. Wrong tag, no text, just an error.",[294,312,314],{"id":313},"the-options","The options",[286,316,317,318,321,322,325,326,329],{},"The key is 64 hex digits. ",[307,319,320],{},"nonce"," is 24 hex digits and required. ",[307,323,324],{},"aad"," is optional hex that the tag covers but nobody encrypts, like a packet header a router has to read. No ",[307,327,328],{},"counter"," here. The RFC fixes it at 1.",[331,332,337],"pre",{"className":333,"code":334,"language":335,"meta":336,"style":336},"language-ts shiki shiki-themes ciphers ciphers ciphers","const aead = create(\"chacha20-poly1305\");\nconst key = \"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\";\nconst nonce = \"070000004041424344454647\";\nconst aad = \"46524f4d3a2048512e\"; \u002F\u002F \"FROM: HQ.\" in hex\naead.encode(\"ATTACK AT DAWN\", { key, nonce, aad }).text;\n\u002F\u002F \"08b82563520a4c81cdc90976213c\" + \"cdabbd7363a02df0262f5ad3936c7d87\"\n","ts","",[307,338,339,369,385,400,420,437],{"__ignoreMap":336},[340,341,344,348,352,355,359,362,366],"span",{"class":342,"line":343},"line",1,[340,345,347],{"class":346},"skH_V","const",[340,349,351],{"class":350},"s38Sx"," aead ",[340,353,354],{"class":346},"=",[340,356,358],{"class":357},"sK71F"," create",[340,360,361],{"class":350},"(",[340,363,365],{"class":364},"shU9J","\"chacha20-poly1305\"",[340,367,368],{"class":350},");\n",[340,370,372,374,377,379,382],{"class":342,"line":371},2,[340,373,347],{"class":346},[340,375,376],{"class":350}," key ",[340,378,354],{"class":346},[340,380,381],{"class":364}," \"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\"",[340,383,384],{"class":350},";\n",[340,386,388,390,393,395,398],{"class":342,"line":387},3,[340,389,347],{"class":346},[340,391,392],{"class":350}," nonce ",[340,394,354],{"class":346},[340,396,397],{"class":364}," \"070000004041424344454647\"",[340,399,384],{"class":350},[340,401,403,405,408,410,413,416],{"class":342,"line":402},4,[340,404,347],{"class":346},[340,406,407],{"class":350}," aad ",[340,409,354],{"class":346},[340,411,412],{"class":364}," \"46524f4d3a2048512e\"",[340,414,415],{"class":350},"; ",[340,417,419],{"class":418},"scIB-","\u002F\u002F \"FROM: HQ.\" in hex\n",[340,421,423,426,429,431,434],{"class":342,"line":422},5,[340,424,425],{"class":350},"aead.",[340,427,428],{"class":357},"encode",[340,430,361],{"class":350},[340,432,433],{"class":364},"\"ATTACK AT DAWN\"",[340,435,436],{"class":350},", { key, nonce, aad }).text;\n",[340,438,440],{"class":342,"line":439},6,[340,441,442],{"class":418},"\u002F\u002F \"08b82563520a4c81cdc90976213c\" + \"cdabbd7363a02df0262f5ad3936c7d87\"\n",[286,444,445,446,449,450,453,454,456,457,459],{},"Fourteen bytes of ciphertext, sixteen of tag. Node's ",[307,447,448],{},"createCipheriv(\"chacha20-poly1305\")"," with the same ",[307,451,452],{},"setAAD"," gives the same bytes. Leave ",[307,455,324],{}," out and the first fourteen stay put, only the tag changes. The text never touches ",[307,458,324],{},".",[294,461,463],{"id":462},"try-to-cheat","Try to cheat",[286,465,466,469,470,473,474,477],{},[307,467,468],{},"DAWN"," to ",[307,471,472],{},"DUSK"," is ",[307,475,476],{},"00140405"," XORed into the last four bytes. Plain ChaCha20 would take that. And this one?",[331,479,481],{"className":333,"code":480,"language":335,"meta":336,"style":336},"aead.decode(\"08b82563520a4c81cdc909622539cdabbd7363a02df0262f5ad3936c7d87\", { key, nonce, aad });\n\u002F\u002F CipherError: [chacha20-poly1305] Tag does not match: wrong key, nonce or aad, or the ciphertext was changed\n",[307,482,483,497],{"__ignoreMap":336},[340,484,485,487,489,491,494],{"class":342,"line":343},[340,486,425],{"class":350},[340,488,309],{"class":357},[340,490,361],{"class":350},[340,492,493],{"class":364},"\"08b82563520a4c81cdc909622539cdabbd7363a02df0262f5ad3936c7d87\"",[340,495,496],{"class":350},", { key, nonce, aad });\n",[340,498,499],{"class":342,"line":371},[340,500,501],{"class":418},"\u002F\u002F CipherError: [chacha20-poly1305] Tag does not match: wrong key, nonce or aad, or the ciphertext was changed\n",[286,503,504,505,508,509,511,512,514,515,517],{},"Nice try. The bytes under the tag do decrypt to ",[307,506,507],{},"ATTACK AT DUSK",", but ",[307,510,309],{}," never shows them. The tag was computed over ",[307,513,468],{},", and a new one needs the key. A wrong nonce or a different ",[307,516,324],{}," ends the same way.",[294,519,521],{"id":520},"checked-against","Checked against",[286,523,524],{},"RFC 8439 section 2.8.2, the sunscreen text sealed byte for byte, and appendix A.5, a real message that opens to text with curly quotes. Poly1305 alone gets section 2.5.2 and test vectors #5 to #11 from appendix A.3. Those are the nasty ones, built to break carries and reductions.",[294,526,528],{"id":527},"what-the-tag-cant-fix","What the tag can't fix",[286,530,531],{},"A reused nonce. Same key, same nonce, same keystream, so XOR of two ciphertexts is still XOR of the texts. Worse, the one-time Poly1305 key repeats too. Two tags under one key let an attacker forge new ones. The RFC says it plainly: the nonce \"MUST not be repeated for the same key\".",[286,533,534,535,538,539,541,542,545,546,548,549,459],{},"A key that isn't 64 hex digits is an ",[307,536,537],{},"InvalidOptionError",", and so is a nonce that isn't 24 or an ",[307,540,324],{}," that isn't whole bytes of hex. A missing key or nonce is a ",[307,543,544],{},"MissingOptionError",". On ",[307,547,309],{},", a ciphertext shorter than the tag or a tag that doesn't match is a ",[307,550,551],{},"CipherError",[286,553,554,555,558],{},"Plain TypeScript, ",[307,556,557],{},"BigInt"," in Poly1305, not constant time. Great for seeing what a tag buys you. Terrible for anything real.",[560,561,562],"style",{},"html pre.shiki code .skH_V, html code.shiki .skH_V{--shiki-light:var(--shiki-token-keyword);--shiki-default:var(--shiki-token-keyword);--shiki-dark:var(--shiki-token-keyword)}html pre.shiki code .s38Sx, html code.shiki .s38Sx{--shiki-light:var(--ui-text-highlighted);--shiki-default:var(--ui-text-highlighted);--shiki-dark:var(--ui-text-highlighted)}html pre.shiki code .sK71F, html code.shiki .sK71F{--shiki-light:var(--shiki-token-function);--shiki-default:var(--shiki-token-function);--shiki-dark:var(--shiki-token-function)}html pre.shiki code .shU9J, html code.shiki .shU9J{--shiki-light:var(--shiki-token-string);--shiki-default:var(--shiki-token-string);--shiki-dark:var(--shiki-token-string)}html pre.shiki code .scIB-, html code.shiki .scIB-{--shiki-light:var(--shiki-token-comment);--shiki-default:var(--shiki-token-comment);--shiki-dark:var(--shiki-token-comment)}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":336,"searchDepth":371,"depth":371,"links":564},[565,566,567,568,569],{"id":296,"depth":371,"text":297},{"id":313,"depth":371,"text":314},{"id":462,"depth":371,"text":463},{"id":520,"depth":371,"text":521},{"id":527,"depth":371,"text":528},"The RFC 8439 AEAD of TLS 1.3 and WireGuard. ChaCha20 encrypts, Poly1305 tags, and decoding refuses a changed byte. Hex out with a 16-byte tag.","md",null,{},true,{"title":272,"description":570},"mZvhGxx8FxD6HDyt4DSPHiz2F8U8KVgbxdnP8DuQHrU",[578,572],{"title":268,"path":269,"stem":270,"description":579,"children":-1},"ChaCha20 with a 24-byte nonce you can pick at random. HChaCha20 turns the key and 16 nonce bytes into a subkey. UTF-8 in, hex out, no padding.",1791184045868]