diff --git a/README.md b/README.md index ec03ed2..a577c84 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ bigint-crypto-utils can be imported to your project with `npm`: npm install bigint-crypto-utils ``` -NPM installation defaults to the minified 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 file](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.js) or the [ES6 module](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.mod.js) from GitHub. +For web browsers, you can also directly download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.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 @@ -29,26 +29,25 @@ Import your module as : const bigintCryptoUtils = require('bigint-crypto-utils') ... // your code here ``` - - Javascript native project + - JavaScript native project ```javascript import * as bigintCryptoUtils from 'bigint-crypto-utils' ... // your code here ``` - - Javascript native browser ES6 mod + - JavaScript native browser ES6 mod ```html - import as bcu from 'bigint-crypto-utils' - ... // your code here ``` - - Javascript native browser IIFE + - JavaScript native browser IIFE ```html - + + ``` - TypeScript ```typescript import * as bigintCryptoUtils from 'bigint-crypto-utils' @@ -56,6 +55,8 @@ Import your module as : ``` > 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`. +And you could use it like in the following: + ```javascript /* Stage 3 BigInts with value 666 can be declared as BigInt('666') or the shorter new no-so-linter-friendly syntax 666n. @@ -65,22 +66,22 @@ be raised. */ const a = BigInt('5') const b = BigInt('2') -const n = BigInt('19') +const n = 19n console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6 -console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))) // prints 3 +console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3 console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2 -console.log(bigintCryptoUtils.randBetween(BigInt(2) ** BigInt(256))) // Prints a cryptographically secure random number between 1 and 2**256 bits. +console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits. async function primeTesting () { // Output of a probable prime of 2048 bits console.log(await bigintCryptoUtils.prime(2048)) // Testing if a number is a probable prime (Miller-Rabin) - const number = 27 + const number = 27n const isPrime = await bigintCryptoUtils.isProbablyPrime(number) if (isPrime) { console.log(`${number} is prime`) @@ -93,78 +94,7 @@ primeTesting() ``` -## bigint-crypto-utils JS Doc - -### Functions - -
-
abs(a)bigint
-

Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0

-
-
bitLength(a)number
-

Returns the bitlength of a number

-
-
eGcd(a, b)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).

-
-
gcd(a, b)bigint
-

Greatest-common divisor of two integers based on the iterative binary algorithm.

-
-
isProbablyPrime(w, [iterations])Promise
-

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)

-
-
lcm(a, b)bigint
-

The least common multiple computed as abs(a*b)/gcd(a,b)

-
-
max(a, b)bigint
-

Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b

-
-
min(a, b)bigint
-

Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b

-
-
modInv(a, n)bigint
-

Modular inverse.

-
-
modPow(b, e, n)bigint
-

Modular exponentiation b**e mod n. Currently using the right-to-left binary method

-
-
prime(bitLength, [iterations])Promise
-

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 >=10.5.0).

-
-
primeSync(bitLength, [iterations])bigint
-

A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator. -The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead.

-
-
randBetween(max, [min])bigint
-

Returns a cryptographically secure random integer between [min,max]

-
-
randBits(bitLength, [forceLength])Buffer | Uint8Array
-

Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()

-
-
randBytes(byteLength, [forceLength])Promise
-

Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()

-
-
randBytesSync(byteLength, [forceLength])Buffer | Uint8Array
-

Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()

-
-
toZn(a, n)bigint
-

Finds the smallest positive element that is congruent to a in modulo n

-
-
- -### Typedefs - -
-
egcdReturn : Object
-

A triple (g, x, y), such that ax + by = g = gcd(a, b).

