working with worker file

This commit is contained in:
Juan Hernández Serrano 2019-04-19 09:42:28 +02:00
commit faefecc21d
21 changed files with 6933 additions and 0 deletions

36
.eslintrc.json Normal file
View File

@ -0,0 +1,36 @@
{
"env": {
"node": true,
"mocha": true,
"worker": true
},
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"no-console": 0,
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
},
"globals": {
"BigInt": "readonly",
"window": "readonly",
"Uint8Array": "readonly"
}
}

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Juan Hernández Serrano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

87
README.hbs Normal file
View File

@ -0,0 +1,87 @@
# bigint-utils
Some extra functions to work with modular arithmetics along with secure random numbers and probable prime (Miller-Rabin primality test) generation/testing using native JS (stage 3) implementation of BigInt. It can be used with Node.js (>=10.4.0) and [Web Browsers supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility).
_The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html)**_
Many platforms provide native support for cryptography, such as [webcrypto](https://w3c.github.io/webcrypto/Overview.html) or [node crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
## Installation
bigint-utils is distributed as both an ES6 and a CJS module.
The ES6 module is built for any [web browser supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility). The module only uses native javascript implementations and no polyfills had been applied.
The CJS module is built as a standard node module.
bigint-utils can be imported to your project with `npm`:
```bash
npm install bigint-utils
```
For web browsers, you can also [download the bundle from GitHub](https://raw.githubusercontent.com/juanelas/bigint-utils/master/dist/bigint-utils-latest.browser.mod.min.js).
## Usage example
With node js:
```javascript
const bigintUtils = require('bigint-utils');
// Stage 3 BigInts with value 666 can be declared as BigInt('666')
// or the shorter new no-so-linter-friendly syntax 666n
let a = BigInt('5');
let b = BigInt('2');
let n = BigInt('19');
console.log(bigintModArith.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
// Generation of a probable prime of 2048 bits
const prime = await bigintUtils.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
if ( await bigintUtils.isProbablyPrime(prime) )
// code if is prime
// Get a cryptographically secure random number between 1 and 2**256 bits.
const rnd = bigintUtils.randBetween(BigInt(2)**256);
```
From a browser, you can just load the module in a html page as:
```html
<script type="module">
import * as bigintUtils from 'bigint-utils-latest.browser.mod.min.js';
let a = BigInt('5');
let b = BigInt('2');
let n = BigInt('19');
console.log(bigintModArith.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
(async function () {
// Generation of a probable prime of 2018 bits
const p = await bigintSecrets.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
const isPrime = await bigintSecrets.isProbablyPrime(p);
alert(p.toString() + '\nIs prime?\n' + isPrime);
// Get a cryptographically secure random number between 1 and 2**256 bits.
const rnd = await bigintSecrets.randBetween(BigInt(2)**256);
alert(rnd);
})();
</script>
```
# bigint-utils JS Doc
{{>main}}
* * *

290
README.md Normal file
View File

@ -0,0 +1,290 @@
# bigint-utils
Some extra functions to work with modular arithmetics along with secure random numbers and probable prime (Miller-Rabin primality test) generation/testing using native JS (stage 3) implementation of BigInt. It can be used with Node.js (>=10.4.0) and [Web Browsers supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility).
_The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html)**_
Many platforms provide native support for cryptography, such as [webcrypto](https://w3c.github.io/webcrypto/Overview.html) or [node crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
## Installation
bigint-utils is distributed as both an ES6 and a CJS module.
The ES6 module is built for any [web browser supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility). The module only uses native javascript implementations and no polyfills had been applied.
The CJS module is built as a standard node module.
bigint-utils can be imported to your project with `npm`:
```bash
npm install bigint-utils
```
For web browsers, you can also [download the bundle from GitHub](https://raw.githubusercontent.com/juanelas/bigint-utils/master/dist/bigint-utils-latest.browser.mod.min.js).
## Usage example
With node js:
```javascript
const bigintUtils = require('bigint-utils');
// Stage 3 BigInts with value 666 can be declared as BigInt('666')
// or the shorter new no-so-linter-friendly syntax 666n
let a = BigInt('5');
let b = BigInt('2');
let n = BigInt('19');
console.log(bigintModArith.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
// Generation of a probable prime of 2048 bits
const prime = await bigintUtils.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
if ( await bigintUtils.isProbablyPrime(prime) )
// code if is prime
// Get a cryptographically secure random number between 1 and 2**256 bits.
const rnd = bigintUtils.randBetween(BigInt(2)**256);
```
From a browser, you can just load the module in a html page as:
```html
<script type="module">
import * as bigintUtils from 'bigint-utils-latest.browser.mod.min.js';
let a = BigInt('5');
let b = BigInt('2');
let n = BigInt('19');
console.log(bigintModArith.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
(async function () {
// Generation of a probable prime of 2018 bits
const p = await bigintSecrets.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
const isPrime = await bigintSecrets.isProbablyPrime(p);
alert(p.toString() + '\nIs prime?\n' + isPrime);
// Get a cryptographically secure random number between 1 and 2**256 bits.
const rnd = await bigintSecrets.randBetween(BigInt(2)**256);
alert(rnd);
})();
</script>
```
# bigint-utils JS Doc
## Constants
<dl>
<dt><a href="#abs">abs</a><code>bigint</code></dt>
<dd><p>Absolute value. abs(a)==a if a&gt;=0. abs(a)==-a if a&lt;0</p>
</dd>
<dt><a href="#gcd">gcd</a><code>bigint</code></dt>
<dd><p>Greatest-common divisor of two integers based on the iterative binary algorithm.</p>
</dd>
<dt><a href="#lcm">lcm</a><code>bigint</code></dt>
<dd><p>The least common multiple computed as abs(a*b)/gcd(a,b)</p>
</dd>
<dt><a href="#toZn">toZn</a><code>bigint</code></dt>
<dd><p>Finds the smallest positive element that is congruent to a in modulo n</p>
</dd>
<dt><a href="#eGcd">eGcd</a><code><a href="#egcdReturn">egcdReturn</a></code></dt>
<dd><p>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).</p>
</dd>
<dt><a href="#modInv">modInv</a><code>bigint</code></dt>
<dd><p>Modular inverse.</p>
</dd>
<dt><a href="#modPow">modPow</a><code>bigint</code></dt>
<dd><p>Modular exponentiation a**b mod n</p>
</dd>
<dt><a href="#randBytes">randBytes</a><code>Promise</code></dt>
<dd><p>Secure random bytes for both node and browsers. Browser implementation uses WebWorkers in order to not lock the main process</p>
</dd>
<dt><a href="#randBetween">randBetween</a><code>Promise</code></dt>
<dd><p>Returns a cryptographically secure random integer between [min,max]</p>
</dd>
<dt><a href="#isProbablyPrime">isProbablyPrime</a><code>Promise</code></dt>
<dd><p>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)</p>
</dd>
<dt><a href="#prime">prime</a><code>Promise</code></dt>
<dd><p>A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator</p>
</dd>
</dl>
## Typedefs
<dl>
<dt><a href="#egcdReturn">egcdReturn</a> : <code>Object</code></dt>
<dd><p>A triple (g, x, y), such that ax + by = g = gcd(a, b).</p>
</dd>
</dl>
<a name="abs"></a>
## abs ⇒ <code>bigint</code>
Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
**Kind**: global constant
**Returns**: <code>bigint</code> - the absolute value of a
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
<a name="gcd"></a>
## gcd ⇒ <code>bigint</code>
Greatest-common divisor of two integers based on the iterative binary algorithm.
**Kind**: global constant
**Returns**: <code>bigint</code> - The greatest common divisor of a and b
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<a name="lcm"></a>
## lcm ⇒ <code>bigint</code>
The least common multiple computed as abs(a*b)/gcd(a,b)
**Kind**: global constant
**Returns**: <code>bigint</code> - The least common multiple of a and b
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<a name="toZn"></a>
## toZn ⇒ <code>bigint</code>
Finds the smallest positive element that is congruent to a in modulo n
**Kind**: global constant
**Returns**: <code>bigint</code> - The smallest positive representation of a in modulo n
| Param | Type | Description |
| --- | --- | --- |
| a | <code>number</code> \| <code>bigint</code> | An integer |
| n | <code>number</code> \| <code>bigint</code> | The modulo |
<a name="eGcd"></a>
## eGcd ⇒ [<code>egcdReturn</code>](#egcdReturn)
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).
**Kind**: global constant
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<a name="modInv"></a>
## modInv ⇒ <code>bigint</code>
Modular inverse.
**Kind**: global constant
**Returns**: <code>bigint</code> - the inverse modulo n
| Param | Type | Description |
| --- | --- | --- |
| a | <code>number</code> \| <code>bigint</code> | The number to find an inverse for |
| n | <code>number</code> \| <code>bigint</code> | The modulo |
<a name="modPow"></a>
## modPow ⇒ <code>bigint</code>
Modular exponentiation a**b mod n
**Kind**: global constant
**Returns**: <code>bigint</code> - a**b mod n
| Param | Type | Description |
| --- | --- | --- |
| a | <code>number</code> \| <code>bigint</code> | base |
| b | <code>number</code> \| <code>bigint</code> | exponent |
| n | <code>number</code> \| <code>bigint</code> | modulo |
<a name="randBytes"></a>
## randBytes ⇒ <code>Promise</code>
Secure random bytes for both node and browsers. Browser implementation uses WebWorkers in order to not lock the main process
**Kind**: global constant
**Returns**: <code>Promise</code> - A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes
| Param | Type | Description |
| --- | --- | --- |
| byteLength | <code>number</code> | The desired number of random bytes |
| forceLength | <code>boolean</code> | If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 |
<a name="randBetween"></a>
## randBetween ⇒ <code>Promise</code>
Returns a cryptographically secure random integer between [min,max]
**Kind**: global constant
**Returns**: <code>Promise</code> - A promise that resolves to a cryptographically secure random bigint between [min,max]
| Param | Type | Description |
| --- | --- | --- |
| max | <code>bigint</code> | Returned value will be <= max |
| min | <code>bigint</code> | Returned value will be >= min |
<a name="isProbablyPrime"></a>
## isProbablyPrime ⇒ <code>Promise</code>
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)
**Kind**: global constant
**Returns**: <code>Promise</code> - A promise that resolve to a boolean that is either true (a probably prime number) or false (definitely composite)
| Param | Type | Description |
| --- | --- | --- |
| w | <code>bigint</code> | An integer to be tested for primality |
| iterations | <code>number</code> | The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 |
<a name="prime"></a>
## prime ⇒ <code>Promise</code>
A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator
**Kind**: global constant
**Returns**: <code>Promise</code> - A promise that resolves to a bigint probable prime of bitLength bits
| Param | Type | Description |
| --- | --- | --- |
| bitLength | <code>number</code> | The required bit length for the generated prime |
| iterations | <code>number</code> | The number of iterations for the Miller-Rabin Probabilistic Primality Test |
<a name="egcdReturn"></a>
## egcdReturn : <code>Object</code>
A triple (g, x, y), such that ax + by = g = gcd(a, b).
**Kind**: global typedef
**Properties**
| Name | Type |
| --- | --- |
| g | <code>bigint</code> |
| x | <code>bigint</code> |
| y | <code>bigint</code> |
* * *

