more tests. Fixed some bugs. Added github actions

This commit is contained in:
juanelas 2020-04-21 00:50:53 +02:00
parent f4430c5f08
commit b3a90aa6bc
20 changed files with 2014 additions and 1055 deletions

81
.github/workflows/nodejs.yml vendored Normal file
View File

@ -0,0 +1,81 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: build, test, check coverage
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [11, 12, 13]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: install
run: npm ci
- name: build
run: npm run build
- name: test
run: npm test
publication:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: EndBug/version-check@v1
id: check
- name: check version changes
if: steps.check.outputs.changed == 'true'
run: 'echo "Version change found! New version: ${{ steps.check.outputs.version }} (${{ steps.check.outputs.type }})"'
- uses: actions/setup-node@v1
if: steps.check.outputs.changed == 'true'
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- name: install
if: steps.check.outputs.changed == 'true'
run: npm ci
- name: build
if: steps.check.outputs.changed == 'true'
run: npm run build
- name: test
if: steps.check.outputs.changed == 'true'
run: npm test
- name: create code coverage report
if: steps.check.outputs.changed == 'true'
run: npm run coverage
- name: send report to coveralls.io
if: steps.check.outputs.changed == 'true'
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: publish to NPM
if: steps.check.outputs.changed == 'true'
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

6
.gitignore vendored
View File

