This commit is contained in:
Juan Hernández Serrano 2019-04-19 12:04:06 +02:00
parent 6cc3343eca
commit 788bcd219f
18 changed files with 505 additions and 387 deletions

View File

@ -1,30 +1,27 @@
# bigint-utils
# bigint-crypto-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).
Utils for working with cryptography using native JS (stage 3) implementation of BigInt. It includes some extra functions to work with modular arithmetics along with secure random numbers and a very fast strong probable prime generation/testing (parallelised multi-threaded Miller-Rabin primality test). 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.
bigint-crypto-utils is distributed for [web browsers supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) as an ES6 module or a IIFE file, and for Node.js (>=10.4.0) as 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`:
bigint-crypto-utils can be imported to your project with `npm`:
```bash
npm install bigint-utils
npm install bigint-crypto-utils
```
NPM installation defaults to the ES6 module for browsers and the CJS for Node.js.
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).
For web browsers, you can also directly download the [IIFE file](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/dist/bigint-crypto-utils-latest.browser.min.js) or the [ES6 module](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/dist/bigint-crypto-utils-latest.browser.mod.min.js) from GitHub.
## Usage example
With node js:
```javascript
const bigintUtils = require('bigint-utils');
const bigintCryptoUtils = require('bigint-crypto-utils');
// Stage 3 BigInts with value 666 can be declared as BigInt('666')
// or the shorter new no-so-linter-friendly syntax 666n
@ -32,55 +29,55 @@ let a = BigInt('5');
let b = BigInt('2');
let n = BigInt('19');
console.log(bigintModArith.modPow(a, b, n)); // prints 6
console.log(bigintCryptoUtils.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))); // prints 2
// Generation of a probable prime of 2048 bits
const prime = await bigintUtils.prime(2048);
const prime = await bigintCryptoUtils.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
if ( await bigintUtils.isProbablyPrime(prime) )
if ( await bigintCryptoUtils.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);
const rnd = bigintCryptoUtils.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';
import * as bigintCryptoUtils 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(bigintCryptoUtils.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))); // prints 2
(async function () {
// Generation of a probable prime of 2018 bits
const p = await bigintSecrets.prime(2048);
const p = await bigintCryptoUtils.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
const isPrime = await bigintSecrets.isProbablyPrime(p);
const isPrime = await bigintCryptoUtils.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);
const rnd = await bigintCryptoUtils.randBetween(BigInt(2)**256);
alert(rnd);
})();
</script>
```
# bigint-utils JS Doc
# bigint-crypto-utils JS Doc
{{>main}}

169
README.md
View File

@ -1,30 +1,27 @@
# bigint-utils
# bigint-crypto-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).
Utils for working with cryptography using native JS (stage 3) implementation of BigInt. It includes some extra functions to work with modular arithmetics along with secure random numbers and a very fast strong probable prime generation/testing (parallelised multi-threaded Miller-Rabin primality test). 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.
bigint-crypto-utils is distributed for [web browsers supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) as an ES6 module or a IIFE file, and for Node.js (>=10.4.0) as 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`:
bigint-crypto-utils can be imported to your project with `npm`:
```bash
npm install bigint-utils
npm install bigint-crypto-utils
```
NPM installation defaults to the ES6 module for browsers and the CJS for Node.js.
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).
For web browsers, you can also directly download the [IIFE file](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/dist/bigint-crypto-utils-latest.browser.min.js) or the [ES6 module](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/dist/bigint-crypto-utils-latest.browser.mod.min.js) from GitHub.
## Usage example
With node js:
```javascript
const bigintUtils = require('bigint-utils');
const bigintCryptoUtils = require('bigint-crypto-utils');
// Stage 3 BigInts with value 666 can be declared as BigInt('666')
// or the shorter new no-so-linter-friendly syntax 666n
@ -32,55 +29,55 @@ let a = BigInt('5');
let b = BigInt('2');
let n = BigInt('19');
console.log(bigintModArith.modPow(a, b, n)); // prints 6
console.log(bigintCryptoUtils.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))); // prints 2
// Generation of a probable prime of 2048 bits
const prime = await bigintUtils.prime(2048);
const prime = await bigintCryptoUtils.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
if ( await bigintUtils.isProbablyPrime(prime) )
if ( await bigintCryptoUtils.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);
const rnd = bigintCryptoUtils.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';
import * as bigintCryptoUtils 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(bigintCryptoUtils.modPow(a, b, n)); // prints 6
console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))); // prints 3
console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))); // prints 2
(async function () {
// Generation of a probable prime of 2018 bits
const p = await bigintSecrets.prime(2048);
const p = await bigintCryptoUtils.prime(2048);
// Testing if a prime is a probable prime (Miller-Rabin)
const isPrime = await bigintSecrets.isProbablyPrime(p);
const isPrime = await bigintCryptoUtils.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);
const rnd = await bigintCryptoUtils.randBetween(BigInt(2)**256);
alert(rnd);
})();
</script>
```
# bigint-utils JS Doc
# bigint-crypto-utils JS Doc
## Constants
@ -88,36 +85,38 @@ From a browser, you can just load the module in a html page as:
<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="#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="#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="#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="#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>
<dt><a href="#prime">prime</a><code>Promise</code></dt>
<dd><p>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).</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>
<dt><a href="#randBytes">randBytes</a><code>Promise</code></dt>
<dd><p>Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</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>
<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>
</dl>
@ -141,6 +140,19 @@ Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
<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="gcd"></a>
## gcd ⇒ <code>bigint</code>
@ -154,6 +166,19 @@ Greatest-common divisor of two integers based on the iterative binary algorithm.
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<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="lcm"></a>
## lcm ⇒ <code>bigint</code>
@ -167,32 +192,6 @@ The least common multiple computed as abs(a*b)/gcd(a,b)
| 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>
@ -220,18 +219,20 @@ Modular exponentiation a**b mod n
| b | <code>number</code> \| <code>bigint</code> | exponent |
| n | <code>number</code> \| <code>bigint</code> | modulo |
<a name="randBytes"></a>
<a name="prime"></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
## prime ⇒ <code>Promise</code>
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).
**Kind**: global constant
**Returns**: <code>Promise</code> - A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes
**Returns**: <code>Promise</code> - A promise that resolves to a bigint probable prime of bitLength bits
| 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 |
| 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="randBetween"></a>
@ -246,31 +247,31 @@ Returns a cryptographically secure random integer between [min,max]
| max | <code>bigint</code> | Returned value will be <= max |
| min | <code>bigint</code> | Returned value will be >= min |
<a name="isProbablyPrime"></a>
<a name="randBytes"></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)
## randBytes<code>Promise</code>
Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
**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)
**Returns**: <code>Promise</code> - A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes
| 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 |
| 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="prime"></a>
<a name="toZn"></a>
## prime ⇒ <code>Promise</code>
A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator
## toZn ⇒ <code>bigint</code>
Finds the smallest positive element that is congruent to a in modulo n
**Kind**: global constant
**Returns**: <code>Promise</code> - A promise that resolves to a bigint probable prime of bitLength bits
**Returns**: <code>bigint</code> - The smallest positive representation of a in modulo n
| 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 | <code>number</code> \| <code>bigint</code> | An integer |
| n | <code>number</code> \| <code>bigint</code> | The modulo |
<a name="egcdReturn"></a>