View File

@ -0,0 +1,89 @@
'use strict';
const rollup = require('rollup');
const replace = require('rollup-plugin-replace');
const resolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const multiEntry = require('rollup-plugin-multi-entry');
const fs = require('fs');
const path = require('path');
const pkgJson = require('../package.json');
const rootDir = path.join(__dirname, '..');
// Let's first create the appropriate html file loading mocha, chai and a bundle of the tests
const templatePath = path.join(rootDir, 'src', 'browser-test-template.html');
const dstDir = path.join(rootDir, 'test', 'browser');
const dstFileName = path.join(dstDir, 'index.html');
const template = fs.readFileSync(templatePath, 'utf-8');
const testsJs = `
<script type="module">
import * as ${camelise(pkgJson.name)} from '${path.relative(templatePath, pkgJson.browser)}'
window.${camelise(pkgJson.name)} = ${camelise(pkgJson.name)};
import './tests.js';
mocha.run();
</script>
`;
fs.writeFileSync(dstFileName, template.replace('{{TESTS}}', testsJs));
/*
Now we create a bundle of all the tests called test.js
require/import of the module and chai are removed since they will be loaded from the html file
*/
const distFile = path.relative(path.join(rootDir, 'test'), pkgJson.main);
const requireModuleLine = `const ${camelise(pkgJson.name)} = require('${path.join(path.dirname(distFile), path.basename(distFile, '.js'))}');`;
const buildOptions = [
{ // Browser
input: {
input: path.join(rootDir, 'test', '*.js'),
external: ['chai'],
plugins: [
multiEntry({ exports: false }),
replace({
'const chai = require(\'chai\');': '',
[requireModuleLine]: '',
'delimiters': ['', ''],
'process.browser': true
}),
resolve({
browser: true,
}),
commonjs()
],
},
output: {
file: path.join(rootDir, 'test', 'browser', 'tests.js'),
format: 'esm'
}
}
];
for (const options of buildOptions) {
build(options);
}
/* --- HELPLER FUNCTIONS --- */
async function build(options) {
// create a bundle
const bundle = await rollup.rollup(options.input);
// generate code
await bundle.generate(options.output);
// or write the bundle to disk
await bundle.write(options.output);
}
function camelise(str) {
return str.replace(/-([a-z])/g,
function (m, w) {
return w.toUpperCase();
});
}

