108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
import { fromBuffer } from './fromBuffer'
|
|
import { _isProbablyPrime, _isProbablyPrimeWorkerUrl } from './isProbablyPrime'
|
|
import { randBits, randBitsSync } from './randBits'
|
|
import { _useWorkers, WorkerToMainMsg, MainToWorkerMsg } from './workerUtils'
|
|
|
|
/**
|
|
* 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 {RangeError}
|
|
* bitLength MUST be > 0
|
|
*
|
|
* @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')
|
|
|
|
/* istanbul ignore if */
|
|
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) })
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const workerList: Worker[] = []
|
|
const _onmessage = (msg: WorkerToMainMsg, newWorker: Worker): void => {
|
|
if (msg.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()
|
|
}
|
|
while (workerList.length > 0) {
|
|
workerList.pop()
|
|
}
|
|
resolve(msg.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 = {
|
|
rnd: rnd,
|
|
iterations: iterations,
|
|
id: msg.id
|
|
}
|
|
newWorker.postMessage(msgToWorker)
|
|
} catch (error) {
|
|
// The worker has already terminated. There is nothing to handle here
|
|
}
|
|
}
|
|
}
|
|
if (IS_BROWSER) { // 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
|
|
const { cpus } = require('os') // eslint-disable-line
|
|
const { Worker } = require('worker_threads') // eslint-disable-line
|
|
for (let i = 0; i < cpus().length - 1; i++) {
|
|
const newWorker = new Worker(__filename)
|
|
newWorker.on('message', (msg: WorkerToMainMsg) => _onmessage(msg, newWorker))
|
|
workerList.push(newWorker)
|
|
}
|
|
}
|
|
for (let i = 0; i < workerList.length; i++) {
|
|
randBits(bitLength, true).then(function (buf: Uint8Array|Buffer) {
|
|
const rnd = fromBuffer(buf)
|
|
workerList[i].postMessage({
|
|
rnd: rnd,
|
|
iterations: 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 {RangeError}
|
|
* bitLength MUST be > 0
|
|
*
|
|
* @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
|
|
}
|