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

744 lines
18 KiB
JavaScript
Raw Normal View History

2019-04-29 11:23:15 +00:00
const _ZERO = BigInt(0);
const _ONE = BigInt(1);
const _TWO = BigInt(2);
2019-04-19 07:42:28 +00:00
/**
* 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);
2019-04-23 14:57:06 +00:00
return (a >= _ZERO) ? a : -a;
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
* 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;
}
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} A triple (g, x, y), such that ax + by = g = gcd(a, b).
2019-04-19 10:05:37 +00:00
*/
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);
if (a <= _ZERO | b <= _ZERO)
return NaN; // a and b MUST be positive
2019-04-23 14:57:06 +00:00
let x = _ZERO;
let y = _ONE;
let u = _ONE;
let v = _ZERO;
2019-04-19 10:05:37 +00:00
2019-04-23 14:57:06 +00:00
while (a !== _ZERO) {
2019-04-19 10:05:37 +00:00
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);
if (a === _ZERO)
return b;
else if (b === _ZERO)
return a;
2019-04-23 14:57:06 +00:00
let shift = _ZERO;
while (!((a | b) & _ONE)) {
a >>= _ONE;
b >>= _ONE;
2019-04-19 07:42:28 +00:00
shift++;
}
2019-04-23 14:57:06 +00:00
while (!(a & _ONE)) a >>= _ONE;
2019-04-19 07:42:28 +00:00
do {
2019-04-23 14:57:06 +00:00
while (!(b & _ONE)) b >>= _ONE;
2019-04-19 07:42:28 +00:00
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
*
* @param {number|bigint} w An integer to be tested for primality
2019-04-19 10:05:37 +00:00
* @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-23 13:22:48 +00:00
* @return {Promise} A promise that resolves 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) {
if (typeof w === 'number') {
w = BigInt(w);
}
2019-04-20 20:11:44 +00:00
{ // browser
return new Promise((resolve, reject) => {
const 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);
if (a === _ZERO && b === _ZERO)
return _ZERO;
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 or NaN if it does not exist
2019-04-19 07:42:28 +00:00
*/
2019-04-19 14:40:11 +00:00
function modInv(a, n) {
if (a == _ZERO | n <= _ZERO)
return NaN;
let egcd = eGcd(toZn(a, n), n);
2019-04-23 14:57:06 +00:00
if (egcd.b !== _ONE) {
return NaN; // modular inverse does not exist
2019-04-19 07:42:28 +00:00
} else {
return toZn(egcd.x, n);
}
2019-04-19 14:40:11 +00:00
}
2019-04-19 07:42:28 +00:00
/**
* Modular exponentiation b**e mod n
* @param {number|bigint} b base
* @param {number|bigint} e exponent
2019-04-19 07:42:28 +00:00
* @param {number|bigint} n modulo
*
* @returns {bigint} b**e mod n
2019-04-19 07:42:28 +00:00
*/
function modPow(b, e, n) {
2019-04-19 07:42:28 +00:00
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);
2019-04-19 07:42:28 +00:00
}
let r = _ONE;
while (e > 0) {
if ((e % _TWO) === _ONE) {
r = (r * b) % n;
2019-04-19 07:42:28 +00:00
}
e = e / _TWO;
b = b**_TWO % n;
2019-04-19 07:42:28 +00:00
}
return r;
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-26 13:03:53 +00:00
function prime(bitLength, iterations = 16) {
if (bitLength < 1)
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`);
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
2019-04-26 13:03:53 +00:00
let buf = randBits(bitLength, true);
let 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
}
2019-04-20 20:11:44 +00:00
}
};
{ //browser
2019-04-26 13:03:53 +00:00
let workerURL = _isProbablyPrimeWorkerUrl();
2019-04-20 20:11:44 +00:00
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++) {
2019-04-26 13:03:53 +00:00
let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf);
workerList[i].postMessage({
'rnd': rnd,
'iterations': iterations,
'id': i
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
*
2019-04-26 13:03:53 +00:00
* @returns {bigint} A cryptographically secure random bigint between [min,max]
2019-04-19 07:42:28 +00:00
*/
2019-04-26 13:03:53 +00:00
function randBetween(max, min = _ONE) {
2019-04-20 20:11:44 +00:00
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-26 13:03:53 +00:00
let buf = 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
*
2019-04-29 10:29:23 +00:00
* @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits
2019-04-20 20:11:44 +00:00
*/
2019-04-26 13:03:53 +00:00
function randBits(bitLength, forceLength = false) {
if (bitLength < 1)
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`);
2019-04-20 20:11:44 +00:00
const byteLength = Math.ceil(bitLength / 8);
let rndBytes = randBytesSync(byteLength, false);
// Fill with 0's the extra bits
2019-04-20 20:11:44 +00:00
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
*
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
2019-04-19 07:42:28 +00:00
*/
2019-04-26 13:03:53 +00:00
function randBytes(byteLength, forceLength = false) {
if (byteLength < 1)
throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`);
let buf;
{ // browser
return new Promise(function (resolve) {
buf = new Uint8Array(byteLength);
self.crypto.getRandomValues(buf);
2019-05-27 14:42:46 +00:00
// 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);
});
}
}
/**
* 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 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}`);
2019-04-26 13:03:53 +00:00
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;
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);
if (n <= 0)
return NaN;
2019-04-19 10:05:37 +00:00
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) {
2019-04-23 14:57:06 +00:00
let ret = _ZERO;
2019-04-19 10:05:37 +00:00
for (let i of buf.values()) {
let bi = BigInt(i);
ret = (ret << BigInt(8)) + bi;
}
return ret;
}
2019-04-26 13:03:53 +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 _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()}`;
2019-04-20 20:11:44 +00:00
2019-04-26 13:03:53 +00:00
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
};
2019-04-26 13:03:53 +00:00
workerCode += `onmessage = ${onmessage.toString()};`;
2019-04-19 10:04:06 +00:00
2019-04-26 13:03:53 +00:00
return _workerUrl(workerCode);
}
2019-04-19 10:05:37 +00:00
2019-04-26 13:03:53 +00:00
function _workerUrl(workerCode) {
workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE
const _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
}
2019-04-26 13:03:53 +00:00
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.
*/
2019-04-23 14:57:06 +00:00
if (w === _TWO)
2019-04-19 07:42:28 +00:00
return true;
2019-04-23 14:57:06 +00:00
else if ((w & _ONE) === _ZERO || w === _ONE)
2019-04-19 07:42:28 +00:00
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;
2019-04-23 14:57:06 +00:00
else if (w % p === _ZERO)
2019-04-19 07:42:28 +00:00
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.
*/
2019-04-23 14:57:06 +00:00
let a = _ZERO, d = w - _ONE;
while (d % _TWO === _ZERO) {
d /= _TWO;
2019-04-19 07:42:28 +00:00
++a;
}
2019-04-23 14:57:06 +00:00
let m = (w - _ONE) / (_TWO ** a);
2019-04-19 07:42:28 +00:00
loop: do {
2019-04-26 13:03:53 +00:00
let b = randBetween(w - _ONE, _TWO);
2019-04-19 07:42:28 +00:00
let z = modPow(b, m, w);
2019-04-23 14:57:06 +00:00
if (z === _ONE || z === w - _ONE)
2019-04-19 07:42:28 +00:00
continue;
for (let j = 1; j < a; j++) {
2019-04-23 14:57:06 +00:00
z = modPow(z, _TWO, w);
if (z === w - _ONE)
2019-04-19 07:42:28 +00:00
continue loop;
2019-04-23 14:57:06 +00:00
if (z === _ONE)
2019-04-19 07:42:28 +00:00
break;
}
return false;
} while (--iterations);
return true;
2019-04-19 10:04:06 +00:00
}
2019-04-19 07:42:28 +00:00
export { abs, bitLength, eGcd, gcd, isProbablyPrime, lcm, modInv, modPow, prime, randBetween, randBits, randBytes, randBytesSync, toZn };