Files

146 lines
7.4 KiB
JavaScript

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;
}
}