View File

@ -27,7 +27,7 @@ const testsJs = `
mocha.run();
</script>
`;
fs.writeFileSync(dstFileName, template.replace('{{TESTS}}', testsJs));
fs.writeFileSync(dstFileName, template.replace('{{TESTS}}', testsJs).replace('{{PKG_NAME}}', pkgJson.name));
/*
Now we create a bundle of all the tests called test.js

View File

@ -27,24 +27,24 @@ const buildOptions = [
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 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'),
@ -59,23 +59,23 @@ const buildOptions = [
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'
// }
// },
{ // 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'),
@ -97,17 +97,6 @@ for (const options of buildOptions) {
}
// 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) {

View File

@ -222,6 +222,51 @@ var bigintUtils = (function (exports) {
* @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) {
{
return new Promise(resolve => {
let worker = _isProbablyPrimeWorker();
worker.onmessage = (event) => {
resolve(event.data.isPrime);
};
worker.postMessage({
'rnd': w,
'iterations': iterations
});
});
}
};
function _isProbablyPrimeWorker() {
async function _onmessage(event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations, false);
postMessage({
'isPrime': isPrime,
'value': event.data.rnd
});
}
let workerCode = `(() => {
'use strict';
const eGcd = ${eGcd.toString()};
const modInv = ${modInv.toString()};
const modPow = ${modPow.toString()};
const toZn = ${toZn.toString()};
const randBytes = ${randBytes.toString()};
const randBetween = ${randBetween.toString()};
const isProbablyPrime = ${_isProbablyPrime.toString()};
${bitLength.toString()}
${fromBuffer.toString()}
onmessage = ${_onmessage.toString()};
})()`;
var _blob = new Blob([workerCode], { type: 'text/javascript' });
return new Worker(window.URL.createObjectURL(_blob));
}
async function _isProbablyPrime(w, iterations = 16) {
/*
PREFILTERING. Even values but 2 are not primes, so don't test.
1 is not a prime and the M-R algorithm needs w>1.
@ -538,7 +583,7 @@ var bigintUtils = (function (exports) {
} while (--iterations);
return true;
};
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator
@ -553,8 +598,7 @@ var bigintUtils = (function (exports) {
{
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`);
let newWorker = _isProbablyPrimeWorker();
newWorker.onmessage = async (event) => {
if (event.data.isPrime) {
// if a prime number has been found, stop all the workers, and return it

View File

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

View File

@ -219,6 +219,51 @@ const randBetween = async function (max, min = 1) {
* @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) {
{
return new Promise(resolve => {
let worker = _isProbablyPrimeWorker();
worker.onmessage = (event) => {
resolve(event.data.isPrime);
};
worker.postMessage({
'rnd': w,
'iterations': iterations
});
});
}
};
function _isProbablyPrimeWorker() {
async function _onmessage(event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations, false);
postMessage({
'isPrime': isPrime,
'value': event.data.rnd
});
}
let workerCode = `(() => {
'use strict';
const eGcd = ${eGcd.toString()};
const modInv = ${modInv.toString()};
const modPow = ${modPow.toString()};
const toZn = ${toZn.toString()};
const randBytes = ${randBytes.toString()};
const randBetween = ${randBetween.toString()};
const isProbablyPrime = ${_isProbablyPrime.toString()};
${bitLength.toString()}
${fromBuffer.toString()}
onmessage = ${_onmessage.toString()};
})()`;
var _blob = new Blob([workerCode], { type: 'text/javascript' });
return new Worker(window.URL.createObjectURL(_blob));
}
async function _isProbablyPrime(w, iterations = 16) {
/*
PREFILTERING. Even values but 2 are not primes, so don't test.
1 is not a prime and the M-R algorithm needs w>1.
@ -535,7 +580,7 @@ const isProbablyPrime = async function (w, iterations = 16) {
} while (--iterations);
return true;
};
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator
@ -550,8 +595,7 @@ const prime = async function (bitLength, iterations = 16) {
{
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`);
let newWorker = _isProbablyPrimeWorker();
newWorker.onmessage = async (event) => {
if (event.data.isPrime) {
// if a prime number has been found, stop all the workers, and return it

View File

@ -1 +1,15 @@
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};
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;d=new Uint8Array(a),self.crypto.getRandomValues(d),b&&(d[0]|=128),c(d)})},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(a,b=16){return new Promise(c=>{let d=_isProbablyPrimeWorker();d.onmessage=a=>{c(a.data.isPrime)},d.postMessage({rnd:a,iterations:b})})};function _isProbablyPrimeWorker(){let a=`(() => {
'use strict';
const eGcd = ${eGcd.toString()};
const modInv = ${modInv.toString()};
const modPow = ${modPow.toString()};
const toZn = ${toZn.toString()};
const randBytes = ${randBytes.toString()};
const randBetween = ${randBetween.toString()};
const isProbablyPrime = ${_isProbablyPrime.toString()};
${bitLength.toString()}
${fromBuffer.toString()}
onmessage = ${async function(a){const b=await isProbablyPrime(a.data.rnd,a.data.iterations,!1);postMessage({isPrime:b,value:a.data.rnd})}.toString()};
})()`;var b=new Blob([a],{type:"text/javascript"});return new Worker(window.URL.createObjectURL(b))}async function _isProbablyPrime(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}const prime=async function(a,b=16){return new Promise(async c=>{{let d=[];for(let e,f=0;f<self.navigator.hardwareConcurrency;f++)e=_isProbablyPrimeWorker(),e.onmessage=async f=>{if(f.data.isPrime){for(let a=0;a<d.length;a++)d[a].terminate();for(;d.length;)d.pop();c(f.data.value)}else{let c=BigInt(0);c=fromBuffer((await randBytes(a/8,!0))),e.postMessage({rnd:c,iterations:b})}},d.push(e);for(const c of d){let d=BigInt(0);d=fromBuffer((await randBytes(a/8,!0))),c.postMessage({rnd:d,iterations:b})}}})};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};

View File

@ -226,6 +226,12 @@ const randBetween = async function (max, min = 1) {
* @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) {
{
return _isProbablyPrime(w, iterations);
}
};
async function _isProbablyPrime(w, iterations = 16) {
/*
PREFILTERING. Even values but 2 are not primes, so don't test.
1 is not a prime and the M-R algorithm needs w>1.
@ -542,7 +548,7 @@ const isProbablyPrime = async function (w, iterations = 16) {
} while (--iterations);
return true;
};
}
/**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator

View File

@ -1,14 +0,0 @@
'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
});
};

View File

@ -1,10 +1,12 @@
{
"name": "bigint-utils",
"name": "bigint-crypto-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",
"description": "Utils for working with cryptography using native JS (stage 3) implementation of BigInt. It includes arbitrary precision modular arithmetics, cryptographically secure random numbers and strong probable prime generation/testing ",
"keywords": [
"modular arithmetics",
"crypto",
"prime",
"random",
"rng",
"prng",
"primality test",
@ -16,9 +18,9 @@
"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",
"repository": "github:juanelas/bigint-crypto-utils",
"main": "./dist/bigint-crypto-utils-latest.node.js",
"browser": "./dist/bigint-crypto-utils-latest.browser.mod.js",
"directories": {
"build": "./build",
"dist": "./dist",
@ -30,6 +32,7 @@
"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",
"build:all": "npm run build && npm run build:browserTests && npm run build:docs",
"prepublishOnly": "npm run build && npm run build:docs"
},
"devDependencies": {

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha Tests</title>
<title>{{PKG_NAME}} - Mocha Tests</title>
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css">
</head>
<body>

View File

@ -12,64 +12,6 @@ export const abs = function (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
@ -112,6 +54,79 @@ export const eGcd = function (a, b) {
};
};
/**
* 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 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) {
if (process.browser) {
return new Promise(resolve => {
let worker = _isProbablyPrimeWorker();
worker.onmessage = (event) => {
worker.terminate();
resolve(event.data.isPrime);
};
worker.postMessage({
'rnd': w,
'iterations': iterations
});
});
} else {
return _isProbablyPrime(w, iterations);
}
};
/**
* 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);
};
/**
* Modular inverse.
*
@ -161,7 +176,92 @@ export const modPow = function (a, b, n) {
};
/**
* Secure random bytes for both node and browsers. Browser implementation uses WebWorkers in order to not lock the main process
* 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).
*
* @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++) {
let newWorker = _isProbablyPrimeWorker();
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);
}
});
};
/**
* 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;
};
/**
* 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
@ -193,44 +293,58 @@ export const randBytes = async function (byteLength, forceLength = false) {
});
};
/**
* 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
* 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 {Promise} A promise that resolves to a cryptographically secure random bigint between [min,max]
* @returns {bigint} The smallest positive representation of a in modulo n
*/
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;
export const toZn = function (a, n) {
n = BigInt(n);
a = BigInt(a) % n;
return (a < 0) ? a + n : a;
};
/**
* 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) {
/* 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;
}
function _isProbablyPrimeWorker() {
async function _onmessage(event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations);
postMessage({
'isPrime': isPrime,
'value': event.data.rnd
});
}
let workerCode = `(() => {'use strict';const eGcd = ${eGcd.toString()};const modInv = ${modInv.toString()};const modPow = ${modPow.toString()};const toZn = ${toZn.toString()};const randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}onmessage = ${_onmessage.toString()};})()`;
var _blob = new Blob([workerCode], { type: 'text/javascript' });
return new Worker(window.URL.createObjectURL(_blob));
}
async function _isProbablyPrime(w, iterations = 16) {
/*
PREFILTERING. Even values but 2 are not primes, so don't test.
1 is not a prime and the M-R algorithm needs w>1.
@ -547,82 +661,4 @@ export const isProbablyPrime = async function (w, iterations = 16) {
} 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

@ -1,14 +0,0 @@
'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

@ -2,7 +2,7 @@
// 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 bigintCryptoUtils = require('../dist/bigint-crypto-utils-latest.node');
const chai = require('chai');
const numbers = [
@ -52,7 +52,7 @@ 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);
const ret = await bigintCryptoUtils.isProbablyPrime(num.value);
chai.expect(ret).to.equal(num.prime);
});
});

View File

@ -2,23 +2,22 @@
// 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 bigintCryptoUtils = require('../dist/bigint-crypto-utils-latest.node');
const chai = require('chai');
const bitLengths = [
256,
512,
1024,
2048,
3072
3072,
4096
];
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);
let prime = await bigintCryptoUtils.prime(bitLength);
const ret = await bigintCryptoUtils.isProbablyPrime(prime);
chai.expect(ret).to.equal(true);
let bits = 1;
do {

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha Tests</title>
<title>bigint-crypto-utils - Mocha Tests</title>
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css">
</head>
<body>
@ -12,8 +12,8 @@
<script type="module">
import * as bigintUtils from '../../dist/bigint-utils-latest.browser.mod.js'
window.bigintUtils = bigintUtils;
import * as bigintCryptoUtils from '../../dist/bigint-crypto-utils-latest.browser.mod.js'
window.bigintCryptoUtils = bigintCryptoUtils;
import './tests.js';
mocha.run();
</script>

View File

@ -50,7 +50,7 @@ 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);
const ret = await bigintCryptoUtils.isProbablyPrime(num.value);
chai.expect(ret).to.equal(num.prime);
});
});
@ -63,19 +63,18 @@ describe('Testing validation of prime numbers', function () {
const bitLengths = [
256,
512,
1024,
2048,
3072
3072,
4096
];
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);
let prime = await bigintCryptoUtils.prime(bitLength);
const ret = await bigintCryptoUtils.isProbablyPrime(prime);
chai.expect(ret).to.equal(true);
let bits = 1;
do {