@ -13,4 +13,8 @@ node_modules/
.vscode .vscode
# IntelliJ # IntelliJ
.idea/ .idea
# MYC output
.nyc_output
coverage/*

View File

@ -20,3 +20,16 @@ npm-debug.log*
# Visual Studio Code # Visual Studio Code
.vscode .vscode
# IntelliJ
.idea
# MYC output
.nyc_output
coverage/*
# Travis
.travis.yml
# GitHub
.github

266
README.md
View File

@ -1,8 +1,15 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
![Node CI](https://github.com/juanelas/bigint-crypto-utils/workflows/Node%20CI/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/juanelas/bigint-crypto-utils/badge.svg?branch=master)](https://coveralls.io/github/juanelas/bigint-crypto-utils?branch=master)
# bigint-crypto-utils # bigint-crypto-utils
Utils for working with cryptography using native JS ([ES-2020](https://tc39.es/ecma262/#sec-bigint-objects)) implementation of BigInt. It includes some extra functions to work with modular arithmetic along with secure random numbers and a fast strong probable prime generator/tester (parallelized multi-threaded Miller-Rabin primality tests if workers are supported). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). Arbitrary precision modular arithmetic, cryptographically secure random numbers and strong probable prime generation/testing.
It relies on the native JS implementation of ([BigInt](https://tc39.es/ecma262/#sec-bigint-objects)). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). The bundles can be imported directly by the browser or in Angular projects, React apps, Node.js, etc.
Secure random numbers are generated using the native crypto implementation of the browsers ([Web Cryptography API](https://w3c.github.io/webcrypto/)) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html)). Strong probable prime generation and testing use Miller-Rabin primality tests and are automatically sped up using parallel workers both in browsers and Node.js.
> 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 [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html). > 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 [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
@ -16,9 +23,7 @@ bigint-crypto-utils can be imported to your project with `npm`:
npm install bigint-crypto-utils npm install bigint-crypto-utils
``` ```
NPM installation defaults to the ES6 module for browsers and the CJS one for Node.js. NPM installation defaults to the ES6 module for browsers and the CJS one for Node.js. For web browsers, you can also directly download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.iife.js) or the [ESM bundle](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.mod.js) from the repository.
For web browsers, you can also directly download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.iife.js) or the [ES6 bundle module](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.mod.js) from GitHub.
## Usage examples ## Usage examples
@ -29,13 +34,30 @@ Import your module as :
const bigintCryptoUtils = require('bigint-crypto-utils') const bigintCryptoUtils = require('bigint-crypto-utils')
... // your code here ... // your code here
``` ```
- JavaScript native or TypeScript project - JavaScript native or TypeScript project (including React and Angular JS)
```javascript ```javascript
import * as bigintCryptoUtils from 'bigint-crypto-utils' import * as bigintCryptoUtils from 'bigint-crypto-utils'
... // your code here ... // your code here
``` ```
> BigInt is [ES-2020](https://tc39.es/ecma262/#sec-bigint-objects). In order to use it with TypeScript you should set `lib` (and probably also `target` and `module`) to `esnext` in `tsconfig.json`. BigInt is [ES-2020](https://tc39.es/ecma262/#sec-bigint-objects). In order to use it with TypeScript you should set `lib` (and probably also `target` and `module`) to `esnext` in `tsconfig.json`.
- JavaScript native browser ES6 mod `bigint-crypto-utils` **CANNOT BE POLYFILLED** to suport older browsers. If you are using webpack/babel to create your production bundles, you should target only the most modern browsers. For instance, for **React** apps created with [`create-react-app`](https://create-react-app.dev/), you should edit your `package.json` and modify the `browserList` so that it only targets the latest browsers (supporting the latest features):
```json
"browserslist": {
"production": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
```
Also, notice that BigInt is [ES-2020](https://tc39.es/ecma262/#sec-bigint-objects). In order to use it with TypeScript you should set `lib` (and probably also `target` and `module`) to `esnext` in `tsconfig.json`.
- JavaScript native browser ES module
```html ```html
<script type="module"> <script type="module">
import * as bigintCryptoUtils from 'lib/index.browser.bundle.mod.js' // Use you actual path to the broser mod bundle import * as bigintCryptoUtils from 'lib/index.browser.bundle.mod.js' // Use you actual path to the broser mod bundle
@ -100,214 +122,9 @@ You can find examples in the [examples folder of the repository](https://github.
## API reference documentation ## API reference documentation
### Functions
<dl>
<dt><a href="#abs">abs(a)</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="#bitLength">bitLength(a)</a><code>number</code></dt>
<dd><p>Returns the bitlength of a number</p>
</dd>
<dt><a href="#eGcd">eGcd(a, b)</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, b)</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, b)</a><code>bigint</code></dt>
<dd><p>The least common multiple computed as abs(a*b)/gcd(a,b)</p>
</dd>
<dt><a href="#max">max(a, b)</a><code>bigint</code></dt>
<dd><p>Maximum. max(a,b)==a if a&gt;=b. max(a,b)==b if a&lt;=b</p>
</dd>
<dt><a href="#min">min(a, b)</a><code>bigint</code></dt>
<dd><p>Minimum. min(a,b)==b if a&gt;=b. min(a,b)==a if a&lt;=b</p>
</dd>
<dt><a href="#modInv">modInv(a, n)</a><code>bigint</code></dt>
<dd><p>Modular inverse.</p>
</dd>
<dt><a href="#modPow">modPow(b, e, n)</a><code>bigint</code></dt>
<dd><p>Modular exponentiation b**e mod n. Currently using the right-to-left binary method</p>
</dd>
<dt><a href="#toZn">toZn(a, n)</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="#isProbablyPrime">isProbablyPrime(w, [iterations])</a><code>Promise.&lt;boolean&gt;</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(bitLength, [iterations])</a><code>Promise.&lt;bigint&gt;</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).
The node version can also use worker_threads if they are available (enabled by default with Node 11 and
and can be enabled at runtime executing node --experimental-worker with node &gt;=10.5.0).</p>
</dd>
<dt><a href="#primeSync">primeSync(bitLength, [iterations])</a><code>bigint</code></dt>
<dd><p>A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator.
The sync version is NOT RECOMMENDED since it won&#39;t use workers and thus it&#39;ll be slower and may freeze thw window in browser&#39;s javascript. Please consider using prime() instead.</p>
</dd>
<dt><a href="#randBetween">randBetween(max, [min])</a><code>bigint</code></dt>
<dd><p>Returns a cryptographically secure random integer between [min,max]</p>
</dd>
<dt><a href="#randBits">randBits(bitLength, [forceLength])</a><code>Promise.&lt;(Buffer|Uint8Array)&gt;</code></dt>
<dd><p>Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</p>
<p>Since version 3.0.0 this is an async function and a new randBitsSync function has been added. If you are migrating from version 2 call randBitsSync instead.</p>
</dd>
<dt><a href="#randBitsSync">randBitsSync(bitLength, [forceLength])</a><code>Buffer</code> | <code>Uint8Array</code></dt>
<dd><p>Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</p>
</dd>
<dt><a href="#randBytes">randBytes(byteLength, [forceLength])</a><code>Promise.&lt;(Buffer|Uint8Array)&gt;</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="#randBytesSync">randBytesSync(byteLength, [forceLength])</a><code>Buffer</code> | <code>Uint8Array</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>
</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(a) ⇒ <code>bigint</code>
Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
**Kind**: global function
**Returns**: <code>bigint</code> - the absolute value of a
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
<a name="bitLength"></a>
### bitLength(a) ⇒ <code>number</code>
Returns the bitlength of a number
**Kind**: global function
**Returns**: <code>number</code> - - the bit length
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
<a name="eGcd"></a>
### eGcd(a, b) ⇒ [<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 function
**Returns**: [<code>egcdReturn</code>](#egcdReturn) - A triple (g, x, y), such that ax + by = g = gcd(a, b).
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<a name="gcd"></a>
### gcd(a, b) ⇒ <code>bigint</code>
Greatest-common divisor of two integers based on the iterative binary algorithm.
**Kind**: global function
**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(a, b) ⇒ <code>bigint</code>
The least common multiple computed as abs(a*b)/gcd(a,b)
**Kind**: global function
**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="max"></a>
### max(a, b) ⇒ <code>bigint</code>
Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b
**Kind**: global function
**Returns**: <code>bigint</code> - maximum of numbers a and b
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<a name="min"></a>
### min(a, b) ⇒ <code>bigint</code>
Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
**Kind**: global function
**Returns**: <code>bigint</code> - minimum of numbers a and b
| Param | Type |
| --- | --- |
| a | <code>number</code> \| <code>bigint</code> |
| b | <code>number</code> \| <code>bigint</code> |
<a name="modInv"></a>
### modInv(a, n) ⇒ <code>bigint</code>
Modular inverse.
**Kind**: global function
**Returns**: <code>bigint</code> - the inverse modulo n or NaN if it does not exist
| 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(b, e, n) ⇒ <code>bigint</code>
Modular exponentiation b**e mod n. Currently using the right-to-left binary method
**Kind**: global function
**Returns**: <code>bigint</code> - b**e mod n
| Param | Type | Description |
| --- | --- | --- |
| b | <code>number</code> \| <code>bigint</code> | base |
| e | <code>number</code> \| <code>bigint</code> | exponent |
| n | <code>number</code> \| <code>bigint</code> | modulo |
<a name="toZn"></a>
### toZn(a, n) ⇒ <code>bigint</code>
Finds the smallest positive element that is congruent to a in modulo n
**Kind**: global function
**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="isProbablyPrime"></a> <a name="isProbablyPrime"></a>
### isProbablyPrime(w, [iterations]) ⇒ <code>Promise.&lt;boolean&gt;</code> ### isProbablyPrime(w, [iterations], [disableWorkers]) ⇒ <code>Promise.&lt;boolean&gt;</code>
The test first tries if any of the first 250 small primes are a factor of the input number and then passes several 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) iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)
@ -318,6 +135,7 @@ iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)
| --- | --- | --- | --- | | --- | --- | --- | --- |
| w | <code>number</code> \| <code>bigint</code> | | An integer to be tested for primality | | w | <code>number</code> \| <code>bigint</code> | | An integer to be tested for primality |
| [iterations] | <code>number</code> | <code>16</code> | The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 | | [iterations] | <code>number</code> | <code>16</code> | The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 |
| [disableWorkers] | <code>boolean</code> | <code>false</code> | Disable the use of workers for the primality test |
<a name="prime"></a> <a name="prime"></a>
@ -353,7 +171,7 @@ The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be
<a name="randBetween"></a> <a name="randBetween"></a>
### randBetween(max, [min]) ⇒ <code>bigint</code> ### randBetween(max, [min]) ⇒ <code>bigint</code>
Returns a cryptographically secure random integer between [min,max] Returns a cryptographically secure random integer between [min,max]. Both numbers must be >=0
**Kind**: global function **Kind**: global function
**Returns**: <code>bigint</code> - A cryptographically secure random bigint between [min,max] **Returns**: <code>bigint</code> - A cryptographically secure random bigint between [min,max]
@ -368,11 +186,8 @@ Returns a cryptographically secure random integer between [min,max]
### randBits(bitLength, [forceLength]) ⇒ <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code> ### randBits(bitLength, [forceLength]) ⇒ <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code>
Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
Since version 3.0.0 this is an async function and a new randBitsSync function has been added. If you are migrating from version 2 call randBitsSync instead.
**Kind**: global function **Kind**: global function
**Returns**: <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code> - A Promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits **Returns**: <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code> - A Promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits
**Since**: 3.0.0
| Param | Type | Default | Description | | Param | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@ -386,7 +201,6 @@ Secure random bits for both node and browsers. Node version uses crypto.randomFi
**Kind**: global function **Kind**: global function
**Returns**: <code>Buffer</code> \| <code>Uint8Array</code> - A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits **Returns**: <code>Buffer</code> \| <code>Uint8Array</code> - A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits
**Since**: 3.0.0
| Param | Type | Default | Description | | Param | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@ -396,7 +210,7 @@ Secure random bits for both node and browsers. Node version uses crypto.randomFi
<a name="randBytes"></a> <a name="randBytes"></a>
### randBytes(byteLength, [forceLength]) ⇒ <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code> ### randBytes(byteLength, [forceLength]) ⇒ <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code>
Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
**Kind**: global function **Kind**: global function
**Returns**: <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code> - A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes **Returns**: <code>Promise.&lt;(Buffer\|Uint8Array)&gt;</code> - A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
@ -419,17 +233,3 @@ Secure random bytes for both node and browsers. Node version uses crypto.randomF
| byteLength | <code>number</code> | | The desired number of random bytes | | byteLength | <code>number</code> | | The desired number of random bytes |
| [forceLength] | <code>boolean</code> | <code>false</code> | If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 | | [forceLength] | <code>boolean</code> | <code>false</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="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

@ -7,15 +7,57 @@ const pkgJson = require('../package.json')
const rootDir = path.join(__dirname, '..') const rootDir = path.join(__dirname, '..')
const template = path.join(rootDir, pkgJson.directories.src, 'doc', 'readme-template.md') function camelise (str) {
return str.replace(/-([a-z])/g,
function (m, w) {
return w.toUpperCase()
})
}
function getRepositoryData () {
if (typeof pkgJson.repository === 'string') {
const repodata = pkgJson.repository.split(/[:/]/)
const repoProvider = repodata[0]
if (repoProvider === 'github' || repoProvider === 'gitlab' || repoProvider === 'bitbucket') {
return {
repoProvider,
repoUsername: repodata[1],
repoName: repodata[2]
}
} else return null
}
}
const { repoProvider, repoUsername, repoName } = getRepositoryData() || { repoProvider: null, repoUsername: null, repoName: null }
let iifeBundle, esmBundle, workflowBadget, coverallsBadge
if (repoProvider && repoProvider === 'github') {
iifeBundle = `[IIFE bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/lib/index.browser.bundle.iife.js)`
esmBundle = `[ESM bundle](https://raw.githubusercontent.com/${repoUsername}/${repoName}/master/lib/index.browser.bundle.mod.js)`
workflowBadget = `![Node CI](https://github.com/${repoUsername}/${repoName}/workflows/Node%20CI/badge.svg)`
coverallsBadge = `[![Coverage Status](https://coveralls.io/repos/github/${repoUsername}/${repoName}/badge.svg?branch=master)](https://coveralls.io/github/${repoUsername}/${repoName}?branch=master)`
}
const templateFile = path.join(rootDir, pkgJson.directories.src, 'doc', 'readme-template.md')
let template = fs.readFileSync(templateFile, { encoding: 'UTF-8' })
.replace(/\{\{PKG_NAME\}\}/g, pkgJson.name)
.replace(/\{\{PKG_CAMELCASE\}\}/g, camelise(pkgJson.name))
.replace(/\{\{IIFE_BUNDLE\}\}/g, iifeBundle || 'IIFE bundle')
.replace(/\{\{ESM_BUNDLE\}\}/g, esmBundle || 'ESM bundle')
if (repoProvider && repoProvider === 'github') {
template = template.replace(/\{\{GITHUB_ACTIONS_BADGES\}\}/g, workflowBadget + '\n' + coverallsBadge)
}
const input = path.join(rootDir, pkgJson.browser) const input = path.join(rootDir, pkgJson.browser)
// Let us replace bigint literals by standard numbers to avoid issues with bigint
const source = fs.readFileSync(input, { encoding: 'UTF-8' }).replace(/([0-9]+)n([,\s\n)])/g, '$1$2') const source = fs.readFileSync(input, { encoding: 'UTF-8' }).replace(/([0-9]+)n([,\s\n)])/g, '$1$2')
const options = { const options = {
source, // we need to use this instead of files in order to avoid issues with esnext features source,
template: fs.readFileSync(template, { encoding: 'UTF-8' }), template,
'heading-depth': 3 // The initial heading depth. For example, with a value of 2 the top-level markdown headings look like "## The heading" 'heading-depth': 3, // The initial heading depth. For example, with a value of 2 the top-level markdown headings look like "## The heading"
// 'global-index-format': 'none' // none, grouped, table, dl. 'global-index-format': 'none' // none, grouped, table, dl.
} }
jsdoc2md.clear().then(() => { jsdoc2md.clear().then(() => {

View File

@ -24,13 +24,24 @@ const pkgCamelisedName = camelise(pkgName)
const input = path.join(srcDir, 'js', 'index.js') const input = path.join(srcDir, 'js', 'index.js')
module.exports = [ module.exports = [
{ // Browser { // Native JS
input: input, input: input,
output: [ output: [
{ {
file: path.join(rootDir, pkgJson.browser), file: path.join(rootDir, pkgJson.browser),
format: 'es' format: 'es'
}
],
plugins: [
replace({
'process.browser': true
})
],
external: ['bigint-mod-arith']
}, },
{ // Browser bundles
input: input,
output: [
{ {
file: path.join(dstDir, 'index.browser.bundle.iife.js'), file: path.join(dstDir, 'index.browser.bundle.iife.js'),
format: 'iife', format: 'iife',
@ -48,24 +59,22 @@ module.exports = [
resolve({ resolve({
browser: true browser: true
}), }),
terser({ terser()
exclude: [path.basename(pkgJson.browser)]
})
] ]
}, },
{ // Node { // Node
input: input, input: input,
output: { output: {
file: path.join(rootDir, pkgJson.main), file: path.join(rootDir, pkgJson.main),
format: 'cjs' format: 'cjs',
esModule: false,
externalLiveBindings: false
}, },
plugins: [ plugins: [
replace({ replace({
'process.browser': false 'process.browser': false
}),
resolve({
browser: true
}) })
] ],
external: ['bigint-mod-arith']
} }
] ]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,213 +1,5 @@
/** import { bitLength, eGcd, modInv, modPow, toZn } from 'bigint-mod-arith'
* Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0 export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from 'bigint-mod-arith'
*
* @param {number|bigint} a
*
* @returns {bigint} the absolute value of a
*/
function abs (a) {
a = BigInt(a)
return (a >= 0n) ? a : -a
}
/**
* Returns the bitlength of a number
*
* @param {number|bigint} a
* @returns {number} - the bit length
*/
function bitLength (a) {
a = BigInt(a)
if (a === 1n) { return 1 }
let bits = 1
do {
bits++
} while ((a >>= 1n) > 1n)
return bits
}
/**
* @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b).
* @property {bigint} g
* @property {bigint} x
* @property {bigint} y
*/
/**
* An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
* Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/
function eGcd (a, b) {
a = BigInt(a)
b = BigInt(b)
if (a <= 0n | b <= 0n) { return NaN } // a and b MUST be positive
let x = 0n
let y = 1n
let u = 1n
let v = 0n
while (a !== 0n) {
const q = b / a
const r = b % a
const m = x - (u * q)
const n = y - (v * q)
b = a
a = r
x = u
y = v
u = m
v = n
}
return {
b: b,
x: x,
y: y
}
}
/**
* 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
*/
function gcd (a, b) {
a = abs(a)
b = abs(b)
if (a === 0n) { return b } else if (b === 0n) { return a }
let shift = 0n
while (!((a | b) & 1n)) {
a >>= 1n
b >>= 1n
shift++
}
while (!(a & 1n)) a >>= 1n
do {
while (!(b & 1n)) b >>= 1n
if (a > b) {
const 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
*/
function lcm (a, b) {
a = BigInt(a)
b = BigInt(b)
if (a === 0n && b === 0n) { return 0n }
return abs(a * b) / gcd(a, b)
}
/**
* Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {bigint} maximum of numbers a and b
*/
function max (a, b) {
a = BigInt(a)
b = BigInt(b)
return (a >= b) ? a : b
}
/**
* Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {bigint} minimum of numbers a and b
*/
function min (a, b) {
a = BigInt(a)
b = BigInt(b)
return (a >= b) ? b : a
}
/**
* Modular inverse.
*
* @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo
*
* @returns {bigint} the inverse modulo n or NaN if it does not exist
*/
function modInv (a, n) {
const egcd = eGcd(toZn(a, n), n)
if (egcd.b !== 1n) {
return NaN // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
}
/**
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method
*
* @param {number|bigint} b base
* @param {number|bigint} e exponent
* @param {number|bigint} n modulo
*
* @returns {bigint} b**e mod n
*/
function modPow (b, e, n) {
n = BigInt(n)
if (n === 0n) { return NaN } else if (n === 1n) { return 0n }
b = toZn(b, n)
e = BigInt(e)
if (e < 0n) {
return modInv(modPow(b, abs(e), n), n)
}
let r = 1n
while (e > 0) {
if ((e % 2n) === 1n) {
r = (r * b) % n
}
e = e / 2n
b = b ** 2n % n
}
return r
}
/**
* 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
*/
function toZn (a, n) {
n = BigInt(n)
if (n <= 0) { return NaN }
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 * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
@ -215,10 +7,11 @@ function toZn (a, n) {
* *
* @param {number | bigint} w An integer to be tested for primality * @param {number | bigint} w An integer to be tested for primality
* @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 * @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
* @param {boolean} [disableWorkers = false] Disable the use of workers for the primality test
* *
* @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) * @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/ */
function isProbablyPrime (w, iterations = 16) { function isProbablyPrime (w, iterations = 16, disableWorkers = false) {
if (typeof w === 'number') { if (typeof w === 'number') {
w = BigInt(w) w = BigInt(w)
} }
@ -259,8 +52,9 @@ function isProbablyPrime (w, iterations = 16) {
* @returns {Promise<bigint>} A promise that resolves to a bigint probable prime of bitLength bits. * @returns {Promise<bigint>} A promise that resolves to a bigint probable prime of bitLength bits.
*/ */
function prime (bitLength, iterations = 16) { function prime (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
/* istanbul ignore if */
if (!_useWorkers) { // If there is no support for workers if (!_useWorkers) { // If there is no support for workers
let rnd = 0n let rnd = 0n
do { do {
@ -305,13 +99,14 @@ function prime (bitLength, iterations = 16) {
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
const buf = randBitsSync(bitLength, true) randBits(bitLength, true).then(function (buf) {
const rnd = fromBuffer(buf) const rnd = fromBuffer(buf)
workerList[i].postMessage({ workerList[i].postMessage({
rnd: rnd, rnd: rnd,
iterations: iterations, iterations: iterations,
id: i id: i
}) })
})
} }
}) })
} }
@ -326,7 +121,7 @@ function prime (bitLength, iterations = 16) {
* @returns {bigint} A bigint probable prime of bitLength bits. * @returns {bigint} A bigint probable prime of bitLength bits.
*/ */
function primeSync (bitLength, iterations = 16) { function primeSync (bitLength, iterations = 16) {
if (bitLength < 1) throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
let rnd = 0n let rnd = 0n
do { do {
rnd = fromBuffer(randBitsSync(bitLength, true)) rnd = fromBuffer(randBitsSync(bitLength, true))
@ -335,14 +130,14 @@ function primeSync (bitLength, iterations = 16) {
} }
/** /**
* Returns a cryptographically secure random integer between [min,max] * Returns a cryptographically secure random integer between [min,max]. Both numbers must be >=0
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} [min = BigInt(1)] Returned value will be >= min * @param {bigint} [min = BigInt(1)] Returned value will be >= min
* *
* @returns {bigint} A cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
function randBetween (max, min = 1n) { function randBetween (max, min = 1n) {
if (max <= min) throw new Error('max must be > min') if (max <= 0n || min < 0n || max <= min) throw new Error('inputs should be max > 0, min >= 0; max > min')
const interval = max - min const interval = max - min
const bitLen = bitLength(interval) const bitLen = bitLength(interval)
let rnd let rnd
@ -356,8 +151,6 @@ function randBetween (max, min = 1n) {
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* *
* Since version 3.0.0 this is an async function and a new randBitsSync function has been added. If you are migrating from version 2 call randBitsSync instead.
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -365,7 +158,7 @@ function randBetween (max, min = 1n) {
*/ */
async function randBits (bitLength, forceLength = false) { async function randBits (bitLength, forceLength = false) {
if (bitLength < 1) { if (bitLength < 1) {
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) throw new RangeError('bitLength MUST be > 0')
} }
const byteLength = Math.ceil(bitLength / 8) const byteLength = Math.ceil(bitLength / 8)
@ -385,7 +178,6 @@ async function randBits (bitLength, forceLength = false) {
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -393,7 +185,7 @@ async function randBits (bitLength, forceLength = false) {
*/ */
function randBitsSync (bitLength, forceLength = false) { function randBitsSync (bitLength, forceLength = false) {
if (bitLength < 1) { if (bitLength < 1) {
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) throw new RangeError('bitLength MUST be > 0')
} }
const byteLength = Math.ceil(bitLength / 8) const byteLength = Math.ceil(bitLength / 8)
@ -411,7 +203,7 @@ function randBitsSync (bitLength, forceLength = false) {
} }
/** /**
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
* *
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
@ -419,19 +211,19 @@ function randBitsSync (bitLength, forceLength = false) {
* @returns {Promise<Buffer | Uint8Array>} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Promise<Buffer | Uint8Array>} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
function randBytes (byteLength, forceLength = false) { function randBytes (byteLength, forceLength = false) {
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
return new Promise(function (resolve, reject) {
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
{ // browser { // browser
return new Promise(function (resolve) {
const buf = new Uint8Array(byteLength) const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf) self.crypto.getRandomValues(buf)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128 if (forceLength) buf[0] = buf[0] | 128
resolve(buf) resolve(buf)
})
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
})
} }
/** /**
@ -443,14 +235,14 @@ function randBytes (byteLength, forceLength = false) {
* @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
function randBytesSync (byteLength, forceLength = false) { function randBytesSync (byteLength, forceLength = false) {
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
{ // browser { // browser
const buf = new Uint8Array(byteLength) const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf) self.crypto.getRandomValues(buf)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) { buf[0] = buf[0] | 128 } if (forceLength) buf[0] = buf[0] | 128
return buf return buf
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
@ -814,4 +606,4 @@ let _useWorkers = false // The following is just to check whether we can use wor
if (self.Worker) _useWorkers = true if (self.Worker) _useWorkers = true
} }
export { abs, bitLength, eGcd, gcd, isProbablyPrime, lcm, max, min, modInv, modPow, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync, toZn } export { isProbablyPrime, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync }

View File

@ -1,217 +1,6 @@
'use strict' 'use strict'
Object.defineProperty(exports, '__esModule', { value: true }) var bigintModArith = require('bigint-mod-arith')
/**
* 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
*/
function abs (a) {
a = BigInt(a)
return (a >= 0n) ? a : -a
}
/**
* Returns the bitlength of a number
*
* @param {number|bigint} a
* @returns {number} - the bit length
*/
function bitLength (a) {
a = BigInt(a)
if (a === 1n) { return 1 }
let bits = 1
do {
bits++
} while ((a >>= 1n) > 1n)
return bits
}
/**
* @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b).
* @property {bigint} g
* @property {bigint} x
* @property {bigint} y
*/
/**
* An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
* Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/
function eGcd (a, b) {
a = BigInt(a)
b = BigInt(b)
if (a <= 0n | b <= 0n) { return NaN } // a and b MUST be positive
let x = 0n
let y = 1n
let u = 1n
let v = 0n
while (a !== 0n) {
const q = b / a
const r = b % a
const m = x - (u * q)
const n = y - (v * q)
b = a
a = r
x = u
y = v
u = m
v = n
}
return {
b: b,
x: x,
y: y
}
}
/**
* 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
*/
function gcd (a, b) {
a = abs(a)
b = abs(b)
if (a === 0n) { return b } else if (b === 0n) { return a }
let shift = 0n
while (!((a | b) & 1n)) {
a >>= 1n
b >>= 1n
shift++
}
while (!(a & 1n)) a >>= 1n
do {
while (!(b & 1n)) b >>= 1n
if (a > b) {
const 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
*/
function lcm (a, b) {
a = BigInt(a)
b = BigInt(b)
if (a === 0n && b === 0n) { return 0n }
return abs(a * b) / gcd(a, b)
}
/**
* Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {bigint} maximum of numbers a and b
*/
function max (a, b) {
a = BigInt(a)
b = BigInt(b)
return (a >= b) ? a : b
}
/**
* Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {bigint} minimum of numbers a and b
*/
function min (a, b) {
a = BigInt(a)
b = BigInt(b)
return (a >= b) ? b : a
}
/**
* Modular inverse.
*
* @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo
*
* @returns {bigint} the inverse modulo n or NaN if it does not exist
*/
function modInv (a, n) {
const egcd = eGcd(toZn(a, n), n)
if (egcd.b !== 1n) {
return NaN // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
}
/**
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method
*
* @param {number|bigint} b base
* @param {number|bigint} e exponent
* @param {number|bigint} n modulo
*
* @returns {bigint} b**e mod n
*/
function modPow (b, e, n) {
n = BigInt(n)
if (n === 0n) { return NaN } else if (n === 1n) { return 0n }
b = toZn(b, n)
e = BigInt(e)
if (e < 0n) {
return modInv(modPow(b, abs(e), n), n)
}
let r = 1n
while (e > 0) {
if ((e % 2n) === 1n) {
r = (r * b) % n
}
e = e / 2n
b = b ** 2n % n
}
return r
}
/**
* 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
*/
function toZn (a, n) {
n = BigInt(n)
if (n <= 0) { return NaN }
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 * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
@ -219,16 +8,18 @@ function toZn (a, n) {
* *
* @param {number | bigint} w An integer to be tested for primality * @param {number | bigint} w An integer to be tested for primality
* @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 * @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
* @param {boolean} [disableWorkers = false] Disable the use of workers for the primality test
* *
* @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) * @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/ */
function isProbablyPrime (w, iterations = 16) { function isProbablyPrime (w, iterations = 16, disableWorkers = false) {
if (typeof w === 'number') { if (typeof w === 'number') {
w = BigInt(w) w = BigInt(w)
} }
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
{ // Node.js { // Node.js
if (_useWorkers) { /* istanbul ignore else */
if (!disableWorkers && _useWorkers) {
const { Worker } = require('worker_threads') const { Worker } = require('worker_threads')
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const worker = new Worker(__filename) const worker = new Worker(__filename)
@ -244,7 +35,6 @@ function isProbablyPrime (w, iterations = 16) {
rnd: w, rnd: w,
iterations: iterations, iterations: iterations,
id: 0 id: 0
}) })
}) })
} else { } else {
@ -269,8 +59,9 @@ function isProbablyPrime (w, iterations = 16) {
* @returns {Promise<bigint>} A promise that resolves to a bigint probable prime of bitLength bits. * @returns {Promise<bigint>} A promise that resolves to a bigint probable prime of bitLength bits.
*/ */
function prime (bitLength, iterations = 16) { function prime (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
/* istanbul ignore if */
if (!_useWorkers) { // If there is no support for workers if (!_useWorkers) { // If there is no support for workers
let rnd = 0n let rnd = 0n
do { do {
@ -316,13 +107,14 @@ function prime (bitLength, iterations = 16) {
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
const buf = randBitsSync(bitLength, true) randBits(bitLength, true).then(function (buf) {
const rnd = fromBuffer(buf) const rnd = fromBuffer(buf)
workerList[i].postMessage({ workerList[i].postMessage({
rnd: rnd, rnd: rnd,
iterations: iterations, iterations: iterations,
id: i id: i
}) })
})
} }
}) })
} }
@ -337,7 +129,7 @@ function prime (bitLength, iterations = 16) {
* @returns {bigint} A bigint probable prime of bitLength bits. * @returns {bigint} A bigint probable prime of bitLength bits.
*/ */
function primeSync (bitLength, iterations = 16) { function primeSync (bitLength, iterations = 16) {
if (bitLength < 1) throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
let rnd = 0n let rnd = 0n
do { do {
rnd = fromBuffer(randBitsSync(bitLength, true)) rnd = fromBuffer(randBitsSync(bitLength, true))
@ -346,16 +138,16 @@ function primeSync (bitLength, iterations = 16) {
} }
/** /**
* Returns a cryptographically secure random integer between [min,max] * Returns a cryptographically secure random integer between [min,max]. Both numbers must be >=0
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} [min = BigInt(1)] Returned value will be >= min * @param {bigint} [min = BigInt(1)] Returned value will be >= min
* *
* @returns {bigint} A cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
function randBetween (max, min = 1n) { function randBetween (max, min = 1n) {
if (max <= min) throw new Error('max must be > min') if (max <= 0n || min < 0n || max <= min) throw new Error('inputs should be max > 0, min >= 0; max > min')
const interval = max - min const interval = max - min
const bitLen = bitLength(interval) const bitLen = bigintModArith.bitLength(interval)
let rnd let rnd
do { do {
const buf = randBitsSync(bitLen) const buf = randBitsSync(bitLen)
@ -367,8 +159,6 @@ function randBetween (max, min = 1n) {
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* *
* Since version 3.0.0 this is an async function and a new randBitsSync function has been added. If you are migrating from version 2 call randBitsSync instead.
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -376,7 +166,7 @@ function randBetween (max, min = 1n) {
*/ */
async function randBits (bitLength, forceLength = false) { async function randBits (bitLength, forceLength = false) {
if (bitLength < 1) { if (bitLength < 1) {
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) throw new RangeError('bitLength MUST be > 0')
} }
const byteLength = Math.ceil(bitLength / 8) const byteLength = Math.ceil(bitLength / 8)
@ -396,7 +186,6 @@ async function randBits (bitLength, forceLength = false) {
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -404,7 +193,7 @@ async function randBits (bitLength, forceLength = false) {
*/ */
function randBitsSync (bitLength, forceLength = false) { function randBitsSync (bitLength, forceLength = false) {
if (bitLength < 1) { if (bitLength < 1) {
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) throw new RangeError('bitLength MUST be > 0')
} }
const byteLength = Math.ceil(bitLength / 8) const byteLength = Math.ceil(bitLength / 8)
@ -422,7 +211,7 @@ function randBitsSync (bitLength, forceLength = false) {
} }
/** /**
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
* *
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
@ -430,19 +219,22 @@ function randBitsSync (bitLength, forceLength = false) {
* @returns {Promise<Buffer | Uint8Array>} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Promise<Buffer | Uint8Array>} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
function randBytes (byteLength, forceLength = false) { function randBytes (byteLength, forceLength = false) {
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
return new Promise(function (resolve, reject) {
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
{ // node {
const crypto = require('crypto') const crypto = require('crypto')
const buf = Buffer.alloc(byteLength) crypto.randomBytes(byteLength, function (err, buf) {
return crypto.randomFill(buf, function (resolve) { /* istanbul ignore if */
if (err) reject(err)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128 if (forceLength) buf[0] = buf[0] | 128
resolve(buf) resolve(buf)
}) })
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
})
} }
/** /**
@ -454,15 +246,14 @@ function randBytes (byteLength, forceLength = false) {
* @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
function randBytesSync (byteLength, forceLength = false) { function randBytesSync (byteLength, forceLength = false) {
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
{ // node { // node
const crypto = require('crypto') const crypto = require('crypto')
const buf = Buffer.alloc(byteLength) const buf = crypto.randomBytes(byteLength)
crypto.randomFillSync(buf)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) { buf[0] = buf[0] | 128 } if (forceLength) buf[0] = buf[0] | 128
return buf return buf
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
@ -780,11 +571,11 @@ function _isProbablyPrime (w, iterations = 16) {
do { do {
const b = randBetween(d, 2n) const b = randBetween(d, 2n)
let z = modPow(b, m, w) let z = bigintModArith.modPow(b, m, w)
if (z === 1n || z === d) continue if (z === 1n || z === d) continue
let j = 1 let j = 1
while (j < a) { while (j < a) {
z = modPow(z, 2n, w) z = bigintModArith.modPow(z, 2n, w)
if (z === d) break if (z === d) break
if (z === 1n) return false if (z === 1n) return false
j++ j++
@ -802,10 +593,12 @@ let _useWorkers = false // The following is just to check whether we can use wor
require.resolve('worker_threads') require.resolve('worker_threads')
_useWorkers = true _useWorkers = true
} catch (e) { } catch (e) {
/* istanbul ignore next */
console.log(`[bigint-crypto-utils] WARNING: console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers. This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading). · With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `) · With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
/* istanbul ignore next */
_useWorkers = true _useWorkers = true
} }
} }
@ -813,6 +606,7 @@ This node version doesn't support worker_threads. You should enable them in orde
if (_useWorkers) { // node.js with support for workers if (_useWorkers) { // node.js with support for workers
const { parentPort, isMainThread } = require('worker_threads') const { parentPort, isMainThread } = require('worker_threads')
/* istanbul ignore if */
if (!isMainThread) { // worker if (!isMainThread) { // worker
parentPort.on('message', function (data) { // Let's start once we are called parentPort.on('message', function (data) { // Let's start once we are called
// data = {rnd: <bigint>, iterations: <number>} // data = {rnd: <bigint>, iterations: <number>}
@ -826,16 +620,17 @@ if (_useWorkers) { // node.js with support for workers
} }
} }
exports.abs = abs exports.abs = bigintModArith.abs
exports.bitLength = bitLength exports.bitLength = bigintModArith.bitLength
exports.eGcd = eGcd exports.eGcd = bigintModArith.eGcd
exports.gcd = gcd exports.gcd = bigintModArith.gcd
exports.lcm = bigintModArith.lcm
exports.max = bigintModArith.max
exports.min = bigintModArith.min
exports.modInv = bigintModArith.modInv
exports.modPow = bigintModArith.modPow
exports.toZn = bigintModArith.toZn
exports.isProbablyPrime = isProbablyPrime exports.isProbablyPrime = isProbablyPrime
exports.lcm = lcm
exports.max = max
exports.min = min
exports.modInv = modInv
exports.modPow = modPow
exports.prime = prime exports.prime = prime
exports.primeSync = primeSync exports.primeSync = primeSync
exports.randBetween = randBetween exports.randBetween = randBetween
@ -843,4 +638,3 @@ exports.randBits = randBits
exports.randBitsSync = randBitsSync exports.randBitsSync = randBitsSync
exports.randBytes = randBytes exports.randBytes = randBytes
exports.randBytesSync = randBytesSync exports.randBytesSync = randBytesSync
exports.toZn = toZn

1247
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"name": "bigint-crypto-utils", "name": "bigint-crypto-utils",
"version": "3.0.1", "version": "3.0.1",
"description": "Utils for working with cryptography using native JS implementation of BigInt. It includes arbitrary precision modular arithmetic, cryptographically secure random numbers and strong probable prime generation/testing.", "description": "Arbitrary precision modular arithmetic, cryptographically secure random numbers and strong probable prime generation/testing. It relies on the native JS implementation of BigInt and therefore it works in modern browsers, Angular, React, Node.js, etc.",
"keywords": [ "keywords": [
"modular arithmetics", "modular arithmetics",
"crypto", "crypto",
@ -10,7 +10,9 @@
"rng", "rng",
"prng", "prng",
"primality test", "primality test",
"BigInt" "BigInt",
"angular",
"react"
], ],
"license": "MIT", "license": "MIT",
"author": { "author": {
@ -29,15 +31,20 @@
"test": "./test", "test": "./test",
"types": "./types" "types": "./types"
}, },
"engines": {
"node": ">=10.4.0"
},
"scripts": { "scripts": {
"test": "mocha", "test": "nyc --check-coverage mocha",
"coverage": "nyc report --reporter=lcov",
"build:js": "rollup -c build/rollup.config.js", "build:js": "rollup -c build/rollup.config.js",
"build:standard": "standard --fix", "build:standard": "standard --fix",
"build:browserTests": "rollup -c build/rollup.tests.config.js", "build:browserTests": "rollup -c build/rollup.tests.config.js",
"build:docs": "node build/build.docs.js", "build:docs": "node build/build.docs.js",
"build:dts": "node build/build.dts.js", "build:dts": "node build/build.dts.js",
"build": "run-s build:**", "build": "run-s build:**",
"prepublishOnly": "npm run build" "preversion": "npm run build && npm run test",
"postversion": "git push"
}, },
"standard": { "standard": {
"env": [ "env": [
@ -57,21 +64,22 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^11.0.2", "@rollup/plugin-commonjs": "^11.1.0",
"@rollup/plugin-multi-entry": "^3.0.0", "@rollup/plugin-multi-entry": "^3.0.0",
"@rollup/plugin-node-resolve": "^7.1.1", "@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-replace": "^2.3.1", "@rollup/plugin-replace": "^2.3.2",
"bigint-mod-arith": "^2.0.4",
"chai": "^4.2.0", "chai": "^4.2.0",
"jsdoc-to-markdown": "^5.0.3", "jsdoc-to-markdown": "^5.0.3",
"mocha": "^7.1.1", "mocha": "^7.1.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"rollup": "^2.3.4", "nyc": "^15.0.1",
"rollup": "^2.6.1",
"rollup-plugin-terser": "^5.3.0", "rollup-plugin-terser": "^5.3.0",
"standard": "^14.3.3", "standard": "^14.3.3",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },
"dependencies": { "dependencies": {
"@types/node": ">=10" "@types/node": ">=10.4.0",
"bigint-mod-arith": "^2.0.7"
} }
} }

View File

@ -1,24 +1,28 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
{{GITHUB_ACTIONS_BADGES}}
# bigint-crypto-utils # {{PKG_NAME}}
Utils for working with cryptography using native JS ([ES-2020](https://tc39.es/ecma262/#sec-bigint-objects)) implementation of BigInt. It includes some extra functions to work with modular arithmetic along with secure random numbers and a fast strong probable prime generator/tester (parallelized multi-threaded Miller-Rabin primality tests if workers are supported). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). Arbitrary precision modular arithmetic, cryptographically secure random numbers and strong probable prime generation/testing.
It relies on the native JS implementation of ([BigInt](https://tc39.es/ecma262/#sec-bigint-objects)). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). The bundles can be imported directly by the browser or in Angular projects, React apps, Node.js, etc.
Secure random numbers are generated using the native crypto implementation of the browsers ([Web Cryptography API](https://w3c.github.io/webcrypto/)) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html)). Strong probable prime generation and testing use Miller-Rabin primality tests and are automatically sped up using parallel workers both in browsers and Node.js.
> 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 [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html). > 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 [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
## Installation ## Installation
bigint-crypto-utils is distributed for [web browsers and/or webviews supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) as an ES6 module or an IIFE file; and for Node.js (>=10.4.0), as a CJS module. {{PKG_NAME}} is distributed for [web browsers and/or webviews supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) as an ES6 module or an IIFE file; and for Node.js (>=10.4.0), as a CJS module.
bigint-crypto-utils can be imported to your project with `npm`: {{PKG_NAME}} can be imported to your project with `npm`:
```bash ```bash
npm install bigint-crypto-utils npm install {{PKG_NAME}}
``` ```
NPM installation defaults to the ES6 module for browsers and the CJS one for Node.js. NPM installation defaults to the ES6 module for browsers and the CJS one for Node.js. For web browsers, you can also directly download the {{IIFE_BUNDLE}} or the {{ESM_BUNDLE}} from the repository.
For web browsers, you can also directly download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.iife.js) or the [ES6 bundle module](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.mod.js) from GitHub.
## Usage examples ## Usage examples
@ -29,13 +33,30 @@ Import your module as :
const bigintCryptoUtils = require('bigint-crypto-utils') const bigintCryptoUtils = require('bigint-crypto-utils')
... // your code here ... // your code here
``` ```
- JavaScript native or TypeScript project - JavaScript native or TypeScript project (including React and Angular JS)
```javascript ```javascript
import * as bigintCryptoUtils from 'bigint-crypto-utils' import * as bigintCryptoUtils from 'bigint-crypto-utils'
... // your code here ... // your code here
``` ```
> BigInt is [ES-2020](https://tc39.es/ecma262/#sec-bigint-objects). In order to use it with TypeScript you should set `lib` (and probably also `target` and `module`) to `esnext` in `tsconfig.json`. BigInt is [ES-2020](https://tc39.es/ecma262/#sec-bigint-objects). In order to use it with TypeScript you should set `lib` (and probably also `target` and `module`) to `esnext` in `tsconfig.json`.
- JavaScript native browser ES6 mod `{{PKG_NAME}}` **CANNOT BE POLYFILLED** to suport older browsers. If you are using webpack/babel to create your production bundles, you should target only the most modern browsers. For instance, for **React** apps created with [`create-react-app`](https://create-react-app.dev/), you should edit your `package.json` and modify the `browserList` so that it only targets the latest browsers (supporting the latest features):
```json
"browserslist": {
"production": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
```
Also, notice that BigInt is [ES-2020](https://tc39.es/ecma262/#sec-bigint-objects). In order to use it with TypeScript you should set `lib` (and probably also `target` and `module`) to `esnext` in `tsconfig.json`.
- JavaScript native browser ES module
```html ```html
<script type="module"> <script type="module">
import * as bigintCryptoUtils from 'lib/index.browser.bundle.mod.js' // Use you actual path to the broser mod bundle import * as bigintCryptoUtils from 'lib/index.browser.bundle.mod.js' // Use you actual path to the broser mod bundle

View File

@ -1,5 +1,6 @@
import { bitLength, eGcd, modInv, modPow, toZn } from 'bigint-mod-arith' import { bitLength, eGcd, modInv, modPow, toZn } from 'bigint-mod-arith'
export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from 'bigint-mod-arith' /* istanbul ignore next */
export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from 'bigint-mod-arith' // already tested for coverage
/** /**
* The test first tries if any of the first 250 small primes are a factor of the input number and then passes several * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several
@ -7,16 +8,18 @@ export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from '
* *
* @param {number | bigint} w An integer to be tested for primality * @param {number | bigint} w An integer to be tested for primality
* @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 * @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
* @param {boolean} [disableWorkers = false] Disable the use of workers for the primality test
* *
* @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) * @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/ */
export function isProbablyPrime (w, iterations = 16) { export function isProbablyPrime (w, iterations = 16, disableWorkers = false) {
if (typeof w === 'number') { if (typeof w === 'number') {
w = BigInt(w) w = BigInt(w)
} }
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
if (!process.browser) { // Node.js if (!process.browser) { // Node.js
if (_useWorkers) { /* istanbul ignore else */
if (!disableWorkers && _useWorkers) {
const { Worker } = require('worker_threads') const { Worker } = require('worker_threads')
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const worker = new Worker(__filename) const worker = new Worker(__filename)
@ -32,7 +35,6 @@ export function isProbablyPrime (w, iterations = 16) {
rnd: w, rnd: w,
iterations: iterations, iterations: iterations,
id: 0 id: 0
}) })
}) })
} else { } else {
@ -76,8 +78,9 @@ export function isProbablyPrime (w, iterations = 16) {
* @returns {Promise<bigint>} A promise that resolves to a bigint probable prime of bitLength bits. * @returns {Promise<bigint>} A promise that resolves to a bigint probable prime of bitLength bits.
*/ */
export function prime (bitLength, iterations = 16) { export function prime (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
/* istanbul ignore if */
if (!_useWorkers) { // If there is no support for workers if (!_useWorkers) { // If there is no support for workers
let rnd = 0n let rnd = 0n
do { do {
@ -130,13 +133,14 @@ export function prime (bitLength, iterations = 16) {
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
const buf = randBitsSync(bitLength, true) randBits(bitLength, true).then(function (buf) {
const rnd = fromBuffer(buf) const rnd = fromBuffer(buf)
workerList[i].postMessage({ workerList[i].postMessage({
rnd: rnd, rnd: rnd,
iterations: iterations, iterations: iterations,
id: i id: i
}) })
})
} }
}) })
} }
@ -151,7 +155,7 @@ export function prime (bitLength, iterations = 16) {
* @returns {bigint} A bigint probable prime of bitLength bits. * @returns {bigint} A bigint probable prime of bitLength bits.
*/ */
export function primeSync (bitLength, iterations = 16) { export function primeSync (bitLength, iterations = 16) {
if (bitLength < 1) throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) if (bitLength < 1) throw new RangeError('bitLength MUST be > 0')
let rnd = 0n let rnd = 0n
do { do {
rnd = fromBuffer(randBitsSync(bitLength, true)) rnd = fromBuffer(randBitsSync(bitLength, true))
@ -160,14 +164,14 @@ export function primeSync (bitLength, iterations = 16) {
} }
/** /**
* Returns a cryptographically secure random integer between [min,max] * Returns a cryptographically secure random integer between [min,max]. Both numbers must be >=0
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} [min = BigInt(1)] Returned value will be >= min * @param {bigint} [min = BigInt(1)] Returned value will be >= min
* *
* @returns {bigint} A cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
export function randBetween (max, min = 1n) { export function randBetween (max, min = 1n) {
if (max <= min) throw new Error('max must be > min') if (max <= 0n || min < 0n || max <= min) throw new Error('inputs should be max > 0, min >= 0; max > min')
const interval = max - min const interval = max - min
const bitLen = bitLength(interval) const bitLen = bitLength(interval)
let rnd let rnd
@ -181,8 +185,6 @@ export function randBetween (max, min = 1n) {
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* *
* Since version 3.0.0 this is an async function and a new randBitsSync function has been added. If you are migrating from version 2 call randBitsSync instead.
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -190,7 +192,7 @@ export function randBetween (max, min = 1n) {
*/ */
export async function randBits (bitLength, forceLength = false) { export async function randBits (bitLength, forceLength = false) {
if (bitLength < 1) { if (bitLength < 1) {
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) throw new RangeError('bitLength MUST be > 0')
} }
const byteLength = Math.ceil(bitLength / 8) const byteLength = Math.ceil(bitLength / 8)
@ -210,7 +212,6 @@ export async function randBits (bitLength, forceLength = false) {
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -218,7 +219,7 @@ export async function randBits (bitLength, forceLength = false) {
*/ */
export function randBitsSync (bitLength, forceLength = false) { export function randBitsSync (bitLength, forceLength = false) {
if (bitLength < 1) { if (bitLength < 1) {
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) throw new RangeError('bitLength MUST be > 0')
} }
const byteLength = Math.ceil(bitLength / 8) const byteLength = Math.ceil(bitLength / 8)
@ -236,7 +237,7 @@ export function randBitsSync (bitLength, forceLength = false) {
} }
/** /**
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
* *
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
@ -244,27 +245,28 @@ export function randBitsSync (bitLength, forceLength = false) {
* @returns {Promise<Buffer | Uint8Array>} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Promise<Buffer | Uint8Array>} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
export function randBytes (byteLength, forceLength = false) { export function randBytes (byteLength, forceLength = false) {
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
return new Promise(function (resolve, reject) {
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
if (!process.browser) { // node if (!process.browser) {
const crypto = require('crypto') const crypto = require('crypto')
const buf = Buffer.alloc(byteLength) crypto.randomBytes(byteLength, function (err, buf) {
return crypto.randomFill(buf, function (resolve) { /* istanbul ignore if */
if (err) reject(err)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128 if (forceLength) buf[0] = buf[0] | 128
resolve(buf) resolve(buf)
}) })
} else { // browser } else { // browser
return new Promise(function (resolve) {
const buf = new Uint8Array(byteLength) const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf) self.crypto.getRandomValues(buf)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) buf[0] = buf[0] | 128 if (forceLength) buf[0] = buf[0] | 128
resolve(buf) resolve(buf)
})
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
})
} }
/** /**
@ -276,21 +278,20 @@ export function randBytes (byteLength, forceLength = false) {
* @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
export function randBytesSync (byteLength, forceLength = false) { export function randBytesSync (byteLength, forceLength = false) {
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } if (byteLength < 1) throw new RangeError('byteLength MUST be > 0')
/* eslint-disable no-lone-blocks */ /* eslint-disable no-lone-blocks */
if (!process.browser) { // node if (!process.browser) { // node
const crypto = require('crypto') const crypto = require('crypto')
const buf = Buffer.alloc(byteLength) const buf = crypto.randomBytes(byteLength)
crypto.randomFillSync(buf)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) { buf[0] = buf[0] | 128 } if (forceLength) buf[0] = buf[0] | 128
return buf return buf
} else { // browser } else { // browser
const buf = new Uint8Array(byteLength) const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf) self.crypto.getRandomValues(buf)
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength) { buf[0] = buf[0] | 128 } if (forceLength) buf[0] = buf[0] | 128
return buf return buf
} }
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
@ -655,10 +656,12 @@ if (!process.browser) { // Node.js
require.resolve('worker_threads') require.resolve('worker_threads')
_useWorkers = true _useWorkers = true
} catch (e) { } catch (e) {
/* istanbul ignore next */
console.log(`[bigint-crypto-utils] WARNING: console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers. This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading). · With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `) · With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
/* istanbul ignore next */
_useWorkers = true _useWorkers = true
} }
} else { // Native JS } else { // Native JS
@ -668,6 +671,7 @@ This node version doesn't support worker_threads. You should enable them in orde
if (!process.browser && _useWorkers) { // node.js with support for workers if (!process.browser && _useWorkers) { // node.js with support for workers
const { parentPort, isMainThread } = require('worker_threads') const { parentPort, isMainThread } = require('worker_threads')
/* istanbul ignore if */
if (!isMainThread) { // worker if (!isMainThread) { // worker
parentPort.on('message', function (data) { // Let's start once we are called parentPort.on('message', function (data) { // Let's start once we are called
// data = {rnd: <bigint>, iterations: <number>} // data = {rnd: <bigint>, iterations: <number>}

View File

@ -7,60 +7,86 @@
const numbers = [ const numbers = [
{ {
value: BigInt(1), value: BigInt(1),
prime: false prime: false,
iterations: 16,
workers: false
}, },
{ {
value: BigInt(2), value: BigInt(2),
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: 3, value: 3,
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: BigInt(15), value: BigInt(15),
prime: false prime: false,
iterations: 32,
workers: false
}, },
{ {
value: 29, value: 29,
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: BigInt('669483106578092405936560831017556154622901950048903016651289'), value: BigInt('669483106578092405936560831017556154622901950048903016651289'),
prime: true prime: true,
iterations: 24,
workers: false
}, },
{ {
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'), value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'),
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'), value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'),
prime: false prime: false,
iterations: 16,
workers: false
}, },
{ {
value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'), value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'),
prime: true prime: true,
iterations: 24,
workers: true
}, },
{ {
value: BigInt('168694196579467171180863939518634764192343817610869919231900537093664715354591592262546800497540343203057121816378265655992490621138321114570420047522219942818258345349322155251835677199539229050711145144861404607171419723967136221126986330819362088262358855325306938646602003059377699727688477555163239222109') * BigInt('144678545212641449725111562354371812236197961234111744040227045242578772124779004756249085154188369039159690638725821245974978963371615699005072473649705367893567309027634121825164880046600125480885803891136149601797439273507802533807541605261215613891134865916295914192271736572001975016089773532547481638243'), value: BigInt('168694196579467171180863939518634764192343817610869919231900537093664715354591592262546800497540343203057121816378265655992490621138321114570420047522219942818258345349322155251835677199539229050711145144861404607171419723967136221126986330819362088262358855325306938646602003059377699727688477555163239222109') * BigInt('144678545212641449725111562354371812236197961234111744040227045242578772124779004756249085154188369039159690638725821245974978963371615699005072473649705367893567309027634121825164880046600125480885803891136149601797439273507802533807541605261215613891134865916295914192271736572001975016089773532547481638243'),
prime: false prime: false,
iterations: 16,
workers: true
}, },
{ {
value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'), value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'),
prime: true prime: true,
iterations: 16,
workers: true
}, },
{ {
value: BigInt('940719693126280825126763871881743336375040232953039527942717290104060740215493004508206768342926022549956464101136893240409560470269654765366248516968645294076406953865805712688760371102637642013723011744011617678651884521901163090779813242269935310225049805992299292275574585773507915278612311449919050091057023179541184986547995894821648553256021675133997240195429424258757033557367142630663053464438840832073753440939208165158795269598771598124509831433327480118038278887538430675994497384283550890544882369140852048496460551123626856255619494025370171790720106325655890348475483349150258338517508459674722099347335608814922179633411167540545786247819334838979610017735984374883325689517847175539632896026875016305529321705457954181425405794479825617747354596485074451489940385640535898876551301296003465792117006135339109817937663957519031436646579178503423889430062127572272773511424424297800355292430651838502733756881154935252456036638082486459287411002911323257940893413982671660332662880068976408321968046549017547143836993553556640198884769590214676797037397502067035957959952990027503148987727895561468097917730167320715053689862847457761993196945361244822787209076446259359976421264285658106819879849052247546957718175231'), value: BigInt('940719693126280825126763871881743336375040232953039527942717290104060740215493004508206768342926022549956464101136893240409560470269654765366248516968645294076406953865805712688760371102637642013723011744011617678651884521901163090779813242269935310225049805992299292275574585773507915278612311449919050091057023179541184986547995894821648553256021675133997240195429424258757033557367142630663053464438840832073753440939208165158795269598771598124509831433327480118038278887538430675994497384283550890544882369140852048496460551123626856255619494025370171790720106325655890348475483349150258338517508459674722099347335608814922179633411167540545786247819334838979610017735984374883325689517847175539632896026875016305529321705457954181425405794479825617747354596485074451489940385640535898876551301296003465792117006135339109817937663957519031436646579178503423889430062127572272773511424424297800355292430651838502733756881154935252456036638082486459287411002911323257940893413982671660332662880068976408321968046549017547143836993553556640198884769590214676797037397502067035957959952990027503148987727895561468097917730167320715053689862847457761993196945361244822787209076446259359976421264285658106819879849052247546957718175231'),
prime: false prime: false,
iterations: 16,
workers: true
} }
]; ];
describe('isProbablyPrime', function () { describe('isProbablyPrime', function () {
this.timeout(90000); this.timeout(90000);
for (const num of numbers) { for (const num of numbers) {
describe(`isProbablyPrime(${num.value})`, function () { describe(`isProbablyPrime(${num.value}, ${num.iterations}, ${!num.workers})`, function () {
it(`should return ${num.prime}`, async function () { it(`should return ${num.prime}`, async function () {
const ret = await _pkg.isProbablyPrime(num.value); let ret;
if (num.iterations === 16 && num.workers) ret = await _pkg.isProbablyPrime(num.value);
else ret = await _pkg.isProbablyPrime(num.value, num.iterations, !num.workers);
chai.expect(ret).to.equal(num.prime); chai.expect(ret).to.equal(num.prime);
}); });
}); });
@ -74,6 +100,7 @@ describe('isProbablyPrime', function () {
// <-- // <--
const bitLengths = [ const bitLengths = [
0,
8, 8,
255, 255,
256, 256,
@ -81,8 +108,7 @@ const bitLengths = [
512, 512,
1024, 1024,
2048, 2048,
3072, 3072
4096
]; ];
describe('prime', function () { describe('prime', function () {
@ -90,8 +116,11 @@ describe('prime', function () {
for (const bitLength of bitLengths) { for (const bitLength of bitLengths) {
describe(`prime(${bitLength})`, function () { describe(`prime(${bitLength})`, function () {
it(`should return a random ${bitLength}-bits probable prime`, async function () { it(`should return a random ${bitLength}-bits probable prime`, async function () {
let primeBitLength = bitLength;
try {
const prime = await _pkg.prime(bitLength); const prime = await _pkg.prime(bitLength);
const primeBitLength = _pkg.bitLength(prime); primeBitLength = _pkg.bitLength(prime);
} catch {}
chai.expect(primeBitLength).to.equal(bitLength); chai.expect(primeBitLength).to.equal(bitLength);
}); });
}); });
@ -101,6 +130,202 @@ describe('prime', function () {
const prime = _pkg.primeSync(1024, 16); const prime = _pkg.primeSync(1024, 16);
const primeBitLength = _pkg.bitLength(prime); const primeBitLength = _pkg.bitLength(prime);
chai.expect(primeBitLength).to.equal(1024); chai.expect(primeBitLength).to.equal(1024);
try {
_pkg.primeSync(0);
} catch (error) {
chai.expect(true).to.equal(true);
}
}); });
}); });
}); });
// Every test file (you can create as many as you want) should start like this
// Please, do NOT touch. They will be automatically removed for browser tests -->
// <--
const numbers$1 = [
{
min: BigInt(1),
max: BigInt(2) ** BigInt(234),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt('122461641436345153'),
max: BigInt(2) ** BigInt(234),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt(146347),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(2),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(-4),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(1),
max: BigInt(-1),
iterations: 1,
error: true,
errorMax: true
}
];
describe('randBetween', function () {
this.timeout(90000);
for (const num of numbers$1) {
describe(`randBetween(${num.max}, ${num.min})`, function () {
if (!num.error) {
it(`[${num.iterations} iterations] should return x such that min < x < max`, function () {
let ret = true;
for (let i = 0; i < num.iterations; i++) {
const x = _pkg.randBetween(num.max, num.min);
ret = ret && x > num.min && x < num.max;
}
chai.expect(ret).to.equal(true);
});
} else {
it('should return error (max <=0 || min <0 || min>=max)', function () {
try {
_pkg.randBetween(num.max, num.min);
chai.expect(num.error).to.equal(false);
} catch (error) {
chai.expect(num.error).to.equal(true);
}
});
}
});
describe(`randBetween(${num.max})`, function () {
if (!num.errorMax) {
it(`[${num.iterations} iterations] should return x such that 1 <= x <= max`, function () {
let ret = true;
for (let i = 0; i < num.iterations; i++) {
const x = _pkg.randBetween(num.max);
ret = ret && x >= BigInt(1) && x <= num.max;
}
chai.expect(ret).to.equal(true);
});
} else {
it('should return error (max <=0)', function () {
try {
_pkg.randBetween(num.max);
chai.expect(num.errorMax).to.equal(false);
} catch (error) {
chai.expect(num.errorMax).to.equal(true);
}
});
}
});
}
});
// Every test file (you can create as many as you want) should start like this
// Please, do NOT touch. They will be automatically removed for browser tests -->
// <--
const iterations = 10;
const bitLengths$1 = [0, 3, 8, 16, 511, 2048];
const byteLengths = [0, 1, 8, 33, 40];
describe('testing randBits', async function () {
for (const bitLength of bitLengths$1) {
describe(`${iterations} of randBitsSync(${bitLength})`, function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', function () {
try {
const randbits = _pkg.randBitsSync(bitLength);
// console.log(JSON.stringify(randbits))
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits).to.be.an.instanceOf(Buffer);
const randbits2 = _pkg.randBitsSync(bitLength, true);
// console.log(JSON.stringify(randbits2))
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits2).to.be.an.instanceOf(Buffer);
} catch (error) {
chai.expect(bitLength).to.be.lte(0);
}
});
}
});
describe(`${iterations} of randBits(${bitLength})`, async function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', async function () {
try {
const randbits = await _pkg.randBits(bitLength);
// console.log(JSON.stringify(randbits))
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits).to.be.an.instanceOf(Buffer);
const randbits2 = await _pkg.randBits(bitLength, true);
// console.log(JSON.stringify(randbits2))
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits2).to.be.an.instanceOf(Buffer);
} catch (error) {
chai.expect(bitLength).to.be.lte(0);
}
});
}
});
}
});
describe('testing randBytes', async function () {
for (const byteLength of byteLengths) {
describe(`${iterations} of randBytesSync(${byteLength})`, function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', function () {
try {
const randbits = _pkg.randBytesSync(byteLength);
console.log(JSON.stringify(randbits));
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits).to.be.an.instanceOf(Buffer);
const randbits2 = _pkg.randBytesSync(byteLength, true);
console.log(JSON.stringify(randbits2));
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits2).to.be.an.instanceOf(Buffer);
} catch (error) {
chai.expect(byteLength).to.be.lte(0);
}
});
}
});
describe(`${iterations} of randBytes(${byteLength})`, async function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', async function () {
try {
const randbits = await _pkg.randBytes(byteLength);
console.log(JSON.stringify(randbits));
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits).to.be.an.instanceOf(Buffer);
const randbits2 = await _pkg.randBytes(byteLength, true);
console.log(JSON.stringify(randbits2));
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array);
else chai.expect(randbits2).to.be.an.instanceOf(Buffer);
} catch (error) {
chai.expect(byteLength).to.be.lte(0);
}
});
}
});
}
});

