summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal/crypto/random.ts
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2023-04-27 19:40:59 +0530
committerGitHub <noreply@github.com>2023-04-27 19:40:59 +0530
commitb0264bea7de1901c1b3ed764454290d10613d14b (patch)
treecacf71dca64df1f6f1dde3b44c70b89ce72d9a03 /ext/node/polyfills/internal/crypto/random.ts
parent742cc3111ccb7c3c12c1b05904be052094657481 (diff)
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.
Diffstat (limited to 'ext/node/polyfills/internal/crypto/random.ts')
-rw-r--r--ext/node/polyfills/internal/crypto/random.ts179
1 files changed, 131 insertions, 48 deletions
diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts
index 04678b6be..32256b13b 100644
--- a/ext/node/polyfills/internal/crypto/random.ts
+++ b/ext/node/polyfills/internal/crypto/random.ts
@@ -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();