[{"data":1,"prerenderedAt":653},["ShallowReactive",2],{"navigation_docs":3,"-ciphers-aes-ccm":160,"-ciphers-aes-ccm-surround":648},[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":159},"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],{"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",{"title":156,"path":157,"stem":158},"AES (OCB)","\u002Fciphers\u002Faes-ocb","2.ciphers\u002F29.aes-ocb","i-solar-library-linear",{"id":161,"title":148,"body":162,"description":641,"extension":642,"links":643,"meta":644,"navigation":645,"path":149,"seo":646,"stem":150,"__hash__":647},"docs\u002F2.ciphers\u002F27.aes-ccm.md",{"type":163,"value":164,"toc":636},"minimark",[165,169,173,181,188,358,361,366,373,379,388,454,460,470,474,489,527,546,550,553,593,606,629,632],[166,167],"cipher-facts",{"name":168},"aes-ccm",[170,171,172],"p",{},"Every other AES mode here decrypts whatever you hand it. Flip a bit and you get a flipped bit back, or garbage, and nobody tells you. CCM is the first one that says no. It's Counter with CBC-MAC, from NIST SP 800-38C and RFC 3610, and it's what Wi-Fi (WPA2) and Bluetooth LE encrypt with.",[170,174,175,176,180],{},"It's two AES passes under one key. The first is a CBC-MAC. AES runs over a header block with the nonce and the length, then over the associated data, then over the text, each block XORed with the last result. What's left at the end is the tag. The second pass is plain ",[177,178,179],"a",{"href":145},"CTR",". The counter block holds the nonce, counter 1 encrypts the text and counter 0 encrypts the tag. Out comes the ciphertext with the tag glued to its end.",[170,182,183,187],{},[184,185,186],"code",{},"decode"," goes the other way. It decrypts, runs the CBC-MAC again and compares. If the tag doesn't match, you get an error and no text at all.",[189,190,195],"pre",{"className":191,"code":192,"language":193,"meta":194,"style":194},"language-ts shiki shiki-themes github-light github-light poimandres","const ccm = create(\"aes-ccm\");\nconst key = \"2b7e151628aed2a6abf7158809cf4f3c\";\nconst nonce = \"000102030405060708090a0b\";\nccm.encode(\"ATTACK AT DAWN\", { key, nonce }).text;\n\u002F\u002F \"9038dc3aa03594330d2d4dca3cb9\" + \"f3d0bc51521e4e075cf5099b1b92fa10\"\nccm.decode(\"9038dc3aa03594330d2d4dca3cb9f3d0bc51521e4e075cf5099b1b92fa10\", { key, nonce }).text;\n\u002F\u002F \"ATTACK AT DAWN\"\n","ts","",[184,196,197,233,254,273,313,320,352],{"__ignoreMap":194},[198,199,202,206,210,213,217,221,225,228,230],"span",{"class":200,"line":201},"line",1,[198,203,205],{"class":204},"s1TYA","const",[198,207,209],{"class":208},"saoiD"," ccm",[198,211,212],{"class":204}," =",[198,214,216],{"class":215},"sULi6"," create",[198,218,220],{"class":219},"sKlNE","(",[198,222,224],{"class":223},"scVjq","\"",[198,226,168],{"class":227},"sQ7BG",[198,229,224],{"class":223},[198,231,232],{"class":219},");\n",[198,234,236,238,241,243,246,249,251],{"class":200,"line":235},2,[198,237,205],{"class":204},[198,239,240],{"class":208}," key",[198,242,212],{"class":204},[198,244,245],{"class":223}," \"",[198,247,248],{"class":227},"2b7e151628aed2a6abf7158809cf4f3c",[198,250,224],{"class":223},[198,252,253],{"class":219},";\n",[198,255,257,259,262,264,266,269,271],{"class":200,"line":256},3,[198,258,205],{"class":204},[198,260,261],{"class":208}," nonce",[198,263,212],{"class":204},[198,265,245],{"class":223},[198,267,268],{"class":227},"000102030405060708090a0b",[198,270,224],{"class":223},[198,272,253],{"class":219},[198,274,276,280,283,286,288,290,293,295,298,300,303,305,308,311],{"class":200,"line":275},4,[198,277,279],{"class":278},"spVlQ","ccm",[198,281,282],{"class":219},".",[198,284,285],{"class":215},"encode",[198,287,220],{"class":219},[198,289,224],{"class":223},[198,291,292],{"class":227},"ATTACK AT DAWN",[198,294,224],{"class":223},[198,296,297],{"class":219},", {",[198,299,240],{"class":278},[198,301,302],{"class":219},",",[198,304,261],{"class":278},[198,306,307],{"class":219}," }).",[198,309,310],{"class":278},"text",[198,312,253],{"class":219},[198,314,316],{"class":200,"line":315},5,[198,317,319],{"class":318},"sjhu3","\u002F\u002F \"9038dc3aa03594330d2d4dca3cb9\" + \"f3d0bc51521e4e075cf5099b1b92fa10\"\n",[198,321,323,325,327,329,331,333,336,338,340,342,344,346,348,350],{"class":200,"line":322},6,[198,324,279],{"class":278},[198,326,282],{"class":219},[198,328,186],{"class":215},[198,330,220],{"class":219},[198,332,224],{"class":223},[198,334,335],{"class":227},"9038dc3aa03594330d2d4dca3cb9f3d0bc51521e4e075cf5099b1b92fa10",[198,337,224],{"class":223},[198,339,297],{"class":219},[198,341,240],{"class":278},[198,343,302],{"class":219},[198,345,261],{"class":278},[198,347,307],{"class":219},[198,349,310],{"class":278},[198,351,253],{"class":219},[198,353,355],{"class":200,"line":354},7,[198,356,357],{"class":318},"\u002F\u002F \"ATTACK AT DAWN\"\n",[170,359,360],{},"Fourteen bytes of text, fourteen bytes of ciphertext, sixteen of tag. There's no padding, CTR doesn't need any.",[362,363,365],"h2",{"id":364},"the-options","The options",[170,367,368,369,372],{},"The key is 32, 48 or 64 hex digits. ",[184,370,371],{},"nonce"," is required, 7 to 13 bytes, so 14 to 26 hex digits. Its length is a trade. The block has 15 bytes for the nonce and the text length together, so a 13-byte nonce leaves two bytes for the length and caps the text at 65535 bytes. A 7-byte one leaves eight.",[170,374,375,378],{},[184,376,377],{},"tagLength"," is in bits, 32 to 128 in steps of 16, default 128. A shorter tag is cheaper to forge. With 32 bits a random guess passes once in about four billion tries, which is fine for a sensor on a radio and not much else.",[170,380,381,384,385,387],{},[184,382,383],{},"aad"," is associated data, in hex. It's covered by the tag but not encrypted and not in the output. A packet header is the usual case. The router has to read it, but nobody should be able to change it. The receiver needs the same ",[184,386,383],{},", or the tag fails:",[189,389,391],{"className":191,"code":390,"language":193,"meta":194,"style":194},"const aad = \"46524f4d3a2048512e\"; \u002F\u002F \"FROM: HQ.\" in hex\nccm.encode(\"ATTACK AT DAWN\", { key, nonce, aad }).text;\n\u002F\u002F \"9038dc3aa03594330d2d4dca3cb9a5038633d04da4a01f58149f30e75d5b\"\n",[184,392,393,415,449],{"__ignoreMap":194},[198,394,395,397,400,402,404,407,409,412],{"class":200,"line":201},[198,396,205],{"class":204},[198,398,399],{"class":208}," aad",[198,401,212],{"class":204},[198,403,245],{"class":223},[198,405,406],{"class":227},"46524f4d3a2048512e",[198,408,224],{"class":223},[198,410,411],{"class":219},"; ",[198,413,414],{"class":318},"\u002F\u002F \"FROM: HQ.\" in hex\n",[198,416,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447],{"class":200,"line":235},[198,418,279],{"class":278},[198,420,282],{"class":219},[198,422,285],{"class":215},[198,424,220],{"class":219},[198,426,224],{"class":223},[198,428,292],{"class":227},[198,430,224],{"class":223},[198,432,297],{"class":219},[198,434,240],{"class":278},[198,436,302],{"class":219},[198,438,261],{"class":278},[198,440,302],{"class":219},[198,442,399],{"class":278},[198,444,307],{"class":219},[198,446,310],{"class":278},[198,448,253],{"class":219},[198,450,451],{"class":200,"line":256},[198,452,453],{"class":318},"\u002F\u002F \"9038dc3aa03594330d2d4dca3cb9a5038633d04da4a01f58149f30e75d5b\"\n",[170,455,456,457,459],{},"The first fourteen bytes didn't change. Only the tag did. The text still goes through the same keystream, and ",[184,458,383],{}," only feeds the MAC.",[170,461,462,465,466,469],{},[184,463,464],{},"openssl enc"," doesn't do AEAD modes, so the reference here is Node's ",[184,467,468],{},"createCipheriv(\"aes-128-ccm\")",", which is OpenSSL underneath. It gives the same bytes. The mode is tested against the three examples from NIST SP 800-38C Appendix C and packet vector #1 from RFC 3610.",[362,471,473],{"id":472},"flipping-bits-again","Flipping bits, again",[170,475,476,477,480,481,484,485,488],{},"In CTR, ",[184,478,479],{},"DAWN"," became ",[184,482,483],{},"DUSK"," by XORing ",[184,486,487],{},"00140405"," into the right place. Same trick here:",[189,490,492],{"className":191,"code":491,"language":193,"meta":194,"style":194},"ccm.decode(\"9038dc3aa03594330d2d4dde38bcf3d0bc51521e4e075cf5099b1b92fa10\", { key, nonce });\n\u002F\u002F CipherError: [aes-ccm] Tag does not match: wrong key, nonce, aad or tagLength, or the ciphertext was changed\n",[184,493,494,522],{"__ignoreMap":194},[198,495,496,498,500,502,504,506,509,511,513,515,517,519],{"class":200,"line":201},[198,497,279],{"class":278},[198,499,282],{"class":219},[198,501,186],{"class":215},[198,503,220],{"class":219},[198,505,224],{"class":223},[198,507,508],{"class":227},"9038dc3aa03594330d2d4dde38bcf3d0bc51521e4e075cf5099b1b92fa10",[198,510,224],{"class":223},[198,512,297],{"class":219},[198,514,240],{"class":278},[198,516,302],{"class":219},[198,518,261],{"class":278},[198,520,521],{"class":219}," });\n",[198,523,524],{"class":200,"line":235},[198,525,526],{"class":318},"\u002F\u002F CipherError: [aes-ccm] Tag does not match: wrong key, nonce, aad or tagLength, or the ciphertext was changed\n",[170,528,529,530,533,534,536,537,539,540,542,543,545],{},"The keystream part still decrypts to ",[184,531,532],{},"ATTACK AT DUSK",", but ",[184,535,186],{}," never shows it. The tag was computed over ",[184,538,479],{},", and faking a new one needs the key. A wrong key, a wrong nonce, a different ",[184,541,383],{}," or ",[184,544,377],{}," all end the same way. That's the whole point.",[362,547,549],{"id":548},"same-nonce-twice","Same nonce twice",[170,551,552],{},"The tag doesn't save you from this one. CCM encrypts with CTR, so one nonce used for two messages gives one keystream, and XOR of the ciphertexts is XOR of the texts:",[189,554,556],{"className":191,"code":555,"language":193,"meta":194,"style":194},"ccm.encode(\"ATTACK AT DUSK\", { key, nonce }).text;\n\u002F\u002F \"9038dc3aa03594330d2d4dde38bc\" + \"b5b40a703a9773be7cec69f7c601bfe6\"\n",[184,557,558,588],{"__ignoreMap":194},[198,559,560,562,564,566,568,570,572,574,576,578,580,582,584,586],{"class":200,"line":201},[198,561,279],{"class":278},[198,563,282],{"class":219},[198,565,285],{"class":215},[198,567,220],{"class":219},[198,569,224],{"class":223},[198,571,532],{"class":227},[198,573,224],{"class":223},[198,575,297],{"class":219},[198,577,240],{"class":278},[198,579,302],{"class":219},[198,581,261],{"class":278},[198,583,307],{"class":219},[198,585,310],{"class":278},[198,587,253],{"class":219},[198,589,590],{"class":200,"line":235},[198,591,592],{"class":318},"\u002F\u002F \"9038dc3aa03594330d2d4dde38bc\" + \"b5b40a703a9773be7cec69f7c601bfe6\"\n",[170,594,595,596,598,599,602,603,605],{},"The first eleven bytes match ",[184,597,479],{}," exactly, ",[184,600,601],{},"ATTACK AT D",", and the next three carry the ",[184,604,483],{}," difference. Every message needs its own nonce. A counter works fine, the nonce only has to be unique, not random.",[170,607,608,609,612,613,615,616,618,619,622,623,625,626,282],{},"A key that isn't 32, 48 or 64 hex digits is an ",[184,610,611],{},"InvalidOptionError",", so is a nonce outside 14 to 26 hex digits, an ",[184,614,383],{}," that isn't whole bytes of hex, or a ",[184,617,377],{}," off the list. A missing key or nonce is a ",[184,620,621],{},"MissingOptionError",". On ",[184,624,186],{},", a ciphertext shorter than the tag or a tag that doesn't match is a ",[184,627,628],{},"CipherError",[170,630,631],{},"Plain TypeScript, not constant time. It's here to show what an authentication tag buys you and what it doesn't. Not for anything real.",[633,634,635],"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);}",{"title":194,"searchDepth":235,"depth":235,"links":637},[638,639,640],{"id":364,"depth":235,"text":365},{"id":472,"depth":235,"text":473},{"id":548,"depth":235,"text":549},"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.","md",null,{},true,{"title":148,"description":641},"1FHRlg8cFC6Jr9TVyUvtlPd4fe7V8qWc2RgUzE0hupw",[649,651],{"title":144,"path":145,"stem":146,"description":650,"children":-1},"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.",{"title":152,"path":153,"stem":154,"description":652,"children":-1},"AES encrypting its own output. The IV goes in, every result goes back in for the next block, and the whole chain gets XORed into the text. UTF-8 text in, hex out, no padding, and decrypting is the same step.",1790275934052]