diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2023-04-27 19:40:59 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-27 19:40:59 +0530 |
commit | b0264bea7de1901c1b3ed764454290d10613d14b (patch) | |
tree | cacf71dca64df1f6f1dde3b44c70b89ce72d9a03 /ext/node/polyfills/internal/crypto/random.ts | |
parent | 742cc3111ccb7c3c12c1b05904be052094657481 (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.ts | 179 |
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(); |