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
|
checks: 10
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// (async function() {
|
(async function() {
|
||||||
// const prime = await pgeneratePrime(36);
|
const prime = await pgeneratePrime(36);
|
||||||
// assert(await pCheckPrime(prime));
|
assert(await pCheckPrime(prime));
|
||||||
// })().then(common.mustCall());
|
})().then(common.mustCall());
|
||||||
|
|
||||||
// assert.throws(() => {
|
assert.throws(() => {
|
||||||
// generatePrimeSync(32, { bigint: '' });
|
generatePrimeSync(32, { bigint: '' });
|
||||||
// }, { code: 'ERR_INVALID_ARG_TYPE' });
|
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||||
|
|
||||||
// assert.throws(() => {
|
assert.throws(() => {
|
||||||
// generatePrime(32, { bigint: '' }, common.mustNotCall());
|
generatePrime(32, { bigint: '' }, common.mustNotCall());
|
||||||
// }, { code: 'ERR_INVALID_ARG_TYPE' });
|
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||||
|
|
||||||
// {
|
{
|
||||||
// const prime = generatePrimeSync(3, { bigint: true });
|
const prime = generatePrimeSync(3, { bigint: true });
|
||||||
// assert.strictEqual(typeof prime, 'bigint');
|
assert.strictEqual(typeof prime, 'bigint');
|
||||||
// assert.strictEqual(prime, 7n);
|
assert.strictEqual(prime, 7n);
|
||||||
// assert(checkPrimeSync(prime));
|
assert(checkPrimeSync(prime));
|
||||||
// checkPrime(prime, common.mustSucceed(assert));
|
checkPrime(prime, common.mustSucceed(assert));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// {
|
{
|
||||||
// generatePrime(3, { bigint: true }, common.mustSucceed((prime) => {
|
generatePrime(3, { bigint: true }, common.mustSucceed((prime) => {
|
||||||
// assert.strictEqual(typeof prime, 'bigint');
|
assert.strictEqual(typeof prime, 'bigint');
|
||||||
// assert.strictEqual(prime, 7n);
|
assert.strictEqual(prime, 7n);
|
||||||
// assert(checkPrimeSync(prime));
|
assert(checkPrimeSync(prime));
|
||||||
// checkPrime(prime, common.mustSucceed(assert));
|
checkPrime(prime, common.mustSucceed(assert));
|
||||||
// }));
|
}));
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
// ['hello', false, {}, []].forEach((i) => {
|
['hello', false, {}, []].forEach((i) => {
|
||||||
// assert.throws(() => generatePrime(i), {
|
assert.throws(() => generatePrime(i), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrimeSync(i), {
|
assert.throws(() => generatePrimeSync(i), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
// ['hello', false, 123].forEach((i) => {
|
['hello', false, 123].forEach((i) => {
|
||||||
// assert.throws(() => generatePrime(80, i, common.mustNotCall()), {
|
assert.throws(() => generatePrime(80, i, common.mustNotCall()), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrimeSync(80, i), {
|
assert.throws(() => generatePrimeSync(80, i), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
// ['hello', false, 123].forEach((i) => {
|
['hello', false, 123].forEach((i) => {
|
||||||
// assert.throws(() => generatePrime(80, {}), {
|
assert.throws(() => generatePrime(80, {}), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
// [-1, 0, 2 ** 31, 2 ** 31 + 1, 2 ** 32 - 1, 2 ** 32].forEach((size) => {
|
[-1, 0, 2 ** 31, 2 ** 31 + 1, 2 ** 32 - 1, 2 ** 32].forEach((size) => {
|
||||||
// assert.throws(() => generatePrime(size, common.mustNotCall()), {
|
assert.throws(() => generatePrime(size, common.mustNotCall()), {
|
||||||
// code: 'ERR_OUT_OF_RANGE',
|
code: 'ERR_OUT_OF_RANGE',
|
||||||
// message: />= 1 && <= 2147483647/
|
message: />= 1 && <= 2147483647/
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrimeSync(size), {
|
assert.throws(() => generatePrimeSync(size), {
|
||||||
// code: 'ERR_OUT_OF_RANGE',
|
code: 'ERR_OUT_OF_RANGE',
|
||||||
// message: />= 1 && <= 2147483647/
|
message: />= 1 && <= 2147483647/
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
// ['test', -1, {}, []].forEach((i) => {
|
['test', -1, {}, []].forEach((i) => {
|
||||||
// assert.throws(() => generatePrime(8, { safe: i }, common.mustNotCall()), {
|
assert.throws(() => generatePrime(8, { safe: i }, common.mustNotCall()), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrime(8, { rem: i }, common.mustNotCall()), {
|
assert.throws(() => generatePrime(8, { rem: i }, common.mustNotCall()), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrime(8, { add: i }, common.mustNotCall()), {
|
assert.throws(() => generatePrime(8, { add: i }, common.mustNotCall()), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrimeSync(8, { safe: i }), {
|
assert.throws(() => generatePrimeSync(8, { safe: i }), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrimeSync(8, { rem: i }), {
|
assert.throws(() => generatePrimeSync(8, { rem: i }), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
code: 'ERR_INVALID_ARG_TYPE'
|
||||||
// });
|
});
|
||||||
// assert.throws(() => generatePrimeSync(8, { add: i }), {
|
assert.throws(() => generatePrimeSync(8, { add: i }), {
|
||||||
// code: 'ERR_INVALID_ARG_TYPE'
|
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()), {
|
assert.throws(() => generatePrime(20, { add: -1n }, common.mustNotCall()), {
|
||||||
// code: 'ERR_OUT_OF_RANGE',
|
code: 'ERR_OUT_OF_RANGE',
|
||||||
// message: 'The value of "options.add" is out of range. It must be >= 0. ' +
|
message: 'The value of "options.add" is out of range. It must be >= 0. ' +
|
||||||
// 'Received -1n'
|
'Received -1n'
|
||||||
// });
|
});
|
||||||
|
|
||||||
// assert.throws(() => generatePrime(20, { rem: -1n }, common.mustNotCall()), {
|
assert.throws(() => generatePrime(20, { rem: -1n }, common.mustNotCall()), {
|
||||||
// code: 'ERR_OUT_OF_RANGE',
|
code: 'ERR_OUT_OF_RANGE',
|
||||||
// message: 'The value of "options.rem" is out of range. It must be >= 0. ' +
|
message: 'The value of "options.rem" is out of range. It must be >= 0. ' +
|
||||||
// 'Received -1n'
|
'Received -1n'
|
||||||
// });
|
});
|
||||||
|
|
||||||
// assert.throws(() => checkPrime(-1n, common.mustNotCall()), {
|
// assert.throws(() => checkPrime(-1n, common.mustNotCall()), {
|
||||||
// code: 'ERR_OUT_OF_RANGE',
|
// code: 'ERR_OUT_OF_RANGE',
|
||||||
// message: 'The value of "candidate" is out of range. It must be >= 0. ' +
|
// message: 'The value of "candidate" is out of range. It must be >= 0. ' +
|
||||||
// 'Received -1n'
|
// 'Received -1n'
|
||||||
// });
|
// });
|
||||||
// }
|
}
|
||||||
|
|
||||||
// generatePrime(80, common.mustSucceed((prime) => {
|
generatePrime(80, common.mustSucceed((prime) => {
|
||||||
// assert(checkPrimeSync(prime));
|
assert(checkPrimeSync(prime));
|
||||||
// checkPrime(prime, common.mustSucceed((result) => {
|
checkPrime(prime, common.mustSucceed((result) => {
|
||||||
// assert(result);
|
assert(result);
|
||||||
// }));
|
}));
|
||||||
// }));
|
}));
|
||||||
|
|
||||||
// assert(checkPrimeSync(generatePrimeSync(80)));
|
assert(checkPrimeSync(generatePrimeSync(80)));
|
||||||
|
|
||||||
// generatePrime(80, {}, common.mustSucceed((prime) => {
|
generatePrime(80, {}, common.mustSucceed((prime) => {
|
||||||
// assert(checkPrimeSync(prime));
|
assert(checkPrimeSync(prime));
|
||||||
// }));
|
}));
|
||||||
|
|
||||||
// assert(checkPrimeSync(generatePrimeSync(80, {})));
|
assert(checkPrimeSync(generatePrimeSync(80, {})));
|
||||||
|
|
||||||
// generatePrime(32, { safe: true }, common.mustSucceed((prime) => {
|
// generatePrime(32, { safe: true }, common.mustSucceed((prime) => {
|
||||||
// assert(checkPrimeSync(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_async,
|
||||||
ops::crypto::op_node_check_prime_bytes,
|
ops::crypto::op_node_check_prime_bytes,
|
||||||
ops::crypto::op_node_check_prime_bytes_async,
|
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,
|
||||||
ops::crypto::op_node_pbkdf2_async,
|
ops::crypto::op_node_pbkdf2_async,
|
||||||
ops::crypto::op_node_hkdf,
|
ops::crypto::op_node_hkdf,
|
||||||
|
|
|
@ -901,3 +901,20 @@ pub async fn op_node_scrypt_async(
|
||||||
})
|
})
|
||||||
.await?
|
.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 rand::Rng;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub struct Prime(num_bigint_dig::BigUint);
|
pub struct Prime(pub num_bigint_dig::BigUint);
|
||||||
|
|
||||||
impl Prime {
|
impl Prime {
|
||||||
pub fn generate(n: usize) -> Self {
|
pub fn generate(n: usize) -> Self {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import randomFill, {
|
||||||
} from "ext:deno_node/internal/crypto/_randomFill.ts";
|
} from "ext:deno_node/internal/crypto/_randomFill.ts";
|
||||||
import randomInt from "ext:deno_node/internal/crypto/_randomInt.ts";
|
import randomInt from "ext:deno_node/internal/crypto/_randomInt.ts";
|
||||||
import {
|
import {
|
||||||
|
validateBoolean,
|
||||||
validateFunction,
|
validateFunction,
|
||||||
validateInt32,
|
validateInt32,
|
||||||
validateObject,
|
validateObject,
|
||||||
|
@ -16,7 +17,10 @@ import {
|
||||||
isAnyArrayBuffer,
|
isAnyArrayBuffer,
|
||||||
isArrayBufferView,
|
isArrayBufferView,
|
||||||
} from "ext:deno_node/internal/util/types.ts";
|
} 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 { default as randomBytes } from "ext:deno_node/internal/crypto/_randomBytes.ts";
|
||||||
export {
|
export {
|
||||||
|
@ -142,62 +146,141 @@ export interface GeneratePrimeOptions {
|
||||||
bigint?: boolean | undefined;
|
bigint?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GeneratePrimeOptionsBigInt extends GeneratePrimeOptions {
|
|
||||||
bigint: true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GeneratePrimeOptionsArrayBuffer extends GeneratePrimeOptions {
|
|
||||||
bigint?: false | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generatePrime(
|
export function generatePrime(
|
||||||
size: number,
|
size: number,
|
||||||
callback: (err: Error | null, prime: ArrayBuffer) => void,
|
options: GeneratePrimeOptions = {},
|
||||||
): void;
|
callback?: (err: Error | null, prime: ArrayBuffer | bigint) => 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,
|
|
||||||
) {
|
) {
|
||||||
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(
|
export function generatePrimeSync(
|
||||||
size: number,
|
size: number,
|
||||||
options: GeneratePrimeOptionsBigInt,
|
options: GeneratePrimeOptions = {},
|
||||||
): bigint;
|
): ArrayBuffer | bigint {
|
||||||
export function generatePrimeSync(
|
const {
|
||||||
size: number,
|
bigint,
|
||||||
options: GeneratePrimeOptionsArrayBuffer,
|
} = validateRandomPrimeJob(size, options);
|
||||||
): ArrayBuffer;
|
|
||||||
export function generatePrimeSync(
|
const prime = ops.op_node_gen_prime(size);
|
||||||
|
if (bigint) return arrayBufferToUnsignedBigInt(prime.buffer);
|
||||||
|
return prime.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateRandomPrimeJob(
|
||||||
size: number,
|
size: number,
|
||||||
options: GeneratePrimeOptions,
|
options: GeneratePrimeOptions,
|
||||||
): ArrayBuffer | bigint;
|
): GeneratePrimeOptions {
|
||||||
export function generatePrimeSync(
|
validateInt32(size, "size", 1);
|
||||||
_size: number,
|
validateObject(options, "options");
|
||||||
_options?:
|
|
||||||
| GeneratePrimeOptionsBigInt
|
let {
|
||||||
| GeneratePrimeOptionsArrayBuffer
|
safe = false,
|
||||||
| GeneratePrimeOptions,
|
bigint = false,
|
||||||
): ArrayBuffer | bigint {
|
add,
|
||||||
notImplemented("crypto.generatePrimeSync");
|
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();
|
export const randomUUID = () => globalThis.crypto.randomUUID();
|
||||||
|
|
Loading…
Reference in a new issue