-
-
+## API reference documentation @@ -217,20 +147,6 @@ Greatest-common divisor of two integers based on the iterative binary algorithm. | a | number \| bigint | | b | number \| bigint | - - -### isProbablyPrime(w, [iterations]) ⇒ Promise -The test first tries if any of the first 250 small primes are a factor of the input number and then passes several -iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1) - -**Kind**: global function -**Returns**: Promise - A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| w | number \| bigint | | An integer to be tested for primality | -| [iterations] | number | 16 | The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 | - ### lcm(a, b) ⇒ bigint @@ -297,89 +213,6 @@ Modular exponentiation b**e mod n. Currently using the right-to-left binary meth | e | number \| bigint | exponent | | n | number \| bigint | modulo | - - -### prime(bitLength, [iterations]) ⇒ Promise -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 >=10.5.0). - -**Kind**: global function -**Returns**: Promise - A promise that resolves to a bigint probable prime of bitLength bits. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| bitLength | number | | The required bit length for the generated prime | -| [iterations] | number | 16 | The number of iterations for the Miller-Rabin Probabilistic Primality Test | - - - -### primeSync(bitLength, [iterations]) ⇒ bigint -A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator. -The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead. - -**Kind**: global function -**Returns**: bigint - A bigint probable prime of bitLength bits. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| bitLength | number | | The required bit length for the generated prime | -| [iterations] | number | 16 | The number of iterations for the Miller-Rabin Probabilistic Primality Test | - - - -### randBetween(max, [min]) ⇒ bigint -Returns a cryptographically secure random integer between [min,max] - -**Kind**: global function -**Returns**: bigint - A cryptographically secure random bigint between [min,max] - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| max | bigint | | Returned value will be <= max | -| [min] | bigint | BigInt(1) | Returned value will be >= min | - - - -### randBits(bitLength, [forceLength]) ⇒ Buffer \| Uint8Array -Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() - -**Kind**: global function -**Returns**: Buffer \| Uint8Array - A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| bitLength | number | | The desired number of random bits | -| [forceLength] | boolean | false | If we want to force the output to have a specific bit length. It basically forces the msb to be 1 | - - - -### randBytes(byteLength, [forceLength]) ⇒ Promise -Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() - -**Kind**: global function -**Returns**: Promise - A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| byteLength | number | | The desired number of random bytes | -| [forceLength] | boolean | false | If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 | - - - -### randBytesSync(byteLength, [forceLength]) ⇒ Buffer \| Uint8Array -Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() - -**Kind**: global function -**Returns**: Buffer \| Uint8Array - A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| byteLength | number | | The desired number of random bytes | -| [forceLength] | boolean | false | If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 | - ### toZn(a, n) ⇒ bigint diff --git a/build/build.docs.js b/build/build.docs.js new file mode 100644 index 0000000..4db18de --- /dev/null +++ b/build/build.docs.js @@ -0,0 +1,24 @@ +'use strict' + +const fs = require('fs') +const jsdoc2md = require('jsdoc-to-markdown') +const path = require('path') +const pkgJson = require('../package.json') + +const rootDir = path.join(__dirname, '..') + +const template = path.join(rootDir, pkgJson.directories.src, 'doc', 'readme-template.md') +const input = path.join(rootDir, pkgJson.directories.lib, 'index.node.js') + +const options = { + source: fs.readFileSync(input, { encoding: 'UTF-8' }), // we need to use this instead of files in order to avoid issues with esnext features + template: fs.readFileSync(template, { encoding: 'UTF-8' }), + '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. +} + +const readmeContents = jsdoc2md.renderSync(options) + +const readmeFile = path.join(rootDir, 'README.md') + +fs.writeFileSync(readmeFile, readmeContents) diff --git a/build/rollup.config.js b/build/rollup.config.js index 018fa2c..3467cf5 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -37,8 +37,8 @@ module.exports = [ replace({ 'process.browser': true }) - ] - // external: ['bigint-crypto-utils'] + ], + external: ['bigint-mod-arith'] }, { // Browser bundles input: input, @@ -61,8 +61,8 @@ module.exports = [ browser: true }), terser({ - mangle: false, - compress: false + // mangle: false, + // compress: false }) ] }, @@ -76,7 +76,7 @@ module.exports = [ output: { file: path.join(dstDir, 'index.node.js'), format: 'cjs' - } - // external: ['bigint-crypto-utils'] + }, + external: ['bigint-mod-arith'] } ] diff --git a/lib/index.browser.bundle.js b/lib/index.browser.bundle.js index cd736f1..47b43ac 100644 --- a/lib/index.browser.bundle.js +++ b/lib/index.browser.bundle.js @@ -1 +1 @@ -var bigintCryptoUtils=function(exports){"use strict";const _ZERO=BigInt(0);const _ONE=BigInt(1);const _TWO=BigInt(2);function abs(a){a=BigInt(a);return a>=_ZERO?a:-a}function bitLength(a){a=BigInt(a);if(a===_ONE){return 1}let bits=1;do{bits++}while((a>>=_ONE)>_ONE);return bits}function eGcd(a,b){a=BigInt(a);b=BigInt(b);if(a<=_ZERO|b<=_ZERO){return NaN}let x=_ZERO;let y=_ONE;let u=_ONE;let v=_ZERO;while(a!==_ZERO){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}}function gcd(a,b){a=abs(a);b=abs(b);if(a===_ZERO){return b}else if(b===_ZERO){return a}let shift=_ZERO;while(!((a|b)&_ONE)){a>>=_ONE;b>>=_ONE;shift++}while(!(a&_ONE))a>>=_ONE;do{while(!(b&_ONE))b>>=_ONE;if(a>b){const x=a;a=b;b=x}b-=a}while(b);return a<{const worker=new Worker(_isProbablyPrimeWorkerUrl());worker.onmessage=event=>{worker.terminate();resolve(event.data.isPrime)};worker.onmessageerror=event=>{reject(event)};worker.postMessage({rnd:w,iterations:iterations,id:0})})}}function lcm(a,b){a=BigInt(a);b=BigInt(b);if(a===_ZERO&&b===_ZERO){return _ZERO}return abs(a*b)/gcd(a,b)}function max(a,b){a=BigInt(a);b=BigInt(b);return a>=b?a:b}function min(a,b){a=BigInt(a);b=BigInt(b);return a>=b?b:a}function modInv(a,n){const egcd=eGcd(toZn(a,n),n);if(egcd.b!==_ONE){return NaN}else{return toZn(egcd.x,n)}}function modPow(b,e,n){n=BigInt(n);if(n===_ZERO){return NaN}else if(n===_ONE){return _ZERO}b=toZn(b,n);e=BigInt(e);if(e<_ZERO){return modInv(modPow(b,abs(e),n),n)}let r=_ONE;while(e>0){if(e%_TWO===_ONE){r=r*b%n}e=e/_TWO;b=b**_TWO%n}return r}function prime(bitLength,iterations=16){if(bitLength<1){throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)}return new Promise(resolve=>{const workerList=[];const _onmessage=(msg,newWorker)=>{if(msg.isPrime){for(let j=0;j_onmessage(event.data,newWorker);workerList.push(newWorker)}}for(let i=0;i 0 and it is ${bitLength}`)}let rnd=_ZERO;do{rnd=fromBuffer(randBytesSync(bitLength/8,true))}while(!_isProbablyPrime(rnd,iterations));return rnd}function randBetween(max,min=_ONE){if(max<=min)throw new Error("max must be > min");const interval=max-min;const bitLen=bitLength(interval);let rnd;do{const buf=randBits(bitLen);rnd=fromBuffer(buf)}while(rnd>interval);return rnd+min}function randBits(bitLength,forceLength=false){if(bitLength<1){throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)}const byteLength=Math.ceil(bitLength/8);const rndBytes=randBytesSync(byteLength,false);const bitLengthMod8=bitLength%8;if(bitLengthMod8){rndBytes[0]=rndBytes[0]&2**bitLengthMod8-1}if(forceLength){const mask=bitLengthMod8?2**(bitLengthMod8-1):128;rndBytes[0]=rndBytes[0]|mask}return rndBytes}function randBytes(byteLength,forceLength=false){if(byteLength<1){throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`)}let buf;{return new Promise((function(resolve){buf=new Uint8Array(byteLength);self.crypto.getRandomValues(buf);if(forceLength){buf[0]=buf[0]|128}resolve(buf)}))}}function randBytesSync(byteLength,forceLength=false){if(byteLength<1){throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`)}let buf;{buf=new Uint8Array(byteLength);self.crypto.getRandomValues(buf)}if(forceLength){buf[0]=buf[0]|128}return buf}function toZn(a,n){n=BigInt(n);if(n<=0){return NaN}a=BigInt(a)%n;return a<0?a+n:a}function fromBuffer(buf){let ret=_ZERO;for(const i of buf.values()){const bi=BigInt(i);ret=(ret< {${workerCode}})()`;const _blob=new Blob([workerCode],{type:"text/javascript"});return window.URL.createObjectURL(_blob)}function _isProbablyPrime(w,iterations=16){if(w===_TWO){return true}else if((w&_ONE)===_ZERO||w===_ONE){return false}const firstPrimes=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597];let p=_ZERO;for(let i=0;i=0n?n:-n}function e(n){if(1n===(n=BigInt(n)))return 1;let t=1;do{t++}while((n>>=1n)>1n);return t}function r(n,t){if((n=BigInt(n))<=0n|(t=BigInt(t))<=0n)return NaN;let e=0n,r=1n,i=1n,o=0n;for(;0n!==n;){const s=t/n,a=t%n,c=e-i*s,u=r-o*s;t=n,n=a,e=i,r=o,i=c,o=u}return{b:t,x:e,y:r}}function i(n,e){if(n=t(n),e=t(e),0n===n)return e;if(0n===e)return n;let r=0n;for(;!(1n&(n|e));)n>>=1n,e>>=1n,r++;for(;!(1n&n);)n>>=1n;do{for(;!(1n&e);)e>>=1n;if(n>e){const t=n;n=e,e=t}e-=n}while(e);return n<0;)e%2n===1n&&(i=i*n%r),e/=2n,n=n**2n%r;return i}function a(n,t){return(t=BigInt(t))<=0?NaN:(n=BigInt(n)%t)<0?n+t:n}async function c(n,t=16){return"number"==typeof n&&(n=BigInt(n)),new Promise((e,r)=>{const i=new Worker(d());i.onmessage=n=>{i.terminate(),e(n.data.isPrime)},i.onmessageerror=n=>{r(n)},i.postMessage({rnd:n,iterations:t,id:0})})}function u(n,t=1n){if(n<=t)throw new Error("max must be > min");const r=n-t,i=e(r);let o;do{o=l(f(i))}while(o>r);return o+t}function f(n,t=!1){if(n<1)throw new RangeError(`bitLength MUST be > 0 and it is ${n}`);const e=g(Math.ceil(n/8),!1),r=n%8;if(r&&(e[0]=e[0]&2**r-1),t){const n=r?2**(r-1):128;e[0]=e[0]|n}return e}function g(n,t=!1){if(n<1)throw new RangeError(`byteLength MUST be > 0 and it is ${n}`);let e;return e=new Uint8Array(n),self.crypto.getRandomValues(e),t&&(e[0]=128|e[0]),e}function l(n){let t=0n;for(const e of n.values()){const n=BigInt(e);t=(t< {${n}})()`;const t=new Blob([n],{type:"text/javascript"});return window.URL.createObjectURL(t)}(n)}function m(n,t=16){if(2n===n)return!0;if(0n===(1n&n)||1n===n)return!1;const e=[3n,5n,7n,11n,13n,17n,19n,23n,29n,31n,37n,41n,43n,47n,53n,59n,61n,67n,71n,73n,79n,83n,89n,97n,101n,103n,107n,109n,113n,127n,131n,137n,139n,149n,151n,157n,163n,167n,173n,179n,181n,191n,193n,197n,199n,211n,223n,227n,229n,233n,239n,241n,251n,257n,263n,269n,271n,277n,281n,283n,293n,307n,311n,313n,317n,331n,337n,347n,349n,353n,359n,367n,373n,379n,383n,389n,397n,401n,409n,419n,421n,431n,433n,439n,443n,449n,457n,461n,463n,467n,479n,487n,491n,499n,503n,509n,521n,523n,541n,547n,557n,563n,569n,571n,577n,587n,593n,599n,601n,607n,613n,617n,619n,631n,641n,643n,647n,653n,659n,661n,673n,677n,683n,691n,701n,709n,719n,727n,733n,739n,743n,751n,757n,761n,769n,773n,787n,797n,809n,811n,821n,823n,827n,829n,839n,853n,857n,859n,863n,877n,881n,883n,887n,907n,911n,919n,929n,937n,941n,947n,953n,967n,971n,977n,983n,991n,997n,1009n,1013n,1019n,1021n,1031n,1033n,1039n,1049n,1051n,1061n,1063n,1069n,1087n,1091n,1093n,1097n,1103n,1109n,1117n,1123n,1129n,1151n,1153n,1163n,1171n,1181n,1187n,1193n,1201n,1213n,1217n,1223n,1229n,1231n,1237n,1249n,1259n,1277n,1279n,1283n,1289n,1291n,1297n,1301n,1303n,1307n,1319n,1321n,1327n,1361n,1367n,1373n,1381n,1399n,1409n,1423n,1427n,1429n,1433n,1439n,1447n,1451n,1453n,1459n,1471n,1481n,1483n,1487n,1489n,1493n,1499n,1511n,1523n,1531n,1543n,1549n,1553n,1559n,1567n,1571n,1579n,1583n,1597n];for(let t=0;t=(t=BigInt(t))?n:t},n.min=function(n,t){return(n=BigInt(n))>=(t=BigInt(t))?t:n},n.modInv=o,n.modPow=s,n.prime=function(n,t=16){if(n<1)throw new RangeError(`bitLength MUST be > 0 and it is ${n}`);return new Promise(e=>{const r=[],i=(i,o)=>{if(i.isPrime){for(let n=0;ni(n.data,t),r.push(t)}}for(let e=0;e 0 and it is ${n}`);let e=0n;do{e=l(g(n/8,!0))}while(!m(e,t));return e},n.randBetween=u,n.randBits=f,n.randBytes=function(n,t=!1){if(n<1)throw new RangeError(`byteLength MUST be > 0 and it is ${n}`);let e;return new Promise((function(r){e=new Uint8Array(n),self.crypto.getRandomValues(e),t&&(e[0]=128|e[0]),r(e)}))},n.randBytesSync=g,n.toZn=a,n}({}); diff --git a/lib/index.browser.bundle.mod.js b/lib/index.browser.bundle.mod.js index ce65930..175b58e 100644 --- a/lib/index.browser.bundle.mod.js +++ b/lib/index.browser.bundle.mod.js @@ -1 +1 @@ -const _ZERO=BigInt(0);const _ONE=BigInt(1);const _TWO=BigInt(2);function abs(a){a=BigInt(a);return a>=_ZERO?a:-a}function bitLength(a){a=BigInt(a);if(a===_ONE){return 1}let bits=1;do{bits++}while((a>>=_ONE)>_ONE);return bits}function eGcd(a,b){a=BigInt(a);b=BigInt(b);if(a<=_ZERO|b<=_ZERO){return NaN}let x=_ZERO;let y=_ONE;let u=_ONE;let v=_ZERO;while(a!==_ZERO){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}}function gcd(a,b){a=abs(a);b=abs(b);if(a===_ZERO){return b}else if(b===_ZERO){return a}let shift=_ZERO;while(!((a|b)&_ONE)){a>>=_ONE;b>>=_ONE;shift++}while(!(a&_ONE))a>>=_ONE;do{while(!(b&_ONE))b>>=_ONE;if(a>b){const x=a;a=b;b=x}b-=a}while(b);return a<{const worker=new Worker(_isProbablyPrimeWorkerUrl());worker.onmessage=event=>{worker.terminate();resolve(event.data.isPrime)};worker.onmessageerror=event=>{reject(event)};worker.postMessage({rnd:w,iterations:iterations,id:0})})}}function lcm(a,b){a=BigInt(a);b=BigInt(b);if(a===_ZERO&&b===_ZERO){return _ZERO}return abs(a*b)/gcd(a,b)}function max(a,b){a=BigInt(a);b=BigInt(b);return a>=b?a:b}function min(a,b){a=BigInt(a);b=BigInt(b);return a>=b?b:a}function modInv(a,n){const egcd=eGcd(toZn(a,n),n);if(egcd.b!==_ONE){return NaN}else{return toZn(egcd.x,n)}}function modPow(b,e,n){n=BigInt(n);if(n===_ZERO){return NaN}else if(n===_ONE){return _ZERO}b=toZn(b,n);e=BigInt(e);if(e<_ZERO){return modInv(modPow(b,abs(e),n),n)}let r=_ONE;while(e>0){if(e%_TWO===_ONE){r=r*b%n}e=e/_TWO;b=b**_TWO%n}return r}function prime(bitLength,iterations=16){if(bitLength<1){throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)}return new Promise(resolve=>{const workerList=[];const _onmessage=(msg,newWorker)=>{if(msg.isPrime){for(let j=0;j_onmessage(event.data,newWorker);workerList.push(newWorker)}}for(let i=0;i 0 and it is ${bitLength}`)}let rnd=_ZERO;do{rnd=fromBuffer(randBytesSync(bitLength/8,true))}while(!_isProbablyPrime(rnd,iterations));return rnd}function randBetween(max,min=_ONE){if(max<=min)throw new Error("max must be > min");const interval=max-min;const bitLen=bitLength(interval);let rnd;do{const buf=randBits(bitLen);rnd=fromBuffer(buf)}while(rnd>interval);return rnd+min}function randBits(bitLength,forceLength=false){if(bitLength<1){throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)}const byteLength=Math.ceil(bitLength/8);const rndBytes=randBytesSync(byteLength,false);const bitLengthMod8=bitLength%8;if(bitLengthMod8){rndBytes[0]=rndBytes[0]&2**bitLengthMod8-1}if(forceLength){const mask=bitLengthMod8?2**(bitLengthMod8-1):128;rndBytes[0]=rndBytes[0]|mask}return rndBytes}function randBytes(byteLength,forceLength=false){if(byteLength<1){throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`)}let buf;{return new Promise((function(resolve){buf=new Uint8Array(byteLength);self.crypto.getRandomValues(buf);if(forceLength){buf[0]=buf[0]|128}resolve(buf)}))}}function randBytesSync(byteLength,forceLength=false){if(byteLength<1){throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`)}let buf;{buf=new Uint8Array(byteLength);self.crypto.getRandomValues(buf)}if(forceLength){buf[0]=buf[0]|128}return buf}function toZn(a,n){n=BigInt(n);if(n<=0){return NaN}a=BigInt(a)%n;return a<0?a+n:a}function fromBuffer(buf){let ret=_ZERO;for(const i of buf.values()){const bi=BigInt(i);ret=(ret< {${workerCode}})()`;const _blob=new Blob([workerCode],{type:"text/javascript"});return window.URL.createObjectURL(_blob)}function _isProbablyPrime(w,iterations=16){if(w===_TWO){return true}else if((w&_ONE)===_ZERO||w===_ONE){return false}const firstPrimes=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597];let p=_ZERO;for(let i=0;i=0n?n:-n}function t(n){if(1n===(n=BigInt(n)))return 1;let t=1;do{t++}while((n>>=1n)>1n);return t}function e(n,t){if((n=BigInt(n))<=0n|(t=BigInt(t))<=0n)return NaN;let e=0n,r=1n,i=1n,o=0n;for(;0n!==n;){const s=t/n,a=t%n,u=e-i*s,c=r-o*s;t=n,n=a,e=i,r=o,i=u,o=c}return{b:t,x:e,y:r}}function r(t,e){if(t=n(t),e=n(e),0n===t)return e;if(0n===e)return t;let r=0n;for(;!(1n&(t|e));)t>>=1n,e>>=1n,r++;for(;!(1n&t);)t>>=1n;do{for(;!(1n&e);)e>>=1n;if(t>e){const n=t;t=e,e=n}e-=t}while(e);return t<=(t=BigInt(t))?n:t}function s(n,t){return(n=BigInt(n))>=(t=BigInt(t))?t:n}function a(n,t){const r=e(c(n,t),t);return 1n!==r.b?NaN:c(r.x,t)}function u(t,e,r){if(0n===(r=BigInt(r)))return NaN;if(1n===r)return 0n;if(t=c(t,r),(e=BigInt(e))<0n)return a(u(t,n(e),r),r);let i=1n;for(;e>0;)e%2n===1n&&(i=i*t%r),e/=2n,t=t**2n%r;return i}function c(n,t){return(t=BigInt(t))<=0?NaN:(n=BigInt(n)%t)<0?n+t:n}async function f(n,t=16){return"number"==typeof n&&(n=BigInt(n)),new Promise((e,r)=>{const i=new Worker(B());i.onmessage=n=>{i.terminate(),e(n.data.isPrime)},i.onmessageerror=n=>{r(n)},i.postMessage({rnd:n,iterations:t,id:0})})}function g(n,t=16){if(n<1)throw new RangeError(`bitLength MUST be > 0 and it is ${n}`);return new Promise(e=>{const r=[],i=(i,o)=>{if(i.isPrime){for(let n=0;ni(n.data,t),r.push(t)}}for(let e=0;e 0 and it is ${n}`);let e=0n;do{e=h($(n/8,!0))}while(!I(e,t));return e}function d(n,e=1n){if(n<=e)throw new Error("max must be > min");const r=n-e,i=t(r);let o;do{o=h(w(i))}while(o>r);return o+e}function w(n,t=!1){if(n<1)throw new RangeError(`bitLength MUST be > 0 and it is ${n}`);const e=$(Math.ceil(n/8),!1),r=n%8;if(r&&(e[0]=e[0]&2**r-1),t){const n=r?2**(r-1):128;e[0]=e[0]|n}return e}function m(n,t=!1){if(n<1)throw new RangeError(`byteLength MUST be > 0 and it is ${n}`);let e;return new Promise((function(r){e=new Uint8Array(n),self.crypto.getRandomValues(e),t&&(e[0]=128|e[0]),r(e)}))}function $(n,t=!1){if(n<1)throw new RangeError(`byteLength MUST be > 0 and it is ${n}`);let e;return e=new Uint8Array(n),self.crypto.getRandomValues(e),t&&(e[0]=128|e[0]),e}function h(n){let t=0n;for(const e of n.values()){const n=BigInt(e);t=(t< {${n}})()`;const t=new Blob([n],{type:"text/javascript"});return window.URL.createObjectURL(t)}(n)}function I(n,t=16){if(2n===n)return!0;if(0n===(1n&n)||1n===n)return!1;const e=[3n,5n,7n,11n,13n,17n,19n,23n,29n,31n,37n,41n,43n,47n,53n,59n,61n,67n,71n,73n,79n,83n,89n,97n,101n,103n,107n,109n,113n,127n,131n,137n,139n,149n,151n,157n,163n,167n,173n,179n,181n,191n,193n,197n,199n,211n,223n,227n,229n,233n,239n,241n,251n,257n,263n,269n,271n,277n,281n,283n,293n,307n,311n,313n,317n,331n,337n,347n,349n,353n,359n,367n,373n,379n,383n,389n,397n,401n,409n,419n,421n,431n,433n,439n,443n,449n,457n,461n,463n,467n,479n,487n,491n,499n,503n,509n,521n,523n,541n,547n,557n,563n,569n,571n,577n,587n,593n,599n,601n,607n,613n,617n,619n,631n,641n,643n,647n,653n,659n,661n,673n,677n,683n,691n,701n,709n,719n,727n,733n,739n,743n,751n,757n,761n,769n,773n,787n,797n,809n,811n,821n,823n,827n,829n,839n,853n,857n,859n,863n,877n,881n,883n,887n,907n,911n,919n,929n,937n,941n,947n,953n,967n,971n,977n,983n,991n,997n,1009n,1013n,1019n,1021n,1031n,1033n,1039n,1049n,1051n,1061n,1063n,1069n,1087n,1091n,1093n,1097n,1103n,1109n,1117n,1123n,1129n,1151n,1153n,1163n,1171n,1181n,1187n,1193n,1201n,1213n,1217n,1223n,1229n,1231n,1237n,1249n,1259n,1277n,1279n,1283n,1289n,1291n,1297n,1301n,1303n,1307n,1319n,1321n,1327n,1361n,1367n,1373n,1381n,1399n,1409n,1423n,1427n,1429n,1433n,1439n,1447n,1451n,1453n,1459n,1471n,1481n,1483n,1487n,1489n,1493n,1499n,1511n,1523n,1531n,1543n,1549n,1553n,1559n,1567n,1571n,1579n,1583n,1597n];for(let t=0;t=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 >= _ZERO) ? 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 === _ONE) { return 1 } - let bits = 1 - do { - bits++ - } while ((a >>= _ONE) > _ONE) - 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 <= _ZERO | b <= _ZERO) { return NaN } // a and b MUST be positive - - let x = _ZERO - let y = _ONE - let u = _ONE - let v = _ZERO - - while (a !== _ZERO) { - 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 === _ZERO) { return b } else if (b === _ZERO) { return a } - - let shift = _ZERO - while (!((a | b) & _ONE)) { - a >>= _ONE - b >>= _ONE - shift++ - } - while (!(a & _ONE)) a >>= _ONE - do { - while (!(b & _ONE)) b >>= _ONE - if (a > b) { - const x = a - a = b - b = x - } - b -= a - } while (b) - - // rescale - return a << shift -} +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' /** * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several * iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1) * - * @param {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 * - * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) + * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) */ async function isProbablyPrime (w, iterations = 16) { if (typeof w === 'number') { @@ -145,96 +38,6 @@ async function isProbablyPrime (w, iterations = 16) { /* eslint-enable no-lone-blocks */ } -/** - * 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 === _ZERO && b === _ZERO) { return _ZERO } - 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 !== _ONE) { - 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 === _ZERO) { return NaN } else if (n === _ONE) { return _ZERO } - - b = toZn(b, n) - - e = BigInt(e) - if (e < _ZERO) { - return modInv(modPow(b, abs(e), n), n) - } - - let r = _ONE - while (e > 0) { - if ((e % _TWO) === _ONE) { - r = (r * b) % n - } - e = e / _TWO - b = b ** _TWO % n - } - return r -} - /** * 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 @@ -245,7 +48,7 @@ function modPow (b, e, n) { * @param {number} bitLength The required bit length for the generated prime * @param {number} [iterations = 16] The number of iterations for the Miller-Rabin Probabilistic Primality Test * - * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. + * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. */ function prime (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } @@ -278,7 +81,7 @@ function prime (bitLength, iterations = 16) { /* eslint-disable no-lone-blocks */ { // browser const workerURL = _isProbablyPrimeWorkerUrl() - for (let i = 0; i < self.navigator.hardwareConcurrency; i++) { + for (let i = 0; i < self.navigator.hardwareConcurrency - 1; i++) { const newWorker = new Worker(workerURL) newWorker.onmessage = (event) => _onmessage(event.data, newWorker) workerList.push(newWorker) @@ -308,7 +111,7 @@ function prime (bitLength, iterations = 16) { */ function primeSync (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } - let rnd = _ZERO + let rnd = 0n do { rnd = fromBuffer(randBytesSync(bitLength / 8, true)) } while (!_isProbablyPrime(rnd, iterations)) @@ -322,7 +125,7 @@ function primeSync (bitLength, iterations = 16) { * * @returns {bigint} A cryptographically secure random bigint between [min,max] */ -function randBetween (max, min = _ONE) { +function randBetween (max, min = 1n) { if (max <= min) throw new Error('max must be > min') const interval = max - min const bitLen = bitLength(interval) @@ -340,7 +143,7 @@ function randBetween (max, min = _ONE) { * @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 * - * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits + * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ function randBits (bitLength, forceLength = false) { if (bitLength < 1) { @@ -367,7 +170,7 @@ function randBits (bitLength, forceLength = false) { * @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 * - * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes + * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } @@ -392,7 +195,7 @@ function randBytes (byteLength, forceLength = false) { * @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 * - * @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) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } @@ -407,25 +210,10 @@ function randBytesSync (byteLength, forceLength = false) { return buf } -/** - * 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 -} - /* HELPER FUNCTIONS */ function fromBuffer (buf) { - let ret = _ZERO + let ret = 0n for (const i of buf.values()) { const bi = BigInt(i) ret = (ret << BigInt(8)) + bi @@ -435,7 +223,7 @@ function fromBuffer (buf) { function _isProbablyPrimeWorkerUrl () { // Let's us first add all the required functions - let workerCode = `'use strict';const _ZERO = BigInt(0);const _ONE = BigInt(1);const _TWO = BigInt(2);const eGcd = ${eGcd.toString()};const modInv = ${modInv.toString()};const modPow = ${modPow.toString()};const toZn = ${toZn.toString()};const randBits = ${randBits.toString()};const randBytesSync = ${randBytesSync.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}` + let workerCode = `'use strict';const ${eGcd.name}=${eGcd.toString()};const ${modInv.name}=${modInv.toString()};const ${modPow.name}=${modPow.toString()};const ${toZn.name}=${toZn.toString()};const ${randBits.name}=${randBits.toString()};const ${randBytesSync.name}=${randBytesSync.toString()};const ${randBetween.name}=${randBetween.toString()};const ${isProbablyPrime.name}=${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}` const onmessage = async function (event) { // Let's start once we are called // event.data = {rnd: , iterations: } @@ -463,269 +251,269 @@ function _isProbablyPrime (w, iterations = 16) { PREFILTERING. Even values but 2 are not primes, so don't test. 1 is not a prime and the M-R algorithm needs w>1. */ - if (w === _TWO) { return true } else if ((w & _ONE) === _ZERO || w === _ONE) { return false } + if (w === 2n) { return true } else if ((w & 1n) === 0n || w === 1n) { return false } /* Test if any of the first 250 small primes are a factor of w. 2 is not tested because it was already tested above. */ const firstPrimes = [ - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - 131, - 137, - 139, - 149, - 151, - 157, - 163, - 167, - 173, - 179, - 181, - 191, - 193, - 197, - 199, - 211, - 223, - 227, - 229, - 233, - 239, - 241, - 251, - 257, - 263, - 269, - 271, - 277, - 281, - 283, - 293, - 307, - 311, - 313, - 317, - 331, - 337, - 347, - 349, - 353, - 359, - 367, - 373, - 379, - 383, - 389, - 397, - 401, - 409, - 419, - 421, - 431, - 433, - 439, - 443, - 449, - 457, - 461, - 463, - 467, - 479, - 487, - 491, - 499, - 503, - 509, - 521, - 523, - 541, - 547, - 557, - 563, - 569, - 571, - 577, - 587, - 593, - 599, - 601, - 607, - 613, - 617, - 619, - 631, - 641, - 643, - 647, - 653, - 659, - 661, - 673, - 677, - 683, - 691, - 701, - 709, - 719, - 727, - 733, - 739, - 743, - 751, - 757, - 761, - 769, - 773, - 787, - 797, - 809, - 811, - 821, - 823, - 827, - 829, - 839, - 853, - 857, - 859, - 863, - 877, - 881, - 883, - 887, - 907, - 911, - 919, - 929, - 937, - 941, - 947, - 953, - 967, - 971, - 977, - 983, - 991, - 997, - 1009, - 1013, - 1019, - 1021, - 1031, - 1033, - 1039, - 1049, - 1051, - 1061, - 1063, - 1069, - 1087, - 1091, - 1093, - 1097, - 1103, - 1109, - 1117, - 1123, - 1129, - 1151, - 1153, - 1163, - 1171, - 1181, - 1187, - 1193, - 1201, - 1213, - 1217, - 1223, - 1229, - 1231, - 1237, - 1249, - 1259, - 1277, - 1279, - 1283, - 1289, - 1291, - 1297, - 1301, - 1303, - 1307, - 1319, - 1321, - 1327, - 1361, - 1367, - 1373, - 1381, - 1399, - 1409, - 1423, - 1427, - 1429, - 1433, - 1439, - 1447, - 1451, - 1453, - 1459, - 1471, - 1481, - 1483, - 1487, - 1489, - 1493, - 1499, - 1511, - 1523, - 1531, - 1543, - 1549, - 1553, - 1559, - 1567, - 1571, - 1579, - 1583, - 1597 + 3n, + 5n, + 7n, + 11n, + 13n, + 17n, + 19n, + 23n, + 29n, + 31n, + 37n, + 41n, + 43n, + 47n, + 53n, + 59n, + 61n, + 67n, + 71n, + 73n, + 79n, + 83n, + 89n, + 97n, + 101n, + 103n, + 107n, + 109n, + 113n, + 127n, + 131n, + 137n, + 139n, + 149n, + 151n, + 157n, + 163n, + 167n, + 173n, + 179n, + 181n, + 191n, + 193n, + 197n, + 199n, + 211n, + 223n, + 227n, + 229n, + 233n, + 239n, + 241n, + 251n, + 257n, + 263n, + 269n, + 271n, + 277n, + 281n, + 283n, + 293n, + 307n, + 311n, + 313n, + 317n, + 331n, + 337n, + 347n, + 349n, + 353n, + 359n, + 367n, + 373n, + 379n, + 383n, + 389n, + 397n, + 401n, + 409n, + 419n, + 421n, + 431n, + 433n, + 439n, + 443n, + 449n, + 457n, + 461n, + 463n, + 467n, + 479n, + 487n, + 491n, + 499n, + 503n, + 509n, + 521n, + 523n, + 541n, + 547n, + 557n, + 563n, + 569n, + 571n, + 577n, + 587n, + 593n, + 599n, + 601n, + 607n, + 613n, + 617n, + 619n, + 631n, + 641n, + 643n, + 647n, + 653n, + 659n, + 661n, + 673n, + 677n, + 683n, + 691n, + 701n, + 709n, + 719n, + 727n, + 733n, + 739n, + 743n, + 751n, + 757n, + 761n, + 769n, + 773n, + 787n, + 797n, + 809n, + 811n, + 821n, + 823n, + 827n, + 829n, + 839n, + 853n, + 857n, + 859n, + 863n, + 877n, + 881n, + 883n, + 887n, + 907n, + 911n, + 919n, + 929n, + 937n, + 941n, + 947n, + 953n, + 967n, + 971n, + 977n, + 983n, + 991n, + 997n, + 1009n, + 1013n, + 1019n, + 1021n, + 1031n, + 1033n, + 1039n, + 1049n, + 1051n, + 1061n, + 1063n, + 1069n, + 1087n, + 1091n, + 1093n, + 1097n, + 1103n, + 1109n, + 1117n, + 1123n, + 1129n, + 1151n, + 1153n, + 1163n, + 1171n, + 1181n, + 1187n, + 1193n, + 1201n, + 1213n, + 1217n, + 1223n, + 1229n, + 1231n, + 1237n, + 1249n, + 1259n, + 1277n, + 1279n, + 1283n, + 1289n, + 1291n, + 1297n, + 1301n, + 1303n, + 1307n, + 1319n, + 1321n, + 1327n, + 1361n, + 1367n, + 1373n, + 1381n, + 1399n, + 1409n, + 1423n, + 1427n, + 1429n, + 1433n, + 1439n, + 1447n, + 1451n, + 1453n, + 1459n, + 1471n, + 1481n, + 1483n, + 1487n, + 1489n, + 1493n, + 1499n, + 1511n, + 1523n, + 1531n, + 1543n, + 1549n, + 1553n, + 1559n, + 1567n, + 1571n, + 1579n, + 1583n, + 1597n ] - let p = _ZERO - for (let i = 0; i < firstPrimes.length && (p <= w); i++) { - p = BigInt(firstPrimes[i]) + + for (let i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) { + const p = firstPrimes[i] if (w === p) { return true - } else if (w % p === _ZERO) { + } else if (w % p === 0n) { return false } } @@ -749,30 +537,48 @@ function _isProbablyPrime (w, iterations = 16) { Comment: Increment i for the do-loop in step 4. 5. Return PROBABLY PRIME. */ - let a = _ZERO; let d = w - _ONE - while (d % _TWO === _ZERO) { - d /= _TWO + let a = 0n + const d = w - 1n + let aux = d + while (aux % 2n === 0n) { + aux /= 2n ++a } - const m = (w - _ONE) / (_TWO ** a) + const m = d / (2n ** a) - /* eslint-disable no-labels */ - loop: do { - const b = randBetween(w - _ONE, _TWO) + // /* eslint-disable no-labels */ + // loop: do { + // const b = randBetween(w - 1n, 2n) + // let z = modPow(b, m, w) + // if (z === 1n || z === w - 1n) { continue } + // for (let j = 1; j < a; j++) { + // z = modPow(z, 2n, w) + // if (z === w - 1n) { continue loop } + // if (z === 1n) { break } + // } + // return false + // } while (--iterations) + // /* eslint-enable no-labels */ + + // return true + + do { + const b = randBetween(d, 2n) let z = modPow(b, m, w) - if (z === _ONE || z === w - _ONE) { continue } - - for (let j = 1; j < a; j++) { - z = modPow(z, _TWO, w) - if (z === w - _ONE) { continue loop } - if (z === _ONE) { break } + if (z === 1n || z === d) { continue } + let j = 1 + while (j < a) { + z = modPow(z, 2n, w) + if (z === d) { break } + if (z === 1n) { return false } + j++ + } + if (z !== d) { + return false } - return false } while (--iterations) - /* eslint-enable no-labels */ - return true } -export { abs, bitLength, eGcd, gcd, isProbablyPrime, lcm, max, min, modInv, modPow, prime, primeSync, randBetween, randBits, randBytes, randBytesSync, toZn } +export { isProbablyPrime, prime, primeSync, randBetween, randBits, randBytes, randBytesSync } diff --git a/lib/index.node.js b/lib/index.node.js index 6dd4138..41ceeb6 100644 --- a/lib/index.node.js +++ b/lib/index.node.js @@ -2,124 +2,16 @@ Object.defineProperty(exports, '__esModule', { value: true }) -const _ZERO = BigInt(0) -const _ONE = BigInt(1) -const _TWO = BigInt(2) - -/** - * 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 >= _ZERO) ? 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 === _ONE) { return 1 } - let bits = 1 - do { - bits++ - } while ((a >>= _ONE) > _ONE) - 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 <= _ZERO | b <= _ZERO) { return NaN } // a and b MUST be positive - - let x = _ZERO - let y = _ONE - let u = _ONE - let v = _ZERO - - while (a !== _ZERO) { - 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 === _ZERO) { return b } else if (b === _ZERO) { return a } - - let shift = _ZERO - while (!((a | b) & _ONE)) { - a >>= _ONE - b >>= _ONE - shift++ - } - while (!(a & _ONE)) a >>= _ONE - do { - while (!(b & _ONE)) b >>= _ONE - if (a > b) { - const x = a - a = b - b = x - } - b -= a - } while (b) - - // rescale - return a << shift -} +var bigintModArith = require('bigint-mod-arith') /** * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several * iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1) * - * @param {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 * - * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) + * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) */ async function isProbablyPrime (w, iterations = 16) { if (typeof w === 'number') { @@ -155,96 +47,6 @@ async function isProbablyPrime (w, iterations = 16) { /* eslint-enable no-lone-blocks */ } -/** - * 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 === _ZERO && b === _ZERO) { return _ZERO } - 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 !== _ONE) { - 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 === _ZERO) { return NaN } else if (n === _ONE) { return _ZERO } - - b = toZn(b, n) - - e = BigInt(e) - if (e < _ZERO) { - return modInv(modPow(b, abs(e), n), n) - } - - let r = _ONE - while (e > 0) { - if ((e % _TWO) === _ONE) { - r = (r * b) % n - } - e = e / _TWO - b = b ** _TWO % n - } - return r -} - /** * 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 @@ -255,13 +57,13 @@ function modPow (b, e, n) { * @param {number} bitLength The required bit length for the generated prime * @param {number} [iterations = 16] The number of iterations for the Miller-Rabin Probabilistic Primality Test * - * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. + * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. */ function prime (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } if (!_useWorkers) { - let rnd = _ZERO + let rnd = 0n do { rnd = fromBuffer(randBytesSync(bitLength / 8, true)) } while (!_isProbablyPrime(rnd, iterations)) @@ -297,7 +99,7 @@ function prime (bitLength, iterations = 16) { { // Node.js const { cpus } = require('os') const { Worker } = require('worker_threads') - for (let i = 0; i < cpus().length; i++) { + for (let i = 0; i < cpus().length - 1; i++) { const newWorker = new Worker(__filename) newWorker.on('message', (msg) => _onmessage(msg, newWorker)) workerList.push(newWorker) @@ -327,7 +129,7 @@ function prime (bitLength, iterations = 16) { */ function primeSync (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } - let rnd = _ZERO + let rnd = 0n do { rnd = fromBuffer(randBytesSync(bitLength / 8, true)) } while (!_isProbablyPrime(rnd, iterations)) @@ -341,10 +143,10 @@ function primeSync (bitLength, iterations = 16) { * * @returns {bigint} A cryptographically secure random bigint between [min,max] */ -function randBetween (max, min = _ONE) { +function randBetween (max, min = 1n) { if (max <= min) throw new Error('max must be > min') const interval = max - min - const bitLen = bitLength(interval) + const bitLen = bigintModArith.bitLength(interval) let rnd do { const buf = randBits(bitLen) @@ -359,7 +161,7 @@ function randBetween (max, min = _ONE) { * @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 * - * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits + * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ function randBits (bitLength, forceLength = false) { if (bitLength < 1) { @@ -386,7 +188,7 @@ function randBits (bitLength, forceLength = false) { * @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 * - * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes + * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } @@ -411,7 +213,7 @@ function randBytes (byteLength, forceLength = false) { * @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 * - * @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) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } @@ -427,25 +229,10 @@ function randBytesSync (byteLength, forceLength = false) { return buf } -/** - * 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 -} - /* HELPER FUNCTIONS */ function fromBuffer (buf) { - let ret = _ZERO + let ret = 0n for (const i of buf.values()) { const bi = BigInt(i) ret = (ret << BigInt(8)) + bi @@ -458,269 +245,269 @@ function _isProbablyPrime (w, iterations = 16) { PREFILTERING. Even values but 2 are not primes, so don't test. 1 is not a prime and the M-R algorithm needs w>1. */ - if (w === _TWO) { return true } else if ((w & _ONE) === _ZERO || w === _ONE) { return false } + if (w === 2n) { return true } else if ((w & 1n) === 0n || w === 1n) { return false } /* Test if any of the first 250 small primes are a factor of w. 2 is not tested because it was already tested above. */ const firstPrimes = [ - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - 131, - 137, - 139, - 149, - 151, - 157, - 163, - 167, - 173, - 179, - 181, - 191, - 193, - 197, - 199, - 211, - 223, - 227, - 229, - 233, - 239, - 241, - 251, - 257, - 263, - 269, - 271, - 277, - 281, - 283, - 293, - 307, - 311, - 313, - 317, - 331, - 337, - 347, - 349, - 353, - 359, - 367, - 373, - 379, - 383, - 389, - 397, - 401, - 409, - 419, - 421, - 431, - 433, - 439, - 443, - 449, - 457, - 461, - 463, - 467, - 479, - 487, - 491, - 499, - 503, - 509, - 521, - 523, - 541, - 547, - 557, - 563, - 569, - 571, - 577, - 587, - 593, - 599, - 601, - 607, - 613, - 617, - 619, - 631, - 641, - 643, - 647, - 653, - 659, - 661, - 673, - 677, - 683, - 691, - 701, - 709, - 719, - 727, - 733, - 739, - 743, - 751, - 757, - 761, - 769, - 773, - 787, - 797, - 809, - 811, - 821, - 823, - 827, - 829, - 839, - 853, - 857, - 859, - 863, - 877, - 881, - 883, - 887, - 907, - 911, - 919, - 929, - 937, - 941, - 947, - 953, - 967, - 971, - 977, - 983, - 991, - 997, - 1009, - 1013, - 1019, - 1021, - 1031, - 1033, - 1039, - 1049, - 1051, - 1061, - 1063, - 1069, - 1087, - 1091, - 1093, - 1097, - 1103, - 1109, - 1117, - 1123, - 1129, - 1151, - 1153, - 1163, - 1171, - 1181, - 1187, - 1193, - 1201, - 1213, - 1217, - 1223, - 1229, - 1231, - 1237, - 1249, - 1259, - 1277, - 1279, - 1283, - 1289, - 1291, - 1297, - 1301, - 1303, - 1307, - 1319, - 1321, - 1327, - 1361, - 1367, - 1373, - 1381, - 1399, - 1409, - 1423, - 1427, - 1429, - 1433, - 1439, - 1447, - 1451, - 1453, - 1459, - 1471, - 1481, - 1483, - 1487, - 1489, - 1493, - 1499, - 1511, - 1523, - 1531, - 1543, - 1549, - 1553, - 1559, - 1567, - 1571, - 1579, - 1583, - 1597 + 3n, + 5n, + 7n, + 11n, + 13n, + 17n, + 19n, + 23n, + 29n, + 31n, + 37n, + 41n, + 43n, + 47n, + 53n, + 59n, + 61n, + 67n, + 71n, + 73n, + 79n, + 83n, + 89n, + 97n, + 101n, + 103n, + 107n, + 109n, + 113n, + 127n, + 131n, + 137n, + 139n, + 149n, + 151n, + 157n, + 163n, + 167n, + 173n, + 179n, + 181n, + 191n, + 193n, + 197n, + 199n, + 211n, + 223n, + 227n, + 229n, + 233n, + 239n, + 241n, + 251n, + 257n, + 263n, + 269n, + 271n, + 277n, + 281n, + 283n, + 293n, + 307n, + 311n, + 313n, + 317n, + 331n, + 337n, + 347n, + 349n, + 353n, + 359n, + 367n, + 373n, + 379n, + 383n, + 389n, + 397n, + 401n, + 409n, + 419n, + 421n, + 431n, + 433n, + 439n, + 443n, + 449n, + 457n, + 461n, + 463n, + 467n, + 479n, + 487n, + 491n, + 499n, + 503n, + 509n, + 521n, + 523n, + 541n, + 547n, + 557n, + 563n, + 569n, + 571n, + 577n, + 587n, + 593n, + 599n, + 601n, + 607n, + 613n, + 617n, + 619n, + 631n, + 641n, + 643n, + 647n, + 653n, + 659n, + 661n, + 673n, + 677n, + 683n, + 691n, + 701n, + 709n, + 719n, + 727n, + 733n, + 739n, + 743n, + 751n, + 757n, + 761n, + 769n, + 773n, + 787n, + 797n, + 809n, + 811n, + 821n, + 823n, + 827n, + 829n, + 839n, + 853n, + 857n, + 859n, + 863n, + 877n, + 881n, + 883n, + 887n, + 907n, + 911n, + 919n, + 929n, + 937n, + 941n, + 947n, + 953n, + 967n, + 971n, + 977n, + 983n, + 991n, + 997n, + 1009n, + 1013n, + 1019n, + 1021n, + 1031n, + 1033n, + 1039n, + 1049n, + 1051n, + 1061n, + 1063n, + 1069n, + 1087n, + 1091n, + 1093n, + 1097n, + 1103n, + 1109n, + 1117n, + 1123n, + 1129n, + 1151n, + 1153n, + 1163n, + 1171n, + 1181n, + 1187n, + 1193n, + 1201n, + 1213n, + 1217n, + 1223n, + 1229n, + 1231n, + 1237n, + 1249n, + 1259n, + 1277n, + 1279n, + 1283n, + 1289n, + 1291n, + 1297n, + 1301n, + 1303n, + 1307n, + 1319n, + 1321n, + 1327n, + 1361n, + 1367n, + 1373n, + 1381n, + 1399n, + 1409n, + 1423n, + 1427n, + 1429n, + 1433n, + 1439n, + 1447n, + 1451n, + 1453n, + 1459n, + 1471n, + 1481n, + 1483n, + 1487n, + 1489n, + 1493n, + 1499n, + 1511n, + 1523n, + 1531n, + 1543n, + 1549n, + 1553n, + 1559n, + 1567n, + 1571n, + 1579n, + 1583n, + 1597n ] - let p = _ZERO - for (let i = 0; i < firstPrimes.length && (p <= w); i++) { - p = BigInt(firstPrimes[i]) + + for (let i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) { + const p = firstPrimes[i] if (w === p) { return true - } else if (w % p === _ZERO) { + } else if (w % p === 0n) { return false } } @@ -744,29 +531,47 @@ function _isProbablyPrime (w, iterations = 16) { Comment: Increment i for the do-loop in step 4. 5. Return PROBABLY PRIME. */ - let a = _ZERO; let d = w - _ONE - while (d % _TWO === _ZERO) { - d /= _TWO + let a = 0n + const d = w - 1n + let aux = d + while (aux % 2n === 0n) { + aux /= 2n ++a } - const m = (w - _ONE) / (_TWO ** a) + const m = d / (2n ** a) - /* eslint-disable no-labels */ - loop: do { - const b = randBetween(w - _ONE, _TWO) - let z = modPow(b, m, w) - if (z === _ONE || z === w - _ONE) { continue } + // /* eslint-disable no-labels */ + // loop: do { + // const b = randBetween(w - 1n, 2n) + // let z = modPow(b, m, w) + // if (z === 1n || z === w - 1n) { continue } + // for (let j = 1; j < a; j++) { + // z = modPow(z, 2n, w) + // if (z === w - 1n) { continue loop } + // if (z === 1n) { break } + // } + // return false + // } while (--iterations) + // /* eslint-enable no-labels */ - for (let j = 1; j < a; j++) { - z = modPow(z, _TWO, w) - if (z === w - _ONE) { continue loop } - if (z === _ONE) { break } + // return true + + do { + const b = randBetween(d, 2n) + let z = bigintModArith.modPow(b, m, w) + if (z === 1n || z === d) { continue } + let j = 1 + while (j < a) { + z = bigintModArith.modPow(z, 2n, w) + if (z === d) { break } + if (z === 1n) { return false } + j++ + } + if (z !== d) { + return false } - return false } while (--iterations) - /* eslint-enable no-labels */ - return true } @@ -801,20 +606,70 @@ if (_useWorkers) { // node.js with support for workers } } -exports.abs = abs -exports.bitLength = bitLength -exports.eGcd = eGcd -exports.gcd = gcd +Object.defineProperty(exports, 'abs', { + enumerable: true, + get: function () { + return bigintModArith.abs + } +}) +Object.defineProperty(exports, 'bitLength', { + enumerable: true, + get: function () { + return bigintModArith.bitLength + } +}) +Object.defineProperty(exports, 'eGcd', { + enumerable: true, + get: function () { + return bigintModArith.eGcd + } +}) +Object.defineProperty(exports, 'gcd', { + enumerable: true, + get: function () { + return bigintModArith.gcd + } +}) +Object.defineProperty(exports, 'lcm', { + enumerable: true, + get: function () { + return bigintModArith.lcm + } +}) +Object.defineProperty(exports, 'max', { + enumerable: true, + get: function () { + return bigintModArith.max + } +}) +Object.defineProperty(exports, 'min', { + enumerable: true, + get: function () { + return bigintModArith.min + } +}) +Object.defineProperty(exports, 'modInv', { + enumerable: true, + get: function () { + return bigintModArith.modInv + } +}) +Object.defineProperty(exports, 'modPow', { + enumerable: true, + get: function () { + return bigintModArith.modPow + } +}) +Object.defineProperty(exports, 'toZn', { + enumerable: true, + get: function () { + return bigintModArith.toZn + } +}) exports.isProbablyPrime = isProbablyPrime -exports.lcm = lcm -exports.max = max -exports.min = min -exports.modInv = modInv -exports.modPow = modPow exports.prime = prime exports.primeSync = primeSync exports.randBetween = randBetween exports.randBits = randBits exports.randBytes = randBytes exports.randBytesSync = randBytesSync -exports.toZn = toZn diff --git a/package-lock.json b/package-lock.json index 3af89ee..999e22e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,8 +105,7 @@ "@types/node": { "version": "13.11.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.0.tgz", - "integrity": "sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ==", - "dev": true + "integrity": "sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ==" }, "@types/resolve": { "version": "0.0.8", @@ -262,6 +261,11 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "bigint-mod-arith": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-2.0.4.tgz", + "integrity": "sha512-sQyEj0XMU4hai3G/+uLwohrGjfUn8rGVWAYnnlFrQhw8YjilptTyJrx7NMimKwQvYr2eXGWGDlYVL3wfE4GIRg==" + }, "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", diff --git a/package.json b/package.json index 5bd9e0f..c42c240 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "build:js": "rollup -c build/rollup.config.js", "build:standard": "standard --fix", "build:browserTests": "rollup -c build/rollup.tests.config.js", - "build:docs": "jsdoc2md --template=./src/doc/readme-template.md --files ./lib/index.browser.mod.js -d 3 > README.md", + "build:docs": "node build/build.docs.js", "build:dts": "node build/build.dts.js", "build": "run-s build:**", "prepublishOnly": "npm run build" @@ -69,5 +69,9 @@ "rollup-plugin-terser": "^5.3.0", "standard": "^14.3.3", "typescript": "^3.8.3" + }, + "dependencies": { + "@types/node": "^13.11.0", + "bigint-mod-arith": "^2.0.4" } } diff --git a/src/doc/readme-template.md b/src/doc/readme-template.md index 37613e0..1f4cb4b 100644 --- a/src/doc/readme-template.md +++ b/src/doc/readme-template.md @@ -16,9 +16,9 @@ bigint-crypto-utils can be imported to your project with `npm`: npm install bigint-crypto-utils ``` -NPM installation defaults to the minified 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 file](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.js) or the [ES6 module](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.mod.js) from GitHub. +For web browsers, you can also directly download the [IIFE bundle](https://raw.githubusercontent.com/juanelas/bigint-crypto-utils/master/lib/index.browser.bundle.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 @@ -29,26 +29,25 @@ Import your module as : const bigintCryptoUtils = require('bigint-crypto-utils') ... // your code here ``` - - Javascript native project + - JavaScript native project ```javascript import * as bigintCryptoUtils from 'bigint-crypto-utils' ... // your code here ``` - - Javascript native browser ES6 mod + - JavaScript native browser ES6 mod ```html - import as bcu from 'bigint-crypto-utils' - ... // your code here ``` - - Javascript native browser IIFE + - JavaScript native browser IIFE ```html - + + ``` - TypeScript ```typescript import * as bigintCryptoUtils from 'bigint-crypto-utils' @@ -56,6 +55,8 @@ Import your module as : ``` > 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`. +And you could use it like in the following: + ```javascript /* Stage 3 BigInts with value 666 can be declared as BigInt('666') or the shorter new no-so-linter-friendly syntax 666n. @@ -65,22 +66,22 @@ be raised. */ const a = BigInt('5') const b = BigInt('2') -const n = BigInt('19') +const n = 19n console.log(bigintCryptoUtils.modPow(a, b, n)) // prints 6 -console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))) // prints 3 +console.log(bigintCryptoUtils.modInv(2n, 5n)) // prints 3 console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))) // prints 2 -console.log(bigintCryptoUtils.randBetween(BigInt(2) ** BigInt(256))) // Prints a cryptographically secure random number between 1 and 2**256 bits. +console.log(bigintCryptoUtils.randBetween(2n ** 256n)) // Prints a cryptographically secure random number between 1 and 2**256 bits. async function primeTesting () { // Output of a probable prime of 2048 bits console.log(await bigintCryptoUtils.prime(2048)) // Testing if a number is a probable prime (Miller-Rabin) - const number = 27 + const number = 27n const isPrime = await bigintCryptoUtils.isProbablyPrime(number) if (isPrime) { console.log(`${number} is prime`) @@ -93,6 +94,6 @@ primeTesting() ``` -## bigint-crypto-utils JS Doc +## API reference documentation {{>main}} diff --git a/src/js/index.js b/src/js/index.js index 1239c48..dc1def5 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,121 +1,14 @@ -const _ZERO = BigInt(0) -const _ONE = BigInt(1) -const _TWO = BigInt(2) - -/** - * 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) { - a = BigInt(a) - return (a >= _ZERO) ? a : -a -} - -/** - * Returns the bitlength of a number - * - * @param {number|bigint} a - * @returns {number} - the bit length - */ -export function bitLength (a) { - a = BigInt(a) - if (a === _ONE) { return 1 } - let bits = 1 - do { - bits++ - } while ((a >>= _ONE) > _ONE) - 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). - */ -export function eGcd (a, b) { - a = BigInt(a) - b = BigInt(b) - if (a <= _ZERO | b <= _ZERO) { return NaN } // a and b MUST be positive - - let x = _ZERO - let y = _ONE - let u = _ONE - let v = _ZERO - - while (a !== _ZERO) { - 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 - */ -export function gcd (a, b) { - a = abs(a) - b = abs(b) - if (a === _ZERO) { return b } else if (b === _ZERO) { return a } - - let shift = _ZERO - while (!((a | b) & _ONE)) { - a >>= _ONE - b >>= _ONE - shift++ - } - while (!(a & _ONE)) a >>= _ONE - do { - while (!(b & _ONE)) b >>= _ONE - if (a > b) { - const x = a - a = b - b = x - } - b -= a - } while (b) - - // rescale - return a << shift -} +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' /** * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several * iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1) * - * @param {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 * - * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) + * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) */ export async function isProbablyPrime (w, iterations = 16) { if (typeof w === 'number') { @@ -170,96 +63,6 @@ export async function isProbablyPrime (w, iterations = 16) { /* eslint-enable no-lone-blocks */ } -/** - * 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, b) { - a = BigInt(a) - b = BigInt(b) - if (a === _ZERO && b === _ZERO) { return _ZERO } - 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 - */ -export 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 - */ -export 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 - */ -export function modInv (a, n) { - const egcd = eGcd(toZn(a, n), n) - if (egcd.b !== _ONE) { - 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 - */ -export function modPow (b, e, n) { - n = BigInt(n) - if (n === _ZERO) { return NaN } else if (n === _ONE) { return _ZERO } - - b = toZn(b, n) - - e = BigInt(e) - if (e < _ZERO) { - return modInv(modPow(b, abs(e), n), n) - } - - let r = _ONE - while (e > 0) { - if ((e % _TWO) === _ONE) { - r = (r * b) % n - } - e = e / _TWO - b = b ** _TWO % n - } - return r -} - /** * 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 @@ -270,13 +73,13 @@ export function modPow (b, e, n) { * @param {number} bitLength The required bit length for the generated prime * @param {number} [iterations = 16] The number of iterations for the Miller-Rabin Probabilistic Primality Test * - * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. + * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. */ export function prime (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } if (!process.browser && !_useWorkers) { - let rnd = _ZERO + let rnd = 0n do { rnd = fromBuffer(randBytesSync(bitLength / 8, true)) } while (!_isProbablyPrime(rnd, iterations)) @@ -311,7 +114,7 @@ export function prime (bitLength, iterations = 16) { /* eslint-disable no-lone-blocks */ if (process.browser) { // browser const workerURL = _isProbablyPrimeWorkerUrl() - for (let i = 0; i < self.navigator.hardwareConcurrency; i++) { + for (let i = 0; i < self.navigator.hardwareConcurrency - 1; i++) { const newWorker = new Worker(workerURL) newWorker.onmessage = (event) => _onmessage(event.data, newWorker) workerList.push(newWorker) @@ -319,7 +122,7 @@ export function prime (bitLength, iterations = 16) { } else { // Node.js const { cpus } = require('os') const { Worker } = require('worker_threads') - for (let i = 0; i < cpus().length; i++) { + for (let i = 0; i < cpus().length - 1; i++) { const newWorker = new Worker(__filename) newWorker.on('message', (msg) => _onmessage(msg, newWorker)) workerList.push(newWorker) @@ -349,7 +152,7 @@ export function prime (bitLength, iterations = 16) { */ export function primeSync (bitLength, iterations = 16) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } - let rnd = _ZERO + let rnd = 0n do { rnd = fromBuffer(randBytesSync(bitLength / 8, true)) } while (!_isProbablyPrime(rnd, iterations)) @@ -363,7 +166,7 @@ export function primeSync (bitLength, iterations = 16) { * * @returns {bigint} A cryptographically secure random bigint between [min,max] */ -export function randBetween (max, min = _ONE) { +export function randBetween (max, min = 1n) { if (max <= min) throw new Error('max must be > min') const interval = max - min const bitLen = bitLength(interval) @@ -381,7 +184,7 @@ export function randBetween (max, min = _ONE) { * @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 * - * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits + * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ export function randBits (bitLength, forceLength = false) { if (bitLength < 1) { @@ -408,7 +211,7 @@ export function randBits (bitLength, forceLength = false) { * @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 * - * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes + * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ export function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } @@ -441,7 +244,7 @@ export function randBytes (byteLength, forceLength = false) { * @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 * - * @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) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } @@ -460,25 +263,10 @@ export function randBytesSync (byteLength, forceLength = false) { return buf } -/** - * 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, n) { - n = BigInt(n) - if (n <= 0) { return NaN } - - a = BigInt(a) % n - return (a < 0) ? a + n : a -} - /* HELPER FUNCTIONS */ function fromBuffer (buf) { - let ret = _ZERO + let ret = 0n for (const i of buf.values()) { const bi = BigInt(i) ret = (ret << BigInt(8)) + bi @@ -488,7 +276,7 @@ function fromBuffer (buf) { function _isProbablyPrimeWorkerUrl () { // Let's us first add all the required functions - let workerCode = `'use strict';const _ZERO = BigInt(0);const _ONE = BigInt(1);const _TWO = BigInt(2);const eGcd = ${eGcd.toString()};const modInv = ${modInv.toString()};const modPow = ${modPow.toString()};const toZn = ${toZn.toString()};const randBits = ${randBits.toString()};const randBytesSync = ${randBytesSync.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}` + let workerCode = `'use strict';const ${eGcd.name}=${eGcd.toString()};const ${modInv.name}=${modInv.toString()};const ${modPow.name}=${modPow.toString()};const ${toZn.name}=${toZn.toString()};const ${randBits.name}=${randBits.toString()};const ${randBytesSync.name}=${randBytesSync.toString()};const ${randBetween.name}=${randBetween.toString()};const ${isProbablyPrime.name}=${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}` const onmessage = async function (event) { // Let's start once we are called // event.data = {rnd: , iterations: } @@ -516,269 +304,269 @@ function _isProbablyPrime (w, iterations = 16) { PREFILTERING. Even values but 2 are not primes, so don't test. 1 is not a prime and the M-R algorithm needs w>1. */ - if (w === _TWO) { return true } else if ((w & _ONE) === _ZERO || w === _ONE) { return false } + if (w === 2n) { return true } else if ((w & 1n) === 0n || w === 1n) { return false } /* Test if any of the first 250 small primes are a factor of w. 2 is not tested because it was already tested above. */ const firstPrimes = [ - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - 131, - 137, - 139, - 149, - 151, - 157, - 163, - 167, - 173, - 179, - 181, - 191, - 193, - 197, - 199, - 211, - 223, - 227, - 229, - 233, - 239, - 241, - 251, - 257, - 263, - 269, - 271, - 277, - 281, - 283, - 293, - 307, - 311, - 313, - 317, - 331, - 337, - 347, - 349, - 353, - 359, - 367, - 373, - 379, - 383, - 389, - 397, - 401, - 409, - 419, - 421, - 431, - 433, - 439, - 443, - 449, - 457, - 461, - 463, - 467, - 479, - 487, - 491, - 499, - 503, - 509, - 521, - 523, - 541, - 547, - 557, - 563, - 569, - 571, - 577, - 587, - 593, - 599, - 601, - 607, - 613, - 617, - 619, - 631, - 641, - 643, - 647, - 653, - 659, - 661, - 673, - 677, - 683, - 691, - 701, - 709, - 719, - 727, - 733, - 739, - 743, - 751, - 757, - 761, - 769, - 773, - 787, - 797, - 809, - 811, - 821, - 823, - 827, - 829, - 839, - 853, - 857, - 859, - 863, - 877, - 881, - 883, - 887, - 907, - 911, - 919, - 929, - 937, - 941, - 947, - 953, - 967, - 971, - 977, - 983, - 991, - 997, - 1009, - 1013, - 1019, - 1021, - 1031, - 1033, - 1039, - 1049, - 1051, - 1061, - 1063, - 1069, - 1087, - 1091, - 1093, - 1097, - 1103, - 1109, - 1117, - 1123, - 1129, - 1151, - 1153, - 1163, - 1171, - 1181, - 1187, - 1193, - 1201, - 1213, - 1217, - 1223, - 1229, - 1231, - 1237, - 1249, - 1259, - 1277, - 1279, - 1283, - 1289, - 1291, - 1297, - 1301, - 1303, - 1307, - 1319, - 1321, - 1327, - 1361, - 1367, - 1373, - 1381, - 1399, - 1409, - 1423, - 1427, - 1429, - 1433, - 1439, - 1447, - 1451, - 1453, - 1459, - 1471, - 1481, - 1483, - 1487, - 1489, - 1493, - 1499, - 1511, - 1523, - 1531, - 1543, - 1549, - 1553, - 1559, - 1567, - 1571, - 1579, - 1583, - 1597 + 3n, + 5n, + 7n, + 11n, + 13n, + 17n, + 19n, + 23n, + 29n, + 31n, + 37n, + 41n, + 43n, + 47n, + 53n, + 59n, + 61n, + 67n, + 71n, + 73n, + 79n, + 83n, + 89n, + 97n, + 101n, + 103n, + 107n, + 109n, + 113n, + 127n, + 131n, + 137n, + 139n, + 149n, + 151n, + 157n, + 163n, + 167n, + 173n, + 179n, + 181n, + 191n, + 193n, + 197n, + 199n, + 211n, + 223n, + 227n, + 229n, + 233n, + 239n, + 241n, + 251n, + 257n, + 263n, + 269n, + 271n, + 277n, + 281n, + 283n, + 293n, + 307n, + 311n, + 313n, + 317n, + 331n, + 337n, + 347n, + 349n, + 353n, + 359n, + 367n, + 373n, + 379n, + 383n, + 389n, + 397n, + 401n, + 409n, + 419n, + 421n, + 431n, + 433n, + 439n, + 443n, + 449n, + 457n, + 461n, + 463n, + 467n, + 479n, + 487n, + 491n, + 499n, + 503n, + 509n, + 521n, + 523n, + 541n, + 547n, + 557n, + 563n, + 569n, + 571n, + 577n, + 587n, + 593n, + 599n, + 601n, + 607n, + 613n, + 617n, + 619n, + 631n, + 641n, + 643n, + 647n, + 653n, + 659n, + 661n, + 673n, + 677n, + 683n, + 691n, + 701n, + 709n, + 719n, + 727n, + 733n, + 739n, + 743n, + 751n, + 757n, + 761n, + 769n, + 773n, + 787n, + 797n, + 809n, + 811n, + 821n, + 823n, + 827n, + 829n, + 839n, + 853n, + 857n, + 859n, + 863n, + 877n, + 881n, + 883n, + 887n, + 907n, + 911n, + 919n, + 929n, + 937n, + 941n, + 947n, + 953n, + 967n, + 971n, + 977n, + 983n, + 991n, + 997n, + 1009n, + 1013n, + 1019n, + 1021n, + 1031n, + 1033n, + 1039n, + 1049n, + 1051n, + 1061n, + 1063n, + 1069n, + 1087n, + 1091n, + 1093n, + 1097n, + 1103n, + 1109n, + 1117n, + 1123n, + 1129n, + 1151n, + 1153n, + 1163n, + 1171n, + 1181n, + 1187n, + 1193n, + 1201n, + 1213n, + 1217n, + 1223n, + 1229n, + 1231n, + 1237n, + 1249n, + 1259n, + 1277n, + 1279n, + 1283n, + 1289n, + 1291n, + 1297n, + 1301n, + 1303n, + 1307n, + 1319n, + 1321n, + 1327n, + 1361n, + 1367n, + 1373n, + 1381n, + 1399n, + 1409n, + 1423n, + 1427n, + 1429n, + 1433n, + 1439n, + 1447n, + 1451n, + 1453n, + 1459n, + 1471n, + 1481n, + 1483n, + 1487n, + 1489n, + 1493n, + 1499n, + 1511n, + 1523n, + 1531n, + 1543n, + 1549n, + 1553n, + 1559n, + 1567n, + 1571n, + 1579n, + 1583n, + 1597n ] - let p = _ZERO - for (let i = 0; i < firstPrimes.length && (p <= w); i++) { - p = BigInt(firstPrimes[i]) + + for (let i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) { + const p = firstPrimes[i] if (w === p) { return true - } else if (w % p === _ZERO) { + } else if (w % p === 0n) { return false } } @@ -802,29 +590,47 @@ function _isProbablyPrime (w, iterations = 16) { Comment: Increment i for the do-loop in step 4. 5. Return PROBABLY PRIME. */ - let a = _ZERO; let d = w - _ONE - while (d % _TWO === _ZERO) { - d /= _TWO + let a = 0n + const d = w - 1n + let aux = d + while (aux % 2n === 0n) { + aux /= 2n ++a } - const m = (w - _ONE) / (_TWO ** a) + const m = d / (2n ** a) - /* eslint-disable no-labels */ - loop: do { - const b = randBetween(w - _ONE, _TWO) + // /* eslint-disable no-labels */ + // loop: do { + // const b = randBetween(w - 1n, 2n) + // let z = modPow(b, m, w) + // if (z === 1n || z === w - 1n) { continue } + // for (let j = 1; j < a; j++) { + // z = modPow(z, 2n, w) + // if (z === w - 1n) { continue loop } + // if (z === 1n) { break } + // } + // return false + // } while (--iterations) + // /* eslint-enable no-labels */ + + // return true + + do { + const b = randBetween(d, 2n) let z = modPow(b, m, w) - if (z === _ONE || z === w - _ONE) { continue } - - for (let j = 1; j < a; j++) { - z = modPow(z, _TWO, w) - if (z === w - _ONE) { continue loop } - if (z === _ONE) { break } + if (z === 1n || z === d) { continue } + let j = 1 + while (j < a) { + z = modPow(z, 2n, w) + if (z === d) { break } + if (z === 1n) { return false } + j++ + } + if (z !== d) { + return false } - return false } while (--iterations) - /* eslint-enable no-labels */ - return true } diff --git a/types/index.d.ts b/types/index.d.ts index 5c1374d..27db427 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,106 +1,13 @@ -/** - * 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 * 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 * - * @return {Promise} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite) + * @return {Promise} 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; -/** - * 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; +export function isProbablyPrime(w: number | bigint, iterations?: number): Promise; /** * 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 @@ -111,9 +18,9 @@ export function modPow(b: number | bigint, e: number | bigint, n: number | bigin * @param {number} bitLength The required bit length for the generated prime * @param {number} [iterations = 16] The number of iterations for the Miller-Rabin Probabilistic Primality Test * - * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. + * @returns {Promise} A promise that resolves to a bigint probable prime of bitLength bits. */ -export function prime(bitLength: number, iterations?: number): Promise; +export function prime(bitLength: number, iterations?: number): Promise; /** * A probably-prime (Miller-Rabin), cryptographically-secure, random-number generator. * The sync version is NOT RECOMMENDED since it won't use workers and thus it'll be slower and may freeze thw window in browser's javascript. Please consider using prime() instead. @@ -138,7 +45,7 @@ export function randBetween(max: bigint, min?: bigint): bigint; * @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 * - * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits + * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ export function randBits(bitLength: number, forceLength?: boolean): Uint8Array | Buffer; /** @@ -147,23 +54,16 @@ export function randBits(bitLength: number, forceLength?: boolean): Uint8Array | * @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 * - * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes + * @returns {Promise} A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ -export function randBytes(byteLength: number, forceLength?: boolean): Promise; +export function randBytes(byteLength: number, forceLength?: boolean): Promise; /** * Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * * @param {number} byteLength The desired number of random bytes * @param {boolean} [forceLength = false] If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * - * @returns {Buffer|Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes + * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes */ export function randBytesSync(byteLength: number, forceLength?: boolean): Uint8Array | Buffer; -/** - * 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; +export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from "bigint-mod-arith";