Hashing and MAC

hash turns data into a fixed-size fingerprint. hmac does the same with a secret mixed in, so only a holder of that secret can make one or check one.

Note

ELI5. A hash is a fingerprint of some data. Run the same bytes through it and you get the same short string every time; change one byte and the string changes completely. It is a one-way trip, so you cannot get the data back out of the fingerprint. A MAC is a fingerprint made with a shared secret mixed in, which means only someone holding that secret can produce a valid one or tell a valid one from a forgery. Use a hash to notice that data changed. Use a MAC to prove a message came from who it claims. Use neither one for passwords: they are fast on purpose, which is exactly wrong for guessing defence. That job belongs to Argon2.

Two functions and their helpers:

#Which one do I want

The question you are answeringThe function
Are these two blobs the same?hash
Has this file changed since I last saw it?hash
Can I store this API token without storing the token itself?hash
Did this webhook really come from the provider?hmacVerify
Can I hand a client a value it cannot tamper with?hmac
Can I store this user password?No. Use argon2Hash.
Can I turn a shared secret into several keys?No. Use hkdf.

#Shared behaviour

Both functions take string | BytesSource and share two options.

algorithm is one of "SHA-1", "SHA-256" (the default), "SHA-384" or "SHA-512". Names are matched case-insensitively, so "sha-256" works. Any other name throws UNSUPPORTED before Web Crypto is reached, and the message lists the four.

returnAs decides the shape of the result: "hex", "base64" (alias "b64"), "base64url" (alias "b64url"), or "uint8array" (alias "bytes"). Omit it and the output mirrors the input: a string in gives hex out, bytes in give bytes out.

import { hash } from "unsecure/hash";

await hash("hello world"); // string in  -> hex string out
await hash(new TextEncoder().encode("hello world")); // bytes in -> Uint8Array out
await hash(bytes, { returnAs: "hex" }); // explicit wins

#Errors

What went wrongCode
A value that is neither text nor bytesINVALID_TYPE
An algorithm or returnAs name the library does not knowUNSUPPORTED
An empty secret (HMAC only)OUT_OF_RANGE
Web Crypto refused the operation after the checks passedPLATFORM, with the runtime's error as cause

All of them are an UnsecureError.