bigint-crypto-utils/src/ts/prime.ts

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
}