mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
fix(ext/node): prime generation (#18861)
Towards https://github.com/denoland/deno/issues/18455 `safe`, `add` and `rem` options are not implemented because there is no rust crate that provides this functionality (except rust-openssl maybe) and its just not clear if this API is used widely.
This commit is contained in:
parent
742cc3111c
commit
b0264bea7d
5 changed files with 256 additions and 154 deletions
|
@ -36,129 +36,129 @@ assert(
|
|||
checks: 10
|
||||
}));
|
||||
|
||||
// (async function() {
|
||||
// const prime = await pgeneratePrime(36);
|
||||
// assert(await pCheckPrime(prime));
|
||||
// })().then(common.mustCall());
|
||||
(async function() {
|
||||
const prime = await pgeneratePrime(36);
|
||||
assert(await pCheckPrime(prime));
|
||||
})().then(common.mustCall());
|
||||
|
||||
// assert.throws(() => {
|
||||
// generatePrimeSync(32, { bigint: '' });
|
||||
// }, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => {
|
||||
generatePrimeSync(32, { bigint: '' });
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
|
||||
// assert.throws(() => {
|
||||
// generatePrime(32, { bigint: '' }, common.mustNotCall());
|
||||
// }, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => {
|
||||
generatePrime(32, { bigint: '' }, common.mustNotCall());
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
|
||||
// {
|
||||
// const prime = generatePrimeSync(3, { bigint: true });
|
||||
// assert.strictEqual(typeof prime, 'bigint');
|
||||
// assert.strictEqual(prime, 7n);
|
||||
// assert(checkPrimeSync(prime));
|
||||
// checkPrime(prime, common.mustSucceed(assert));
|
||||
// }
|
||||
{
|
||||
const prime = generatePrimeSync(3, { bigint: true });
|
||||
assert.strictEqual(typeof prime, 'bigint');
|
||||
assert.strictEqual(prime, 7n);
|
||||
assert(checkPrimeSync(prime));
|
||||
checkPrime(prime, common.mustSucceed(assert));
|
||||
}
|
||||
|
||||
// {
|
||||
// generatePrime(3, { bigint: true }, common.mustSucceed((prime) => {
|
||||
// assert.strictEqual(typeof prime, 'bigint');
|
||||
// assert.strictEqual(prime, 7n);
|
||||
// assert(checkPrimeSync(prime));
|
||||
// checkPrime(prime, common.mustSucceed(assert));
|
||||
// }));
|
||||
// }
|
||||
{
|
||||
generatePrime(3, { bigint: true }, common.mustSucceed((prime) => {
|
||||
assert.strictEqual(typeof prime, 'bigint');
|
||||
assert.strictEqual(prime, 7n);
|
||||
assert(checkPrimeSync(prime));
|
||||
checkPrime(prime, common.mustSucceed(assert));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
// ['hello', false, {}, []].forEach((i) => {
|
||||
// assert.throws(() => generatePrime(i), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrimeSync(i), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// });
|
||||
['hello', false, {}, []].forEach((i) => {
|
||||
assert.throws(() => generatePrime(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
// ['hello', false, 123].forEach((i) => {
|
||||
// assert.throws(() => generatePrime(80, i, common.mustNotCall()), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrimeSync(80, i), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// });
|
||||
['hello', false, 123].forEach((i) => {
|
||||
assert.throws(() => generatePrime(80, i, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(80, i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
// ['hello', false, 123].forEach((i) => {
|
||||
// assert.throws(() => generatePrime(80, {}), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// });
|
||||
['hello', false, 123].forEach((i) => {
|
||||
assert.throws(() => generatePrime(80, {}), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
// [-1, 0, 2 ** 31, 2 ** 31 + 1, 2 ** 32 - 1, 2 ** 32].forEach((size) => {
|
||||
// assert.throws(() => generatePrime(size, common.mustNotCall()), {
|
||||
// code: 'ERR_OUT_OF_RANGE',
|
||||
// message: />= 1 && <= 2147483647/
|
||||
// });
|
||||
// assert.throws(() => generatePrimeSync(size), {
|
||||
// code: 'ERR_OUT_OF_RANGE',
|
||||
// message: />= 1 && <= 2147483647/
|
||||
// });
|
||||
// });
|
||||
[-1, 0, 2 ** 31, 2 ** 31 + 1, 2 ** 32 - 1, 2 ** 32].forEach((size) => {
|
||||
assert.throws(() => generatePrime(size, common.mustNotCall()), {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: />= 1 && <= 2147483647/
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(size), {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: />= 1 && <= 2147483647/
|
||||
});
|
||||
});
|
||||
|
||||
// ['test', -1, {}, []].forEach((i) => {
|
||||
// assert.throws(() => generatePrime(8, { safe: i }, common.mustNotCall()), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrime(8, { rem: i }, common.mustNotCall()), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrime(8, { add: i }, common.mustNotCall()), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrimeSync(8, { safe: i }), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrimeSync(8, { rem: i }), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// assert.throws(() => generatePrimeSync(8, { add: i }), {
|
||||
// code: 'ERR_INVALID_ARG_TYPE'
|
||||
// });
|
||||
// });
|
||||
['test', -1, {}, []].forEach((i) => {
|
||||
assert.throws(() => generatePrime(8, { safe: i }, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrime(8, { rem: i }, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrime(8, { add: i }, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(8, { safe: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(8, { rem: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(8, { add: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
// {
|
||||
// // Negative BigInts should not be converted to 0 silently.
|
||||
{
|
||||
// Negative BigInts should not be converted to 0 silently.
|
||||
|
||||
// assert.throws(() => generatePrime(20, { add: -1n }, common.mustNotCall()), {
|
||||
// code: 'ERR_OUT_OF_RANGE',
|
||||
// message: 'The value of "options.add" is out of range. It must be >= 0. ' +
|
||||
// 'Received -1n'
|
||||
// });
|
||||
assert.throws(() => generatePrime(20, { add: -1n }, common.mustNotCall()), {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: 'The value of "options.add" is out of range. It must be >= 0. ' +
|
||||
'Received -1n'
|
||||
});
|
||||
|
||||
// assert.throws(() => generatePrime(20, { rem: -1n }, common.mustNotCall()), {
|
||||
// code: 'ERR_OUT_OF_RANGE',
|
||||
// message: 'The value of "options.rem" is out of range. It must be >= 0. ' +
|
||||
// 'Received -1n'
|
||||
// });
|
||||
assert.throws(() => generatePrime(20, { rem: -1n }, common.mustNotCall()), {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: 'The value of "options.rem" is out of range. It must be >= 0. ' +
|
||||
'Received -1n'
|
||||
});
|
||||
|
||||
// assert.throws(() => checkPrime(-1n, common.mustNotCall()), {
|
||||
// code: 'ERR_OUT_OF_RANGE',
|
||||
// message: 'The value of "candidate" is out of range. It must be >= 0. ' +
|
||||
// 'Received -1n'
|
||||
// });
|
||||
// }
|
||||
// assert.throws(() => checkPrime(-1n, common.mustNotCall()), {
|
||||
// code: 'ERR_OUT_OF_RANGE',
|
||||
// message: 'The value of "candidate" is out of range. It must be >= 0. ' +
|
||||
// 'Received -1n'
|
||||
// });
|
||||
}
|
||||
|
||||
// generatePrime(80, common.mustSucceed((prime) => {
|
||||
// assert(checkPrimeSync(prime));
|
||||
// checkPrime(prime, common.mustSucceed((result) => {
|
||||
// assert(result);
|
||||
// }));
|
||||
// }));
|
||||
generatePrime(80, common.mustSucceed((prime) => {
|
||||
assert(checkPrimeSync(prime));
|
||||
checkPrime(prime, common.mustSucceed((result) => {
|
||||
assert(result);
|
||||
}));
|
||||
}));
|
||||
|
||||
// assert(checkPrimeSync(generatePrimeSync(80)));
|
||||
assert(checkPrimeSync(generatePrimeSync(80)));
|
||||
|
||||
// generatePrime(80, {}, common.mustSucceed((prime) => {
|
||||
// assert(checkPrimeSync(prime));
|
||||
// }));
|
||||
generatePrime(80, {}, common.mustSucceed((prime) => {
|
||||
assert(checkPrimeSync(prime));
|
||||
}));
|
||||
|
||||
// assert(checkPrimeSync(generatePrimeSync(80, {})));
|
||||
assert(checkPrimeSync(generatePrimeSync(80, {})));
|
||||
|
||||
// generatePrime(32, { safe: true }, common.mustSucceed((prime) => {
|
||||
// assert(checkPrimeSync(prime));
|
||||
|
|
|
@ -213,6 +213,8 @@ deno_core::extension!(deno_node,
|
|||
ops::crypto::op_node_check_prime_async,
|
||||
ops::crypto::op_node_check_prime_bytes,
|
||||
ops::crypto::op_node_check_prime_bytes_async,
|
||||
ops::crypto::op_node_gen_prime,
|
||||
ops::crypto::op_node_gen_prime_async,
|
||||
ops::crypto::op_node_pbkdf2,
|
||||
ops::crypto::op_node_pbkdf2_async,
|
||||
ops::crypto::op_node_hkdf,
|
||||
|
|
|
@ -901,3 +901,20 @@ pub async fn op_node_scrypt_async(
|
|||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gen_prime(size: usize) -> ZeroCopyBuf {
|
||||
primes::Prime::generate(size).0.to_bytes_be().into()
|
||||
}
|
||||
|
||||
#[op]
|
||||
pub fn op_node_gen_prime(size: usize) -> ZeroCopyBuf {
|
||||
gen_prime(size)
|
||||
}
|
||||
|
||||
#[op]
|
||||
pub async fn op_node_gen_prime_async(
|
||||
size: usize,
|
||||
) -> Result<ZeroCopyBuf, AnyError> {
|
||||
Ok(tokio::task::spawn_blocking(move || gen_prime(size)).await?)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use num_traits::Zero;
|
|||
use rand::Rng;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct Prime(num_bigint_dig::BigUint);
|
||||
pub struct Prime(pub num_bigint_dig::BigUint);
|
||||
|
||||
impl Prime {
|
||||
pub fn generate(n: usize) -> Self {
|
||||
|
|
|
@ -8,6 +8,7 @@ import randomFill, {
|
|||
} from "ext:deno_node/internal/crypto/_randomFill.ts";
|
||||
import randomInt from "ext:deno_node/internal/crypto/_randomInt.ts";
|
||||
import {
|
||||
validateBoolean,
|
||||
validateFunction,
|
||||
validateInt32,
|
||||
validateObject,
|
||||
|
@ -16,7 +17,10 @@ import {
|
|||
isAnyArrayBuffer,
|
||||
isArrayBufferView,
|
||||
} from "ext:deno_node/internal/util/types.ts";
|
||||
import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
|
||||
import {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_OUT_OF_RANGE,
|
||||
} from "ext:deno_node/internal/errors.ts";
|
||||
|
||||
export { default as randomBytes } from "ext:deno_node/internal/crypto/_randomBytes.ts";
|
||||
export {
|
||||
|
@ -142,62 +146,141 @@ export interface GeneratePrimeOptions {
|
|||
bigint?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface GeneratePrimeOptionsBigInt extends GeneratePrimeOptions {
|
||||
bigint: true;
|
||||
}
|
||||
|
||||
export interface GeneratePrimeOptionsArrayBuffer extends GeneratePrimeOptions {
|
||||
bigint?: false | undefined;
|
||||
}
|
||||
|
||||
export function generatePrime(
|
||||
size: number,
|
||||
callback: (err: Error | null, prime: ArrayBuffer) => void,
|
||||
): void;
|
||||
export function generatePrime(
|
||||
size: number,
|
||||
options: GeneratePrimeOptionsBigInt,
|
||||
callback: (err: Error | null, prime: bigint) => void,
|
||||
): void;
|
||||
export function generatePrime(
|
||||
size: number,
|
||||
options: GeneratePrimeOptionsArrayBuffer,
|
||||
callback: (err: Error | null, prime: ArrayBuffer) => void,
|
||||
): void;
|
||||
export function generatePrime(
|
||||
size: number,
|
||||
options: GeneratePrimeOptions,
|
||||
callback: (err: Error | null, prime: ArrayBuffer | bigint) => void,
|
||||
): void;
|
||||
export function generatePrime(
|
||||
_size: number,
|
||||
_options?: unknown,
|
||||
_callback?: unknown,
|
||||
options: GeneratePrimeOptions = {},
|
||||
callback?: (err: Error | null, prime: ArrayBuffer | bigint) => void,
|
||||
) {
|
||||
notImplemented("crypto.generatePrime");
|
||||
validateInt32(size, "size", 1);
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
validateFunction(callback, "callback");
|
||||
const {
|
||||
bigint,
|
||||
} = validateRandomPrimeJob(size, options);
|
||||
core.opAsync2("op_node_gen_prime_async", size).then((prime: Uint8Array) =>
|
||||
bigint ? arrayBufferToUnsignedBigInt(prime.buffer) : prime.buffer
|
||||
).then((prime: ArrayBuffer | bigint) => {
|
||||
callback?.(null, prime);
|
||||
});
|
||||
}
|
||||
|
||||
export function generatePrimeSync(size: number): ArrayBuffer;
|
||||
export function generatePrimeSync(
|
||||
size: number,
|
||||
options: GeneratePrimeOptionsBigInt,
|
||||
): bigint;
|
||||
export function generatePrimeSync(
|
||||
size: number,
|
||||
options: GeneratePrimeOptionsArrayBuffer,
|
||||
): ArrayBuffer;
|
||||
export function generatePrimeSync(
|
||||
options: GeneratePrimeOptions = {},
|
||||
): ArrayBuffer | bigint {
|
||||
const {
|
||||
bigint,
|
||||
} = validateRandomPrimeJob(size, options);
|
||||
|
||||
const prime = ops.op_node_gen_prime(size);
|
||||
if (bigint) return arrayBufferToUnsignedBigInt(prime.buffer);
|
||||
return prime.buffer;
|
||||
}
|
||||
|
||||
function validateRandomPrimeJob(
|
||||
size: number,
|
||||
options: GeneratePrimeOptions,
|
||||
): ArrayBuffer | bigint;
|
||||
export function generatePrimeSync(
|
||||
_size: number,
|
||||
_options?:
|
||||
| GeneratePrimeOptionsBigInt
|
||||
| GeneratePrimeOptionsArrayBuffer
|
||||
| GeneratePrimeOptions,
|
||||
): ArrayBuffer | bigint {
|
||||
notImplemented("crypto.generatePrimeSync");
|
||||
): GeneratePrimeOptions {
|
||||
validateInt32(size, "size", 1);
|
||||
validateObject(options, "options");
|
||||
|
||||
let {
|
||||
safe = false,
|
||||
bigint = false,
|
||||
add,
|
||||
rem,
|
||||
} = options!;
|
||||
|
||||
validateBoolean(safe, "options.safe");
|
||||
validateBoolean(bigint, "options.bigint");
|
||||
|
||||
if (add !== undefined) {
|
||||
if (typeof add === "bigint") {
|
||||
add = unsignedBigIntToBuffer(add, "options.add");
|
||||
} else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
"options.add",
|
||||
[
|
||||
"ArrayBuffer",
|
||||
"TypedArray",
|
||||
"Buffer",
|
||||
"DataView",
|
||||
"bigint",
|
||||
],
|
||||
add,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (rem !== undefined) {
|
||||
if (typeof rem === "bigint") {
|
||||
rem = unsignedBigIntToBuffer(rem, "options.rem");
|
||||
} else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
"options.rem",
|
||||
[
|
||||
"ArrayBuffer",
|
||||
"TypedArray",
|
||||
"Buffer",
|
||||
"DataView",
|
||||
"bigint",
|
||||
],
|
||||
rem,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@littledivy): safe, add and rem options are not implemented.
|
||||
if (safe || add || rem) {
|
||||
notImplemented("safe, add and rem options are not implemented.");
|
||||
}
|
||||
|
||||
return {
|
||||
safe,
|
||||
bigint,
|
||||
add,
|
||||
rem,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 48 is the ASCII code for '0', 97 is the ASCII code for 'a'.
|
||||
* @param {number} number An integer between 0 and 15.
|
||||
* @returns {number} corresponding to the ASCII code of the hex representation
|
||||
* of the parameter.
|
||||
*/
|
||||
const numberToHexCharCode = (number: number): number =>
|
||||
(number < 10 ? 48 : 87) + number;
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} buf An ArrayBuffer.
|
||||
* @return {bigint}
|
||||
*/
|
||||
function arrayBufferToUnsignedBigInt(buf: ArrayBuffer): bigint {
|
||||
const length = buf.byteLength;
|
||||
const chars: number[] = Array(length * 2);
|
||||
const view = new DataView(buf);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const val = view.getUint8(i);
|
||||
chars[2 * i] = numberToHexCharCode(val >> 4);
|
||||
chars[2 * i + 1] = numberToHexCharCode(val & 0xf);
|
||||
}
|
||||
|
||||
return BigInt(`0x${String.fromCharCode(...chars)}`);
|
||||
}
|
||||
|
||||
function unsignedBigIntToBuffer(bigint: bigint, name: string) {
|
||||
if (bigint < 0) {
|
||||
throw new ERR_OUT_OF_RANGE(name, ">= 0", bigint);
|
||||
}
|
||||
|
||||
const hex = bigint.toString(16);
|
||||
const padded = hex.padStart(hex.length + (hex.length % 2), 0);
|
||||
return Buffer.from(padded, "hex");
|
||||
}
|
||||
|
||||
export const randomUUID = () => globalThis.crypto.randomUUID();
|
||||
|
|
Loading…
Reference in a new issue