[{"data":1,"prerenderedAt":643},["ShallowReactive",2],{"navigation_docs":3,"-guide-verify":148,"-guide-verify-surround":638},[4,40],{"title":5,"path":6,"stem":7,"children":8,"icon":39},"Guide","\u002Fguide","1.guide\u002F01.index",[9,11,15,19,23,27,31,35],{"title":10,"path":6,"stem":7},"Getting Started",{"title":12,"path":13,"stem":14},"Hashing","\u002Fguide\u002Fhashing","1.guide\u002F02.hashing",{"title":16,"path":17,"stem":18},"HMAC and verify","\u002Fguide\u002Fverify","1.guide\u002F03.verify",{"title":20,"path":21,"stem":22},"Salted KDFs","\u002Fguide\u002Fkdf","1.guide\u002F04.kdf",{"title":24,"path":25,"stem":26},"CLI","\u002Fguide\u002Fcli","1.guide\u002F05.cli",{"title":28,"path":29,"stem":30},"Agents","\u002Fguide\u002Fagents","1.guide\u002F06.agents",{"title":32,"path":33,"stem":34},"Custom algorithms","\u002Fguide\u002Fcustom","1.guide\u002F07.custom",{"title":36,"path":37,"stem":38},"Playground","\u002Fguide\u002Fplayground","1.guide\u002F08.playground","i-lucide-book-open",{"title":41,"path":42,"stem":43,"children":44,"icon":147},"Algorithms","\u002Falgorithms","2.algorithms\u002F00.index",[45,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],{"title":46,"path":42,"stem":43},"Every algorithm",{"title":48,"path":49,"stem":50},"SHA-256","\u002Falgorithms\u002Fsha256","2.algorithms\u002F01.sha256",{"title":52,"path":53,"stem":54},"SHA-384","\u002Falgorithms\u002Fsha384","2.algorithms\u002F02.sha384",{"title":56,"path":57,"stem":58},"SHA-512","\u002Falgorithms\u002Fsha512","2.algorithms\u002F03.sha512",{"title":60,"path":61,"stem":62},"SHA-512Half","\u002Falgorithms\u002Fsha512-half","2.algorithms\u002F04.sha512-half",{"title":64,"path":65,"stem":66},"SHA3-256","\u002Falgorithms\u002Fsha3-256","2.algorithms\u002F05.sha3-256",{"title":68,"path":69,"stem":70},"SHA3-512","\u002Falgorithms\u002Fsha3-512","2.algorithms\u002F06.sha3-512",{"title":72,"path":73,"stem":74},"Keccak-256","\u002Falgorithms\u002Fkeccak256","2.algorithms\u002F07.keccak256",{"title":76,"path":77,"stem":78},"BLAKE2b","\u002Falgorithms\u002Fblake2b","2.algorithms\u002F08.blake2b",{"title":80,"path":81,"stem":82},"BLAKE2b-256","\u002Falgorithms\u002Fblake2b-256","2.algorithms\u002F09.blake2b-256",{"title":84,"path":85,"stem":86},"BLAKE2b-224","\u002Falgorithms\u002Fblake2b-224","2.algorithms\u002F10.blake2b-224",{"title":88,"path":89,"stem":90},"BLAKE2s","\u002Falgorithms\u002Fblake2s","2.algorithms\u002F11.blake2s",{"title":92,"path":93,"stem":94},"BLAKE3","\u002Falgorithms\u002Fblake3","2.algorithms\u002F12.blake3",{"title":96,"path":97,"stem":98},"BLAKE-256","\u002Falgorithms\u002Fblake256","2.algorithms\u002F13.blake256",{"title":100,"path":101,"stem":102},"RIPEMD-160","\u002Falgorithms\u002Fripemd160","2.algorithms\u002F14.ripemd160",{"title":104,"path":105,"stem":106},"HASH160","\u002Falgorithms\u002Fhash160","2.algorithms\u002F15.hash160",{"title":108,"path":109,"stem":110},"HASH256","\u002Falgorithms\u002Fhash256","2.algorithms\u002F16.hash256",{"title":112,"path":113,"stem":114},"MD5","\u002Falgorithms\u002Fmd5","2.algorithms\u002F17.md5",{"title":116,"path":117,"stem":118},"SHA-1","\u002Falgorithms\u002Fsha1","2.algorithms\u002F18.sha1",{"title":120,"path":121,"stem":122},"SHA-0","\u002Falgorithms\u002Fsha0","2.algorithms\u002F19.sha0",{"title":124,"path":125,"stem":126},"CRC-32","\u002Falgorithms\u002Fcrc32","2.algorithms\u002F20.crc32",{"title":128,"path":129,"stem":130},"CRC-16\u002FXMODEM","\u002Falgorithms\u002Fcrc16-xmodem","2.algorithms\u002F21.crc16-xmodem",{"title":132,"path":133,"stem":134},"xxHash (XXH64)","\u002Falgorithms\u002Fxxhash","2.algorithms\u002F22.xxhash",{"title":136,"path":137,"stem":138},"FNV-1a (64-bit)","\u002Falgorithms\u002Ffnv1a","2.algorithms\u002F23.fnv1a",{"title":140,"path":141,"stem":142},"scrypt","\u002Falgorithms\u002Fscrypt","2.algorithms\u002F24.scrypt",{"title":144,"path":145,"stem":146},"PBKDF2","\u002Falgorithms\u002Fpbkdf2","2.algorithms\u002F25.pbkdf2","i-lucide-library",{"id":149,"title":16,"body":150,"description":631,"extension":632,"links":633,"meta":634,"navigation":212,"path":17,"seo":636,"stem":18,"__hash__":637},"docs\u002F1.guide\u002F03.verify.md",{"type":151,"value":152,"toc":625},"minimark",[153,158,175,275,290,324,327,338,342,348,480,494,503,507,578,589,595,599,621],[154,155,157],"h2",{"id":156},"hmac-is-a-key-away","HMAC is a key away",[159,160,161,162,166,167,170,171,174],"p",{},"Pass ",[163,164,165],"code",{},"key"," and the same ",[163,168,169],{},"hash()"," computes an HMAC. The result says so in ",[163,172,173],{},"operation",".",[176,177,182],"pre",{"className":178,"code":179,"language":180,"meta":181,"style":181},"language-ts shiki shiki-themes hashes hashes hashes","import { create } from \"@agntn\u002Fhashes\";\n\nconst tag = create(\"sha256\").hash(\"message\", { key: \"secret\" });\ntag.digest; \u002F\u002F \"8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b\"\ntag.operation; \u002F\u002F \"hmac\"\n","ts","",[163,183,184,207,214,256,266],{"__ignoreMap":181},[185,186,189,193,197,200,204],"span",{"class":187,"line":188},"line",1,[185,190,192],{"class":191},"skH_V","import",[185,194,196],{"class":195},"s38Sx"," { create } ",[185,198,199],{"class":191},"from",[185,201,203],{"class":202},"shU9J"," \"@agntn\u002Fhashes\"",[185,205,206],{"class":195},";\n",[185,208,210],{"class":187,"line":209},2,[185,211,213],{"emptyLinePlaceholder":212},true,"\n",[185,215,217,220,223,226,230,233,236,239,242,244,247,250,253],{"class":187,"line":216},3,[185,218,219],{"class":191},"const",[185,221,222],{"class":195}," tag ",[185,224,225],{"class":191},"=",[185,227,229],{"class":228},"sK71F"," create",[185,231,232],{"class":195},"(",[185,234,235],{"class":202},"\"sha256\"",[185,237,238],{"class":195},").",[185,240,241],{"class":228},"hash",[185,243,232],{"class":195},[185,245,246],{"class":202},"\"message\"",[185,248,249],{"class":195},", { key: ",[185,251,252],{"class":202},"\"secret\"",[185,254,255],{"class":195}," });\n",[185,257,259,262],{"class":187,"line":258},4,[185,260,261],{"class":195},"tag.digest; ",[185,263,265],{"class":264},"scIB-","\u002F\u002F \"8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b\"\n",[185,267,269,272],{"class":187,"line":268},5,[185,270,271],{"class":195},"tag.operation; ",[185,273,274],{"class":264},"\u002F\u002F \"hmac\"\n",[159,276,277,278,281,282,285,286,289],{},"The key is text read as UTF-8, or a ",[163,279,280],{},"Uint8Array",". The CLI and the tools can't pass bytes, so a binary key like a BIP32 chain code goes in as hex or base64 with ",[163,283,284],{},"--key-encoding"," or ",[163,287,288],{},"keyEncoding",". HMAC exists for the algorithms built on a block, SHA-2, SHA-3, Keccak-256, BLAKE2b and BLAKE2s, RIPEMD-160, MD5, SHA-1 and SHA-0. Everything else, BLAKE3 and the Bitcoin compositions included, throws instead of pretending:",[176,291,293],{"className":178,"code":292,"language":180,"meta":181,"style":181},"create(\"blake3\").hash(\"message\", { key: \"secret\" });\n\u002F\u002F HashError: [blake3] blake3 has no HMAC mode\n",[163,294,295,319],{"__ignoreMap":181},[185,296,297,300,302,305,307,309,311,313,315,317],{"class":187,"line":188},[185,298,299],{"class":228},"create",[185,301,232],{"class":195},[185,303,304],{"class":202},"\"blake3\"",[185,306,238],{"class":195},[185,308,241],{"class":228},[185,310,232],{"class":195},[185,312,246],{"class":202},[185,314,249],{"class":195},[185,316,252],{"class":202},[185,318,255],{"class":195},[185,320,321],{"class":187,"line":209},[185,322,323],{"class":264},"\u002F\u002F HashError: [blake3] blake3 has no HMAC mode\n",[159,325,326],{},"BLAKE3 has its own keyed mode, and it isn't HMAC. Handing you a plain BLAKE3 digest because it ignored the key would be the worst possible answer, so you get an error.",[159,328,329,330,334,335,174],{},"The HMAC column on the ",[331,332,333],"a",{"href":42},"algorithm list"," says which is which, read from ",[163,336,337],{},"info().hmac",[154,339,341],{"id":340},"verify-compares-bytes","Verify compares bytes",[159,343,344,347],{},[163,345,346],{},"digestMatches(result, expected)"," decodes both digests in the result's encoding and compares the bytes. It goes through every byte even after the first difference, so the time it takes doesn't tell where they differ.",[176,349,351],{"className":178,"code":350,"language":180,"meta":181,"style":181},"import { create, digestMatches } from \"@agntn\u002Fhashes\";\n\nconst hex = create(\"sha256\").hash(\"abc\");\ndigestMatches(hex, \"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD\"); \u002F\u002F true\n\nconst b64 = create(\"sha256\").hash(\"abc\", { encoding: \"base64\" });\ndigestMatches(b64, \"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\"); \u002F\u002F true\ndigestMatches(b64, \"ungwv48bz+pbqudexa4ii7adyaowf3qctbd\u002Fyfiafa0=\"); \u002F\u002F false\n",[163,352,353,366,370,397,414,418,450,465],{"__ignoreMap":181},[185,354,355,357,360,362,364],{"class":187,"line":188},[185,356,192],{"class":191},[185,358,359],{"class":195}," { create, digestMatches } ",[185,361,199],{"class":191},[185,363,203],{"class":202},[185,365,206],{"class":195},[185,367,368],{"class":187,"line":209},[185,369,213],{"emptyLinePlaceholder":212},[185,371,372,374,377,379,381,383,385,387,389,391,394],{"class":187,"line":216},[185,373,219],{"class":191},[185,375,376],{"class":195}," hex ",[185,378,225],{"class":191},[185,380,229],{"class":228},[185,382,232],{"class":195},[185,384,235],{"class":202},[185,386,238],{"class":195},[185,388,241],{"class":228},[185,390,232],{"class":195},[185,392,393],{"class":202},"\"abc\"",[185,395,396],{"class":195},");\n",[185,398,399,402,405,408,411],{"class":187,"line":258},[185,400,401],{"class":228},"digestMatches",[185,403,404],{"class":195},"(hex, ",[185,406,407],{"class":202},"\"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD\"",[185,409,410],{"class":195},"); ",[185,412,413],{"class":264},"\u002F\u002F true\n",[185,415,416],{"class":187,"line":268},[185,417,213],{"emptyLinePlaceholder":212},[185,419,421,423,426,428,430,432,434,436,438,440,442,445,448],{"class":187,"line":420},6,[185,422,219],{"class":191},[185,424,425],{"class":195}," b64 ",[185,427,225],{"class":191},[185,429,229],{"class":228},[185,431,232],{"class":195},[185,433,235],{"class":202},[185,435,238],{"class":195},[185,437,241],{"class":228},[185,439,232],{"class":195},[185,441,393],{"class":202},[185,443,444],{"class":195},", { encoding: ",[185,446,447],{"class":202},"\"base64\"",[185,449,255],{"class":195},[185,451,453,455,458,461,463],{"class":187,"line":452},7,[185,454,401],{"class":228},[185,456,457],{"class":195},"(b64, ",[185,459,460],{"class":202},"\"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\"",[185,462,410],{"class":195},[185,464,413],{"class":264},[185,466,468,470,472,475,477],{"class":187,"line":467},8,[185,469,401],{"class":228},[185,471,457],{"class":195},[185,473,474],{"class":202},"\"ungwv48bz+pbqudexa4ii7adyaowf3qctbd\u002Fyfiafa0=\"",[185,476,410],{"class":195},[185,478,479],{"class":264},"\u002F\u002F false\n",[159,481,482,483,486,487,489,490,493],{},"Hex ignores case because ",[163,484,485],{},"A"," and ",[163,488,331],{}," are the same nibble. Base64 doesn't, because they aren't the same byte. Lowercasing both sides before comparing is the classic shortcut, and for base64 it's a bug that says yes to a digest that isn't yours. Surrounding whitespace is trimmed. A string that isn't valid in the encoding is a plain ",[163,491,492],{},"false",", not a crash.",[159,495,496,499,500,174],{},[163,497,498],{},"binary"," can't be compared as text, and asking is an ",[163,501,502],{},"InvalidOptionError",[154,504,506],{"id":505},"from-the-terminal","From the terminal",[176,508,512],{"className":509,"code":510,"language":511,"meta":181,"style":181},"language-bash shiki shiki-themes hashes hashes hashes","hashes verify sha256 abc \"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\" -e base64\n# MATCH sha256 ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\n\nhashes verify sha256 abc \"ungwv48bz+pbqudexa4ii7adyaowf3qctbd\u002Fyfiafa0=\" -e base64\n# MISMATCH sha256\n#   expected ungwv48bz+pbqudexa4ii7adyaowf3qctbd\u002Fyfiafa0=\n#   actual   ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\n","bash",[163,513,514,537,542,546,563,568,573],{"__ignoreMap":181},[185,515,516,519,522,525,528,531,534],{"class":187,"line":188},[185,517,518],{"class":228},"hashes",[185,520,521],{"class":202}," verify",[185,523,524],{"class":202}," sha256",[185,526,527],{"class":202}," abc",[185,529,530],{"class":202}," \"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\"",[185,532,533],{"class":202}," -e",[185,535,536],{"class":202}," base64\n",[185,538,539],{"class":187,"line":209},[185,540,541],{"class":264},"# MATCH sha256 ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\n",[185,543,544],{"class":187,"line":216},[185,545,213],{"emptyLinePlaceholder":212},[185,547,548,550,552,554,556,559,561],{"class":187,"line":258},[185,549,518],{"class":228},[185,551,521],{"class":202},[185,553,524],{"class":202},[185,555,527],{"class":202},[185,557,558],{"class":202}," \"ungwv48bz+pbqudexa4ii7adyaowf3qctbd\u002Fyfiafa0=\"",[185,560,533],{"class":202},[185,562,536],{"class":202},[185,564,565],{"class":187,"line":268},[185,566,567],{"class":264},"# MISMATCH sha256\n",[185,569,570],{"class":187,"line":420},[185,571,572],{"class":264},"#   expected ungwv48bz+pbqudexa4ii7adyaowf3qctbd\u002Fyfiafa0=\n",[185,574,575],{"class":187,"line":452},[185,576,577],{"class":264},"#   actual   ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD\u002FYfIAFa0=\n",[159,579,580,581,584,585,588],{},"A mismatch exits with 1, so ",[163,582,583],{},"hashes verify ... && deploy"," does what it reads like. ",[163,586,587],{},"-e"," is the encoding of the expected digest.",[159,590,591,594],{},[163,592,593],{},"hashes hmac sha256 message secret"," prints the tag alone. An algorithm without an HMAC mode is refused before anything is hashed.",[154,596,598],{"id":597},"for-a-kdf","For a KDF",[159,600,601,602,486,605,608,609,612,613,615,616,618,619,174],{},"scrypt and PBKDF2 draw a random salt when you don't give one, so verifying without the salt could only ever say no. ",[163,603,604],{},"hashes verify",[163,606,607],{},"hash_verify"," refuse to run without it, with an error that tells you which salt they want. In the library you pass the same ",[163,610,611],{},"salt"," and costs to ",[163,614,169],{}," and then ",[163,617,401],{},". More on ",[331,620,20],{"href":21},[622,623,624],"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 .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 .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 .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":181,"searchDepth":209,"depth":209,"links":626},[627,628,629,630],{"id":156,"depth":209,"text":157},{"id":340,"depth":209,"text":341},{"id":505,"depth":209,"text":506},{"id":597,"depth":209,"text":598},"Key a digest with HMAC and compare an expected digest byte for byte. Hex ignores case and base64 never does","md",null,{"icon":635},"i-lucide-check-check",{"title":16,"description":631},"c9zHyoMlTlYHSzBI6kGBdAXrGmjPNQ2Rs6ZyrylQXOY",[639,641],{"title":12,"path":13,"stem":14,"description":640,"children":-1},"Pick an algorithm by name and hash text or bytes. Hex and base64 and base64url or raw bytes out",{"title":20,"path":21,"stem":22,"description":642,"children":-1},"scrypt and PBKDF2 draw a salt when you give none and print it next to the digest. Keep it or the digest is useless",1790683656173]