[{"data":1,"prerenderedAt":635},["ShallowReactive",2],{"navigation_docs":3,"-ciphers-aes-ctr":156,"-ciphers-aes-ctr-surround":630},[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-solar-book-2-linear",{"title":37,"path":38,"stem":39,"children":40,"icon":155},"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],{"title":42,"path":38,"stem":39},"Overview",{"title":44,"path":45,"stem":46},"Caesar","\u002Fciphers\u002Fcaesar","2.ciphers\u002F01.caesar",{"title":48,"path":49,"stem":50},"ROT-13","\u002Fciphers\u002Frot13","2.ciphers\u002F02.rot13",{"title":52,"path":53,"stem":54},"ROT-47","\u002Fciphers\u002Frot47","2.ciphers\u002F03.rot47",{"title":56,"path":57,"stem":58},"Atbash","\u002Fciphers\u002Fatbash","2.ciphers\u002F04.atbash",{"title":60,"path":61,"stem":62},"Affine","\u002Fciphers\u002Faffine","2.ciphers\u002F05.affine",{"title":64,"path":65,"stem":66},"Vigenère","\u002Fciphers\u002Fvigenere","2.ciphers\u002F06.vigenere",{"title":68,"path":69,"stem":70},"Trithemius","\u002Fciphers\u002Ftrithemius","2.ciphers\u002F07.trithemius",{"title":72,"path":73,"stem":74},"Alberti","\u002Fciphers\u002Falberti","2.ciphers\u002F08.alberti",{"title":76,"path":77,"stem":78},"Playfair","\u002Fciphers\u002Fplayfair","2.ciphers\u002F09.playfair",{"title":80,"path":81,"stem":82},"Polybius square","\u002Fciphers\u002Fpolybius","2.ciphers\u002F10.polybius",{"title":84,"path":85,"stem":86},"Morse code","\u002Fciphers\u002Fmorse","2.ciphers\u002F11.morse",{"title":88,"path":89,"stem":90},"Bacon's cipher","\u002Fciphers\u002Fbacon","2.ciphers\u002F12.bacon",{"title":92,"path":93,"stem":94},"Tap code","\u002Fciphers\u002Ftap-code","2.ciphers\u002F13.tap-code",{"title":96,"path":97,"stem":98},"ADFGVX","\u002Fciphers\u002Fadfgvx","2.ciphers\u002F14.adfgvx",{"title":100,"path":101,"stem":102},"Bifid","\u002Fciphers\u002Fbifid","2.ciphers\u002F15.bifid",{"title":104,"path":105,"stem":106},"Rail fence","\u002Fciphers\u002Frail-fence","2.ciphers\u002F16.rail-fence",{"title":108,"path":109,"stem":110},"Columnar transposition","\u002Fciphers\u002Fcolumnar","2.ciphers\u002F17.columnar",{"title":112,"path":113,"stem":114},"Enigma M3","\u002Fciphers\u002Fenigma","2.ciphers\u002F18.enigma",{"title":116,"path":117,"stem":118},"Beaufort","\u002Fciphers\u002Fbeaufort","2.ciphers\u002F19.beaufort",{"title":120,"path":121,"stem":122},"Autokey","\u002Fciphers\u002Fautokey","2.ciphers\u002F20.autokey",{"title":124,"path":125,"stem":126},"AES (ECB)","\u002Fciphers\u002Faes","2.ciphers\u002F21.aes",{"title":128,"path":129,"stem":130},"Triple DES (ECB)","\u002Fciphers\u002Ftriple-des","2.ciphers\u002F22.triple-des",{"title":132,"path":133,"stem":134},"AES (LRW)","\u002Fciphers\u002Faes-lrw","2.ciphers\u002F23.aes-lrw",{"title":136,"path":137,"stem":138},"AES (CBC)","\u002Fciphers\u002Faes-cbc","2.ciphers\u002F24.aes-cbc",{"title":140,"path":141,"stem":142},"AES (CFB)","\u002Fciphers\u002Faes-cfb","2.ciphers\u002F25.aes-cfb",{"title":144,"path":145,"stem":146},"AES (CTR)","\u002Fciphers\u002Faes-ctr","2.ciphers\u002F26.aes-ctr",{"title":148,"path":149,"stem":150},"AES (CCM)","\u002Fciphers\u002Faes-ccm","2.ciphers\u002F27.aes-ccm",{"title":152,"path":153,"stem":154},"AES (OFB)","\u002Fciphers\u002Faes-ofb","2.ciphers\u002F28.aes-ofb","i-solar-library-linear",{"id":157,"title":144,"body":158,"description":623,"extension":624,"links":625,"meta":626,"navigation":627,"path":145,"seo":628,"stem":146,"__hash__":629},"docs\u002F2.ciphers\u002F26.aes-ctr.md",{"type":159,"value":160,"toc":619},"minimark",[161,165,174,184,187,197,362,370,427,430,435,450,491,502,506,512,588,591,612,615],[162,163],"cipher-facts",{"name":164},"aes-ctr",[166,167,168,169,173],"p",{},"CTR is counter mode, from NIST SP 800-38A like CBC and CFB. It's the simplest way to turn AES into a stream cipher. Start with a 16-byte counter block, that's ",[170,171,172],"code",{},"iv",". AES encrypts it, and the result gets XORed into the first 16 bytes of text. Then the counter goes up by one, and AES encrypts that for the next 16 bytes. Nothing from the text ever goes back in.",[166,175,176,177,180,181,183],{},"The counter is the whole block read as one 128-bit number. Thirty-two ",[170,178,179],{},"f","s plus one is thirty-two zeros, same as in OpenSSL. Some protocols split the block into a nonce and a shorter counter. Here the nonce is just whatever the high bytes of ",[170,182,172],{}," are.",[166,185,186],{},"No feedback means a few nice things. Encrypting and decrypting are the same XOR, so AES only runs forward. There's no padding, fourteen bytes of text give fourteen bytes of ciphertext. And any block can be computed on its own, you don't need the ones before it. GCM, the mode TLS uses, is CTR with an authentication tag on top.",[166,188,189,190,192,193,196],{},"The key is 32, 48 or 64 hex digits, ",[170,191,172],{}," is 32 and it's required. ",[170,194,195],{},"decode"," wants hex back, any whole number of bytes.",[198,199,204],"pre",{"className":200,"code":201,"language":202,"meta":203,"style":203},"language-ts shiki shiki-themes github-light github-light poimandres","const ctr = create(\"aes-ctr\");\nconst key = \"2b7e151628aed2a6abf7158809cf4f3c\";\nconst iv = \"000102030405060708090a0b0c0d0e0f\";\nctr.encode(\"ATTACK AT DAWN\", { key, iv }).text; \u002F\u002F \"11aa338dda2612f78e2973a8cce1\"\nctr.decode(\"11aa338dda2612f78e2973a8cce1\", { key, iv }).text; \u002F\u002F \"ATTACK AT DAWN\"\n","ts","",[170,205,206,242,263,282,327],{"__ignoreMap":203},[207,208,211,215,219,222,226,230,234,237,239],"span",{"class":209,"line":210},"line",1,[207,212,214],{"class":213},"s1TYA","const",[207,216,218],{"class":217},"saoiD"," ctr",[207,220,221],{"class":213}," =",[207,223,225],{"class":224},"sULi6"," create",[207,227,229],{"class":228},"sKlNE","(",[207,231,233],{"class":232},"scVjq","\"",[207,235,164],{"class":236},"sQ7BG",[207,238,233],{"class":232},[207,240,241],{"class":228},");\n",[207,243,245,247,250,252,255,258,260],{"class":209,"line":244},2,[207,246,214],{"class":213},[207,248,249],{"class":217}," key",[207,251,221],{"class":213},[207,253,254],{"class":232}," \"",[207,256,257],{"class":236},"2b7e151628aed2a6abf7158809cf4f3c",[207,259,233],{"class":232},[207,261,262],{"class":228},";\n",[207,264,266,268,271,273,275,278,280],{"class":209,"line":265},3,[207,267,214],{"class":213},[207,269,270],{"class":217}," iv",[207,272,221],{"class":213},[207,274,254],{"class":232},[207,276,277],{"class":236},"000102030405060708090a0b0c0d0e0f",[207,279,233],{"class":232},[207,281,262],{"class":228},[207,283,285,289,292,295,297,299,302,304,307,309,312,314,317,320,323],{"class":209,"line":284},4,[207,286,288],{"class":287},"spVlQ","ctr",[207,290,291],{"class":228},".",[207,293,294],{"class":224},"encode",[207,296,229],{"class":228},[207,298,233],{"class":232},[207,300,301],{"class":236},"ATTACK AT DAWN",[207,303,233],{"class":232},[207,305,306],{"class":228},", {",[207,308,249],{"class":287},[207,310,311],{"class":228},",",[207,313,270],{"class":287},[207,315,316],{"class":228}," }).",[207,318,319],{"class":287},"text",[207,321,322],{"class":228},"; ",[207,324,326],{"class":325},"sjhu3","\u002F\u002F \"11aa338dda2612f78e2973a8cce1\"\n",[207,328,330,332,334,336,338,340,343,345,347,349,351,353,355,357,359],{"class":209,"line":329},5,[207,331,288],{"class":287},[207,333,291],{"class":228},[207,335,195],{"class":224},[207,337,229],{"class":228},[207,339,233],{"class":232},[207,341,342],{"class":236},"11aa338dda2612f78e2973a8cce1",[207,344,233],{"class":232},[207,346,306],{"class":228},[207,348,249],{"class":287},[207,350,311],{"class":228},[207,352,270],{"class":287},[207,354,316],{"class":228},[207,356,319],{"class":287},[207,358,322],{"class":228},[207,360,361],{"class":325},"\u002F\u002F \"ATTACK AT DAWN\"\n",[166,363,364,365,369],{},"Looks familiar? It's byte for byte what ",[366,367,368],"a",{"href":141},"CFB"," gives for this text. Both XOR the first block with AES over the IV. From the second block on they split, CFB encrypts the ciphertext and CTR encrypts the counter. OpenSSL agrees:",[198,371,375],{"className":372,"code":373,"language":374,"meta":203,"style":203},"language-bash shiki shiki-themes github-light github-light poimandres","printf %s \"ATTACK AT DAWN\" | openssl enc -aes-128-ctr -K 2b7e151628aed2a6abf7158809cf4f3c -iv 000102030405060708090a0b0c0d0e0f | xxd -p\n","bash",[170,376,377],{"__ignoreMap":203},[207,378,379,383,387,389,391,393,396,400,403,407,410,413,416,419,421,424],{"class":209,"line":210},[207,380,382],{"class":381},"s39Ir","printf",[207,384,386],{"class":385},"sX7Zv"," %s",[207,388,254],{"class":232},[207,390,301],{"class":236},[207,392,233],{"class":232},[207,394,395],{"class":213}," |",[207,397,399],{"class":398},"sseY5"," openssl",[207,401,402],{"class":385}," enc",[207,404,406],{"class":405},"sqT1Y"," -aes-128-ctr",[207,408,409],{"class":405}," -K",[207,411,412],{"class":385}," 2b7e151628aed2a6abf7158809cf4f3c",[207,414,415],{"class":405}," -iv",[207,417,418],{"class":385}," 000102030405060708090a0b0c0d0e0f",[207,420,395],{"class":213},[207,422,423],{"class":398}," xxd",[207,425,426],{"class":405}," -p\n",[166,428,429],{},"The mode is tested against the CTR-AES128, CTR-AES192 and CTR-AES256 vectors from NIST SP 800-38A, F.5.",[431,432,434],"h2",{"id":433},"flipping-bits","Flipping bits",[166,436,437,438,441,442,445,446,449],{},"A flipped ciphertext bit flips the same plaintext bit, and that's all it does. ",[170,439,440],{},"DAWN"," and ",[170,443,444],{},"DUSK"," differ by ",[170,447,448],{},"00140405",":",[198,451,453],{"className":200,"code":452,"language":202,"meta":203,"style":203},"ctr.decode(\"11aa338dda2612f78e2973bcc8e4\", { key, iv }).text;\n\u002F\u002F \"ATTACK AT DUSK\"\n",[170,454,455,486],{"__ignoreMap":203},[207,456,457,459,461,463,465,467,470,472,474,476,478,480,482,484],{"class":209,"line":210},[207,458,288],{"class":287},[207,460,291],{"class":228},[207,462,195],{"class":224},[207,464,229],{"class":228},[207,466,233],{"class":232},[207,468,469],{"class":236},"11aa338dda2612f78e2973bcc8e4",[207,471,233],{"class":232},[207,473,306],{"class":228},[207,475,249],{"class":287},[207,477,311],{"class":228},[207,479,270],{"class":287},[207,481,316],{"class":228},[207,483,319],{"class":287},[207,485,262],{"class":228},[207,487,488],{"class":209,"line":244},[207,489,490],{"class":325},"\u002F\u002F \"ATTACK AT DUSK\"\n",[166,492,493,494,497,498,501],{},"In CFB this only worked cleanly in the last segment. Here it works anywhere. Flip the same bytes in ",[170,495,496],{},"ATTACK AT DAWN, RETREAT AT DUSK."," and you get ",[170,499,500],{},"ATTACK AT DUSK, RETREAT AT DUSK.",", no garbage in the next block. CFB throws on the same edit, the second block comes out as bytes that aren't UTF-8.",[431,503,505],{"id":504},"same-iv-twice","Same IV twice",[166,507,508,509,511],{},"This is the one that actually hurts. Same key and same ",[170,510,172],{}," means the same keystream. XOR two ciphertexts and the keystream cancels out, what's left is the XOR of the two plaintexts:",[198,513,515],{"className":200,"code":514,"language":202,"meta":203,"style":203},"ctr.encode(\"ATTACK AT DAWN\", { key, iv }).text; \u002F\u002F \"11aa338dda2612f78e2973a8cce1\"\nctr.encode(\"ATTACK AT DUSK\", { key, iv }).text; \u002F\u002F \"11aa338dda2612f78e2973bcc8e4\"\n\u002F\u002F XOR: \"0000000000000000000000140405\"\n",[170,516,517,549,583],{"__ignoreMap":203},[207,518,519,521,523,525,527,529,531,533,535,537,539,541,543,545,547],{"class":209,"line":210},[207,520,288],{"class":287},[207,522,291],{"class":228},[207,524,294],{"class":224},[207,526,229],{"class":228},[207,528,233],{"class":232},[207,530,301],{"class":236},[207,532,233],{"class":232},[207,534,306],{"class":228},[207,536,249],{"class":287},[207,538,311],{"class":228},[207,540,270],{"class":287},[207,542,316],{"class":228},[207,544,319],{"class":287},[207,546,322],{"class":228},[207,548,326],{"class":325},[207,550,551,553,555,557,559,561,564,566,568,570,572,574,576,578,580],{"class":209,"line":244},[207,552,288],{"class":287},[207,554,291],{"class":228},[207,556,294],{"class":224},[207,558,229],{"class":228},[207,560,233],{"class":232},[207,562,563],{"class":236},"ATTACK AT DUSK",[207,565,233],{"class":232},[207,567,306],{"class":228},[207,569,249],{"class":287},[207,571,311],{"class":228},[207,573,270],{"class":287},[207,575,316],{"class":228},[207,577,319],{"class":287},[207,579,322],{"class":228},[207,581,582],{"class":325},"\u002F\u002F \"11aa338dda2612f78e2973bcc8e4\"\n",[207,584,585],{"class":209,"line":265},[207,586,587],{"class":325},"\u002F\u002F XOR: \"0000000000000000000000140405\"\n",[166,589,590],{},"No key needed. Guess a bit of one message and you read the same bit of the other. It's the two-time pad, and plenty of real CTR and GCM code got broken exactly this way. Every message needs its own counter range.",[166,592,593,594,597,598,600,601,604,605,607,608,611],{},"A key that isn't 32, 48 or 64 hex digits is an ",[170,595,596],{},"InvalidOptionError",", so is an ",[170,599,172],{}," that isn't 32 hex digits. A missing key or IV is a ",[170,602,603],{},"MissingOptionError",". On ",[170,606,195],{},", an odd number of hex digits is a ",[170,609,610],{},"CipherError",". No padding means only the UTF-8 check catches a wrong key. Most wrong keys fail it, a short message can still come out as valid garbage.",[166,613,614],{},"No integrity check, plain TypeScript, not constant time. For puzzles and for seeing why nonces matter. Don't protect anything real with it.",[616,617,618],"style",{},"html pre.shiki code .s1TYA, html code.shiki .s1TYA{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#91B4D5}html pre.shiki code .saoiD, html code.shiki .saoiD{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#E4F0FB}html pre.shiki code .sULi6, html code.shiki .sULi6{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#E4F0FBD0}html pre.shiki code .sKlNE, html code.shiki .sKlNE{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#A6ACCD}html pre.shiki code .scVjq, html code.shiki .scVjq{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#A6ACCD}html pre.shiki code .sQ7BG, html code.shiki .sQ7BG{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#5DE4C7}html pre.shiki code .spVlQ, html code.shiki .spVlQ{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E4F0FB}html pre.shiki code .sjhu3, html code.shiki .sjhu3{--shiki-light:#6A737D;--shiki-light-font-style:inherit;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#767C9DB0;--shiki-dark-font-style:italic}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);}html pre.shiki code .s39Ir, html code.shiki .s39Ir{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#91B4D5}html pre.shiki code .sX7Zv, html code.shiki .sX7Zv{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#ADD7FF}html pre.shiki code .sseY5, html code.shiki .sseY5{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#91B4D5}html pre.shiki code .sqT1Y, html code.shiki .sqT1Y{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#ADD7FF}",{"title":203,"searchDepth":244,"depth":244,"links":620},[621,622],{"id":433,"depth":244,"text":434},{"id":504,"depth":244,"text":505},"AES over a counter. Each block of the keystream is the next counter value run through AES, XORed into the text. UTF-8 text in, hex out, no padding, and decrypting is the same step.","md",null,{},true,{"title":144,"description":623},"QvDNRdz700c9BF_JeVVf4FjRStV8hr-fuzoyqk4uYVw",[631,633],{"title":140,"path":141,"stem":142,"description":632,"children":-1},"AES as a keystream. Every segment of ciphertext is fed back into the register that makes the next one, 1, 8 or 128 bits at a time. UTF-8 text in, hex out, not a byte of padding.",{"title":148,"path":149,"stem":150,"description":634,"children":-1},"AES in CTR mode with a CBC-MAC tag on the end. Decoding checks the tag first and refuses anything that was changed. UTF-8 text in, hex out, text bytes plus the tag.",1790272611143]