[{"data":1,"prerenderedAt":714},["ShallowReactive",2],{"navigation_docs":3,"-ciphers-aes-ocb":160,"-ciphers-aes-ocb-surround":711},[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":156,"body":162,"description":704,"extension":705,"links":706,"meta":707,"navigation":708,"path":157,"seo":709,"stem":158,"__hash__":710},"docs\u002F2.ciphers\u002F29.aes-ocb.md",{"type":163,"value":164,"toc":699},"minimark",[165,169,177,180,352,355,360,367,373,426,429,435,501,504,511,515,534,572,582,586,589,666,669,692,695],[166,167],"cipher-facts",{"name":168},"aes-ocb",[170,171,172,176],"p",{},[173,174,175],"a",{"href":149},"CCM"," gets its tag by running AES over everything twice. Once for the MAC, once for the keystream. OCB does both jobs in one pass. It's Phillip Rogaway's Offset Codebook, the third version, written down in RFC 7253. It's about twice as fast as CCM, and for years the patents kept most people away. Rogaway has since dropped all of them, and now it's in OpenSSL.",[170,178,179],{},"The trick is the offset. Every block gets its own mask, built from the key, the nonce and the block's position. The block is XORed with that mask, goes through AES and gets XORed with the same mask again. So it's a codebook, like ECB, only every block has a different one. The tag comes from AES over the XOR of all plaintext blocks, masked once more and mixed with a hash of the associated data. A short last block doesn't go through AES at all. It's XORed with AES of its offset, like a keystream.",[181,182,187],"pre",{"className":183,"code":184,"language":185,"meta":186,"style":186},"language-ts shiki shiki-themes github-light github-light poimandres","const ocb = create(\"aes-ocb\");\nconst key = \"2b7e151628aed2a6abf7158809cf4f3c\";\nconst nonce = \"000102030405060708090a0b\";\nocb.encode(\"ATTACK AT DAWN\", { key, nonce }).text;\n\u002F\u002F \"d5ae8f2ca0693c898f8e7466703c\" + \"a89edcaab00caca2cf36bfcac3794412\"\nocb.decode(\"d5ae8f2ca0693c898f8e7466703ca89edcaab00caca2cf36bfcac3794412\", { key, nonce }).text;\n\u002F\u002F \"ATTACK AT DAWN\"\n","ts","",[188,189,190,226,247,266,306,313,346],"code",{"__ignoreMap":186},[191,192,195,199,203,206,210,214,218,221,223],"span",{"class":193,"line":194},"line",1,[191,196,198],{"class":197},"s1TYA","const",[191,200,202],{"class":201},"saoiD"," ocb",[191,204,205],{"class":197}," =",[191,207,209],{"class":208},"sULi6"," create",[191,211,213],{"class":212},"sKlNE","(",[191,215,217],{"class":216},"scVjq","\"",[191,219,168],{"class":220},"sQ7BG",[191,222,217],{"class":216},[191,224,225],{"class":212},");\n",[191,227,229,231,234,236,239,242,244],{"class":193,"line":228},2,[191,230,198],{"class":197},[191,232,233],{"class":201}," key",[191,235,205],{"class":197},[191,237,238],{"class":216}," \"",[191,240,241],{"class":220},"2b7e151628aed2a6abf7158809cf4f3c",[191,243,217],{"class":216},[191,245,246],{"class":212},";\n",[191,248,250,252,255,257,259,262,264],{"class":193,"line":249},3,[191,251,198],{"class":197},[191,253,254],{"class":201}," nonce",[191,256,205],{"class":197},[191,258,238],{"class":216},[191,260,261],{"class":220},"000102030405060708090a0b",[191,263,217],{"class":216},[191,265,246],{"class":212},[191,267,269,273,276,279,281,283,286,288,291,293,296,298,301,304],{"class":193,"line":268},4,[191,270,272],{"class":271},"spVlQ","ocb",[191,274,275],{"class":212},".",[191,277,278],{"class":208},"encode",[191,280,213],{"class":212},[191,282,217],{"class":216},[191,284,285],{"class":220},"ATTACK AT DAWN",[191,287,217],{"class":216},[191,289,290],{"class":212},", {",[191,292,233],{"class":271},[191,294,295],{"class":212},",",[191,297,254],{"class":271},[191,299,300],{"class":212}," }).",[191,302,303],{"class":271},"text",[191,305,246],{"class":212},[191,307,309],{"class":193,"line":308},5,[191,310,312],{"class":311},"sjhu3","\u002F\u002F \"d5ae8f2ca0693c898f8e7466703c\" + \"a89edcaab00caca2cf36bfcac3794412\"\n",[191,314,316,318,320,323,325,327,330,332,334,336,338,340,342,344],{"class":193,"line":315},6,[191,317,272],{"class":271},[191,319,275],{"class":212},[191,321,322],{"class":208},"decode",[191,324,213],{"class":212},[191,326,217],{"class":216},[191,328,329],{"class":220},"d5ae8f2ca0693c898f8e7466703ca89edcaab00caca2cf36bfcac3794412",[191,331,217],{"class":216},[191,333,290],{"class":212},[191,335,233],{"class":271},[191,337,295],{"class":212},[191,339,254],{"class":271},[191,341,300],{"class":212},[191,343,303],{"class":271},[191,345,246],{"class":212},[191,347,349],{"class":193,"line":348},7,[191,350,351],{"class":311},"\u002F\u002F \"ATTACK AT DAWN\"\n",[170,353,354],{},"Fourteen bytes of text, fourteen of ciphertext, sixteen of tag. No padding, same as CCM.",[356,357,359],"h2",{"id":358},"the-options","The options",[170,361,362,363,366],{},"The key is 32, 48 or 64 hex digits. ",[188,364,365],{},"nonce"," is required and can be anything from 1 to 15 bytes, so 2 to 30 hex digits. The examples in RFC 7253 all use 12. Unlike CCM, the nonce length doesn't cap the text.",[170,368,369,372],{},[188,370,371],{},"tagLength"," is in bits, 64, 96 or 128, default 128. Those are the three the RFC names. And here's the catch that CCM doesn't have. The tag length goes into the nonce block, so it changes the ciphertext too, not only the tag:",[181,374,376],{"className":183,"code":375,"language":185,"meta":186,"style":186},"ocb.encode(\"ATTACK AT DAWN\", { key, nonce, tagLength: 64 }).text;\n\u002F\u002F \"006442d918150ca6a4daf6903e6b\" + \"6141a3b9b9a68fe8\"\n",[188,377,378,421],{"__ignoreMap":186},[191,379,380,382,384,386,388,390,392,394,396,398,400,402,404,408,411,415,417,419],{"class":193,"line":194},[191,381,272],{"class":271},[191,383,275],{"class":212},[191,385,278],{"class":208},[191,387,213],{"class":212},[191,389,217],{"class":216},[191,391,285],{"class":220},[191,393,217],{"class":216},[191,395,290],{"class":212},[191,397,233],{"class":271},[191,399,295],{"class":212},[191,401,254],{"class":271},[191,403,295],{"class":212},[191,405,407],{"class":406},"snHMy"," tagLength",[191,409,410],{"class":212},":",[191,412,414],{"class":413},"siHFe"," 64",[191,416,300],{"class":212},[191,418,303],{"class":271},[191,420,246],{"class":212},[191,422,423],{"class":193,"line":228},[191,424,425],{"class":311},"\u002F\u002F \"006442d918150ca6a4daf6903e6b\" + \"6141a3b9b9a68fe8\"\n",[170,427,428],{},"Not a single byte in common with the 128-bit version. So cutting a long tag short doesn't give you a valid short one, it gives you an error.",[170,430,431,434],{},[188,432,433],{},"aad"," works like in CCM. It's hex that the tag covers, nobody encrypts, and the output doesn't carry. The receiver needs the same value:",[181,436,438],{"className":183,"code":437,"language":185,"meta":186,"style":186},"const aad = \"46524f4d3a2048512e\"; \u002F\u002F \"FROM: HQ.\" in hex\nocb.encode(\"ATTACK AT DAWN\", { key, nonce, aad }).text;\n\u002F\u002F \"d5ae8f2ca0693c898f8e7466703c\" + \"3540bc18319e6bafb89838f7fa34b4d6\"\n",[188,439,440,462,496],{"__ignoreMap":186},[191,441,442,444,447,449,451,454,456,459],{"class":193,"line":194},[191,443,198],{"class":197},[191,445,446],{"class":201}," aad",[191,448,205],{"class":197},[191,450,238],{"class":216},[191,452,453],{"class":220},"46524f4d3a2048512e",[191,455,217],{"class":216},[191,457,458],{"class":212},"; ",[191,460,461],{"class":311},"\u002F\u002F \"FROM: HQ.\" in hex\n",[191,463,464,466,468,470,472,474,476,478,480,482,484,486,488,490,492,494],{"class":193,"line":228},[191,465,272],{"class":271},[191,467,275],{"class":212},[191,469,278],{"class":208},[191,471,213],{"class":212},[191,473,217],{"class":216},[191,475,285],{"class":220},[191,477,217],{"class":216},[191,479,290],{"class":212},[191,481,233],{"class":271},[191,483,295],{"class":212},[191,485,254],{"class":271},[191,487,295],{"class":212},[191,489,446],{"class":271},[191,491,300],{"class":212},[191,493,303],{"class":271},[191,495,246],{"class":212},[191,497,498],{"class":193,"line":249},[191,499,500],{"class":311},"\u002F\u002F \"d5ae8f2ca0693c898f8e7466703c\" + \"3540bc18319e6bafb89838f7fa34b4d6\"\n",[170,502,503],{},"The text bytes stay the same, only the tag moves. The associated data goes through its own hash, and that hash lands only in the tag.",[170,505,506,507,510],{},"The reference is Node's ",[188,508,509],{},"createCipheriv(\"aes-128-ocb\")",", OpenSSL underneath, and it gives the same bytes. The mode is also checked against the sample results in RFC 7253 Appendix A, the iterated one included, for all nine named parameter sets.",[356,512,514],{"id":513},"flipping-bits","Flipping bits",[170,516,517,518,521,522,525,526,529,530,533],{},"The ",[188,519,520],{},"DAWN"," to ",[188,523,524],{},"DUSK"," trick from ",[173,527,528],{"href":145},"CTR",", XOR ",[188,531,532],{},"00140405"," into the right place:",[181,535,537],{"className":183,"code":536,"language":185,"meta":186,"style":186},"ocb.decode(\"d5ae8f2ca0693c898f8e74727439a89edcaab00caca2cf36bfcac3794412\", { key, nonce });\n\u002F\u002F CipherError: [aes-ocb] Tag does not match: wrong key, nonce, aad or tagLength, or the ciphertext was changed\n",[188,538,539,567],{"__ignoreMap":186},[191,540,541,543,545,547,549,551,554,556,558,560,562,564],{"class":193,"line":194},[191,542,272],{"class":271},[191,544,275],{"class":212},[191,546,322],{"class":208},[191,548,213],{"class":212},[191,550,217],{"class":216},[191,552,553],{"class":220},"d5ae8f2ca0693c898f8e74727439a89edcaab00caca2cf36bfcac3794412",[191,555,217],{"class":216},[191,557,290],{"class":212},[191,559,233],{"class":271},[191,561,295],{"class":212},[191,563,254],{"class":271},[191,565,566],{"class":212}," });\n",[191,568,569],{"class":193,"line":228},[191,570,571],{"class":311},"\u002F\u002F CipherError: [aes-ocb] Tag does not match: wrong key, nonce, aad or tagLength, or the ciphertext was changed\n",[170,573,574,575,578,579,581],{},"Those fourteen bytes are a short last block, a keystream, so they do decrypt to ",[188,576,577],{},"ATTACK AT DUSK",". ",[188,580,322],{}," just never shows it. The tag covers the plaintext, and a new tag needs the key.",[356,583,585],{"id":584},"same-nonce-twice","Same nonce twice",[170,587,588],{},"This one hurts OCB more than CCM. Same key and nonce means the same offsets. For a short block that's the same keystream, and XOR of two ciphertexts is XOR of two texts, like in CTR. For whole blocks it's the same codebook, so an equal block at the same position comes out equal:",[181,590,592],{"className":183,"code":591,"language":185,"meta":186,"style":186},"ocb.encode(\"ATTACK AT DAWN!!ATTACK AT DAWN!!\", { key, nonce }).text;\n\u002F\u002F \"14a72cedef94a49f1beb3c87797ef1a6\" + \"5b6f33e68b4cb242e94f7067c115b888\" + tag\nocb.encode(\"ATTACK AT DAWN!!RETREAT AT NOON!\", { key, nonce }).text;\n\u002F\u002F \"14a72cedef94a49f1beb3c87797ef1a6\" + \"8e5aa111385224ba68acd9d30701144b\" + tag\n",[188,593,594,625,630,661],{"__ignoreMap":186},[191,595,596,598,600,602,604,606,609,611,613,615,617,619,621,623],{"class":193,"line":194},[191,597,272],{"class":271},[191,599,275],{"class":212},[191,601,278],{"class":208},[191,603,213],{"class":212},[191,605,217],{"class":216},[191,607,608],{"class":220},"ATTACK AT DAWN!!ATTACK AT DAWN!!",[191,610,217],{"class":216},[191,612,290],{"class":212},[191,614,233],{"class":271},[191,616,295],{"class":212},[191,618,254],{"class":271},[191,620,300],{"class":212},[191,622,303],{"class":271},[191,624,246],{"class":212},[191,626,627],{"class":193,"line":228},[191,628,629],{"class":311},"\u002F\u002F \"14a72cedef94a49f1beb3c87797ef1a6\" + \"5b6f33e68b4cb242e94f7067c115b888\" + tag\n",[191,631,632,634,636,638,640,642,645,647,649,651,653,655,657,659],{"class":193,"line":249},[191,633,272],{"class":271},[191,635,275],{"class":212},[191,637,278],{"class":208},[191,639,213],{"class":212},[191,641,217],{"class":216},[191,643,644],{"class":220},"ATTACK AT DAWN!!RETREAT AT NOON!",[191,646,217],{"class":216},[191,648,290],{"class":212},[191,650,233],{"class":271},[191,652,295],{"class":212},[191,654,254],{"class":271},[191,656,300],{"class":212},[191,658,303],{"class":271},[191,660,246],{"class":212},[191,662,663],{"class":193,"line":268},[191,664,665],{"class":311},"\u002F\u002F \"14a72cedef94a49f1beb3c87797ef1a6\" + \"8e5aa111385224ba68acd9d30701144b\" + tag\n",[170,667,668],{},"Inside one message the two equal blocks look nothing alike, because each has its own position. Across two messages under one nonce, the first block gives it away. RFC 7253 says a reused nonce also leaks what forging a tag needs. So every message gets its own nonce. A counter is fine, it only has to be unique.",[170,670,671,672,675,676,678,679,681,682,685,686,688,689,275],{},"A key that isn't 32, 48 or 64 hex digits is an ",[188,673,674],{},"InvalidOptionError",", so is a nonce outside 2 to 30 hex digits, an ",[188,677,433],{}," that isn't whole bytes of hex, or a ",[188,680,371],{}," other than 64, 96 or 128. A missing key or nonce is a ",[188,683,684],{},"MissingOptionError",". On ",[188,687,322],{},", a ciphertext shorter than the tag or a tag that doesn't match is a ",[188,690,691],{},"CipherError",[170,693,694],{},"Plain TypeScript, not constant time. It's here to show how one AES pass can encrypt and authenticate at once. Not for anything real.",[696,697,698],"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 .snHMy, html code.shiki .snHMy{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#ADD7FF}html pre.shiki code .siHFe, html code.shiki .siHFe{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#5DE4C7}",{"title":186,"searchDepth":228,"depth":228,"links":700},[701,702,703],{"id":358,"depth":228,"text":359},{"id":513,"depth":228,"text":514},{"id":584,"depth":228,"text":585},"AES in OCB mode, RFC 7253. One AES call per block encrypts and authenticates at once, and decoding refuses anything that was changed. UTF-8 text in, hex out, text bytes plus the tag.","md",null,{},true,{"title":156,"description":704},"7JAI2ifBheW5Fkci81xxnxaQE66nj8xkX9m9wbuzzDc",[712,706],{"title":152,"path":153,"stem":154,"description":713,"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.",1790275934142]