import { createHash, createHmac, hash, pbkdf2Sync, hkdf, hkdfSync, scrypt, scryptSync } from "node:crypto"; export function reportKdf() { const invalid = (label, run) => { try { run(); console.log(label, "ok"); } catch (error) { console.log(label, error.name, error.code, error.message); } }; for (const value of [undefined, null, 42, {}]) { invalid("ikm-type", () => hkdfSync("sha256", value, "", "", 1)); invalid("salt-type", () => hkdfSync("sha256", "", value, "", 1)); invalid("info-type", () => hkdfSync("sha256", "", "", value, 1)); invalid("password-type", () => scryptSync(value, "", 1)); invalid("callback-type", () => hkdf("sha256", "", "", "", 1, value)); } for (const options of [{N:"16"}, {cost:null}, {r:true}, {maxmem:Infinity}, null, true, "options"]) { invalid("options-type", () => scryptSync("p", "s", 1, options)); } invalid("digest-type", () => hkdfSync(42, "", "", "", 1)); invalid("scrypt-callback-type", () => scrypt("p", "s", 1, {N:16}, 42)); { for (const algorithm of ["sha224", "sha384", "sha512"]) { for (const length of [0, 1, 55, 56, 63, 64, 65, 111, 112, 127, 128, 129, 255, 256, 1024]) { const input = "a".repeat(length); console.log("hash", algorithm, length, hash(algorithm, input)); const digest = createHash(algorithm).update(input.slice(0, 37)); const copy = digest.copy(); digest.update(input.slice(37)); copy.update(input.slice(37)); console.log("chunks", digest.digest("hex"), copy.digest().toString("base64")); } for (const keyLength of [0, 63, 64, 65, 127, 128, 129, 257]) { console.log("hmac", algorithm, keyLength, createHmac(algorithm, "k".repeat(keyLength)).update("héllo ☃".repeat(20)).digest("hex")); } console.log("pbkdf2", algorithm, pbkdf2Sync("p".repeat(129), "salt", 3, 83, algorithm).toString("hex")); } } { let reads = ""; const options = { get N() { reads += "N,"; return 16; }, get cost() { reads += "cost,"; return undefined; }, get r() { reads += "r,"; return 1; }, get blockSize() { reads += "blockSize,"; return undefined; }, get p() { reads += "p,"; return 1; }, get parallelization() { reads += "parallelization,"; return undefined; }, get maxmem() { reads += "maxmem,"; return 2432; }, }; console.log("getters", scryptSync("p", "s", 8, options).toString("hex"), reads); const optional = { N: undefined, r: 1, p: 1, cost: 16, maxmem: 2432 }; console.log("optional", scryptSync("p", "s", 8, optional).toString("hex")); let trace = ""; function password() { trace += "password,"; return "p"; } function salt() { trace += "salt,"; return "s"; } function length() { trace += "length,"; return 8; } function config() { trace += "options,"; return { N: 16, r: 1, p: 1 }; } console.log("arguments", scryptSync(password(), salt(), length(), config()).toString("hex"), trace); } { function caught(label , run ) { try { run(); console.log(label, "no-error"); } catch (error) { if (error instanceof Error) console.log(label, error.name, (error ).code, error.message); } } for (const length of [0, -1, 1.5, NaN, Infinity, 9007199254740992]) { caught("hkdf-length", () => { hkdfSync("sha256", "key", "salt", "", length); }); } caught("hkdf-info", () => { hkdfSync("sha256", "key", "salt", "x".repeat(1025), 1); }); caught("hkdf-keylen", () => { hkdfSync("sha256", "key", "salt", "", 8161); }); const algorithm = "nope"; caught("hkdf-digest", () => { hkdfSync(algorithm, "key", "salt", "", 1); }); let called = false; caught("hkdf-async-keylen", () => { hkdf("sha1", "key", "salt", "", 5101, () => { called = true; }); }); caught("hkdf-async-digest", () => { hkdf(algorithm, "key", "salt", "", 1, () => { called = true; }); }); console.log("invalid-callback-not-called", called); for (const length of [-1, 1.5, NaN, 2147483648]) caught("scrypt-length", () => { scryptSync("p", "s", length); }); for (const cost of [-1, 1.5, NaN, 4294967296, 1, 3]) caught("scrypt-cost", () => { scryptSync("p", "s", 1, { N: cost }); }); caught("scrypt-alias-cost", () => { scryptSync("p", "s", 1, { cost: -1 }); }); caught("scrypt-conflict-N", () => { scryptSync("p", "s", 1, { N: 16, cost: 16 }); }); caught("scrypt-conflict-r", () => { scryptSync("p", "s", 1, { r: 1, blockSize: 1 }); }); caught("scrypt-conflict-p", () => { scryptSync("p", "s", 1, { p: 1, parallelization: 1 }); }); caught("scrypt-async-invalid", () => { scrypt("p", "s", 1, { N: 3 }, () => { called = true; }); }); caught("scrypt-maxmem-range", () => { scryptSync("p", "s", 1, { maxmem: -1 }); }); caught("scrypt-memory-limit", () => { scryptSync("p", "s", 1, { N: 16, r: 1, p: 1, maxmem: 2431 }); }); caught("scrypt-default-memory", () => { scryptSync("p", "s", 0, { N: 32768 }); }); console.log("invalid-callback-still-not-called", called); } { for (const algorithm of ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]) { const key = hkdfSync(algorithm, "secret", "salt", "context", 83); console.log("hkdf", algorithm, key instanceof ArrayBuffer, Buffer.from(key).toString("hex")); console.log("empty", algorithm, Buffer.from(hkdfSync(algorithm, "", "", "", 17)).toString("hex")); } const rfcIkm = Buffer.alloc(22, 11); console.log("rfc5869", Buffer.from(hkdfSync("sha256", rfcIkm, Buffer.from("000102030405060708090a0b0c", "hex"), Buffer.from("f0f1f2f3f4f5f6f7f8f9", "hex"), 42)).toString("hex")); console.log("hkdf-info-limit", Buffer.from(hkdfSync("SHA512", "héllo ☃", "salt", "x".repeat(1024), 65)).toString("hex")); console.log("hkdf-max", Buffer.from(hkdfSync("sha256", "key", "salt", "info", 8160)).subarray(8130).toString("hex")); console.log("rfc7914", scryptSync("", "", 64, { N: 16, r: 1, p: 1 }).toString("hex")); console.log("scrypt-default", scryptSync("password", "salt", 32).toString("hex")); console.log("scrypt-parallel", scryptSync("password", "NaCl", 64, { N: 1024, r: 8, p: 16 }).toString("hex")); console.log("scrypt-aliases", scryptSync("päss ☃", "salt", 33, { cost: 32, blockSize: 2, parallelization: 3, maxmem: 65536 }).toString("hex")); console.log("scrypt-zero", scryptSync("p", "s", 0).length, scryptSync("p", "s", 1, { N: 0, r: 0, p: 0, maxmem: 0 }).toString("hex")); console.log("scrypt-memory-boundary", scryptSync("p", "s", 17, { N: 16, r: 1, p: 1, maxmem: 2432 }).toString("hex")); const storage = new ArrayBuffer(16); const bytes = new Uint8Array(storage); bytes.set([0, 1, 2, 3, 4, 5, 6, 7]); const words = new Uint16Array(storage, 2, 2); const view = new DataView(storage, 4, 3); console.log("hkdf-views", Buffer.from(hkdfSync("sha384", words, view, storage, 49)).toString("hex")); console.log("scrypt-views", scryptSync(words, view, 17, { N: 16, r: 1 }).toString("hex")); console.log("unchanged", Buffer.from(storage).toString("hex")); let sync = true; hkdf("sha256", "", "", "", 0, (zeroError, empty) => { console.log("hkdf-zero-callback", zeroError instanceof Error, zeroError?.message, empty === undefined); hkdf("sha512", "key", "salt", "info", 65, (error, key) => { console.log("hkdf-callback", sync, error === null, key instanceof ArrayBuffer, Buffer.from(key).toString("hex")); scrypt("password", "salt", 17, { N: 16, r: 2 }, (err, derived) => { console.log("scrypt-callback", err === null, Buffer.isBuffer(derived), derived.toString("hex")); scrypt("p", "s", 0, (lastError, empty) => console.log("scrypt-default-callback", lastError === null, empty.length)); }); }); }); queueMicrotask(() => console.log("microtask-before-callback")); sync = false; } }