613 lines
13 KiB
JavaScript
613 lines
13 KiB
JavaScript
/**
|
||
* 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
|
||
*/
|
||
const abs = function (a) {
|
||
a = BigInt(a);
|
||
return (a >= BigInt(0)) ? a : -a;
|
||
};
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
const gcd = function (a, b) {
|
||
a = abs(a);
|
||
b = abs(b);
|
||
let shift = BigInt(0);
|
||
while (!((a | b) & BigInt(1))) {
|
||
a >>= BigInt(1);
|
||
b >>= BigInt(1);
|
||
shift++;
|
||
}
|
||
while (!(a & BigInt(1))) a >>= BigInt(1);
|
||
do {
|
||
while (!(b & BigInt(1))) b >>= BigInt(1);
|
||
if (a > b) {
|
||
let x = a;
|
||
a = b;
|
||
b = x;
|
||
}
|
||
b -= a;
|
||
} while (b);
|
||
|
||
// rescale
|
||
return a << shift;
|
||
};
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
const lcm = function (a, b) {
|
||
a = BigInt(a);
|
||
b = BigInt(b);
|
||
return abs(a * b) / gcd(a, b);
|
||
};
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
const toZn = function (a, n) {
|
||
n = BigInt(n);
|
||
a = BigInt(a) % n;
|
||
return (a < 0) ? a + n : a;
|
||
};
|
||
|
||
/**
|
||
* @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}
|
||
*/
|
||
const eGcd = function (a, b) {
|
||
a = BigInt(a);
|
||
b = BigInt(b);
|
||
let x = BigInt(0);
|
||
let y = BigInt(1);
|
||
let u = BigInt(1);
|
||
let v = BigInt(0);
|
||
|
||
while (a !== BigInt(0)) {
|
||
let q = b / a;
|
||
let r = b % a;
|
||
let m = x - (u * q);
|
||
let n = y - (v * q);
|
||
b = a;
|
||
a = r;
|
||
x = u;
|
||
y = v;
|
||
u = m;
|
||
v = n;
|
||
}
|
||
return {
|
||
b: b,
|
||
x: x,
|
||
y: y
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
const modInv = function (a, n) {
|
||
let egcd = eGcd(a, n);
|
||
if (egcd.b !== BigInt(1)) {
|
||
return null; // modular inverse does not exist
|
||
} else {
|
||
return toZn(egcd.x, n);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Modular exponentiation a**b mod n
|
||
* @param {number|bigint} a base
|
||
* @param {number|bigint} b exponent
|
||
* @param {number|bigint} n modulo
|
||
*
|
||
* @returns {bigint} a**b mod n
|
||
*/
|
||
const modPow = function (a, b, n) {
|
||
// See Knuth, volume 2, section 4.6.3.
|
||
n = BigInt(n);
|
||
a = toZn(a, n);
|
||
b = BigInt(b);
|
||
if (b < BigInt(0)) {
|
||
return modInv(modPow(a, abs(b), n), n);
|
||
}
|
||
let result = BigInt(1);
|
||
let x = a;
|
||
while (b > 0) {
|
||
var leastSignificantBit = b % BigInt(2);
|
||
b = b / BigInt(2);
|
||
if (leastSignificantBit == BigInt(1)) {
|
||
result = result * x;
|
||
result = result % n;
|
||
}
|
||
x = x * x;
|
||
x = x % n;
|
||
}
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* Secure random bytes for both node and browsers. Browser implementation uses WebWorkers in order to not lock the main process
|
||
*
|
||
* @param {number} byteLength The desired number of random bytes
|
||
* @param {boolean} forceLength 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 filled with cryptographically secure random bytes
|
||
*/
|
||
const randBytes = async function (byteLength, forceLength = false) {
|
||
return new Promise((resolve) => {
|
||
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;
|
||
resolve(buf);
|
||
}
|
||
});
|
||
};
|
||
|
||
|
||
/**
|
||
* Returns a cryptographically secure random integer between [min,max]
|
||
* @param {bigint} max Returned value will be <= max
|
||
* @param {bigint} min Returned value will be >= min
|
||
*
|
||
* @returns {Promise} A promise that resolves to a cryptographically secure random bigint between [min,max]
|
||
*/
|
||
const randBetween = async function (max, min = 1) {
|
||
let bitLen = bitLength(max);
|
||
let byteLength = bitLen >> 3;
|
||
let remaining = bitLen - (byteLength * 8);
|
||
let extraBits;
|
||
if (remaining > 0) {
|
||
byteLength++;
|
||
extraBits = 2 ** remaining - 1;
|
||
}
|
||
|
||
let rnd;
|
||
do {
|
||
let buf = await randBytes(byteLength);
|
||
// remove extra bits
|
||
if (remaining > 0)
|
||
buf[0] = buf[0] & extraBits;
|
||
rnd = fromBuffer(buf);
|
||
} while (rnd > max || rnd < min);
|
||
return rnd;
|
||
};
|
||
|
||
/**
|
||
* 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 {bigint} w An integer to be tested for primality
|
||
* @param {number} iterations 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 resolve to a boolean that is either true (a probably prime number) or false (definitely composite)
|
||
*/
|
||
const isProbablyPrime = async function (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 === BigInt(2))
|
||
return true;
|
||
else if ((w & BigInt(1)) === BigInt(0) || w === BigInt(1))
|
||
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,
|
||
];
|
||
for (let i = 0; i < firstPrimes.length && (BigInt(firstPrimes[i]) <= w); i++) {
|
||
const p = BigInt(firstPrimes[i]);
|
||
if (w === p)
|
||
return true;
|
||
else if (w % p === BigInt(0))
|
||
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 = BigInt(0), d = w - BigInt(1);
|
||
while (d % BigInt(2) === BigInt(0)) {
|
||
d /= BigInt(2);
|
||
++a;
|
||
}
|
||
|
||
let m = (w - BigInt(1)) / (BigInt(2) ** a);
|
||
|
||
loop: do {
|
||
let b = await randBetween(w - BigInt(1), 2);
|
||
let z = modPow(b, m, w);
|
||
if (z === BigInt(1) || z === w - BigInt(1))
|
||
continue;
|
||
|
||
for (let j = 1; j < a; j++) {
|
||
z = modPow(z, BigInt(2), w);
|
||
if (z === w - BigInt(1))
|
||
continue loop;
|
||
if (z === BigInt(1))
|
||
break;
|
||
}
|
||
return false;
|
||
} while (--iterations);
|
||
|
||
return true;
|
||
};
|
||
|
||
/**
|
||
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator
|
||
*
|
||
* @param {number} bitLength The required bit length for the generated prime
|
||
* @param {number} iterations 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
|
||
*/
|
||
const prime = async function (bitLength, iterations = 16) {
|
||
return new Promise(async (resolve) => {
|
||
{
|
||
let workerList = [];
|
||
for (let i = 0; i < self.navigator.hardwareConcurrency; i++) {
|
||
const moduleDir = new URL('./', import.meta.url).pathname;
|
||
let newWorker = new Worker(`${moduleDir}/workerPrimalityTest.js`);
|
||
newWorker.onmessage = async (event) => {
|
||
if (event.data.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(event.data.value);
|
||
} else { // if a composite is found, make the worker test another random number
|
||
let rnd = BigInt(0);
|
||
rnd = fromBuffer(await randBytes(bitLength / 8, true));
|
||
newWorker.postMessage({
|
||
'rnd': rnd,
|
||
'iterations': iterations
|
||
});
|
||
}
|
||
};
|
||
workerList.push(newWorker);
|
||
}
|
||
|
||
for (const worker of workerList) {
|
||
let rnd = BigInt(0);
|
||
rnd = fromBuffer(await randBytes(bitLength / 8, true));
|
||
worker.postMessage({
|
||
'rnd': rnd,
|
||
'iterations': iterations
|
||
});
|
||
}
|
||
|
||
}
|
||
});
|
||
};
|
||
|
||
|
||
|
||
|
||
/* HELPER FUNCTIONS */
|
||
|
||
function fromBuffer(buf) {
|
||
let ret = BigInt(0);
|
||
for (let i of buf.values()) {
|
||
let bi = BigInt(i);
|
||
ret = (ret << BigInt(8)) + bi;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
function bitLength(a) {
|
||
let bits = 1;
|
||
do {
|
||
bits++;
|
||
} while ((a >>= BigInt(1)) > BigInt(1));
|
||
return bits;
|
||
}
|
||
|
||
export { abs, eGcd, gcd, isProbablyPrime, lcm, modInv, modPow, prime, randBetween, randBytes, toZn };
|