1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -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:
Levente Kurusa 2023-04-12 02:57:57 +02:00 committed by GitHub
parent 8820f6e922
commit 65b0d2316d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 37 additions and 18 deletions

View file

@ -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]", () => {

View file

@ -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/FisherYates_shuffle#Modulo_bias
let dist = Uniform::from(min..max);
Ok(dist.sample(&mut rng))
}

View file

@ -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,

View file

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

View file

@ -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);