Skip to content

Scrypt asks for memory it does not need.

That sounds like a flaw and it is the design. Custom cracking hardware is cheap to build for arithmetic and expensive to build for RAM, so a function that demands a large block of memory per guess raises an attacker's cost in a way that iteration counts cannot.

Everything below this line runs locally

N = 131,072 · about 128 MiB per hash

The parameter that surprises people

Memory use is roughly 128 × N × r bytes. Both numbers matter, and changing only N gives you a fraction of the memory the number suggests. The readout above updates as you change either one, so you can see what a setting actually asks for before you put it into production.

N = 2^14, r = 8
16 MiB per hash
N = 2^17, r = 8
128 MiB per hash
N = 2^17, r = 1
16 MiB per hash
N = 2^20, r = 8
1 GiB per hash

The middle two rows share the same N and differ by a factor of eight in memory. That is the trap.

In your own code

Node.js

import { scrypt, randomBytes } from 'node:crypto';

const salt = randomBytes(16);
scrypt(password, salt, 32, { N: 2 ** 17, r: 8, p: 1, maxmem: 256 * 1024 * 1024 }, (err, key) => {
  // store salt and key together with the parameters
});

Python

import hashlib, os

salt = os.urandom(16)
key = hashlib.scrypt(password.encode(), salt=salt, n=2**17, r=8, p=1, dklen=32)

Go

import "golang.org/x/crypto/scrypt"

key, err := scrypt.Key([]byte(password), salt, 1<<17, 8, 1, 32)

Node enforces a maxmem ceiling of 32 MiB by default and throws once your parameters go past it, which is the usual reason a working local script fails the first time N goes up.

Questions people ask

What do N, r and p actually do?
N is the cost factor and must be a power of two. r is the block size. Memory use is roughly 128 times N times r bytes, so the two of them together decide how much RAM one hash needs. p is parallelism, and raising it multiplies the work without changing the memory.
Why does raising N alone not use much memory?
Because memory depends on N and r together. Setting N to 2^20 with r left at 1 asks for 128 MiB, while the same N with r at 8 asks for a gigabyte. This interaction is the most common way scrypt gets configured wrong.
Is scrypt still a good choice?
It is sound and well studied. For a new project Argon2id is the better default, since it lets you set memory and time independently instead of deriving one from the other. Scrypt is a reasonable option where an Argon2 library is not available.
Why is there no encoded output format?
Scrypt has no single standard string format the way bcrypt and Argon2 do. Different projects invented their own, so this page returns the raw derived key as hex and you store the parameters and salt alongside it.
Why did the browser tab stall?
You asked for more memory than the tab could allocate quickly. A high N with a high r can reach a gigabyte per hash. Lower one of them and it will finish.

Next