From c52d54f66bf3d479ee7224c7d19e02907b1da3ff Mon Sep 17 00:00:00 2001 From: juanelas Date: Tue, 7 Apr 2020 19:29:23 +0200 Subject: [PATCH] randBitsSync --- README.md | 110 +++++ build/build.docs.js | 14 +- build/rollup.config.js | 18 +- lib/index.browser.bundle.js | 2 +- lib/index.browser.bundle.mod.js | 821 +++++++++++++++++++++++++++++++- lib/index.browser.mod.js | 56 ++- lib/index.node.js | 55 ++- package.json | 2 +- src/js/index.js | 67 ++- types/index.d.ts | 11 +- 10 files changed, 1097 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index a577c84..1f25376 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,116 @@ Finds the smallest positive element that is congruent to a in modulo n | a | number \| bigint | An integer | | n | number \| bigint | The modulo | + + +### isProbablyPrime(w, [iterations]) ⇒ Promise.<boolean> +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.<boolean> - 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 | + + + +### prime(bitLength, [iterations]) ⇒ Promise.<bigint> +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.<bigint> - 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]) ⇒ Promise.<(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**: Promise.<(Buffer\|Uint8Array)> - A Promise that resolves to 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 | + + + +### randBitsSync(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.<(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**: Promise.<(Buffer\|Uint8Array)> - 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 | + ### egcdReturn : Object diff --git a/build/build.docs.js b/build/build.docs.js index 4db18de..c71462b 100644 --- a/build/build.docs.js +++ b/build/build.docs.js @@ -8,17 +8,19 @@ 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 input = path.join(rootDir, pkgJson.directories.lib, 'index.browser.bundle.mod.js') +const source = fs.readFileSync(input, { encoding: 'UTF-8' }).replace(/([0-9]+)n([,\s\n)])/g, '$1$2') 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 + source, // 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) +jsdoc2md.clear().then(() => { + const readmeContents = jsdoc2md.renderSync(options) -const readmeFile = path.join(rootDir, 'README.md') - -fs.writeFileSync(readmeFile, readmeContents) + const readmeFile = path.join(rootDir, 'README.md') + fs.writeFileSync(readmeFile, readmeContents) +}) diff --git a/build/rollup.config.js b/build/rollup.config.js index 3467cf5..ae7cd52 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -29,7 +29,7 @@ module.exports = [ input: input, output: [ { - file: path.join(dstDir, 'index.browser.mod.js'), + file: path.join(rootDir, pkgJson.browser), format: 'esm' } ], @@ -46,7 +46,17 @@ module.exports = [ { file: path.join(dstDir, 'index.browser.bundle.js'), format: 'iife', - name: pkgCamelisedName + name: pkgCamelisedName, + plugins: [ + terser() + ] + }, + { + file: path.join(dstDir, 'index.browser.bundle.min.mod.js'), + format: 'es', + plugins: [ + terser() + ] }, { file: path.join(dstDir, 'index.browser.bundle.mod.js'), @@ -59,10 +69,6 @@ module.exports = [ }), resolve({ browser: true - }), - terser({ - // mangle: false, - // compress: false }) ] }, diff --git a/lib/index.browser.bundle.js b/lib/index.browser.bundle.js index 47b43ac..d68a3d1 100644 --- a/lib/index.browser.bundle.js +++ b/lib/index.browser.bundle.js @@ -1 +1 @@ -var bigintCryptoUtils=function(n){"use strict";function t(n){return(n=BigInt(n))>=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}({}); +var bigintCryptoUtils=function(n){"use strict";function t(n){return(n=BigInt(n))>=0n?n:-n}function r(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 r=0n,e=1n,i=1n,o=0n;for(;0n!==n;){const s=t/n,a=t%n,c=r-i*s,u=e-o*s;t=n,n=a,r=i,e=o,i=c,o=u}return{b:t,x:r,y:e}}function i(n,r){if(n=t(n),r=t(r),0n===n)return r;if(0n===r)return n;let e=0n;for(;!(1n&(n|r));)n>>=1n,r>>=1n,e++;for(;!(1n&n);)n>>=1n;do{for(;!(1n&r);)r>>=1n;if(n>r){const t=n;n=r,r=t}r-=n}while(r);return n<0;)r%2n===1n&&(i=i*n%e),r/=2n,n=n**2n%e;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((r,e)=>{const i=new Worker(m());i.onmessage=n=>{i.terminate(),r(n.data.isPrime)},i.onmessageerror=n=>{e(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 e=n-t,i=r(e);let o;do{o=l(f(i))}while(o>e);return o+t}function f(n,t=!1){if(n<1)throw new RangeError(`bitLength MUST be > 0 and it is ${n}`);const r=d(Math.ceil(n/8),!1),e=n%8;if(e&&(r[0]=r[0]&2**e-1),t){const n=e?2**(e-1):128;r[0]=r[0]|n}return r}function g(n,t=!1){if(n<1)throw new RangeError(`byteLength MUST be > 0 and it is ${n}`);return new Promise((function(r){const e=new Uint8Array(n);self.crypto.getRandomValues(e),t&&(e[0]=128|e[0]),r(e)}))}function d(n,t=!1){if(n<1)throw new RangeError(`byteLength MUST be > 0 and it is ${n}`);{const r=new Uint8Array(n);return self.crypto.getRandomValues(r),t&&(r[0]=128|r[0]),r}}function l(n){let t=0n;for(const r of n.values()){const n=BigInt(r);t=(t< {${n}})()`;const t=new Blob([n],{type:"text/javascript"});return window.URL.createObjectURL(t)}(n)}function w(n,t=16){if(2n===n)return!0;if(0n===(1n&n)||1n===n)return!1;const r=[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(r=>{const e=[],i=(i,o)=>{if(i.isPrime){for(let n=0;ni(n.data,t),e.push(t)}}for(let r=0;r 0 and it is ${n}`);let r=0n;do{r=l(d(n/8,!0))}while(!w(r,t));return r},n.randBetween=u,n.randBits=async function(n,t=!1){if(n<1)throw new RangeError(`bitLength MUST be > 0 and it is ${n}`);const r=Math.ceil(n/8),e=n%8,i=await g(r,!1);if(e&&(i[0]=i[0]&2**e-1),t){const n=e?2**(e-1):128;i[0]=i[0]|n}return i},n.randBitsSync=f,n.randBytes=g,n.randBytesSync=d,n.toZn=a,n}({}); diff --git a/lib/index.browser.bundle.mod.js b/lib/index.browser.bundle.mod.js index 175b58e..ebac7ac 100644 --- a/lib/index.browser.bundle.mod.js +++ b/lib/index.browser.bundle.mod.js @@ -1 +1,820 @@ -function n(n){return(n=BigInt(n))>=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 >= 0n) ? a : -a +} + +/** + * Returns the bitlength of a number + * + * @param {number|bigint} a + * @returns {number} - the bit length + */ +function bitLength (a) { + a = BigInt(a) + if (a === 1n) { return 1 } + let bits = 1 + do { + bits++ + } while ((a >>= 1n) > 1n) + return bits +} + +/** + * @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b). + * @property {bigint} g + * @property {bigint} x + * @property {bigint} y + */ +/** + * An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm. + * Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b). + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b). + */ +function eGcd (a, b) { + a = BigInt(a) + b = BigInt(b) + if (a <= 0n | b <= 0n) { return NaN } // a and b MUST be positive + + let x = 0n + let y = 1n + let u = 1n + let v = 0n + + while (a !== 0n) { + const q = b / a + const r = b % a + const m = x - (u * q) + const n = y - (v * q) + b = a + a = r + x = u + y = v + u = m + v = n + } + return { + b: b, + x: x, + y: y + } +} + +/** + * Greatest-common divisor of two integers based on the iterative binary algorithm. + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The greatest common divisor of a and b + */ +function gcd (a, b) { + a = abs(a) + b = abs(b) + if (a === 0n) { return b } else if (b === 0n) { return a } + + let shift = 0n + while (!((a | b) & 1n)) { + a >>= 1n + b >>= 1n + shift++ + } + while (!(a & 1n)) a >>= 1n + do { + while (!(b & 1n)) b >>= 1n + if (a > b) { + const x = a + a = b + b = x + } + b -= a + } while (b) + + // rescale + return a << shift +} + +/** + * The least common multiple computed as abs(a*b)/gcd(a,b) + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The least common multiple of a and b + */ +function lcm (a, b) { + a = BigInt(a) + b = BigInt(b) + if (a === 0n && b === 0n) { return 0n } + return abs(a * b) / gcd(a, b) +} + +/** + * Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} maximum of numbers a and b + */ +function max (a, b) { + a = BigInt(a) + b = BigInt(b) + return (a >= b) ? a : b +} + +/** + * Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} minimum of numbers a and b + */ +function min (a, b) { + a = BigInt(a) + b = BigInt(b) + return (a >= b) ? b : a +} + +/** + * Modular inverse. + * + * @param {number|bigint} a The number to find an inverse for + * @param {number|bigint} n The modulo + * + * @returns {bigint} the inverse modulo n or NaN if it does not exist + */ +function modInv (a, n) { + const egcd = eGcd(toZn(a, n), n) + if (egcd.b !== 1n) { + return NaN // modular inverse does not exist + } else { + return toZn(egcd.x, n) + } +} + +/** + * Modular exponentiation b**e mod n. Currently using the right-to-left binary method + * + * @param {number|bigint} b base + * @param {number|bigint} e exponent + * @param {number|bigint} n modulo + * + * @returns {bigint} b**e mod n + */ +function modPow (b, e, n) { + n = BigInt(n) + if (n === 0n) { return NaN } else if (n === 1n) { return 0n } + + b = toZn(b, n) + + e = BigInt(e) + if (e < 0n) { + return modInv(modPow(b, abs(e), n), n) + } + + let r = 1n + while (e > 0) { + if ((e % 2n) === 1n) { + r = (r * b) % n + } + e = e / 2n + b = b ** 2n % n + } + return r +} + +/** + * Finds the smallest positive element that is congruent to a in modulo n + * @param {number|bigint} a An integer + * @param {number|bigint} n The modulo + * + * @returns {bigint} The smallest positive representation of a in modulo n + */ +function toZn (a, n) { + n = BigInt(n) + if (n <= 0) { return NaN } + + a = BigInt(a) % n + return (a < 0) ? a + n : a +} + +/** + * The test first tries if any of the first 250 small primes are a factor of the input number and then passes several + * 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} [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) + */ +async function isProbablyPrime (w, iterations = 16) { + if (typeof w === 'number') { + w = BigInt(w) + } + /* eslint-disable no-lone-blocks */ + { // browser + return new Promise((resolve, reject) => { + 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 + }) + }) + } + /* eslint-enable no-lone-blocks */ +} + +/** + * 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). + * + * @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. + */ +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) { + // if a prime number has been found, stop all the workers, and return it + for (let j = 0; j < workerList.length; j++) { + workerList[j].terminate() + } + while (workerList.length) { + workerList.pop() + } + resolve(msg.value) + } else { // if a composite is found, make the worker test another random number + const buf = randBitsSync(bitLength, true) + const rnd = fromBuffer(buf) + try { + newWorker.postMessage({ + rnd: rnd, + iterations: iterations, + id: msg.id + }) + } catch (error) { + // The worker has already terminated. There is nothing to handle here + } + } + } + /* eslint-disable no-lone-blocks */ + { // browser + const workerURL = _isProbablyPrimeWorkerUrl() + 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) + } + } + /* eslint-enable no-lone-blocks */ + for (let i = 0; i < workerList.length; i++) { + const buf = randBitsSync(bitLength, true) + const rnd = fromBuffer(buf) + workerList[i].postMessage({ + rnd: rnd, + iterations: iterations, + id: i + }) + } + }) +} + +/** + * 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. + * + * @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 {bigint} A bigint probable prime of bitLength bits. + */ +function primeSync (bitLength, iterations = 16) { + if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } + let rnd = 0n + do { + rnd = fromBuffer(randBytesSync(bitLength / 8, true)) + } while (!_isProbablyPrime(rnd, iterations)) + return rnd +} + +/** + * Returns a cryptographically secure random integer between [min,max] + * @param {bigint} max Returned value will be <= max + * @param {bigint} [min = BigInt(1)] Returned value will be >= min + * + * @returns {bigint} A cryptographically secure random bigint between [min,max] + */ +function randBetween (max, min = 1n) { + if (max <= min) throw new Error('max must be > min') + const interval = max - min + const bitLen = bitLength(interval) + let rnd + do { + const buf = randBitsSync(bitLen) + rnd = fromBuffer(buf) + } while (rnd > interval) + return rnd + min +} + +/** + * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() + * + * @param {number} bitLength The desired number of random bits + * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. 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 bits + */ +async 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 bitLengthMod8 = bitLength % 8 + + const rndBytes = await randBytes(byteLength, false) + if (bitLengthMod8) { + // Fill with 0's the extra bits + rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1) + } + if (forceLength) { + const mask = bitLengthMod8 ? 2 ** (bitLengthMod8 - 1) : 128 + rndBytes[0] = rndBytes[0] | mask + } + return rndBytes +} + +/** + * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() + * + * @param {number} bitLength The desired number of random bits + * @param {boolean} [forceLength = 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 + */ +function randBitsSync (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) { + // Fill with 0's the extra bits + rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1) + } + if (forceLength) { + const mask = bitLengthMod8 ? 2 ** (bitLengthMod8 - 1) : 128 + rndBytes[0] = rndBytes[0] | mask + } + return rndBytes +} + +/** + * 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 {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}`) } + + /* eslint-disable no-lone-blocks */ + { // browser + return new Promise(function (resolve) { + const buf = new Uint8Array(byteLength) + self.crypto.getRandomValues(buf) + // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength + if (forceLength) { buf[0] = buf[0] | 128 } + resolve(buf) + }) + } + /* eslint-enable no-lone-blocks */ +} + +/** + * 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 + */ +function randBytesSync (byteLength, forceLength = false) { + if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } + + /* eslint-disable no-lone-blocks */ + { // browser + const buf = new Uint8Array(byteLength) + self.crypto.getRandomValues(buf) + // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength + if (forceLength) { buf[0] = buf[0] | 128 } + return buf + } + /* eslint-enable no-lone-blocks */ +} + +/* HELPER FUNCTIONS */ + +function fromBuffer (buf) { + let ret = 0n + for (const i of buf.values()) { + const bi = BigInt(i) + ret = (ret << BigInt(8)) + bi + } + return ret +} + +function _isProbablyPrimeWorkerUrl () { + // Let's us first add all the required functions + 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 ${randBitsSync.name}=${randBitsSync.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: } + const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations) + postMessage({ + isPrime: isPrime, + value: event.data.rnd, + id: event.data.id + }) + } + + workerCode += `onmessage = ${onmessage.toString()};` + + return _workerUrl(workerCode) +} + +function _workerUrl (workerCode) { + workerCode = `(() => {${workerCode}})()` // encapsulate IIFE + const _blob = new Blob([workerCode], { type: 'text/javascript' }) + return window.URL.createObjectURL(_blob) +} + +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 === 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 = [ + 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 i = 0; i < firstPrimes.length && (firstPrimes[i] <= w); i++) { + const p = firstPrimes[i] + if (w === p) { + return true + } else if (w % p === 0n) { + return false + } + } + + /* + 1. Let a be the largest integer such that 2**a divides w−1. + 2. m = (w−1) / 2**a. + 3. wlen = len (w). + 4. For i = 1 to iterations do + 4.1 Obtain a string b of wlen bits from an RBG. + Comment: Ensure that 1 < b < w−1. + 4.2 If ((b ≤ 1) or (b ≥ w−1)), then go to step 4.1. + 4.3 z = b**m mod w. + 4.4 If ((z = 1) or (z = w − 1)), then go to step 4.7. + 4.5 For j = 1 to a − 1 do. + 4.5.1 z = z**2 mod w. + 4.5.2 If (z = w−1), then go to step 4.7. + 4.5.3 If (z = 1), then go to step 4.6. + 4.6 Return COMPOSITE. + 4.7 Continue. + Comment: Increment i for the do-loop in step 4. + 5. Return PROBABLY PRIME. + */ + let a = 0n + const d = w - 1n + let aux = d + while (aux % 2n === 0n) { + aux /= 2n + ++a + } + + const m = d / (2n ** a) + + // /* 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 === 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 + } + } while (--iterations) + return true +} + +export { abs, bitLength, eGcd, gcd, isProbablyPrime, lcm, max, min, modInv, modPow, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync, toZn } diff --git a/lib/index.browser.mod.js b/lib/index.browser.mod.js index d27bbf3..9fabc13 100644 --- a/lib/index.browser.mod.js +++ b/lib/index.browser.mod.js @@ -65,7 +65,7 @@ function prime (bitLength, iterations = 16) { } resolve(msg.value) } else { // if a composite is found, make the worker test another random number - const buf = randBits(bitLength, true) + const buf = randBitsSync(bitLength, true) const rnd = fromBuffer(buf) try { newWorker.postMessage({ @@ -89,7 +89,7 @@ function prime (bitLength, iterations = 16) { } /* eslint-enable no-lone-blocks */ for (let i = 0; i < workerList.length; i++) { - const buf = randBits(bitLength, true) + const buf = randBitsSync(bitLength, true) const rnd = fromBuffer(buf) workerList[i].postMessage({ rnd: rnd, @@ -131,12 +131,40 @@ function randBetween (max, min = 1n) { const bitLen = bitLength(interval) let rnd do { - const buf = randBits(bitLen) + const buf = randBitsSync(bitLen) rnd = fromBuffer(buf) } while (rnd > interval) return rnd + min } +/** + * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() + * + * @param {number} bitLength The desired number of random bits + * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. 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 bits + */ +async 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 bitLengthMod8 = bitLength % 8 + + const rndBytes = await randBytes(byteLength, false) + if (bitLengthMod8) { + // Fill with 0's the extra bits + rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1) + } + if (forceLength) { + const mask = bitLengthMod8 ? 2 ** (bitLengthMod8 - 1) : 128 + rndBytes[0] = rndBytes[0] | mask + } + return rndBytes +} + /** * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * @@ -145,7 +173,7 @@ function randBetween (max, min = 1n) { * * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ -function randBits (bitLength, forceLength = false) { +function randBitsSync (bitLength, forceLength = false) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } @@ -175,18 +203,17 @@ function randBits (bitLength, forceLength = false) { function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } - let buf /* eslint-disable no-lone-blocks */ { // browser return new Promise(function (resolve) { - buf = new Uint8Array(byteLength) + const buf = new Uint8Array(byteLength) self.crypto.getRandomValues(buf) // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength) { buf[0] = buf[0] | 128 } resolve(buf) }) } - /* eslint-disable no-lone-blocks */ + /* eslint-enable no-lone-blocks */ } /** @@ -200,14 +227,15 @@ function randBytes (byteLength, forceLength = false) { function randBytesSync (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } - let buf + /* eslint-disable no-lone-blocks */ { // browser - buf = new Uint8Array(byteLength) + const buf = new Uint8Array(byteLength) self.crypto.getRandomValues(buf) + // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength + if (forceLength) { buf[0] = buf[0] | 128 } + return buf } - // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength - if (forceLength) { buf[0] = buf[0] | 128 } - return buf + /* eslint-enable no-lone-blocks */ } /* HELPER FUNCTIONS */ @@ -223,7 +251,7 @@ function fromBuffer (buf) { function _isProbablyPrimeWorkerUrl () { // Let's us first add all the required functions - 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()}` + 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 ${randBitsSync.name}=${randBitsSync.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: } @@ -581,4 +609,4 @@ function _isProbablyPrime (w, iterations = 16) { return true } -export { isProbablyPrime, prime, primeSync, randBetween, randBits, randBytes, randBytesSync } +export { isProbablyPrime, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync } diff --git a/lib/index.node.js b/lib/index.node.js index 41ceeb6..b622616 100644 --- a/lib/index.node.js +++ b/lib/index.node.js @@ -82,7 +82,7 @@ function prime (bitLength, iterations = 16) { } resolve(msg.value) } else { // if a composite is found, make the worker test another random number - const buf = randBits(bitLength, true) + const buf = randBitsSync(bitLength, true) const rnd = fromBuffer(buf) try { newWorker.postMessage({ @@ -107,7 +107,7 @@ function prime (bitLength, iterations = 16) { } /* eslint-enable no-lone-blocks */ for (let i = 0; i < workerList.length; i++) { - const buf = randBits(bitLength, true) + const buf = randBitsSync(bitLength, true) const rnd = fromBuffer(buf) workerList[i].postMessage({ rnd: rnd, @@ -149,12 +149,40 @@ function randBetween (max, min = 1n) { const bitLen = bigintModArith.bitLength(interval) let rnd do { - const buf = randBits(bitLen) + const buf = randBitsSync(bitLen) rnd = fromBuffer(buf) } while (rnd > interval) return rnd + min } +/** + * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() + * + * @param {number} bitLength The desired number of random bits + * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. 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 bits + */ +async 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 bitLengthMod8 = bitLength % 8 + + const rndBytes = await randBytes(byteLength, false) + if (bitLengthMod8) { + // Fill with 0's the extra bits + rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1) + } + if (forceLength) { + const mask = bitLengthMod8 ? 2 ** (bitLengthMod8 - 1) : 128 + rndBytes[0] = rndBytes[0] | mask + } + return rndBytes +} + /** * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * @@ -163,7 +191,7 @@ function randBetween (max, min = 1n) { * * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ -function randBits (bitLength, forceLength = false) { +function randBitsSync (bitLength, forceLength = false) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } @@ -193,18 +221,17 @@ function randBits (bitLength, forceLength = false) { function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } - let buf /* eslint-disable no-lone-blocks */ { // node const crypto = require('crypto') - buf = Buffer.alloc(byteLength) + const buf = Buffer.alloc(byteLength) return crypto.randomFill(buf, function (resolve) { // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength) { buf[0] = buf[0] | 128 } resolve(buf) }) } - /* eslint-disable no-lone-blocks */ + /* eslint-enable no-lone-blocks */ } /** @@ -218,15 +245,16 @@ function randBytes (byteLength, forceLength = false) { function randBytesSync (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } - let buf + /* eslint-disable no-lone-blocks */ { // node const crypto = require('crypto') - buf = Buffer.alloc(byteLength) + const buf = Buffer.alloc(byteLength) crypto.randomFillSync(buf) + // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength + if (forceLength) { buf[0] = buf[0] | 128 } + return buf } - // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength - if (forceLength) { buf[0] = buf[0] | 128 } - return buf + /* eslint-enable no-lone-blocks */ } /* HELPER FUNCTIONS */ @@ -576,6 +604,7 @@ function _isProbablyPrime (w, iterations = 16) { } let _useWorkers = true // The following is just to check whether Node.js can use workers +/* eslint-disable no-lone-blocks */ { // Node.js _useWorkers = (function _workers () { try { @@ -590,6 +619,7 @@ This node version doesn't support worker_threads. You should enable them in orde } })() } +/* eslint-enable no-lone-blocks */ if (_useWorkers) { // node.js with support for workers const { parentPort, isMainThread } = require('worker_threads') @@ -671,5 +701,6 @@ exports.prime = prime exports.primeSync = primeSync exports.randBetween = randBetween exports.randBits = randBits +exports.randBitsSync = randBitsSync exports.randBytes = randBytes exports.randBytesSync = randBytesSync diff --git a/package.json b/package.json index 34eb736..e95ef76 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "ignore": [ "/test/browser/", "/lib/index.browser.bundle.js", - "/lib/index.browser.bundle.mod.js" + "/lib/index.browser.bundle.min.mod.js" ] }, "devDependencies": { diff --git a/src/js/index.js b/src/js/index.js index dc1def5..f39b7ab 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -8,9 +8,9 @@ export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn } from ' * @param {number | bigint} w An integer to be tested for primality * @param {number} [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) + * @returns {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) { +export function isProbablyPrime (w, iterations = 16) { if (typeof w === 'number') { w = BigInt(w) } @@ -98,7 +98,7 @@ export function prime (bitLength, iterations = 16) { } resolve(msg.value) } else { // if a composite is found, make the worker test another random number - const buf = randBits(bitLength, true) + const buf = randBitsSync(bitLength, true) const rnd = fromBuffer(buf) try { newWorker.postMessage({ @@ -130,7 +130,7 @@ export function prime (bitLength, iterations = 16) { } /* eslint-enable no-lone-blocks */ for (let i = 0; i < workerList.length; i++) { - const buf = randBits(bitLength, true) + const buf = randBitsSync(bitLength, true) const rnd = fromBuffer(buf) workerList[i].postMessage({ rnd: rnd, @@ -172,12 +172,40 @@ export function randBetween (max, min = 1n) { const bitLen = bitLength(interval) let rnd do { - const buf = randBits(bitLen) + const buf = randBitsSync(bitLen) rnd = fromBuffer(buf) } while (rnd > interval) return rnd + min } +/** + * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() + * + * @param {number} bitLength The desired number of random bits + * @param {boolean} [forceLength = false] If we want to force the output to have a specific bit length. 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 bits + */ +export async 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 bitLengthMod8 = bitLength % 8 + + const rndBytes = await randBytes(byteLength, false) + if (bitLengthMod8) { + // Fill with 0's the extra bits + rndBytes[0] = rndBytes[0] & (2 ** bitLengthMod8 - 1) + } + if (forceLength) { + const mask = bitLengthMod8 ? 2 ** (bitLengthMod8 - 1) : 128 + rndBytes[0] = rndBytes[0] | mask + } + return rndBytes +} + /** * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * @@ -186,7 +214,7 @@ export function randBetween (max, min = 1n) { * * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ -export function randBits (bitLength, forceLength = false) { +export function randBitsSync (bitLength, forceLength = false) { if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) } @@ -216,11 +244,10 @@ export function randBits (bitLength, forceLength = false) { export function randBytes (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } - let buf /* eslint-disable no-lone-blocks */ if (!process.browser) { // node const crypto = require('crypto') - buf = Buffer.alloc(byteLength) + const buf = Buffer.alloc(byteLength) return crypto.randomFill(buf, function (resolve) { // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength) { buf[0] = buf[0] | 128 } @@ -228,14 +255,14 @@ export function randBytes (byteLength, forceLength = false) { }) } else { // browser return new Promise(function (resolve) { - buf = new Uint8Array(byteLength) + const buf = new Uint8Array(byteLength) self.crypto.getRandomValues(buf) // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength) { buf[0] = buf[0] | 128 } resolve(buf) }) } - /* eslint-disable no-lone-blocks */ + /* eslint-enable no-lone-blocks */ } /** @@ -249,18 +276,22 @@ export function randBytes (byteLength, forceLength = false) { export function randBytesSync (byteLength, forceLength = false) { if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) } - let buf + /* eslint-disable no-lone-blocks */ if (!process.browser) { // node const crypto = require('crypto') - buf = Buffer.alloc(byteLength) + const buf = Buffer.alloc(byteLength) crypto.randomFillSync(buf) + // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength + if (forceLength) { buf[0] = buf[0] | 128 } + return buf } else { // browser - buf = new Uint8Array(byteLength) + const buf = new Uint8Array(byteLength) self.crypto.getRandomValues(buf) + // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength + if (forceLength) { buf[0] = buf[0] | 128 } + return buf } - // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength - if (forceLength) { buf[0] = buf[0] | 128 } - return buf + /* eslint-enable no-lone-blocks */ } /* HELPER FUNCTIONS */ @@ -276,7 +307,7 @@ function fromBuffer (buf) { function _isProbablyPrimeWorkerUrl () { // Let's us first add all the required functions - 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()}` + 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 ${randBitsSync.name}=${randBitsSync.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: } @@ -635,6 +666,7 @@ function _isProbablyPrime (w, iterations = 16) { } let _useWorkers = true // The following is just to check whether Node.js can use workers +/* eslint-disable no-lone-blocks */ if (!process.browser) { // Node.js _useWorkers = (function _workers () { try { @@ -649,6 +681,7 @@ This node version doesn't support worker_threads. You should enable them in orde } })() } +/* eslint-enable no-lone-blocks */ if (!process.browser && _useWorkers) { // node.js with support for workers const { parentPort, isMainThread } = require('worker_threads') diff --git a/types/index.d.ts b/types/index.d.ts index 27db427..eb9fc62 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -39,6 +39,15 @@ export function primeSync(bitLength: number, iterations?: number): bigint; * @returns {bigint} A cryptographically secure random bigint between [min,max] */ export function randBetween(max: bigint, min?: bigint): bigint; +/** + * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() + * + * @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 {Promise} A Promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits + */ +export function randBits(bitLength: number, forceLength?: boolean): Promise; /** * Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() * @@ -47,7 +56,7 @@ export function randBetween(max: bigint, min?: bigint): bigint; * * @returns {Buffer | Uint8Array} A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits */ -export function randBits(bitLength: number, forceLength?: boolean): Uint8Array | Buffer; +export function randBitsSync(bitLength: number, forceLength?: boolean): Uint8Array | Buffer; /** * Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() *