bigint-crypto-utils/dist/bigint-crypto-utils-latest....

697 lines
19 KiB
JavaScript
Raw Normal View History

2019-04-19 10:05:37 +00:00
var bigintCryptoUtils = (function (exports) {
2019-04-19 07:42:28 +00:00
'use strict';
/**
* 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
*/
2019-04-19 14:40:11 +00:00
function abs(a) {
2019-04-19 07:42:28 +00:00
a = BigInt(a);
return (a >= BigInt(0)) ? a : -a;
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
2019-04-19 10:05:37 +00:00
/**
* @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}
*/
2019-04-19 14:40:11 +00:00
function eGcd(a, b) {
2019-04-19 10:05:37 +00:00
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
};
2019-04-19 14:40:11 +00:00
}
2019-04-19 10:05:37 +00:00
2019-04-19 07:42:28 +00:00
/**
* 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
*/
2019-04-19 14:40:11 +00:00
function gcd(a, b) {
2019-04-19 07:42:28 +00:00
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;
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
2019-04-20 20:11:44 +00:00
* 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)
2019-04-19 07:42:28 +00:00
*
2019-04-19 10:05:37 +00:00
* @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
2019-04-19 07:42:28 +00:00
*
2019-04-19 10:05:37 +00:00
* @return {Promise} A promise that resolve to a boolean that is either true (a probably prime number) or false (definitely composite)
2019-04-19 07:42:28 +00:00
*/
2019-04-19 14:40:11 +00:00
async function isProbablyPrime(w, iterations = 16) {
2019-04-20 20:11:44 +00:00
{ // browser
return new Promise((resolve, reject) => {
2019-04-19 14:17:09 +00:00
let worker = new Worker(_isProbablyPrimeWorkerURL());
2019-04-19 10:05:37 +00:00
worker.onmessage = (event) => {
worker.terminate();
resolve(event.data.isPrime);
};
2019-04-20 20:11:44 +00:00
worker.onmessageerror = (event) => {
reject(event);
};
2019-04-19 10:05:37 +00:00
worker.postMessage({
'rnd': w,
2019-04-20 20:11:44 +00:00
'iterations': iterations,
'id': 0
2019-04-19 10:05:37 +00:00
});
});
}
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
2019-04-19 10:05:37 +00:00
* The least common multiple computed as abs(a*b)/gcd(a,b)
2019-04-19 07:42:28 +00:00
* @param {number|bigint} a
* @param {number|bigint} b
*
2019-04-19 10:05:37 +00:00
* @returns {bigint} The least common multiple of a and b
2019-04-19 07:42:28 +00:00
*/
2019-04-19 14:40:11 +00:00
function lcm(a, b) {
2019-04-19 07:42:28 +00:00
a = BigInt(a);
b = BigInt(b);
2019-04-19 10:05:37 +00:00
return abs(a * b) / gcd(a, b);
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
* 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
*/
2019-04-19 14:40:11 +00:00
function modInv(a, n) {
2019-04-19 07:42:28 +00:00
let egcd = eGcd(a, n);
if (egcd.b !== BigInt(1)) {
return null; // modular inverse does not exist
} else {
return toZn(egcd.x, n);
}
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
* 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
*/
2019-04-19 14:40:11 +00:00
function modPow(a, b, n) {
2019-04-19 07:42:28 +00:00
// 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;
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
2019-04-19 10:05:37 +00:00
* 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).
2019-04-20 20:21:41 +00:00
* The node version can also use worker_threads if they are available (enabled by default with Node 11 and
2019-04-21 07:39:28 +00:00
* and can be enabled at runtime executing node --experimental-worker with node >=10.5.0).
2019-04-19 07:42:28 +00:00
*
2019-04-19 10:05:37 +00:00
* @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
2019-04-19 07:42:28 +00:00
*
2019-04-19 10:05:37 +00:00
* @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits
2019-04-19 07:42:28 +00:00
*/
2019-04-19 14:40:11 +00:00
async function prime(bitLength, iterations = 16) {
2019-04-20 20:11:44 +00:00
return new Promise((resolve) => {
let 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
randBits(bitLength, true).then((buf) => {
let rnd = fromBuffer(buf);
try {
2019-04-19 10:05:37 +00:00
newWorker.postMessage({
'rnd': rnd,
2019-04-20 20:11:44 +00:00
'iterations': iterations,
'id': msg.id
2019-04-19 10:05:37 +00:00
});
2019-04-20 20:11:44 +00:00
} catch (error) {
// The worker has already terminated. There is nothing to handle here
2019-04-19 10:05:37 +00:00
}
2019-04-20 20:11:44 +00:00
});
}
};
{ //browser
let workerURL = _isProbablyPrimeWorkerURL();
for (let i = 0; i < self.navigator.hardwareConcurrency; i++) {
let newWorker = new Worker(workerURL);
newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
2019-04-19 10:05:37 +00:00
workerList.push(newWorker);
}
2019-04-20 20:11:44 +00:00
}
for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then((buf) => {
let rnd = fromBuffer(buf);
workerList[i].postMessage({
2019-04-19 10:05:37 +00:00
'rnd': rnd,
2019-04-20 20:11:44 +00:00
'iterations': iterations,
'id': i
2019-04-19 10:05:37 +00:00
});
2019-04-20 20:11:44 +00:00
});
2019-04-19 07:42:28 +00:00
}
});
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
* 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]
*/
2019-04-20 20:11:44 +00:00
async function randBetween(max, min = BigInt(1)) {
if (max <= min) throw new Error('max must be > min');
const interval = max - min;
let bitLen = bitLength(interval);
2019-04-19 07:42:28 +00:00
let rnd;
do {
2019-04-20 20:11:44 +00:00
let buf = await randBits(bitLen);
2019-04-19 07:42:28 +00:00
rnd = fromBuffer(buf);
2019-04-20 20:11:44 +00:00
} 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 If we want to force the output to have a specific bit length. It basically forces the msb to be 1
*
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bits
*/
async function randBits(bitLength, forceLength = false) {
const byteLength = Math.ceil(bitLength / 8);
let rndBytes = await randBytes(byteLength, false);
// Fill with 0's the extra birs
rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1);
if (forceLength) {
let mask = (bitLength % 8) ? 2 ** ((bitLength % 8) - 1) : 128;
rndBytes[0] = rndBytes[0] | mask;
}
return rndBytes;
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
2019-04-19 10:05:37 +00:00
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
2019-04-19 07:42:28 +00:00
*
2019-04-19 10:05:37 +00:00
* @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
2019-04-19 07:42:28 +00:00
*
2019-04-19 10:05:37 +00:00
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes
2019-04-19 07:42:28 +00:00
*/
2019-04-19 14:40:11 +00:00
async function randBytes(byteLength, forceLength = false) {
2019-04-19 10:05:37 +00:00
return new Promise((resolve) => {
let buf;
2019-04-19 10:04:06 +00:00
2019-04-19 10:05:37 +00:00
{ // 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);
}
});
2019-04-19 14:40:11 +00:00
}
2019-04-19 10:05:37 +00:00
/**
* 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
*/
2019-04-19 14:40:11 +00:00
function toZn(a, n) {
2019-04-19 10:05:37 +00:00
n = BigInt(n);
a = BigInt(a) % n;
return (a < 0) ? a + n : a;
2019-04-19 14:40:11 +00:00
}
2019-04-19 10:05:37 +00:00
/* 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;
}
2019-04-19 14:17:09 +00:00
function _isProbablyPrimeWorkerURL() {
2019-04-20 20:11:44 +00:00
// Let's us first add all the required functions
let workerCode = `'use strict';const eGcd = ${eGcd.toString()};const modInv = ${modInv.toString()};const modPow = ${modPow.toString()};const toZn = ${toZn.toString()};const randBits = ${randBits.toString()};const randBytes = ${randBytes.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
2019-04-19 10:04:06 +00:00
// event.data = {rnd: <bigint>, iterations: <number>}
2019-04-19 10:05:37 +00:00
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations);
2019-04-19 10:04:06 +00:00
postMessage({
'isPrime': isPrime,
2019-04-20 20:11:44 +00:00
'value': event.data.rnd,
'id': event.data.id
2019-04-19 10:04:06 +00:00
});
2019-04-20 20:11:44 +00:00
};
workerCode += `onmessage = ${_onmessage.toString()};`;
2019-04-19 10:04:06 +00:00
2019-04-20 20:11:44 +00:00
workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE
2019-04-19 10:05:37 +00:00
2019-04-19 10:04:06 +00:00
var _blob = new Blob([workerCode], { type: 'text/javascript' });
2019-04-19 14:17:09 +00:00
return window.URL.createObjectURL(_blob);
2019-04-19 10:04:06 +00:00
}
async function _isProbablyPrime(w, iterations = 16) {
2019-04-19 07:42:28 +00:00
/*
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 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 = 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 {
2019-04-20 20:11:44 +00:00
let b = await randBetween(w - BigInt(1), BigInt(2));
2019-04-19 07:42:28 +00:00
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;
2019-04-19 10:04:06 +00:00
}
2019-04-19 07:42:28 +00:00
exports.abs = abs;
exports.eGcd = eGcd;
exports.gcd = gcd;
exports.isProbablyPrime = isProbablyPrime;
exports.lcm = lcm;
exports.modInv = modInv;
exports.modPow = modPow;
exports.prime = prime;
exports.randBetween = randBetween;
2019-04-20 20:11:44 +00:00
exports.randBits = randBits;
2019-04-19 07:42:28 +00:00
exports.randBytes = randBytes;
exports.toZn = toZn;
return exports;
}({}));