From 10012c2fe312a4f7ddc5217adaa6718c91bfb819 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Tue, 28 Mar 2023 16:26:38 +0530 Subject: feat(ext/node): add `crypto.checkPrime` API (#18465) Towards #18455 This commit implements `checkPrimeSync` and `checkPrime` in node:crypto using the Miller-Rabin primality test (fun fact: it actually is a test for composite numbers) It first compares the candidate against many known small primes and if not, proceeds to run the Miller-Rabin primality test. http://nickle.org/examples/miller-rabin.5c used as reference implementation. --- ext/node/polyfills/internal/crypto/random.ts | 89 +++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 7 deletions(-) (limited to 'ext/node/polyfills/internal/crypto') diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts index cd2333d8c..07d91976a 100644 --- a/ext/node/polyfills/internal/crypto/random.ts +++ b/ext/node/polyfills/internal/crypto/random.ts @@ -7,6 +7,16 @@ import randomFill, { randomFillSync, } from "ext:deno_node/internal/crypto/_randomFill.ts"; import randomInt from "ext:deno_node/internal/crypto/_randomInt.ts"; +import { + validateFunction, + validateInt32, + validateObject, +} from "ext:deno_node/internal/validators.mjs"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "ext:deno_node/internal/util/types.ts"; +import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; export { default as randomBytes } from "ext:deno_node/internal/crypto/_randomBytes.ts"; export { @@ -15,6 +25,9 @@ export { } from "ext:deno_node/internal/crypto/_randomFill.ts"; export { default as randomInt } from "ext:deno_node/internal/crypto/_randomInt.ts"; +const { core } = globalThis.__bootstrap; +const { ops } = core; + export type LargeNumberLike = | ArrayBufferView | SharedArrayBuffer @@ -43,18 +56,80 @@ export function checkPrime( callback: (err: Error | null, result: boolean) => void, ): void; export function checkPrime( - _candidate: LargeNumberLike, - _options?: CheckPrimeOptions | ((err: Error | null, result: boolean) => void), - _callback?: (err: Error | null, result: boolean) => void, + candidate: LargeNumberLike, + options: CheckPrimeOptions | ((err: Error | null, result: boolean) => void) = + {}, + callback?: (err: Error | null, result: boolean) => void, ) { - notImplemented("crypto.checkPrime"); + if (typeof options === "function") { + callback = options; + options = {}; + } + + validateFunction(callback, "callback"); + validateObject(options, "options"); + + const { + checks = 0, + } = options!; + + validateInt32(checks, "options.checks", 0); + + let op = "op_node_check_prime_bytes_async"; + if (typeof candidate === "bigint") { + op = "op_node_check_prime_async"; + } else if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) { + throw new ERR_INVALID_ARG_TYPE( + "candidate", + [ + "ArrayBuffer", + "TypedArray", + "Buffer", + "DataView", + "bigint", + ], + candidate, + ); + } + + core.opAsync(op, candidate, checks).then( + (result) => { + callback?.(null, result); + }, + ).catch((err) => { + callback?.(err, false); + }); } export function checkPrimeSync( - _candidate: LargeNumberLike, - _options?: CheckPrimeOptions, + candidate: LargeNumberLike, + options: CheckPrimeOptions = {}, ): boolean { - notImplemented("crypto.checkPrimeSync"); + validateObject(options, "options"); + + const { + checks = 0, + } = options!; + + validateInt32(checks, "options.checks", 0); + + if (typeof candidate === "bigint") { + return ops.op_node_check_prime(candidate, checks); + } else if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) { + throw new ERR_INVALID_ARG_TYPE( + "candidate", + [ + "ArrayBuffer", + "TypedArray", + "Buffer", + "DataView", + "bigint", + ], + candidate, + ); + } + + return ops.op_node_check_prime_bytes(candidate, checks); } export interface GeneratePrimeOptions { -- cgit v1.2.3