mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 00:29:09 -05:00
refactor(node/crypto): port polyfill to Rust for randomInt, randomFill, randomFillSync (#18658)
Pretty much as per the title, I'd welcome some feedback especially around the array/buffer handling in the two randomFill functions.
This commit is contained in:
parent
8820f6e922
commit
65b0d2316d
5 changed files with 37 additions and 18 deletions
|
@ -6,6 +6,7 @@ import {
|
||||||
assertNotEquals,
|
assertNotEquals,
|
||||||
assertThrows,
|
assertThrows,
|
||||||
} from "../../../../test_util/std/testing/asserts.ts";
|
} from "../../../../test_util/std/testing/asserts.ts";
|
||||||
|
import { deferred } from "../../../../test_util/std/async/deferred.ts";
|
||||||
|
|
||||||
const validateNonZero = (buf: Buffer) => {
|
const validateNonZero = (buf: Buffer) => {
|
||||||
if (!buf.some((ch) => ch > 0)) throw new Error("Error");
|
if (!buf.some((ch) => ch > 0)) throw new Error("Error");
|
||||||
|
@ -15,14 +16,18 @@ const validateZero = (buf: Buffer) => {
|
||||||
buf.forEach((val) => assertEquals(val, 0));
|
buf.forEach((val) => assertEquals(val, 0));
|
||||||
};
|
};
|
||||||
|
|
||||||
Deno.test("[node/crypto.randomFill]", () => {
|
Deno.test("[node/crypto.randomFill]", async () => {
|
||||||
|
const promise = deferred();
|
||||||
const buf = Buffer.alloc(10);
|
const buf = Buffer.alloc(10);
|
||||||
const before = buf.toString("hex");
|
const before = buf.toString("hex");
|
||||||
|
|
||||||
randomFill(buf, 5, 5, (_err, bufTwo) => {
|
randomFill(buf, 5, 5, (_err, bufTwo) => {
|
||||||
const after = bufTwo?.toString("hex");
|
const after = bufTwo?.toString("hex");
|
||||||
assertEquals(before.slice(0, 10), after?.slice(0, 10));
|
assertEquals(before.slice(0, 10), after?.slice(0, 10));
|
||||||
|
promise.resolve(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("[node/crypto.randomFillSync]", () => {
|
Deno.test("[node/crypto.randomFillSync]", () => {
|
||||||
|
|
|
@ -9,6 +9,8 @@ use deno_core::StringOrBuffer;
|
||||||
use deno_core::ZeroCopyBuf;
|
use deno_core::ZeroCopyBuf;
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
use rand::distributions::Distribution;
|
||||||
|
use rand::distributions::Uniform;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -478,3 +480,13 @@ pub async fn op_node_hkdf_async(
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
pub fn op_node_random_int(min: i32, max: i32) -> Result<i32, AnyError> {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
// Uniform distribution is required to avoid Modulo Bias
|
||||||
|
// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle#Modulo_bias
|
||||||
|
let dist = Uniform::from(min..max);
|
||||||
|
|
||||||
|
Ok(dist.sample(&mut rng))
|
||||||
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@ deno_core::extension!(deno_node,
|
||||||
crypto::op_node_generate_secret,
|
crypto::op_node_generate_secret,
|
||||||
crypto::op_node_generate_secret_async,
|
crypto::op_node_generate_secret_async,
|
||||||
crypto::op_node_sign,
|
crypto::op_node_sign,
|
||||||
|
crypto::op_node_random_int,
|
||||||
crypto::x509::op_node_x509_parse,
|
crypto::x509::op_node_x509_parse,
|
||||||
crypto::x509::op_node_x509_ca,
|
crypto::x509::op_node_x509_ca,
|
||||||
crypto::x509::op_node_x509_check_email,
|
crypto::x509::op_node_x509_check_email,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
import randomBytes, {
|
import {
|
||||||
MAX_SIZE as kMaxUint32,
|
MAX_SIZE as kMaxUint32,
|
||||||
} from "ext:deno_node/internal/crypto/_randomBytes.ts";
|
} from "ext:deno_node/internal/crypto/_randomBytes.ts";
|
||||||
import { Buffer } from "ext:deno_node/buffer.ts";
|
import { Buffer } from "ext:deno_node/buffer.ts";
|
||||||
|
const { core } = globalThis.__bootstrap;
|
||||||
|
const { ops } = core;
|
||||||
|
|
||||||
const kBufferMaxLength = 0x7fffffff;
|
const kBufferMaxLength = 0x7fffffff;
|
||||||
|
|
||||||
|
@ -62,11 +64,14 @@ export default function randomFill(
|
||||||
assertOffset(offset as number, buf.length);
|
assertOffset(offset as number, buf.length);
|
||||||
assertSize(size as number, offset as number, buf.length);
|
assertSize(size as number, offset as number, buf.length);
|
||||||
|
|
||||||
randomBytes(size as number, (err, bytes) => {
|
core.opAsync("op_node_generate_secret_async", Math.floor(size as number))
|
||||||
if (err) return cb!(err, buf);
|
.then(
|
||||||
bytes?.copy(buf, offset as number);
|
(randomData: Uint8Array) => {
|
||||||
cb!(null, buf);
|
const randomBuf = Buffer.from(randomData.buffer);
|
||||||
});
|
randomBuf.copy(buf, offset as number, 0, size as number);
|
||||||
|
cb!(null, buf);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function randomFillSync(buf: Buffer, offset = 0, size?: number) {
|
export function randomFillSync(buf: Buffer, offset = 0, size?: number) {
|
||||||
|
@ -76,9 +81,10 @@ export function randomFillSync(buf: Buffer, offset = 0, size?: number) {
|
||||||
|
|
||||||
assertSize(size, offset, buf.length);
|
assertSize(size, offset, buf.length);
|
||||||
|
|
||||||
const bytes = randomBytes(size);
|
const bytes: Uint8Array = new Uint8Array(Math.floor(size));
|
||||||
|
ops.op_node_generate_secret(bytes);
|
||||||
bytes.copy(buf, offset);
|
const bytesBuf: Buffer = Buffer.from(bytes.buffer);
|
||||||
|
bytesBuf.copy(buf, offset, 0, size);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
const ops = globalThis.Deno.core.ops;
|
||||||
|
|
||||||
export default function randomInt(max: number): number;
|
export default function randomInt(max: number): number;
|
||||||
export default function randomInt(min: number, max: number): number;
|
export default function randomInt(min: number, max: number): number;
|
||||||
export default function randomInt(
|
export default function randomInt(
|
||||||
|
@ -40,16 +42,9 @@ export default function randomInt(
|
||||||
throw new Error("Min is bigger than Max!");
|
throw new Error("Min is bigger than Max!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const randomBuffer = new Uint32Array(1);
|
|
||||||
|
|
||||||
globalThis.crypto.getRandomValues(randomBuffer);
|
|
||||||
|
|
||||||
const randomNumber = randomBuffer[0] / (0xffffffff + 1);
|
|
||||||
|
|
||||||
min = Math.ceil(min);
|
min = Math.ceil(min);
|
||||||
max = Math.floor(max);
|
max = Math.floor(max);
|
||||||
|
const result = ops.op_node_random_int(min, max);
|
||||||
const result = Math.floor(randomNumber * (max - min)) + min;
|
|
||||||
|
|
||||||
if (cb) {
|
if (cb) {
|
||||||
cb(null, result);
|
cb(null, result);
|
||||||
|
|
Loading…
Reference in a new issue