Argon2id
Argon2id
The Argon2 that RFC 9106 tells you to pick.
- Digest
- keyLength you pick
- HMAC
- no key mode
- Security
- The primary variant of RFC 9106
- Family
- 3 in Argon2
Sample
- hex
- f5122bfd86ada0736205873b5d95a42849fc4ee7880900a1995dc8c12314dcd0
- base64
- 9RIr/YatoHNiBYc7XZWkKEn8TueICQChmV3IwSMU3NA=
Options
Access
- Create
create("argon2id") - CLI
hashes argon2id 'hello world' --salt 73616c7473616c74 --memory 1024 --iterations 2 --parallelism 1 - Tryplayground with the sample above
- Kinargon2i, argon2d
Three Argon2 variants, one engine, and a letter that decides how paranoid it gets. RFC 9106 calls argon2id the primary one. It reads memory in a fixed order for the first half of the first pass, like argon2i. After that the reads follow the data, like argon2d. A side channel learns little. A GPU still pays for every KiB.
create("argon2id").hash("password", {
salt: "73616c7473616c74",
memory: 1024,
iterations: 2,
parallelism: 1,
}).digest;
// "820ecc0dff8ed2ee188788d8bd38d7269b3a7118c1210bc9e2becc40ebf5039f"
That salt is saltsalt in hex. Why not plain salt? Argon2 wants at least 8 bytes, so four get an InvalidOptionError. Leave the salt out and 32 random bytes come back in result.options. Salted KDFs says why you keep them.
memory counts KiB, not bytes. The defaults are RFC 9106's second recommended option: 64 MiB, 3 passes, 4 lanes, 32 bytes out. Its first option wants 2 GiB. Fine on a server, rude in a browser tab. So the page above runs its sample at 1 MiB.
Lanes change the result, not the speed. This is plain TypeScript on one thread, and the lanes run one after another. Matching a hash from somewhere else? Copy all four numbers, or you get a perfectly valid wrong digest.
secret and associatedData take hex. They're K and X in the RFC, mixed into the first hash. A pepper goes in secret. associatedData comes back in result.options, the secret doesn't. You already have it, and a log doesn't need it.
Need the bytes for another library? @agntn/hashes/argon2 has all three variants as plain functions:
import { argon2id } from "@agntn/hashes/argon2";
const text = new TextEncoder();
argon2id(text.encode("password"), text.encode("saltsalt"), {
memory: 1024,
iterations: 2,
parallelism: 1,
keyLength: 32,
});
// the same 32 bytes as above
No defaults and no random salt here. You pass the four numbers, and secret and associatedData as bytes if you need them. Anything RFC 9106 rules out throws before a single block is allocated. Asking for 2 GiB? It'll try. Only the tools cap memory.