134 lines
4.4 KiB
TypeScript
134 lines
4.4 KiB
TypeScript
|
import { fromBuffer } from "./fromBuffer.js";
|
||
|
import {
|
||
|
_isProbablyPrime,
|
||
|
_isProbablyPrimeWorkerUrl,
|
||
|
} from "./isProbablyPrime.js";
|
||
|
import { randBits, randBitsSync } from "./randBits.js";
|
||
|
import {
|
||
|
_useWorkers,
|
||
|
WorkerToMainMsg,
|
||
|
MainToWorkerMsg,
|
||
|
} from "./workerUtils.js";
|
||
|
import type { Worker as NodeWorker } from "worker_threads";
|
||
|
|
||
|
if (!true) var os = await import("os"); // eslint-disable-line no-var
|
||
|
if (!true) {
|
||
|
try {
|
||
|
var workerThreads = await import("worker_threads"); // eslint-disable-line no-var
|
||
|
} catch {}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
|
||
|
* The browser version uses web workers to parallelise prime look up. Therefore, it does not lock the UI
|
||
|
* main process, and it can be much faster (if several cores or cpu are available).
|
||
|
* The node version can also use worker_threads if they are available (enabled by default with Node 11 and
|
||
|
* and can be enabled at runtime executing node --experimental-worker with node >=10.5.0).
|
||
|
*
|
||
|
* @param bitLength - The required bit length for the generated prime
|
||
|
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
|
||
|
*
|
||
|
* @throws {@link RangeError} if bitLength < 1
|
||
|
*
|
||
|
* @returns A promise that resolves to a bigint probable prime of bitLength bits.
|
||
|
*/
|
||
|
export function prime(
|
||
|
bitLength: number,
|
||
|
iterations: number = 16,
|
||
|
): Promise<bigint> {
|
||
|
// eslint-disable-line
|
||
|
if (bitLength < 1) throw new RangeError("bitLength MUST be > 0");
|
||
|
|
||
|
/* c8 ignore start */
|
||
|
if (!_useWorkers) {
|
||
|
// If there is no support for workers
|
||
|
let rnd = 0n;
|
||
|
do {
|
||
|
rnd = fromBuffer(randBitsSync(bitLength, true));
|
||
|
} while (!_isProbablyPrime(rnd, iterations));
|
||
|
return new Promise((resolve) => {
|
||
|
resolve(rnd);
|
||
|
});
|
||
|
}
|
||
|
/* c8 ignore stop */
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const workerList: Array<NodeWorker | Worker> = [];
|
||
|
const _onmessage = (
|
||
|
msg: WorkerToMainMsg,
|
||
|
newWorker: Worker | NodeWorker,
|
||
|
): void => {
|
||
|
if (msg._bcu.isPrime) {
|
||
|
// if a prime number has been found, stop all the workers, and return it
|
||
|
for (let j = 0; j < workerList.length; j++) {
|
||
|
workerList[j].terminate(); // eslint-disable-line @typescript-eslint/no-floating-promises
|
||
|
}
|
||
|
while (workerList.length > 0) {
|
||
|
workerList.pop();
|
||
|
}
|
||
|
resolve(msg._bcu.value);
|
||
|
} else {
|
||
|
// if a composite is found, make the worker test another random number
|
||
|
const buf = randBitsSync(bitLength, true);
|
||
|
const rnd = fromBuffer(buf);
|
||
|
try {
|
||
|
const msgToWorker: MainToWorkerMsg = {
|
||
|
_bcu: {
|
||
|
rnd,
|
||
|
iterations,
|
||
|
id: msg._bcu.id,
|
||
|
},
|
||
|
};
|
||
|
newWorker.postMessage(msgToWorker);
|
||
|
} catch (error) {
|
||
|
// The worker has already terminated. There is nothing to handle here
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
if (true) {
|
||
|
// browser
|
||
|
const workerURL = _isProbablyPrimeWorkerUrl();
|
||
|
for (let i = 0; i < self.navigator.hardwareConcurrency - 1; i++) {
|
||
|
const newWorker = new Worker(workerURL);
|
||
|
newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
|
||
|
workerList.push(newWorker);
|
||
|
}
|
||
|
} else {
|
||
|
// Node.js
|
||
|
}
|
||
|
for (let i = 0; i < workerList.length; i++) {
|
||
|
randBits(bitLength, true)
|
||
|
.then(function (buf: Uint8Array | Buffer) {
|
||
|
const rnd = fromBuffer(buf);
|
||
|
workerList[i].postMessage({
|
||
|
_bcu: {
|
||
|
rnd,
|
||
|
iterations,
|
||
|
id: i,
|
||
|
},
|
||
|
});
|
||
|
})
|
||
|
.catch(reject);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
|
||
|
* The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead.
|
||
|
*
|
||
|
* @param bitLength - The required bit length for the generated prime
|
||
|
* @param iterations - The number of iterations for the Miller-Rabin Probabilistic Primality Test
|
||
|
*
|
||
|
* @throws {@link RangeError} if bitLength < 1
|
||
|
*
|
||
|
* @returns A bigint probable prime of bitLength bits.
|
||
|
*/
|
||
|
export function primeSync(bitLength: number, iterations: number = 16): bigint {
|
||
|
if (bitLength < 1) throw new RangeError("bitLength MUST be > 0");
|
||
|
let rnd = 0n;
|
||
|
do {
|
||
|
rnd = fromBuffer(randBitsSync(bitLength, true));
|
||
|
} while (!_isProbablyPrime(rnd, iterations));
|
||
|
return rnd;
|
||
|
}
|