aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/bindings.zig38
-rw-r--r--src/bun.js/webcore.zig197
-rw-r--r--src/deps/boringssl.translated.zig29
-rw-r--r--src/node-fallbacks/crypto.js49
-rw-r--r--test/bun.js/crypto-scrypt.test.js265
5 files changed, 568 insertions, 10 deletions
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index d15cfd674..add89ea77 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -1837,20 +1837,40 @@ pub const JSGlobalObject = extern struct {
createSyntheticModule_(this, &export_names, names.len, &export_values, names.len);
}
+ pub fn createErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue {
+ if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) {
+ var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator());
+ var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable;
+ defer buf.deinit();
+ var writer = buf.writer();
+ writer.print(fmt, args) catch
+ // if an exception occurs in the middle of formatting the error message, it's better to just return the formatting string than an error about an error
+ return ZigString.static(fmt).toErrorInstance(this);
+ var str = ZigString.fromUTF8(buf.toOwnedSliceLeaky());
+ return str.toErrorInstance(this);
+ } else {
+ return ZigString.static(fmt).toErrorInstance(this);
+ }
+ }
+
+ pub fn createRangeError(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue {
+ const err = createErrorInstance(this, fmt, args);
+ err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_OUT_OF_RANGE)).toValue(this));
+ return err;
+ }
+
+ pub fn createInvalidArgs(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue {
+ const err = createErrorInstance(this, fmt, args);
+ err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE)).toValue(this));
+ return err;
+ }
+
pub fn throw(
this: *JSGlobalObject,
comptime fmt: string,
args: anytype,
) void {
- if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) {
- var str = ZigString.init(std.fmt.allocPrint(this.bunVM().allocator, fmt, args) catch return);
- str.markUTF8();
- var err = str.toErrorInstance(this);
- this.vm().throwError(this, err);
- this.bunVM().allocator.free(ZigString.untagged(str.ptr)[0..str.len]);
- } else {
- this.vm().throwError(this, ZigString.static(fmt).toValue(this));
- }
+ this.vm().throwError(this, this.createErrorInstance(fmt, args));
}
pub fn throwValue(
diff --git a/src/bun.js/webcore.zig b/src/bun.js/webcore.zig
index 10ba5051e..2d1605049 100644
--- a/src/bun.js/webcore.zig
+++ b/src/bun.js/webcore.zig
@@ -366,6 +366,7 @@ pub const Crypto = struct {
.getRandomValues = JSC.DOMCall("Crypto", @This(), "getRandomValues", JSC.JSValue, JSC.DOMEffect.top),
.randomUUID = JSC.DOMCall("Crypto", @This(), "randomUUID", *JSC.JSString, JSC.DOMEffect.top),
.timingSafeEqual = JSC.DOMCall("Crypto", @This(), "timingSafeEqual", JSC.JSValue, JSC.DOMEffect.top),
+ .scryptSync = .{ .rfn = JSC.wrapWithHasContainer(Crypto, "scryptSync", false, false, false) },
},
.{},
);
@@ -380,6 +381,202 @@ pub const Crypto = struct {
.{},
);
+ pub fn scryptSyncValidate(
+ globalThis: *JSC.JSGlobalObject,
+ options: ?JSC.JSValue,
+ ) JSC.JSValue {
+ var blockSize: usize = 8;
+ var cost: usize = 16384;
+ var parallelization: usize = 1;
+ var maxmem: usize = 32 * 1024 * 1024;
+
+ if (options) |options_value| outer: {
+ if (options_value.isUndefined() or options_value == .zero)
+ break :outer;
+
+ if (!options_value.isObject()) {
+ return globalThis.createInvalidArgs("options must be an object", .{});
+ }
+
+ if (options_value.getTruthy(globalThis, "cost") orelse options_value.get(globalThis, "N")) |N_value| {
+ const N_int = N_value.to(i64);
+ if (N_int < 0 or !N_value.isNumber()) {
+ return globalThis.createRangeError("N must be a positive integer", .{});
+ } else if (N_int != 0) {
+ cost = @intCast(usize, N_int);
+ }
+ }
+
+ if (options_value.getTruthy(globalThis, "blockSize") orelse options_value.get(globalThis, "r")) |r_value| {
+ const r_int = r_value.to(i64);
+ if (r_int < 0 or !r_value.isNumber()) {
+ return globalThis.createRangeError("r must be a positive integer", .{});
+ } else if (r_int != 0) {
+ blockSize = @intCast(usize, r_int);
+ }
+ }
+
+ if (options_value.getTruthy(globalThis, "parallelization") orelse options_value.get(globalThis, "p")) |p_value| {
+ const p_int = p_value.to(i64);
+ if (p_int < 0 or !p_value.isNumber()) {
+ return globalThis.createRangeError("p must be a positive integer", .{});
+ } else if (p_int != 0) {
+ parallelization = @intCast(usize, p_int);
+ }
+ }
+
+ if (options_value.getTruthy(globalThis, "maxmem")) |value| {
+ const p_int = value.to(i64);
+ if (p_int < 0 or !value.isNumber()) {
+ return globalThis.createInvalidArgs("maxmem must be a positive integer", .{});
+ } else if (p_int != 0) {
+ maxmem = @intCast(usize, p_int);
+ }
+ }
+ }
+
+ if (cost < 2 or cost > 0x3fffffff) {
+ return globalThis.createRangeError("N must be greater than 1 and less than 2^30", .{});
+ }
+
+ if (cost == 0 or (cost & (cost - 1)) != 0) {
+ return globalThis.createRangeError("N must be a power of 2 greater than 1", .{});
+ }
+
+ if ((BoringSSL.EVP_PBE_scrypt(
+ null,
+ 0,
+ null,
+ 0,
+ cost,
+ blockSize,
+ parallelization,
+ maxmem,
+ null,
+ 0,
+ ) != 1)) {
+ return globalThis.createErrorInstance("scrypt parameters are invalid", .{});
+ }
+ var slice: []u8 = undefined;
+ slice.len = 0;
+ return JSC.ArrayBuffer.create(globalThis, slice, .ArrayBuffer);
+ }
+
+ pub fn scryptSync(
+ globalThis: *JSC.JSGlobalObject,
+ password: JSC.Node.StringOrBuffer,
+ salt: JSC.Node.StringOrBuffer,
+ keylen_value: JSC.JSValue,
+ options: ?JSC.JSValue,
+ ) JSC.JSValue {
+ const password_string = password.slice();
+ const salt_string = salt.slice();
+
+ if (keylen_value.isEmptyOrUndefinedOrNull()) {
+ return globalThis.createInvalidArgs("keylen must be a number", .{});
+ }
+
+ const keylen_int = keylen_value.to(i64);
+ if (keylen_int < 0) {
+ return globalThis.createRangeError("keylen must be a positive integer", .{});
+ } else if (keylen_int == 0) {
+ return scryptSyncValidate(globalThis, options);
+ } else if (keylen_int > 0x7fffffff) {
+ return globalThis.createRangeError("keylen must be less than 2^31", .{});
+ }
+
+ var blockSize: usize = 8;
+ var cost: usize = 16384;
+ var parallelization: usize = 1;
+ var maxmem: usize = 32 * 1024 * 1024;
+ const keylen = @intCast(u32, @truncate(i33, keylen_int));
+
+ if (options) |options_value| outer: {
+ if (options_value.isUndefined() or options_value == .zero)
+ break :outer;
+
+ if (!options_value.isObject()) {
+ return globalThis.createInvalidArgs("options must be an object", .{});
+ }
+
+ if (options_value.getTruthy(globalThis, "cost") orelse options_value.get(globalThis, "N")) |N_value| {
+ const N_int = N_value.to(i64);
+ if (N_int < 0 or !N_value.isNumber()) {
+ return globalThis.createRangeError("N must be a positive integer", .{});
+ } else if (N_int != 0) {
+ cost = @intCast(usize, N_int);
+ }
+ }
+
+ if (options_value.getTruthy(globalThis, "blockSize") orelse options_value.get(globalThis, "r")) |r_value| {
+ const r_int = r_value.to(i64);
+ if (r_int < 0 or !r_value.isNumber()) {
+ return globalThis.createRangeError("r must be a positive integer", .{});
+ } else if (r_int != 0) {
+ blockSize = @intCast(usize, r_int);
+ }
+ }
+
+ if (options_value.getTruthy(globalThis, "parallelization") orelse options_value.get(globalThis, "p")) |p_value| {
+ const p_int = p_value.to(i64);
+ if (p_int < 0 or !p_value.isNumber()) {
+ return globalThis.createRangeError("p must be a positive integer", .{});
+ } else if (p_int != 0) {
+ parallelization = @intCast(usize, p_int);
+ }
+ }
+
+ if (options_value.getTruthy(globalThis, "maxmem")) |value| {
+ const p_int = value.to(i64);
+ if (p_int < 0 or !value.isNumber()) {
+ return globalThis.createInvalidArgs("maxmem must be a positive integer", .{});
+ } else if (p_int != 0) {
+ maxmem = @intCast(usize, p_int);
+ }
+ }
+ }
+
+ if (cost < 2 or cost > 0x3fffffff) {
+ return globalThis.createRangeError("N must be greater than 1 and less than 2^30", .{});
+ }
+
+ if (cost == 0 or (cost & (cost - 1)) != 0) {
+ return globalThis.createRangeError("N must be a power of 2 greater than 1", .{});
+ }
+
+ var stackbuf: [1024]u8 = undefined;
+ var buf: []u8 = &stackbuf;
+ var needs_deinit = false;
+ defer if (needs_deinit) globalThis.allocator().free(buf);
+ if (keylen > buf.len) {
+ // i don't think its a real scenario, but just in case
+ buf = globalThis.allocator().alloc(u8, keylen) catch {
+ globalThis.throw("Failed to allocate memory", .{});
+ return JSC.JSValue.jsUndefined();
+ };
+ needs_deinit = true;
+ } else {
+ buf.len = keylen;
+ }
+
+ if (BoringSSL.EVP_PBE_scrypt(
+ password_string.ptr,
+ password_string.len,
+ salt_string.ptr,
+ salt_string.len,
+ cost,
+ blockSize,
+ parallelization,
+ maxmem,
+ buf.ptr,
+ keylen,
+ ) != 1) {
+ return globalThis.createErrorInstance("Failed to derive key", .{});
+ }
+
+ return JSC.ArrayBuffer.create(globalThis, buf, .ArrayBuffer);
+ }
+
pub fn timingSafeEqual(
globalThis: *JSC.JSGlobalObject,
_: JSC.JSValue,
diff --git a/src/deps/boringssl.translated.zig b/src/deps/boringssl.translated.zig
index 24d54aa1f..ceaa67890 100644
--- a/src/deps/boringssl.translated.zig
+++ b/src/deps/boringssl.translated.zig
@@ -1457,7 +1457,34 @@ pub extern fn EVP_PKEY_print_private(out: [*c]BIO, pkey: [*c]const EVP_PKEY, ind
pub extern fn EVP_PKEY_print_params(out: [*c]BIO, pkey: [*c]const EVP_PKEY, indent: c_int, pctx: ?*ASN1_PCTX) c_int;
pub extern fn PKCS5_PBKDF2_HMAC(password: [*c]const u8, password_len: usize, salt: [*c]const u8, salt_len: usize, iterations: c_uint, digest: ?*const EVP_MD, key_len: usize, out_key: [*c]u8) c_int;
pub extern fn PKCS5_PBKDF2_HMAC_SHA1(password: [*c]const u8, password_len: usize, salt: [*c]const u8, salt_len: usize, iterations: c_uint, key_len: usize, out_key: [*c]u8) c_int;
-pub extern fn EVP_PBE_scrypt(password: [*c]const u8, password_len: usize, salt: [*c]const u8, salt_len: usize, N: u64, r: u64, p: u64, max_mem: usize, out_key: [*c]u8, key_len: usize) c_int;
+/// EVP_PBE_scrypt expands |password| into a secret key of length |key_len| using
+/// scrypt, as described in RFC 7914, and writes the result to |out_key|. It
+/// returns one on success and zero on allocation failure, if the memory required
+/// for the operation exceeds |max_mem|, or if any of the parameters are invalid
+/// as described below.
+///
+/// |N|, |r|, and |p| are as described in RFC 7914 section 6. They determine the
+/// cost of the operation. If |max_mem| is zero, a defult limit of 32MiB will be
+/// used.
+///
+/// The parameters are considered invalid under any of the following conditions:
+/// - |r| or |p| are zero
+/// - |p| > (2^30 - 1) / |r|
+/// - |N| is not a power of two
+/// - |N| > 2^32
+/// - |N| > 2^(128 * |r| / 8)
+pub extern fn EVP_PBE_scrypt(
+ password: [*c]const u8,
+ password_len: usize,
+ salt: [*c]const u8,
+ salt_len: usize,
+ N: u64,
+ r: u64,
+ p: u64,
+ max_mem: usize,
+ out_key: [*c]u8,
+ key_len: usize,
+) c_int;
pub extern fn EVP_PKEY_CTX_new(pkey: [*c]EVP_PKEY, e: ?*ENGINE) ?*EVP_PKEY_CTX;
pub extern fn EVP_PKEY_CTX_new_id(id: c_int, e: ?*ENGINE) ?*EVP_PKEY_CTX;
pub extern fn EVP_PKEY_CTX_free(ctx: ?*EVP_PKEY_CTX) void;
diff --git a/src/node-fallbacks/crypto.js b/src/node-fallbacks/crypto.js
index 0f428df13..4a0e4c735 100644
--- a/src/node-fallbacks/crypto.js
+++ b/src/node-fallbacks/crypto.js
@@ -1,5 +1,7 @@
export * from "crypto-browserify";
+export var DEFAULT_ENCODING = "buffer";
+
// we deliberately reference crypto. directly here because we want to preserve the This binding
export const getRandomValues = (array) => {
return crypto.getRandomValues(array);
@@ -25,15 +27,62 @@ export const timingSafeEqual =
throw new RangeError("Input buffers must have the same length");
}
+ // these error checks are also performed in the function
+ // however there is a bug where exceptions return no value
return crypto.timingSafeEqual(a, b);
}
: undefined;
+export const scryptSync =
+ "scryptSync" in crypto
+ ? (password, salt, keylen, options) => {
+ const res = crypto.scryptSync(password, salt, keylen, options);
+ return DEFAULT_ENCODING !== "buffer"
+ ? new Buffer(res).toString(DEFAULT_ENCODING)
+ : new Buffer(res);
+ }
+ : undefined;
+
+export const scrypt =
+ "scryptSync" in crypto
+ ? function (password, salt, keylen, options, callback) {
+ if (typeof options === "function") {
+ callback = options;
+ options = undefined;
+ }
+
+ if (typeof callback !== "function") {
+ var err = new TypeError("callback must be a function");
+ err.code = "ERR_INVALID_CALLBACK";
+ throw err;
+ }
+
+ try {
+ const result = crypto.scryptSync(password, salt, keylen, options);
+ process.nextTick(
+ callback,
+ null,
+ DEFAULT_ENCODING !== "buffer"
+ ? new Buffer(result).toString(DEFAULT_ENCODING)
+ : new Buffer(result),
+ );
+ } catch (err) {
+ throw err;
+ }
+ }
+ : undefined;
+
if (timingSafeEqual) {
// hide it from stack trace
Object.defineProperty(timingSafeEqual, "name", {
value: "::bunternal::",
});
+ Object.defineProperty(scrypt, "name", {
+ value: "::bunternal::",
+ });
+ Object.defineProperty(scryptSync, "name", {
+ value: "::bunternal::",
+ });
}
export const webcrypto = crypto;
diff --git a/test/bun.js/crypto-scrypt.test.js b/test/bun.js/crypto-scrypt.test.js
new file mode 100644
index 000000000..4b9f632c0
--- /dev/null
+++ b/test/bun.js/crypto-scrypt.test.js
@@ -0,0 +1,265 @@
+// most of these tests are taken from Node.js
+// thank you Node.js team for the tests
+import { expect, it } from "bun:test";
+const crypto = require("crypto");
+
+const assert = {
+ strictEqual: (a, b) => {
+ expect(a).toEqual(b);
+ },
+ deepStrictEqual: (a, b) => {
+ expect(a).toEqual(b);
+ },
+ throws: (fn, err) => {
+ try {
+ fn();
+ throw "Fail";
+ } catch (e) {
+ if (err.name) {
+ expect(e?.name).toEqual(err.name);
+ }
+
+ // if (err.message) {
+ // expect(err.message.test(e?.message)).toBeTruthy();
+ // }
+ if (err.code) {
+ expect(e?.code).toEqual(err.code);
+ }
+
+ expect(e).not.toEqual("Fail");
+ return;
+ }
+ },
+};
+
+const good = [
+ // Zero-length key is legal, functions as a parameter validation check.
+ {
+ pass: "",
+ salt: "",
+ keylen: 0,
+ N: 16,
+ p: 1,
+ r: 1,
+ expected: "",
+ },
+ // Test vectors from https://tools.ietf.org/html/rfc7914#page-13 that
+ // should pass. Note that the test vector with N=1048576 is omitted
+ // because it takes too long to complete and uses over 1 GB of memory.
+ {
+ pass: "",
+ salt: "",
+ keylen: 64,
+ N: 16,
+ p: 1,
+ r: 1,
+ expected:
+ "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442" +
+ "fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906",
+ },
+ {
+ pass: "password",
+ salt: "NaCl",
+ keylen: 64,
+ N: 1024,
+ p: 16,
+ r: 8,
+ expected:
+ "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162" +
+ "2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640",
+ },
+ {
+ pass: "pleaseletmein",
+ salt: "SodiumChloride",
+ keylen: 64,
+ N: 16384,
+ p: 1,
+ r: 8,
+ expected:
+ "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2" +
+ "d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887",
+ },
+ {
+ pass: "",
+ salt: "",
+ keylen: 64,
+ cost: 16,
+ parallelization: 1,
+ blockSize: 1,
+ expected:
+ "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442" +
+ "fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906",
+ },
+ {
+ pass: "password",
+ salt: "NaCl",
+ keylen: 64,
+ cost: 1024,
+ parallelization: 16,
+ blockSize: 8,
+ expected:
+ "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162" +
+ "2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640",
+ },
+ {
+ pass: "pleaseletmein",
+ salt: "SodiumChloride",
+ keylen: 64,
+ cost: 16384,
+ parallelization: 1,
+ blockSize: 8,
+ expected:
+ "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2" +
+ "d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887",
+ },
+];
+
+// Test vectors that should fail.
+const bad = [
+ { N: 1, p: 1, r: 1 }, // N < 2
+ { N: 3, p: 1, r: 1 }, // Not power of 2.
+ { N: 1, cost: 1 }, // Both N and cost
+ // TODO: these should error, but I don't quite understand why.
+ // { p: 1, parallelization: 1 }, // Both p and parallelization
+ // { r: 1, blockSize: 1 }, // Both r and blocksize
+];
+
+// Test vectors where 128*N*r exceeds maxmem.
+const toobig = [
+ { N: 2 ** 16, p: 1, r: 1 }, // N >= 2**(r*16)
+ { N: 2, p: 2 ** 30, r: 1 }, // p > (2**30-1)/r
+ { N: 2 ** 20, p: 1, r: 8 },
+ { N: 2 ** 10, p: 1, r: 8, maxmem: 2 ** 20 },
+];
+
+const badargs = [
+ {
+ args: [],
+ expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"password"/ */ },
+ },
+ {
+ args: [null],
+ expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"password"/ */ },
+ },
+ {
+ args: [""],
+ expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"salt"/ */ },
+ },
+ {
+ args: ["", null],
+ expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"salt"/ */ },
+ },
+ {
+ args: ["", ""],
+ expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"keylen"/ */ },
+ },
+ {
+ args: ["", "", null],
+ expected: { code: "ERR_INVALID_ARG_TYPE" /*message: /"keylen"/ */ },
+ },
+ // TODO: throw on these
+ // {
+ // args: ["", "", 0.42],
+ // expected: { code: "ERR_OUT_OF_RANGE" /*message: /"keylen"/ */ },
+ // },
+ // {
+ // args: ["", "", -42],
+ // expected: { code: "ERR_OUT_OF_RANGE" /*message: /"keylen"/ */ },
+ // },
+ // {
+ // args: ["", "", 2147485780],
+ // expected: { code: "ERR_OUT_OF_RANGE" /*message: /"keylen"/ */ },
+ // },
+];
+
+it("scrypt good", () => {
+ for (const options of good) {
+ const { pass, salt, keylen, expected } = options;
+ const actual = crypto.scryptSync(pass, salt, keylen, options);
+ assert.strictEqual(actual.toString("hex"), expected);
+ }
+});
+
+it("scrypt bad", () => {
+ for (const options of bad) {
+ const expected = {
+ message: /Invalid scrypt param/,
+ };
+ assert.throws(
+ () => crypto.scryptSync("pass", "salt", 1, options),
+ expected,
+ );
+ }
+});
+
+it("scrypt toobig", () => {
+ for (const options of toobig) {
+ const expected = {
+ message: /Invalid scrypt param/,
+ };
+ assert.throws(
+ () => crypto.scryptSync("pass", "salt", 1, options),
+ expected,
+ );
+ }
+});
+
+it("scrypt defaults eql", () => {
+ {
+ const defaults = { N: 16384, p: 1, r: 8 };
+ const expected = crypto.scryptSync("pass", "salt", 1, defaults);
+ const actual = crypto.scryptSync("pass", "salt", 1);
+ assert.deepStrictEqual(actual.toString("hex"), expected.toString("hex"));
+ }
+});
+
+// TODO: DEFAULT_ENCODING is read-only
+// it("scrypt defaults encoding", () => {
+// {
+// const defaultEncoding = crypto.DEFAULT_ENCODING;
+// const defaults = { N: 16384, p: 1, r: 8 };
+// const expected = crypto.scryptSync("pass", "salt", 1, defaults);
+
+// const testEncoding = "latin1";
+// crypto.DEFAULT_ENCODING = testEncoding;
+// const actual = crypto.scryptSync("pass", "salt", 1);
+// assert.deepStrictEqual(actual, expected.toString(testEncoding));
+
+// crypto.DEFAULT_ENCODING = defaultEncoding;
+// }
+// });
+
+it("scrypt badargs", () => {
+ {
+ for (const { args, expected } of badargs) {
+ assert.throws(() => crypto.scryptSync(...args), expected);
+ }
+ }
+
+ {
+ const expected = { code: "ERR_INVALID_ARG_TYPE" };
+ assert.throws(() => crypto.scryptSync("", "", 42, null), expected);
+ // assert.throws(() => crypto.scryptSync("", "", 42, {}, null), expected);
+ // assert.throws(() => crypto.scryptSync("", "", 42, {}), expected);
+ // assert.throws(() => crypto.scryptSync("", "", 42, {}, {}), expected);
+ }
+
+ // {
+ // // Values for maxmem that do not fit in 32 bits but that are still safe
+ // // integers should be allowed.
+ // crypto.scrypt(
+ // "",
+ // "",
+ // 4,
+ // { maxmem: 2 ** 52 },
+ // common.mustSucceed((actual) => {
+ // assert.strictEqual(actual.toString("hex"), "d72c87d0");
+ // }),
+ // );
+
+ // // Values that exceed Number.isSafeInteger should not be allowed.
+ // assert.throws(() => crypto.scryptSync("", "", 0, { maxmem: 2 ** 53 }), {
+ // code: "ERR_OUT_OF_RANGE",
+ // });
+ // }
+});