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

417 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { eGcd, modInv, modPow, toZn, bitLength } from 'bigint-mod-arith'
import { fromBuffer } from './fromBuffer'
import { randBetween } from './randBetween'
import { randBitsSync } from './randBits'
import { randBytesSync } from './randBytes'
import { _useWorkers, _workerUrl, WorkerToMainMsg, MainToWorkerMsg } from './workerUtils'
/**
* The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
* iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)
*
* @param w - A positive integer to be tested for primality
* @param iterations - The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
* @param disableWorkers - Disable the use of workers for the primality test
*
* @throws {RangeError}
* w MUST be >= 0
*
* @returns A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/
export function isProbablyPrime (w: number|bigint, iterations: number = 16, disableWorkers: boolean = false): Promise<boolean> { // eslint-disable-line
if (typeof w === 'number') {
w = BigInt(w)
}
if (w < 0n) throw RangeError('w MUST be >= 0')
if (!IS_BROWSER) { // Node.js
/* istanbul ignore else */
if (!disableWorkers && _useWorkers) {
const { Worker } = require('worker_threads') // eslint-disable-line
return new Promise((resolve, reject) => {
const worker = new Worker(__filename)
worker.on('message', (data: WorkerToMainMsg) => {
worker.terminate()
resolve(data.isPrime)
})
worker.on('error', reject)
const msg: MainToWorkerMsg = {
rnd: w as bigint,
iterations: iterations,
id: 0
}
worker.postMessage(msg)
})
} else {
return new Promise((resolve) => {
resolve(_isProbablyPrime(w as bigint, iterations))
})
}
} else { // browser
return new Promise((resolve, reject) => {
const worker = new Worker(_isProbablyPrimeWorkerUrl())
worker.onmessage = (event) => {
worker.terminate()
resolve(event.data.isPrime)
}
worker.onmessageerror = (event) => {
reject(event)
}
const msg: MainToWorkerMsg = {
rnd: w as bigint,
iterations: iterations,
id: 0
}
worker.postMessage(msg)
})
}
}
export function _isProbablyPrime (w: bigint, iterations: number): boolean {
/*
PREFILTERING. Even values but 2 are not primes, so don't test.
1 is not a prime and the M-R algorithm needs w>1.
*/
if (w === 2n) return true
else if ((w & 1n) === 0n || w === 1n) return false
/*
Test if any of the first 250 small primes are a factor of w. 2 is not tested because it was already tested above.
*/
const firstPrimes = [
3n,
5n,
7n,
11n,
13n,
17n,
19n,
23n,
29n,
31n,
37n,
41n,
43n,
47n,
53n,
59n,
61n,
67n,
71n,
73n,
79n,
83n,
89n,
97n,
101n,
103n,
107n,
109n,
113n,
127n,
131n,
137n,
139n,
149n,
151n,
157n,
163n,
167n,
173n,
179n,
181n,
191n,
193n,
197n,
199n,
211n,
223n,
227n,
229n,
233n,
239n,
241n,
251n,
257n,
263n,
269n,
271n,
277n,
281n,
283n,
293n,
307n,
311n,
313n,
317n,
331n,
337n,
347n,
349n,
353n,
359n,
367n,
373n,
379n,
383n,
389n,
397n,
401n,
409n,
419n,
421n,
431n,
433n,
439n,
443n,
449n,
457n,
461n,
463n,
467n,
479n,
487n,
491n,
499n,
503n,
509n,
521n,
523n,
541n,
547n,
557n,
563n,
569n,
571n,
577n,
587n,
593n,
599n,
601n,
607n,
613n,
617n,
619n,
631n,
641n,
643n,
647n,
653n,
659n,
661n,
673n,
677n,
683n,
691n,
701n,
709n,
719n,
727n,
733n,
739n,
743n,
751n,
757n,
761n,
769n,
773n,
787n,
797n,
809n,
811n,
821n,
823n,
827n,
829n,
839n,
853n,
857n,
859n,
863n,
877n,
881n,
883n,
887n,
907n,
911n,
919n,
929n,
937n,
941n,
947n,
953n,
967n,
971n,
977n,
983n,
991n,
997n,
1009n,
1013n,
1019n,
1021n,
1031n,
1033n,
1039n,
1049n,
1051n,
1061n,
1063n,
1069n,
1087n,
1091n,
1093n,
1097n,
1103n,
1109n,
1117n,
1123n,
1129n,
1151n,
1153n,
1163n,
1171n,
1181n,
1187n,
1193n,
1201n,
1213n,
1217n,
1223n,
1229n,
1231n,
1237n,
1249n,
1259n,
1277n,
1279n,
1283n,
1289n,
1291n,
1297n,
1301n,
1303n,
1307n,
1319n,
1321n,
1327n,
1361n,
1367n,
1373n,
1381n,
1399n,
1409n,
1423n,
1427n,
1429n,
1433n,
1439n,
1447n,
1451n,
1453n,
1459n,
1471n,
1481n,
1483n,
1487n,
1489n,
1493n,
1499n,
1511n,
1523n,
1531n,
1543n,
1549n,
1553n,
1559n,
1567n,
1571n,
1579n,
1583n,
1597n
]
for (let i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) {
const p = firstPrimes[i]
if (w === p) return true
else if (w % p === 0n) return false
}
/*
1. Let a be the largest integer such that 2**a divides w1.
2. m = (w1) / 2**a.
3. wlen = len (w).
4. For i = 1 to iterations do
4.1 Obtain a string b of wlen bits from an RBG.
Comment: Ensure that 1 < b < w1.
4.2 If ((b ≤ 1) or (b ≥ w1)), then go to step 4.1.
4.3 z = b**m mod w.
4.4 If ((z = 1) or (z = w 1)), then go to step 4.7.
4.5 For j = 1 to a 1 do.
4.5.1 z = z**2 mod w.
4.5.2 If (z = w1), then go to step 4.7.
4.5.3 If (z = 1), then go to step 4.6.
4.6 Return COMPOSITE.
4.7 Continue.
Comment: Increment i for the do-loop in step 4.
5. Return PROBABLY PRIME.
*/
let a = 0n
const d = w - 1n
let aux = d
while (aux % 2n === 0n) {
aux /= 2n
++a
}
const m = d / (2n ** a)
do {
const b = randBetween(d, 2n)
let z = modPow(b, m, w)
if (z === 1n || z === d) continue
let j = 1
while (j < a) {
z = modPow(z, 2n, w)
if (z === d) break
if (z === 1n) return false
j++
}
if (z !== d) return false
} while (--iterations !== 0)
return true
}
export function _isProbablyPrimeWorkerUrl (): string {
// Let's us first add all the required functions
let workerCode = `'use strict';const ${eGcd.name}=${eGcd.toString()};const ${modInv.name}=${modInv.toString()};const ${modPow.name}=${modPow.toString()};const ${toZn.name}=${toZn.toString()};const ${randBitsSync.name}=${randBitsSync.toString()};const ${randBytesSync.name}=${randBytesSync.toString()};const ${randBetween.name}=${randBetween.toString()};const ${isProbablyPrime.name}=${_isProbablyPrime.toString()};${bitLength.toString()};${fromBuffer.toString()};`
workerCode += `onmessage=async function(e){const m={isPrime:await ${isProbablyPrime.name}(e.data.rnd,e.data.iterations),value:e.data.rnd,id:e.data.id};postMessage(m);}`
return _workerUrl(workerCode)
}
if (!IS_BROWSER && _useWorkers) { // node.js with support for workers
const { parentPort, isMainThread } = require('worker_threads') // eslint-disable-line
const isWorker = !(isMainThread as boolean)
/* istanbul ignore if */
if (isWorker) { // worker
parentPort.on('message', function (data: MainToWorkerMsg) { // Let's start once we are called
const isPrime = _isProbablyPrime(data.rnd, data.iterations)
const msg: WorkerToMainMsg = {
isPrime: isPrime,
value: data.rnd,
id: data.id
}
parentPort.postMessage(msg)
})
}
}