const _ZERO = BigInt(0) const _ONE = BigInt(1) const _TWO = BigInt(2) /** * Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0 * * @param {number|bigint} a * * @returns {bigint} the absolute value of a */ function abs (a) { a = BigInt(a) return (a >= _ZERO) ? a : -a } /** * Returns the bitlength of a number * * @param {number|bigint} a * @returns {number} - the bit length */ function bitLength (a) { a = BigInt(a) if (a === _ONE) { return 1 } let bits = 1 do { bits++ } while ((a >>= _ONE) > _ONE) return bits } /** * @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b). * @property {bigint} g * @property {bigint} x * @property {bigint} y */ /** * An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm. * Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b). * * @param {number|bigint} a * @param {number|bigint} b * * @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b). */ function eGcd (a, b) { a = BigInt(a) b = BigInt(b) if (a <= _ZERO | b <= _ZERO) { return NaN } // a and b MUST be positive let x = _ZERO let y = _ONE let u = _ONE let v = _ZERO while (a !== _ZERO) { const q = b / a const r = b % a const m = x - (u * q) const n = y - (v * q) b = a a = r x = u y = v u = m v = n } return { b: b, x: x, y: y } } /** * Greatest-common divisor of two integers based on the iterative binary algorithm. * * @param {number|bigint} a * @param {number|bigint} b * * @returns {bigint} The greatest common divisor of a and b */ function gcd (a, b) { a = abs(a) b = abs(b) if (a === _ZERO) { return b } else if (b === _ZERO) { return a } let shift = _ZERO while (!((a | b) & _ONE)) { a >>= _ONE b >>= _ONE shift++ } while (!(a & _ONE)) a >>= _ONE do { while (!(b & _ONE)) b >>= _ONE if (a > b) { const x = a a = b b = x } b -= a } while (b) // rescale return a << shift } /** * 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 {number|bigint} w An integer to be tested for primality * @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 * * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) */ async function isProbablyPrime (w, iterations = 16) { if (typeof w === 'number') { w = BigInt(w) } /* eslint-disable no-lone-blocks */ { // 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) } worker.postMessage({ rnd: w, iterations: iterations, id: 0 }) }) } /* eslint-enable no-lone-blocks */ } /** * The least common multiple computed as abs(a*b)/gcd(a,b) * @param {number|bigint} a * @param {number|bigint} b * * @returns {bigint} The least common multiple of a and b */ function lcm (a, b) { a = BigInt(a) b = BigInt(b) if (a === _ZERO && b === _ZERO) { return _ZERO } return abs(a * b) / gcd(a, b) } /** * Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b * * @param {number|bigint} a * @param {number|bigint} b * * @returns {bigint} maximum of numbers a and b */ function max (a, b) { a = BigInt(a) b = BigInt(b) return (a >= b) ? a : b } /** * Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b * * @param {number|bigint} a * @param {number|bigint} b * * @returns {bigint} minimum of numbers a and b */ function min (a, b) { a = BigInt(a) b = BigInt(b) return (a >= b) ? b : a } /** * Modular inverse. * * @param {number|bigint} a The number to find an inverse for * @param {number|bigint} n The modulo * * @returns {bigint} the inverse modulo n or NaN if it does not exist */ function modInv (a, n) { const egcd = eGcd(toZn(a, n), n) if (egcd.b !== _ONE) { return NaN // modular inverse does not exist } else { return toZn(egcd.x, n) } } /** * Modular exponentiation b**e mod n. Currently using the right-to-left binary method * * @param {number|bigint} b base * @param {number|bigint} e exponent * @param {number|bigint} n modulo * * @returns {bigint} b**e mod n */ function modPow (b, e, n) { n = BigInt(n) if (n === _ZERO) { return NaN } else if (n === _ONE) { return _ZERO } b = toZn(b, n) e = BigInt(e) if (e < _ZERO) { return modInv(modPow(b, abs(e), n), n) } let r = _ONE while (e > 0) { if ((e % _TWO) === _ONE) { r = (r * b) % n } e = e / _TWO b = b ** _TWO % n } return r } /** * 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 {number} bitLength The required bit length for the generated prime * @param {number} [iterations = 16] The number of iterations for the Miller-Rabin Probabilistic Primality Test * * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. */ function prime (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } return new Promise((resolve) => { const workerList = [] const _onmessage = (msg, newWorker) => { 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) { workerList.pop() } resolve(msg.value) } else { // if a composite is found, make the worker test another random number const buf = randBits(bitLength, true) const rnd = fromBuffer(buf) try { newWorker.postMessage({ rnd: rnd, iterations: iterations, id: msg.id }) } catch (error) { // The worker has already terminated. There is nothing to handle here } } } /* eslint-disable no-lone-blocks */ { // browser const workerURL = _isProbablyPrimeWorkerUrl() for (let i = 0; i < self.navigator.hardwareConcurrency; i++) { const newWorker = new Worker(workerURL) newWorker.onmessage = (event) => _onmessage(event.data, newWorker) workerList.push(newWorker) } } /* eslint-enable no-lone-blocks */ for (let i = 0; i < workerList.length; i++) { const buf = randBits(bitLength, true) const rnd = fromBuffer(buf) workerList[i].postMessage({ rnd: rnd, iterations: iterations, id: i }) } }) } /** * 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 {number} bitLength The required bit length for the generated prime * @param {number} [iterations = 16] The number of iterations for the Miller-Rabin Probabilistic Primality Test * * @returns {bigint} A bigint probable prime of bitLength bits. */ function primeSync (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } let rnd = _ZERO do { rnd = fromBuffer(randBytesSync(bitLength / 8, true)) } while (!_isProbablyPrime(rnd, iterations)) return rnd } /** * Returns a cryptographically secure random integer between [min,max] * @param {bigint} max Returned value will be <= max * @param {bigint} [min = BigInt(1)] Returned value will be >= min * * @returns {bigint} A cryptographically secure random bigint between [min,max] */ function randBetween (max, min = _ONE) { if (max <= min) throw new Error('max must be > min') const interval = max - min const bitLen = bitLength(interval) let rnd do { const buf = randBits(bitLen) rnd = fromBuffer(buf) } while (rnd > interval) return rnd + min } /** * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * * @param {number} bitLength The desired number of random bits * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ function randBits (bitLength, forceLength = false) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } const byteLength = Math.ceil(bitLength / 8) const rndBytes = randBytesSync(byteLength, false) const bitLengthMod8 = bitLength % 8 if (bitLengthMod8) { // Fill with 0's the extra bits rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1) } if (forceLength) { const mask = bitLengthMod8 ? 2 ** (bitLengthMod8 - 1) : 128 rndBytes[0] = rndBytes[0] | mask } return rndBytes } /** * Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * * @param {number} byteLength The desired number of random bytes * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } let buf /* eslint-disable no-lone-blocks */ { // browser return new Promise(function (resolve) { buf = new Uint8Array(byteLength) self.crypto.getRandomValues(buf) // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength) { buf[0] = buf[0] | 128 } resolve(buf) }) } /* eslint-disable no-lone-blocks */ } /** * Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * * @param {number} byteLength The desired number of random bytes * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ function randBytesSync (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } let buf { // browser buf = new Uint8Array(byteLength) self.crypto.getRandomValues(buf) } // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength) { buf[0] = buf[0] | 128 } return buf } /** * Finds the smallest positive element that is congruent to a in modulo n * @param {number|bigint} a An integer * @param {number|bigint} n The modulo * * @returns {bigint} The smallest positive representation of a in modulo n */ function toZn (a, n) { n = BigInt(n) if (n <= 0) { return NaN } a = BigInt(a) % n return (a < 0) ? a + n : a } /* HELPER FUNCTIONS */ function fromBuffer (buf) { let ret = _ZERO for (const i of buf.values()) { const bi = BigInt(i) ret = (ret << BigInt(8)) + bi } return ret } function _isProbablyPrimeWorkerUrl () { // Let's us first add all the required functions let workerCode = `'use strict';const _ZERO = BigInt(0);const _ONE = BigInt(1);const _TWO = BigInt(2);const eGcd = ${eGcd.toString()};const modInv = ${modInv.toString()};const modPow = ${modPow.toString()};const toZn = ${toZn.toString()};const randBits = ${randBits.toString()};const randBytesSync = ${randBytesSync.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}` const onmessage = async function (event) { // Let's start once we are called // event.data = {rnd: , iterations: } const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations) postMessage({ isPrime: isPrime, value: event.data.rnd, id: event.data.id }) } workerCode += `onmessage = ${onmessage.toString()};` return _workerUrl(workerCode) } function _workerUrl (workerCode) { workerCode = `(() => {${workerCode}})()` // encapsulate IIFE const _blob = new Blob([workerCode], { type: 'text/javascript' }) return window.URL.createObjectURL(_blob) } function _isProbablyPrime (w, iterations = 16) { /* 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 === _TWO) { return true } else if ((w & _ONE) === _ZERO || w === _ONE) { 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 = [ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597 ] let p = _ZERO for (let i = 0; i < firstPrimes.length && (p <= w); i++) { p = BigInt(firstPrimes[i]) if (w === p) { return true } else if (w % p === _ZERO) { return false } } /* 1. Let a be the largest integer such that 2**a divides w−1. 2. m = (w−1) / 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 < w−1. 4.2 If ((b ≤ 1) or (b ≥ w−1)), 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 = w−1), 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 = _ZERO; let d = w - _ONE while (d % _TWO === _ZERO) { d /= _TWO ++a } const m = (w - _ONE) / (_TWO ** a) /* eslint-disable no-labels */ loop: do { const b = randBetween(w - _ONE, _TWO) let z = modPow(b, m, w) if (z === _ONE || z === w - _ONE) { continue } for (let j = 1; j < a; j++) { z = modPow(z, _TWO, w) if (z === w - _ONE) { continue loop } if (z === _ONE) { break } } return false } while (--iterations) /* eslint-enable no-labels */ return true } export { abs, bitLength, eGcd, gcd, isProbablyPrime, lcm, max, min, modInv, modPow, prime, primeSync, randBetween, randBits, randBytes, randBytesSync, toZn }