mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -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,
|
||||
assertThrows,
|
||||
} from "../../../../test_util/std/testing/asserts.ts";
|
||||
import { deferred } from "../../../../test_util/std/async/deferred.ts";
|
||||
|
||||
const validateNonZero = (buf: Buffer) => {
|
||||
if (!buf.some((ch) => ch > 0)) throw new Error("Error");
|
||||
|
@ -15,14 +16,18 @@ const validateZero = (buf: Buffer) => {
|
|||
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 before = buf.toString("hex");
|
||||
|
||||
randomFill(buf, 5, 5, (_err, bufTwo) => {
|
||||
const after = bufTwo?.toString("hex");
|
||||
assertEquals(before.slice(0, 10), after?.slice(0, 10));
|
||||
promise.resolve(true);
|
||||
});
|
||||
|
||||
await promise;
|
||||
});
|
||||
|
||||
Deno.test("[node/crypto.randomFillSync]", () => {
|
||||
|
|
|
@ -9,6 +9,8 @@ use deno_core::StringOrBuffer;
|
|||
use deno_core::ZeroCopyBuf;
|
||||
use hkdf::Hkdf;
|
||||
use num_bigint::BigInt;
|
||||
use rand::distributions::Distribution;
|
||||
use rand::distributions::Uniform;
|
||||
use rand::Rng;
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
|
@ -478,3 +480,13 @@ pub async fn op_node_hkdf_async(
|
|||
})
|
||||
.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_async,
|
||||
crypto::op_node_sign,
|
||||
crypto::op_node_random_int,
|
||||
crypto::x509::op_node_x509_parse,
|
||||
crypto::x509::op_node_x509_ca,
|
||||
crypto::x509::op_node_x509_check_email,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
import randomBytes, {
|
||||
import {
|
||||
MAX_SIZE as kMaxUint32,
|
||||
} from "ext:deno_node/internal/crypto/_randomBytes.ts";
|
||||
import { Buffer } from "ext:deno_node/buffer.ts";
|
||||
const { core } = globalThis.__bootstrap;
|
||||
const { ops } = core;
|
||||
|
||||
const kBufferMaxLength = 0x7fffffff;
|
||||
|
||||
|
@ -62,11 +64,14 @@ export default function randomFill(
|
|||
assertOffset(offset as number, buf.length);
|
||||
assertSize(size as number, offset as number, buf.length);
|
||||
|
||||
randomBytes(size as number, (err, bytes) => {
|
||||
if (err) return cb!(err, buf);
|
||||
bytes?.copy(buf, offset as number);
|
||||
cb!(null, buf);
|
||||
});
|
||||
core.opAsync("op_node_generate_secret_async", Math.floor(size as number))
|
||||
.then(
|
||||
(randomData: Uint8Array) => {
|
||||
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) {
|
||||
|
@ -76,9 +81,10 @@ export function randomFillSync(buf: Buffer, offset = 0, size?: number) {
|
|||
|
||||
assertSize(size, offset, buf.length);
|
||||
|
||||
const bytes = randomBytes(size);
|
||||
|
||||
bytes.copy(buf, offset);
|
||||
const bytes: Uint8Array = new Uint8Array(Math.floor(size));
|
||||
ops.op_node_generate_secret(bytes);
|
||||
const bytesBuf: Buffer = Buffer.from(bytes.buffer);
|
||||
bytesBuf.copy(buf, offset, 0, size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// 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(min: number, max: number): number;
|
||||
export default function randomInt(
|
||||
|
@ -40,16 +42,9 @@ export default function randomInt(
|
|||
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);
|
||||
max = Math.floor(max);
|
||||
|
||||
const result = Math.floor(randomNumber * (max - min)) + min;
|
||||
const result = ops.op_node_random_int(min, max);
|
||||
|
||||
if (cb) {
|
||||
cb(null, result);
|
||||
|
|
Loading…
Reference in a new issue