From f70e8e3f904c47b57c384f14bca5573e660c7b32 Mon Sep 17 00:00:00 2001 From: Chris Veness Date: Mon, 21 Oct 2024 12:48:34 +0100 Subject: [PATCH] Increase pickParams() timing loop (fixes #18) --- CHANGELOG.md | 6 ++++++ README.md | 2 ++ scrypt.js | 19 ++++++++++++++----- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82ac1f8..026a0be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Increase pickParams() timing loop + ## [3.0.0] - 2024-10-18 ### Changed diff --git a/README.md b/README.md index 04e7ee6..48ee574 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ API `Scrypt.pickParams(maxtime, maxmem, maxmemfrac)` – return scrypt parameters for given operational parameters. +Percival’s calculation for optimal parameters can be used to verify Valsorda’s / Percival’s [recommendation of 15](https://words.filippo.io/the-scrypt-parameters) for logN; though in empirical tests (in 2024) it appears to underestimate logN by one or two – timing tests are the most reliable way to validate optimal parameters. + - `maxtime` is the maximum time in seconds scrypt will spend computing the derived encryption key from the password (0.1 seconds is recommended for interactive logins). - `maxmem` (optional) is the maximum RAM scrypt will use when computing the derived encryption key, in bytes (default maximum available physical memory). - `maxmemfrac` (optional) is the maximum fraction of available RAM scrypt will use for computing the derived encryption key (default 0.5); if not within the range 0 < maxmemfrac <= 0.5, this will be set to 0.5. diff --git a/scrypt.js b/scrypt.js index c05fc5c..ef07f36 100644 --- a/scrypt.js +++ b/scrypt.js @@ -29,6 +29,8 @@ class Scrypt { /** * Produce derived key using scrypt as a key derivation function. * + * Recommended parameter values (2017) are logN:15, r:8, p:1; words.filippo.io/the-scrypt-parameters. + * * @param {string|Uint8Array|Buffer} passphrase - Secret value such as a password from which key is to be derived. * @param {Object} params - Scrypt parameters. * @param {number} params.logN - CPU/memory cost parameter. @@ -225,7 +227,13 @@ class Scrypt { /** * Calculate scrypt parameters from maxtime, maxmem, maxmemfrac values. * - * Adapted from Colin Percival's code: see github.com/Tarsnap/scrypt/tree/master/lib. + * Adapted from Colin Percival's code: see github.com/Tarsnap/scrypt/tree/master/lib/scryptenc. + * Percival recommended an interactive login delay of "up to 100ms". + * + * NOTE: empirical tests in 2024 suggest this approach underestimates logN by one or two, and + * confirm Filippo Valsorda's recommendation for logN=15; ("the biggest power of two that + * will run in less than 100ms") - timing tests are the most reliable way to validate + * optimal parameters. * * Returned parameters may vary depending on computer specs & current loading. * @@ -247,12 +255,13 @@ class Scrypt { // Colin Percival measures how many scrypts can be done in one clock tick using C/POSIX // clock_getres() / CLOCKS_PER_SEC (usually just one?); we will use performance.now() to get - // a DOMHighResTimeStamp. (Following meltdown/spectre timing attacks Chrome reduced the high - // res timestamp resolution to 100µs, so we'll be conservative and do a 1ms run - typically - // 1..10 minimal scrypts). + // a DOMHighResTimeStamp. (Following meltdown/spectre timing attacks high-res timestamp + // resolution has been 'coarsened' to 100µs, so we'll be conservative and do a 100ms run - + // typically 100..1000 minimal scrypts, noting greater timing variability in JavaScript than + // in C). let i = 0; const start = performance.now(); - while (performance.now()-start < 1) { + while (performance.now()-start < 100) { // 100ms run opensslScryptSync('', '', 64, { N: 128, r: 1, p: 1 }); i += 512; // we invoked the salsa20/8 core 512 times }