View File

@ -9,60 +9,86 @@ const chai = require('chai')
const numbers = [ const numbers = [
{ {
value: BigInt(1), value: BigInt(1),
prime: false prime: false,
iterations: 16,
workers: false
}, },
{ {
value: BigInt(2), value: BigInt(2),
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: 3, value: 3,
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: BigInt(15), value: BigInt(15),
prime: false prime: false,
iterations: 32,
workers: false
}, },
{ {
value: 29, value: 29,
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: BigInt('669483106578092405936560831017556154622901950048903016651289'), value: BigInt('669483106578092405936560831017556154622901950048903016651289'),
prime: true prime: true,
iterations: 24,
workers: false
}, },
{ {
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'), value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550077'),
prime: true prime: true,
iterations: 16,
workers: false
}, },
{ {
value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'), value: BigInt('2074722246773485207821695222107608587480996474721117292752992589912196684750549658310084416732550079'),
prime: false prime: false,
iterations: 16,
workers: false
}, },
{ {
value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'), value: BigInt('115922179551495973383410176342643722334557255682879605864838806293659619625004303206250384392546855063844106965156287951749387634112551089284595541103692716528774876311641700929986988023197242224581099872580798960693521778607396791006450968430359009613295725905514216842343121690916290236558767890728449777'),
prime: true prime: true,
iterations: 24,
workers: true
}, },
{ {
value: BigInt('168694196579467171180863939518634764192343817610869919231900537093664715354591592262546800497540343203057121816378265655992490621138321114570420047522219942818258345349322155251835677199539229050711145144861404607171419723967136221126986330819362088262358855325306938646602003059377699727688477555163239222109') * BigInt('144678545212641449725111562354371812236197961234111744040227045242578772124779004756249085154188369039159690638725821245974978963371615699005072473649705367893567309027634121825164880046600125480885803891136149601797439273507802533807541605261215613891134865916295914192271736572001975016089773532547481638243'), value: BigInt('168694196579467171180863939518634764192343817610869919231900537093664715354591592262546800497540343203057121816378265655992490621138321114570420047522219942818258345349322155251835677199539229050711145144861404607171419723967136221126986330819362088262358855325306938646602003059377699727688477555163239222109') * BigInt('144678545212641449725111562354371812236197961234111744040227045242578772124779004756249085154188369039159690638725821245974978963371615699005072473649705367893567309027634121825164880046600125480885803891136149601797439273507802533807541605261215613891134865916295914192271736572001975016089773532547481638243'),
prime: false prime: false,
iterations: 16,
workers: true
}, },
{ {
value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'), value: BigInt('918145944120889203205646923554374144932845997937845799234617798611046542304088105084854788397071323714642587188481158334265864050544813693415594035822877094557870151480865568334936301231664228940480803192289508235412296324312748621874408067955753620604885023289655277704554716080844406284392300643321715285709865081125252390440327650852470312931679380011885102491340191287595160450544053114365852338670819405357496612993587404998677760882578064637552397840566752638770525765833183986360029736508910848408875329873614164495552615086579144675027852136994842529623698055210822311666048300438808691619782893307972452223713060928388502843564836966586109748062827799521852219158489504529458627699284110902303538160168376473182639384638674469114371472053977558648090155686345760457454061117853710619580819749222459422610617170567016772342291486643520567969321969827786373531753524990712622940069883277763528926899970596407140603912036918433859986491820017690762751824769335720368488097262208835708414085501930989486498185503469986946236128468697606998536541209764920494156326791142098506801288127033229779646920082892258428128572765585196779698362187479280520327053508580551167899837393726371144977951402741307021389967382422805567365901203'),
prime: true prime: true,
iterations: 16,
workers: true
}, },
{ {
value: BigInt('940719693126280825126763871881743336375040232953039527942717290104060740215493004508206768342926022549956464101136893240409560470269654765366248516968645294076406953865805712688760371102637642013723011744011617678651884521901163090779813242269935310225049805992299292275574585773507915278612311449919050091057023179541184986547995894821648553256021675133997240195429424258757033557367142630663053464438840832073753440939208165158795269598771598124509831433327480118038278887538430675994497384283550890544882369140852048496460551123626856255619494025370171790720106325655890348475483349150258338517508459674722099347335608814922179633411167540545786247819334838979610017735984374883325689517847175539632896026875016305529321705457954181425405794479825617747354596485074451489940385640535898876551301296003465792117006135339109817937663957519031436646579178503423889430062127572272773511424424297800355292430651838502733756881154935252456036638082486459287411002911323257940893413982671660332662880068976408321968046549017547143836993553556640198884769590214676797037397502067035957959952990027503148987727895561468097917730167320715053689862847457761993196945361244822787209076446259359976421264285658106819879849052247546957718175231'), value: BigInt('940719693126280825126763871881743336375040232953039527942717290104060740215493004508206768342926022549956464101136893240409560470269654765366248516968645294076406953865805712688760371102637642013723011744011617678651884521901163090779813242269935310225049805992299292275574585773507915278612311449919050091057023179541184986547995894821648553256021675133997240195429424258757033557367142630663053464438840832073753440939208165158795269598771598124509831433327480118038278887538430675994497384283550890544882369140852048496460551123626856255619494025370171790720106325655890348475483349150258338517508459674722099347335608814922179633411167540545786247819334838979610017735984374883325689517847175539632896026875016305529321705457954181425405794479825617747354596485074451489940385640535898876551301296003465792117006135339109817937663957519031436646579178503423889430062127572272773511424424297800355292430651838502733756881154935252456036638082486459287411002911323257940893413982671660332662880068976408321968046549017547143836993553556640198884769590214676797037397502067035957959952990027503148987727895561468097917730167320715053689862847457761993196945361244822787209076446259359976421264285658106819879849052247546957718175231'),
prime: false prime: false,
iterations: 16,
workers: true
} }
] ]
describe('isProbablyPrime', function () { describe('isProbablyPrime', function () {
this.timeout(90000) this.timeout(90000)
for (const num of numbers) { for (const num of numbers) {
describe(`isProbablyPrime(${num.value})`, function () { describe(`isProbablyPrime(${num.value}, ${num.iterations}, ${!num.workers})`, function () {
it(`should return ${num.prime}`, async function () { it(`should return ${num.prime}`, async function () {
const ret = await _pkg.isProbablyPrime(num.value) let ret
if (num.iterations === 16 && num.workers) ret = await _pkg.isProbablyPrime(num.value)
else ret = await _pkg.isProbablyPrime(num.value, num.iterations, !num.workers)
chai.expect(ret).to.equal(num.prime) chai.expect(ret).to.equal(num.prime)
}) })
}) })

View File

@ -7,6 +7,7 @@ const chai = require('chai')
// <-- // <--
const bitLengths = [ const bitLengths = [
0,
8, 8,
255, 255,
256, 256,
@ -14,8 +15,7 @@ const bitLengths = [
512, 512,
1024, 1024,
2048, 2048,
3072, 3072
4096
] ]
describe('prime', function () { describe('prime', function () {
@ -23,8 +23,11 @@ describe('prime', function () {
for (const bitLength of bitLengths) { for (const bitLength of bitLengths) {
describe(`prime(${bitLength})`, function () { describe(`prime(${bitLength})`, function () {
it(`should return a random ${bitLength}-bits probable prime`, async function () { it(`should return a random ${bitLength}-bits probable prime`, async function () {
let primeBitLength = bitLength
try {
const prime = await _pkg.prime(bitLength) const prime = await _pkg.prime(bitLength)
const primeBitLength = _pkg.bitLength(prime) primeBitLength = _pkg.bitLength(prime)
} catch {}
chai.expect(primeBitLength).to.equal(bitLength) chai.expect(primeBitLength).to.equal(bitLength)
}) })
}) })
@ -34,6 +37,11 @@ describe('prime', function () {
const prime = _pkg.primeSync(1024, 16) const prime = _pkg.primeSync(1024, 16)
const primeBitLength = _pkg.bitLength(prime) const primeBitLength = _pkg.bitLength(prime)
chai.expect(primeBitLength).to.equal(1024) chai.expect(primeBitLength).to.equal(1024)
try {
_pkg.primeSync(0)
} catch (error) {
chai.expect(true).to.equal(true)
}
}) })
}) })
}) })

100
test/randBetween.js Normal file
View File

@ -0,0 +1,100 @@
'use strict'
// Every test file (you can create as many as you want) should start like this
// Please, do NOT touch. They will be automatically removed for browser tests -->
const _pkg = require('../lib/index.node')
const chai = require('chai')
// <--
const numbers = [
{
min: BigInt(1),
max: BigInt(2) ** BigInt(234),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt('122461641436345153'),
max: BigInt(2) ** BigInt(234),
iterations: 100,
error: false,
errorMax: false
},
{
min: BigInt(146347),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(2),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(-4),
max: BigInt(2),
iterations: 1,
error: true,
errorMax: false
},
{
min: BigInt(1),
max: BigInt(-1),
iterations: 1,
error: true,
errorMax: true
}
]
describe('randBetween', function () {
this.timeout(90000)
for (const num of numbers) {
describe(`randBetween(${num.max}, ${num.min})`, function () {
if (!num.error) {
it(`[${num.iterations} iterations] should return x such that min < x < max`, function () {
let ret = true
for (let i = 0; i < num.iterations; i++) {
const x = _pkg.randBetween(num.max, num.min)
ret = ret && x > num.min && x < num.max
}
chai.expect(ret).to.equal(true)
})
} else {
it('should return error (max <=0 || min <0 || min>=max)', function () {
try {
_pkg.randBetween(num.max, num.min)
chai.expect(num.error).to.equal(false)
} catch (error) {
chai.expect(num.error).to.equal(true)
}
})
}
})
describe(`randBetween(${num.max})`, function () {
if (!num.errorMax) {
it(`[${num.iterations} iterations] should return x such that 1 <= x <= max`, function () {
let ret = true
for (let i = 0; i < num.iterations; i++) {
const x = _pkg.randBetween(num.max)
ret = ret && x >= BigInt(1) && x <= num.max
}
chai.expect(ret).to.equal(true)
})
} else {
it('should return error (max <=0)', function () {
try {
_pkg.randBetween(num.max)
chai.expect(num.errorMax).to.equal(false)
} catch (error) {
chai.expect(num.errorMax).to.equal(true)
}
})
}
})
}
})

93
test/random.js Normal file
View File

@ -0,0 +1,93 @@
'use strict'
// Every test file (you can create as many as you want) should start like this
// Please, do NOT touch. They will be automatically removed for browser tests -->
const _pkg = require('../lib/index.node')
const chai = require('chai')
// <--
const iterations = 10
const bitLengths = [0, 3, 8, 16, 511, 2048]
const byteLengths = [0, 1, 8, 33, 40]
describe('testing randBits', async function () {
for (const bitLength of bitLengths) {
describe(`${iterations} of randBitsSync(${bitLength})`, function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', function () {
try {
const randbits = _pkg.randBitsSync(bitLength)
// console.log(JSON.stringify(randbits))
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits).to.be.an.instanceOf(Buffer)
const randbits2 = _pkg.randBitsSync(bitLength, true)
// console.log(JSON.stringify(randbits2))
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits2).to.be.an.instanceOf(Buffer)
} catch (error) {
chai.expect(bitLength).to.be.lte(0)
}
})
}
})
describe(`${iterations} of randBits(${bitLength})`, async function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', async function () {
try {
const randbits = await _pkg.randBits(bitLength)
// console.log(JSON.stringify(randbits))
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits).to.be.an.instanceOf(Buffer)
const randbits2 = await _pkg.randBits(bitLength, true)
// console.log(JSON.stringify(randbits2))
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits2).to.be.an.instanceOf(Buffer)
} catch (error) {
chai.expect(bitLength).to.be.lte(0)
}
})
}
})
}
})
describe('testing randBytes', async function () {
for (const byteLength of byteLengths) {
describe(`${iterations} of randBytesSync(${byteLength})`, function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', function () {
try {
const randbits = _pkg.randBytesSync(byteLength)
console.log(JSON.stringify(randbits))
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits).to.be.an.instanceOf(Buffer)
const randbits2 = _pkg.randBytesSync(byteLength, true)
console.log(JSON.stringify(randbits2))
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits2).to.be.an.instanceOf(Buffer)
} catch (error) {
chai.expect(byteLength).to.be.lte(0)
}
})
}
})
describe(`${iterations} of randBytes(${byteLength})`, async function () {
for (let i = 0; i < iterations; i++) {
it('should return a buffer', async function () {
try {
const randbits = await _pkg.randBytes(byteLength)
console.log(JSON.stringify(randbits))
if (randbits instanceof Uint8Array) chai.expect(randbits).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits).to.be.an.instanceOf(Buffer)
const randbits2 = await _pkg.randBytes(byteLength, true)
console.log(JSON.stringify(randbits2))
if (randbits2 instanceof Uint8Array) chai.expect(randbits2).to.be.an.instanceOf(Uint8Array)
else chai.expect(randbits2).to.be.an.instanceOf(Buffer)
} catch (error) {
chai.expect(byteLength).to.be.lte(0)
}
})
}
})
}
})