135
build/build.rollup.js Normal file
View File

@ -0,0 +1,135 @@
'use strict';
const rollup = require('rollup');
const replace = require('rollup-plugin-replace');
const minify = require('rollup-plugin-babel-minify');
const fs = require('fs');
const path = require('path');
const pkgJson = require('../package.json');
const rootDir = path.join(__dirname, '..');
const srcDir = path.join(rootDir, 'src');
const dstDir = path.join(rootDir, 'dist');
const buildOptions = [
{ // Browser
input: {
input: path.join(srcDir, 'main.js'),
plugins: [
replace({
'process.browser': true
})
]
},
output: {
file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.js`),
format: 'iife',
name: camelise(pkgJson.name)
}
},
// { // Browser minified
// input: {
// input: path.join(srcDir, 'main.js'),
// plugins: [
// replace({
// 'process.browser': true
// }),
// minify({
// 'comments': false
// })
// ],
// },
// output: {
// file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.min.js`),
// format: 'iife',
// name: camelise(pkgJson.name)
// }
// },
{ // Browser esm
input: {
input: path.join(srcDir, 'main.js'),
plugins: [
replace({
'process.browser': true
})
]
},
output: {
file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.mod.js`),
format: 'esm'
}
},
// { // Browser esm minified
// input: {
// input: path.join(srcDir, 'main.js'),
// plugins: [
// replace({
// 'process.browser': true
// }),
// minify({
// 'comments': false
// })
// ],
// },
// output: {
// file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.mod.min.js`),
// format: 'esm'
// }
// },
{ // Node
input: {
input: path.join(srcDir, 'main.js'),
plugins: [
replace({
'process.browser': false
})
]
},
output: {
file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.node.js`),
format: 'cjs'
}
}
];
for (const options of buildOptions) {
build(options);
}
// Let's manually build the worker file
const workerFilename = path.join(srcDir, 'workerPrimalityTest.js');
const dstFileName = path.join(dstDir, 'workerPrimalityTest.js');
const workerFile = fs.readFileSync(workerFilename, 'utf-8');
fs.writeFileSync(dstFileName, workerFile.replace('{{IIFE}}', `./${pkgJson.name}-latest.browser.js`));
/* --- HELPLER FUNCTIONS --- */
async function build(options) {
// create a bundle
const bundle = await rollup.rollup(options.input);
// generate code
await bundle.generate(options.output);
// or write the bundle to disk
await bundle.write(options.output);
// copy the latest build as pkg_name-latest
fs.copyFileSync(
options.output.file,
options.output.file.replace(`${pkgJson.name}-${pkgJson.version}.`, `${pkgJson.name}-latest.`)
);
}
function camelise(str) {
return str.replace(/-([a-z])/g,
function (m, w) {
return w.toUpperCase();
});
}

629
dist/bigint-utils-latest.browser.js vendored Normal file
View File

@ -0,0 +1,629 @@
var bigintUtils = (function (exports) {
'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
*/
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 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 {
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('./', (document.currentScript && document.currentScript.src || new URL('bigint-utils-0.9.0.browser.js', document.baseURI).href)).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;
}
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;
exports.randBytes = randBytes;
exports.toZn = toZn;
return exports;
}({}));

View File

@ -0,0 +1 @@
var bigintUtils=function(a){'use strict';function b(a){let b=BigInt(0);for(let c of a.values()){let a=BigInt(c);b=(b<<BigInt(8))+a}return b}function c(b){let c=1;do c++;while((b>>=BigInt(1))>BigInt(1));return c}const d=function(b){return b=BigInt(b),b>=BigInt(0)?b:-b},e=function(c,e){c=d(c),e=d(e);let f=BigInt(0);for(;!((c|e)&BigInt(1));)c>>=BigInt(1),e>>=BigInt(1),f++;for(;!(c&BigInt(1));)c>>=BigInt(1);do{for(;!(e&BigInt(1));)e>>=BigInt(1);if(c>e){let a=c;c=e,e=a}e-=c}while(e);return c<<f},f=function(b,c){return c=BigInt(c),b=BigInt(b)%c,0>b?b+c:b},g=function(c,d){c=BigInt(c),d=BigInt(d);let e=BigInt(0),f=BigInt(1),g=BigInt(1),h=BigInt(0);for(;c!==BigInt(0);){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}},h=function(b,a){let c=g(b,a);return c.b===BigInt(1)?f(c.x,a):null},i=function(c,e,g){if(g=BigInt(g),c=f(c,g),e=BigInt(e),e<BigInt(0))return h(i(c,d(e),g),g);let j=BigInt(1),k=c;for(;0<e;){var l=e%BigInt(2);e/=BigInt(2),l==BigInt(1)&&(j*=k,j%=g),k*=k,k%=g}return j},j=async function(a,b=!1){return new Promise(c=>{let d;const e=(a,d)=>{b&&(d[0]|=128),c(d)};d=new Uint8Array(a),l(d,e)})},k=async function(a,d=1){let e,f=c(a),g=f>>3,h=f-8*g;0<h&&(g++,e=2**h-1);let i;do{let a=await j(g);0<h&&(a[0]&=e),i=b(a)}while(i>a||i<d);return i},l=function(){function a(a,e){c[b]=e,d.postMessage({buf:a,id:b}),b++}let b=0;const c={},d=function(a){const b=new window.Blob(["("+a.toString()+")()"],{type:"text/javascript"}),d=new Worker(window.URL.createObjectURL(b));return d.onmessage=function(a){const{id:b,buf:d}=a.data;c[b]&&(c[b](!1,d),delete c[b])},d}(()=>{onmessage=function(a){const b=self.crypto.getRandomValues(a.data.buf);self.postMessage({buf:b,id:a.data.id})}});return a}();return a.abs=d,a.eGcd=g,a.gcd=e,a.isProbablyPrime=async function(c,b=16){if(c===BigInt(2))return!0;if((c&BigInt(1))===BigInt(0)||c===BigInt(1))return!1;const e=[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 a=0;a<e.length&&BigInt(e[a])<=c;a++){const b=BigInt(e[a]);if(c===b)return!0;if(c%b===BigInt(0))return!1}let f=BigInt(0),g=c-BigInt(1);for(;g%BigInt(2)===BigInt(0);)g/=BigInt(2),++f;let h=(c-BigInt(1))/BigInt(2)**f;loop:do{let a=await k(c-BigInt(1),2),b=i(a,h,c);if(b===BigInt(1)||b===c-BigInt(1))continue;for(let a=1;a<f;a++){if(b=i(b,BigInt(2),c),b===c-BigInt(1))continue loop;if(b===BigInt(1))break}return!1}while(--b);return!0},a.lcm=function(c,f){return c=BigInt(c),f=BigInt(f),d(c*f)/e(c,f)},a.modInv=h,a.modPow=i,a.prime=async function(a,c=16){{let d=[];for(let e=0;e<window.navigator.hardwareConcurrency;e++){const e=new URL("./",document.currentScript&&document.currentScript.src||new URL("bigint-utils-0.9.0.browser.min.js",document.baseURI).href).pathname;let f=new Worker(`${e}/workerPrimalityTest.js`);f.onmessage(async e=>{if(e.data.isPrime){for(let a=0;a<d.length;a++)d[a].terminate();for(;d.length;)d.pop();return e.data.value}else{let d=BigInt(0);d=b((await j(a/8,!0))),f.postMessage({rnd:d,iterations:c})}}),d.push(f)}for(const e of d){let d=BigInt(0);d=b((await j(a/8,!0))),e.postMessage({rnd:d,iterations:c})}}},a.randBetween=k,a.randBytes=j,a.toZn=f,a}({});

612
dist/bigint-utils-latest.browser.mod.js vendored Normal file
View File

@ -0,0 +1,612 @@
/**
* 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 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 {
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 };

View File

@ -0,0 +1 @@
const abs=function(b){return b=BigInt(b),b>=BigInt(0)?b:-b},gcd=function(c,d){c=abs(c),d=abs(d);let e=BigInt(0);for(;!((c|d)&BigInt(1));)c>>=BigInt(1),d>>=BigInt(1),e++;for(;!(c&BigInt(1));)c>>=BigInt(1);do{for(;!(d&BigInt(1));)d>>=BigInt(1);if(c>d){let a=c;c=d,d=a}d-=c}while(d);return c<<e},lcm=function(c,d){return c=BigInt(c),d=BigInt(d),abs(c*d)/gcd(c,d)},toZn=function(b,c){return c=BigInt(c),b=BigInt(b)%c,0>b?b+c:b},eGcd=function(c,d){c=BigInt(c),d=BigInt(d);let e=BigInt(0),f=BigInt(1),g=BigInt(1),h=BigInt(0);for(;c!==BigInt(0);){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}},modInv=function(b,a){let c=eGcd(b,a);return c.b===BigInt(1)?toZn(c.x,a):null},modPow=function(c,d,e){if(e=BigInt(e),c=toZn(c,e),d=BigInt(d),d<BigInt(0))return modInv(modPow(c,abs(d),e),e);let f=BigInt(1),g=c;for(;0<d;){var h=d%BigInt(2);d/=BigInt(2),h==BigInt(1)&&(f*=g,f%=e),g*=g,g%=e}return f},randBytes=async function(a,b=!1){return new Promise(c=>{let d;const e=(a,d)=>{b&&(d[0]|=128),c(d)};d=new Uint8Array(a),getRandomValuesWorker(d,e)})},randBetween=async function(a,b=1){let c,d=bitLength(a),e=d>>3,f=d-8*e;0<f&&(e++,c=2**f-1);let g;do{let a=await randBytes(e);0<f&&(a[0]&=c),g=fromBuffer(a)}while(g>a||g<b);return g},isProbablyPrime=async function(c,b=16){if(c===BigInt(2))return!0;if((c&BigInt(1))===BigInt(0)||c===BigInt(1))return!1;const e=[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 a=0;a<e.length&&BigInt(e[a])<=c;a++){const b=BigInt(e[a]);if(c===b)return!0;if(c%b===BigInt(0))return!1}let f=BigInt(0),g=c-BigInt(1);for(;g%BigInt(2)===BigInt(0);)g/=BigInt(2),++f;let h=(c-BigInt(1))/BigInt(2)**f;loop:do{let a=await randBetween(c-BigInt(1),2),b=modPow(a,h,c);if(b===BigInt(1)||b===c-BigInt(1))continue;for(let a=1;a<f;a++){if(b=modPow(b,BigInt(2),c),b===c-BigInt(1))continue loop;if(b===BigInt(1))break}return!1}while(--b);return!0},prime=async function(a,b=16){{let c=[];for(let d,e=0;e<window.navigator.hardwareConcurrency;e++)d=new Worker("./workerPrimalityTest.js"),d.onmessage(async e=>{if(e.data.isPrime){for(let a=0;a<c.length;a++)c[a].terminate();for(;c.length;)c.pop();return e.data.value}else{let c=BigInt(0);c=fromBuffer((await randBytes(a/8,!0))),d.postMessage({rnd:c,iterations:b})}}),c.push(d);for(const d of c){let c=BigInt(0);c=fromBuffer((await randBytes(a/8,!0))),d.postMessage({rnd:c,iterations:b})}}},getRandomValuesWorker=function(){function a(a,e){c[b]=e,d.postMessage({buf:a,id:b}),b++}let b=0;const c={},d=function(a){const b=new window.Blob(["("+a.toString()+")()"],{type:"text/javascript"}),d=new Worker(window.URL.createObjectURL(b));return d.onmessage=function(a){const{id:b,buf:d}=a.data;c[b]&&(c[b](!1,d),delete c[b])},d}(()=>{onmessage=function(a){const b=self.crypto.getRandomValues(a.data.buf);self.postMessage({buf:b,id:a.data.id})}});return a}();function fromBuffer(a){let b=BigInt(0);for(let c of a.values()){let a=BigInt(c);b=(b<<BigInt(8))+a}return b}function bitLength(b){let c=1;do c++;while((b>>=BigInt(1))>BigInt(1));return c}export{abs,eGcd,gcd,isProbablyPrime,lcm,modInv,modPow,prime,randBetween,randBytes,toZn};

599
dist/bigint-utils-latest.node.js vendored Normal file
View File

@ -0,0 +1,599 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**
* 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;
{ // node
const crypto = require('crypto');
buf = Buffer.alloc(byteLength);
crypto.randomFill(buf, (err, 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 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 {
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 rnd = BigInt(0);
do {
rnd = fromBuffer(await randBytes(bitLength / 8, true));
} while (! await isProbablyPrime(rnd, iterations));
resolve(rnd);
}
});
};
/* 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;
}
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;
exports.randBytes = randBytes;
exports.toZn = toZn;

14
dist/workerPrimalityTest.js vendored Normal file
View File

@ -0,0 +1,14 @@
'use strict';
//const window = self;
importScripts('./bigint-utils-latest.browser.js'); // to be replaced during build with rollup replace
onmessage = async function(event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await bigintUtils.isProbablyPrime(event.data.rnd, event.data.iterations);
postMessage({
'isPrime': isPrime,
'value' : event.data.rnd
});
};

3515
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

46
package.json Normal file
View File

@ -0,0 +1,46 @@
{
"name": "bigint-utils",
"version": "0.9.0",
"description": "Arbitrary precision modular arithmetics, cryptographically secure random numbers and prime generation/testing using native JS (stage 3) implementation of BigInt",
"keywords": [
"modular arithmetics",
"prime",
"rng",
"prng",
"primality test",
"BigInt"
],
"license": "MIT",
"author": {
"name": "Juan Hernández Serrano",
"email": "jserrano@entel.upc.edu",
"url": "https://github.com/juanelas"
},
"repository": "github:juanelas/bigint-utils",
"main": "./dist/bigint-utils-latest.node.js",
"browser": "./dist/bigint-utils-latest.browser.mod.js",
"directories": {
"build": "./build",
"dist": "./dist",
"src": "./src",
"test": "./test"
},
"scripts": {
"test": "mocha --timeout 600000",
"build": "node build/build.rollup.js",
"build:browserTests": "node build/build.browser.tests.js",
"build:docs": "jsdoc2md --template=README.hbs --files ./src/main.js > README.md",
"prepublishOnly": "npm run build && npm run build:docs"
},
"devDependencies": {
"chai": "^4.2.0",
"jsdoc-to-markdown": "^4.0.1",
"mocha": "^6.1.3",
"rollup": "^1.10.0",
"rollup-plugin-babel-minify": "^8.0.0",
"rollup-plugin-commonjs": "^9.3.4",
"rollup-plugin-multi-entry": "^2.1.0",
"rollup-plugin-node-resolve": "^4.2.3",
"rollup-plugin-replace": "^2.2.0"
}
}

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha Tests</title>
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="../../node_modules/mocha/mocha.js"></script>
<script src="../../node_modules/chai/chai.js"></script>
<script>mocha.setup('bdd'); mocha.setup({ timeout: 60000 });</script>
{{TESTS}}
</body>
</html>

628
src/main.js Normal file
View File

@ -0,0 +1,628 @@
'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
*/
export 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
*/
export 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
*/
export 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
*/
export 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}
*/
export 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
*/
export 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
*/
export 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
*/
export const randBytes = async function (byteLength, forceLength = false) {
return new Promise((resolve) => {
let buf;
if (!process.browser) { // node
const crypto = require('crypto');
buf = Buffer.alloc(byteLength);
crypto.randomFill(buf, (err, 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);
});
} else { // 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]
*/
export 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)
*/
export 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 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 {
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
*/
export const prime = async function (bitLength, iterations = 16) {
return new Promise(async (resolve) => {
if (process.browser) {
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
});
}
} else {
let rnd = BigInt(0);
do {
rnd = fromBuffer(await randBytes(bitLength / 8, true));
} while (! await isProbablyPrime(rnd, iterations));
resolve(rnd);
}
});
};
/* 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;
}

View File

@ -0,0 +1,14 @@
'use strict';
//const window = self;
importScripts('{{IIFE}}'); // to be replaced during build with rollup replace
onmessage = async function(event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await bigintUtils.isProbablyPrime(event.data.rnd, event.data.iterations);
postMessage({
'isPrime': isPrime,
'value' : event.data.rnd
});
};

View File

@ -0,0 +1,60 @@
'use strict';
// For the browser test builder to work you MUST import them module in a variable that
// is the camelised version of the package name.
const bigintUtils = require('../dist/bigint-utils-latest.node');
const chai = require('chai');
const numbers = [
{
value: BigInt(1),
prime: false
},
{
value: BigInt(2),
prime: true
},
{
value: BigInt(3),
prime: true
},
{
value: BigInt(15),
prime: false
},
{
value: BigInt(29),
prime: true
},
{
value: BigInt('669483106578092405936560831017556154622901950048903016651289'),
prime: true
},
{
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'),
prime: true
},
{
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'),
prime: false
},
{
value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'),
prime: true
},
{
value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'),
prime: true
}
];
describe('Testing validation of prime numbers', function () {
for (const num of numbers) {
describe(`isProbablyPrime(${num.value})`, function () {
it(`should return ${num.prime}`, async function () {
const ret = await bigintUtils.isProbablyPrime(num.value);
chai.expect(ret).to.equal(num.prime);
});
});
}
});

View File

@ -0,0 +1,31 @@
'use strict';
// For the browser test builder to work you MUST import them module in a variable that
// is the camelised version of the package name.
const bigintUtils = require('../dist/bigint-utils-latest.node');
const chai = require('chai');
const bitLengths = [
256,
512,
1024,
2048,
3072
];
describe('Testing generation of prime numbers', function () {
for (const bitLength of bitLengths) {
describe(`Executing prime(${bitLength})`, function () {
it(`should return a random ${bitLength}-bits probable prime`, async function () {
let prime = await bigintUtils.prime(bitLength);
const ret = await bigintUtils.isProbablyPrime(prime);
chai.expect(ret).to.equal(true);
let bits = 1;
do {
bits++;
} while ((prime >>= BigInt(1)) > BigInt(1));
chai.expect(bits).to.equal(bitLength);
});
});
}
});

22
test/browser/index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha Tests</title>
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="../../node_modules/mocha/mocha.js"></script>
<script src="../../node_modules/chai/chai.js"></script>
<script>mocha.setup('bdd'); mocha.setup({ timeout: 60000 });</script>
<script type="module">
import * as bigintUtils from '../../dist/bigint-utils-latest.browser.mod.js'
window.bigintUtils = bigintUtils;
import './tests.js';
mocha.run();
</script>
</body>
</html>

88
test/browser/tests.js Normal file
View File

@ -0,0 +1,88 @@
// For the browser test builder to work you MUST import them module in a variable that
// is the camelised version of the package name.
const numbers = [
{
value: BigInt(1),
prime: false
},
{
value: BigInt(2),
prime: true
},
{
value: BigInt(3),
prime: true
},
{
value: BigInt(15),
prime: false
},
{
value: BigInt(29),
prime: true
},
{
value: BigInt('669483106578092405936560831017556154622901950048903016651289'),
prime: true
},
{
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'),
prime: true
},
{
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'),
prime: false
},
{
value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'),
prime: true
},
{
value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'),
prime: true
}
];
describe('Testing validation of prime numbers', function () {
for (const num of numbers) {
describe(`isProbablyPrime(${num.value})`, function () {
it(`should return ${num.prime}`, async function () {
const ret = await bigintUtils.isProbablyPrime(num.value);
chai.expect(ret).to.equal(num.prime);
});
});
}
});
// For the browser test builder to work you MUST import them module in a variable that
// is the camelised version of the package name.
const bitLengths = [
256,
512,
1024,
2048,
3072
];
describe('Testing generation of prime numbers', function () {
for (const bitLength of bitLengths) {
describe(`Executing prime(${bitLength})`, function () {
it(`should return a random ${bitLength}-bits probable prime`, async function () {
let prime = await bigintUtils.prime(bitLength);
const ret = await bigintUtils.isProbablyPrime(prime);
chai.expect(ret).to.equal(true);
let bits = 1;
do {
bits++;
} while ((prime >>= BigInt(1)) > BigInt(1));
chai.expect(bits).to.equal(bitLength);
});
});
}
});