112
types/index.d.ts vendored
View File

@ -1,106 +1,14 @@
/**
* A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/
export type egcdReturn = {
g: bigint;
x: bigint;
y: bigint;
};
/**
* 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 function abs(a: number | bigint): bigint;
/**
* Returns the bitlength of a number
*
* @param {number|bigint} a
* @returns {number} - the bit length
*/
export function bitLength(a: number | bigint): number;
/**
* @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b).
* @property {bigint} g
* @property {bigint} x
* @property {bigint} y
*/
/**
* An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
* Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/
export function eGcd(a: number | bigint, b: number | bigint): egcdReturn;
/**
* 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 function gcd(a: number | bigint, b: number | bigint): bigint;
/** /**
* The test first tries if any of the first 250 small primes are a factor of the input number and then passes several * 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) * iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)
* *
* @param {number | bigint} w An integer to be tested for primality * @param {number | bigint} w An integer to be tested for primality
* @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 * @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
* @param {boolean} [disableWorkers = false] Disable the use of workers for the primality test
* *
* @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) * @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
*/ */
export function isProbablyPrime(w: number | bigint, iterations?: number): Promise<boolean>; export function isProbablyPrime(w: number | bigint, iterations?: number, disableWorkers?: boolean): Promise<boolean>;
/**
* 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 function lcm(a: number | bigint, b: number | bigint): bigint;
/**
* Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {bigint} maximum of numbers a and b
*/
export function max(a: number | bigint, b: number | bigint): bigint;
/**
* Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
*
* @param {number|bigint} a
* @param {number|bigint} b
*
* @returns {bigint} minimum of numbers a and b
*/
export function min(a: number | bigint, b: number | bigint): bigint;
/**
* Modular inverse.
*
* @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo
*
* @returns {bigint} the inverse modulo n or NaN if it does not exist
*/
export function modInv(a: number | bigint, n: number | bigint): bigint;
/**
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method
*
* @param {number|bigint} b base
* @param {number|bigint} e exponent
* @param {number|bigint} n modulo
*
* @returns {bigint} b**e mod n
*/
export function modPow(b: number | bigint, e: number | bigint, n: number | bigint): bigint;
/** /**
* A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator. * 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 * The browser version uses web workers to parallelise prime look up. Therefore, it does not lock the UI
@ -125,7 +33,7 @@ export function prime(bitLength: number, iterations?: number): Promise<bigint>;
*/ */
export function primeSync(bitLength: number, iterations?: number): bigint; export function primeSync(bitLength: number, iterations?: number): bigint;
/** /**
* Returns a cryptographically secure random integer between [min,max] * Returns a cryptographically secure random integer between [min,max]. Both numbers must be >=0
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} [min = BigInt(1)] Returned value will be >= min * @param {bigint} [min = BigInt(1)] Returned value will be >= min
* *
@ -135,8 +43,6 @@ export function randBetween(max: bigint, min?: bigint): bigint;
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* *
* Since version 3.0.0 this is an async function and a new randBitsSync function has been added. If you are migrating from version 2 call randBitsSync instead.
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -145,7 +51,6 @@ export function randBetween(max: bigint, min?: bigint): bigint;
export function randBits(bitLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>; export function randBits(bitLength: number, forceLength?: boolean): Promise<Uint8Array | Buffer>;
/** /**
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
* @since 3.0.0
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. It basically forces the msb to be 1
* *
@ -153,7 +58,7 @@ export function randBits(bitLength: number, forceLength?: boolean): Promise<Uint
*/ */
export function randBitsSync(bitLength: number, forceLength?: boolean): Uint8Array | Buffer; export function randBitsSync(bitLength: number, forceLength?: boolean): Uint8Array | Buffer;
/** /**
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * Secure random bytes for both node and browsers. Node version uses crypto.randomBytes() and browser one self.crypto.getRandomValues()
* *
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
@ -170,11 +75,4 @@ export function randBytes(byteLength: number, forceLength?: boolean): Promise<Ui
* @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
*/ */
export function randBytesSync(byteLength: number, forceLength?: boolean): Uint8Array | Buffer; export function randBytesSync(byteLength: number, forceLength?: boolean): Uint8Array | Buffer;
/** export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from "bigint-mod-arith";
* 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 function toZn(a: number | bigint, n: number | bigint): bigint;