If it is not truly async then is sync

This commit is contained in:
juanelas 2019-04-26 15:03:53 +02:00
parent 292c78dc5a
commit 3f571c9dd6
7 changed files with 210 additions and 225 deletions

View File

@ -114,13 +114,13 @@ 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 The node version can also use worker_threads if they are available (enabled by default with Node 11 and
and can be enabled at runtime executing node --experimental-worker with node &gt;=10.5.0).</p> and can be enabled at runtime executing node --experimental-worker with node &gt;=10.5.0).</p>
</dd> </dd>
<dt><a href="#randBetween">randBetween(max, min)</a><code>Promise</code></dt> <dt><a href="#randBetween">randBetween(max, min)</a><code>bigint</code></dt>
<dd><p>Returns a cryptographically secure random integer between [min,max]</p> <dd><p>Returns a cryptographically secure random integer between [min,max]</p>
</dd> </dd>
<dt><a href="#randBits">randBits(bitLength, forceLength)</a><code>Promise</code></dt> <dt><a href="#randBits">randBits(bitLength, forceLength)</a><code>Buffer</code> | <code>Uint8Array</code></dt>
<dd><p>Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</p> <dd><p>Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</p>
</dd> </dd>
<dt><a href="#randBytes">randBytes(byteLength, forceLength)</a><code>Promise</code></dt> <dt><a href="#randBytes">randBytes(byteLength, forceLength)</a><code>Buffer</code> | <code>Uint8Array</code></dt>
<dd><p>Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</p> <dd><p>Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()</p>
</dd> </dd>
<dt><a href="#toZn">toZn(a, n)</a><code>bigint</code></dt> <dt><a href="#toZn">toZn(a, n)</a><code>bigint</code></dt>
@ -247,11 +247,11 @@ and can be enabled at runtime executing node --experimental-worker with node >=1
<a name="randBetween"></a> <a name="randBetween"></a>
## randBetween(max, min) ⇒ <code>Promise</code> ## randBetween(max, min) ⇒ <code>bigint</code>
Returns a cryptographically secure random integer between [min,max] Returns a cryptographically secure random integer between [min,max]
**Kind**: global function **Kind**: global function
**Returns**: <code>Promise</code> - A promise that resolves to a cryptographically secure random bigint between [min,max] **Returns**: <code>bigint</code> - A cryptographically secure random bigint between [min,max]
| Param | Type | Description | | Param | Type | Description |
| --- | --- | --- | | --- | --- | --- |
@ -260,11 +260,11 @@ Returns a cryptographically secure random integer between [min,max]
<a name="randBits"></a> <a name="randBits"></a>
## randBits(bitLength, forceLength) ⇒ <code>Promise</code> ## randBits(bitLength, forceLength) ⇒ <code>Buffer</code> \| <code>Uint8Array</code>
Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
**Kind**: global function **Kind**: global function
**Returns**: <code>Promise</code> - A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bits **Returns**: <code>Buffer</code> \| <code>Uint8Array</code> - A Buffer/UInt8Array filled with cryptographically secure random bits
| Param | Type | Description | | Param | Type | Description |
| --- | --- | --- | | --- | --- | --- |
@ -273,11 +273,11 @@ Secure random bits for both node and browsers. Node version uses crypto.randomFi
<a name="randBytes"></a> <a name="randBytes"></a>
## randBytes(byteLength, forceLength) ⇒ <code>Promise</code> ## randBytes(byteLength, forceLength) ⇒ <code>Buffer</code> \| <code>Uint8Array</code>
Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues() Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
**Kind**: global function **Kind**: global function
**Returns**: <code>Promise</code> - A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes **Returns**: <code>Buffer</code> \| <code>Uint8Array</code> - A Buffer/UInt8Array filled with cryptographically secure random bytes
| Param | Type | Description | | Param | Type | Description |
| --- | --- | --- | | --- | --- | --- |

View File

@ -99,7 +99,7 @@ var bigintCryptoUtils = (function (exports) {
async function isProbablyPrime(w, iterations = 16) { async function isProbablyPrime(w, iterations = 16) {
{ // browser { // browser
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let worker = new Worker(_isProbablyPrimeWorkerURL()); let worker = new Worker(_isProbablyPrimeWorkerUrl());
worker.onmessage = (event) => { worker.onmessage = (event) => {
worker.terminate(); worker.terminate();
@ -192,7 +192,7 @@ var bigintCryptoUtils = (function (exports) {
* *
* @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
*/ */
async function prime(bitLength, iterations = 16) { function prime(bitLength, iterations = 16) {
return new Promise((resolve) => { return new Promise((resolve) => {
let workerList = []; let workerList = [];
const _onmessage = (msg, newWorker) => { const _onmessage = (msg, newWorker) => {
@ -206,22 +206,21 @@ var bigintCryptoUtils = (function (exports) {
} }
resolve(msg.value); resolve(msg.value);
} else { // if a composite is found, make the worker test another random number } else { // if a composite is found, make the worker test another random number
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
try { try {
newWorker.postMessage({ newWorker.postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': msg.id 'id': msg.id
}); });
} catch (error) { } catch (error) {
// The worker has already terminated. There is nothing to handle here // The worker has already terminated. There is nothing to handle here
} }
});
} }
}; };
{ //browser { //browser
let workerURL = _isProbablyPrimeWorkerURL(); let workerURL = _isProbablyPrimeWorkerUrl();
for (let i = 0; i < self.navigator.hardwareConcurrency; i++) { for (let i = 0; i < self.navigator.hardwareConcurrency; i++) {
let newWorker = new Worker(workerURL); let newWorker = new Worker(workerURL);
newWorker.onmessage = (event) => _onmessage(event.data, newWorker); newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
@ -229,13 +228,12 @@ var bigintCryptoUtils = (function (exports) {
} }
} }
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
workerList[i].postMessage({ workerList[i].postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': i 'id': i
});
}); });
} }
}); });
@ -246,15 +244,15 @@ var bigintCryptoUtils = (function (exports) {
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} min Returned value will be >= min * @param {bigint} min Returned value will be >= min
* *
* @returns {Promise} A promise that resolves to a cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
async function randBetween(max, min = _ONE) { function randBetween(max, min = _ONE) {
if (max <= min) throw new Error('max must be > min'); if (max <= min) throw new Error('max must be > min');
const interval = max - min; const interval = max - min;
let bitLen = bitLength(interval); let bitLen = bitLength(interval);
let rnd; let rnd;
do { do {
let buf = await randBits(bitLen); let buf = randBits(bitLen);
rnd = fromBuffer(buf); rnd = fromBuffer(buf);
} while (rnd > interval); } while (rnd > interval);
return rnd + min; return rnd + min;
@ -266,11 +264,11 @@ var bigintCryptoUtils = (function (exports) {
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} forceLength If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} forceLength 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 filled with cryptographically secure random bits * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bits
*/ */
async function randBits(bitLength, forceLength = false) { function randBits(bitLength, forceLength = false) {
const byteLength = Math.ceil(bitLength / 8); const byteLength = Math.ceil(bitLength / 8);
let rndBytes = await randBytes(byteLength, false); let rndBytes = randBytes(byteLength, false);
// Fill with 0's the extra birs // Fill with 0's the extra birs
rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1); rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1);
if (forceLength) { if (forceLength) {
@ -286,21 +284,18 @@ var bigintCryptoUtils = (function (exports) {
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
* *
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bytes
*/ */
async function randBytes(byteLength, forceLength = false) { function randBytes(byteLength, forceLength = false) {
return new Promise((resolve) => { let buf;
let buf; { // browser
buf = new Uint8Array(byteLength);
{ // browser self.crypto.getRandomValues(buf);
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 fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength)
if (forceLength) buf[0] = buf[0] | 128;
buf[0] = buf[0] | 128; return buf;
resolve(buf);
}
});
} }
/** /**
@ -337,11 +332,11 @@ var bigintCryptoUtils = (function (exports) {
return bits; return bits;
} }
function _isProbablyPrimeWorkerURL() { function _isProbablyPrimeWorkerUrl() {
// Let's us first add all the required functions // 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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`; 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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`;
const _onmessage = async function (event) { // Let's start once we are called const onmessage = async function (event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>} // event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations); const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations);
postMessage({ postMessage({
@ -350,16 +345,19 @@ var bigintCryptoUtils = (function (exports) {
'id': event.data.id 'id': event.data.id
}); });
}; };
workerCode += `onmessage = ${_onmessage.toString()};`;
workerCode += `onmessage = ${onmessage.toString()};`;
return _workerUrl(workerCode);
}
function _workerUrl(workerCode) {
workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE
var _blob = new Blob([workerCode], { type: 'text/javascript' }); var _blob = new Blob([workerCode], { type: 'text/javascript' });
return window.URL.createObjectURL(_blob); return window.URL.createObjectURL(_blob);
} }
async function _isProbablyPrime(w, iterations = 16) { function _isProbablyPrime(w, iterations = 16) {
/* /*
PREFILTERING. Even values but 2 are not primes, so don't test. 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. 1 is not a prime and the M-R algorithm needs w>1.
@ -660,7 +658,7 @@ var bigintCryptoUtils = (function (exports) {
let m = (w - _ONE) / (_TWO ** a); let m = (w - _ONE) / (_TWO ** a);
loop: do { loop: do {
let b = await randBetween(w - _ONE, _TWO); let b = randBetween(w - _ONE, _TWO);
let z = modPow(b, m, w); let z = modPow(b, m, w);
if (z === _ONE || z === w - _ONE) if (z === _ONE || z === w - _ONE)
continue; continue;

View File

@ -1 +1 @@
var bigintCryptoUtils=function(a){'use strict';function c(b){return b=BigInt(b),b>=q?b:-b}function d(c,d){c=BigInt(c),d=BigInt(d);let e=q,f=r,g=r,h=q;for(;c!==q;){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}}function e(d,e){d=c(d),e=c(e);let f=q;for(;!((d|e)&r);)d>>=r,e>>=r,f++;for(;!(d&r);)d>>=r;do{for(;!(e&r);)e>>=r;if(d>e){let a=d;d=e,e=a}e-=d}while(e);return d<<f}async function f(a,b=16){return new Promise((c,d)=>{let e=new Worker(o());e.onmessage=a=>{e.terminate(),c(a.data.isPrime)},e.onmessageerror=a=>{d(a)},e.postMessage({rnd:a,iterations:b,id:0})})}function g(b,a){let c=d(b,a);return c.b===r?l(c.x,a):null}function h(d,e,f){if(f=BigInt(f),d=l(d,f),e=BigInt(e),e<q)return g(h(d,c(e),f),f);let i=r,j=d;for(;0<e;){var k=e%s;e/=s,k==r&&(i*=j,i%=f),j*=j,j%=f}return i}async function i(a,b=r){if(a<=b)throw new Error("max must be > min");const c=a-b;let d,e=n(c);do{let a=await j(e);d=m(a)}while(d>c);return d+b}async function j(a,b=!1){var c=Math.ceil;const d=c(a/8);let e=await k(d,!1);if(e[0]&=2**(a%8)-1,b){let b=a%8?2**(a%8-1):128;e[0]|=b}return e}async function k(a,b=!1){return new Promise(c=>{let d;d=new Uint8Array(a),self.crypto.getRandomValues(d),b&&(d[0]|=128),c(d)})}function l(b,c){return c=BigInt(c),b=BigInt(b)%c,0>b?b+c:b}function m(a){let b=q;for(let c of a.values()){let a=BigInt(c);b=(b<<BigInt(8))+a}return b}function n(b){let c=1;do c++;while((b>>=r)>r);return c}function o(){let a=`'use strict';const _ZERO = BigInt(0);const _ONE = BigInt(1);const _TWO = BigInt(2);const eGcd = ${d.toString()};const modInv = ${g.toString()};const modPow = ${h.toString()};const toZn = ${l.toString()};const randBits = ${j.toString()};const randBytes = ${k.toString()};const randBetween = ${i.toString()};const isProbablyPrime = ${p.toString()};${n.toString()}${m.toString()}`;a+=`onmessage = ${async function(a){const b=await f(a.data.rnd,a.data.iterations);postMessage({isPrime:b,value:a.data.rnd,id:a.data.id})}.toString()};`,a=`(() => {${a}})()`;var b=new Blob([a],{type:"text/javascript"});return window.URL.createObjectURL(b)}async function p(c,b=16){if(c===s)return!0;if((c&r)===q||c===r)return!1;const e=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597];for(let a=0;a<e.length&&BigInt(e[a])<=c;a++){const b=BigInt(e[a]);if(c===b)return!0;if(c%b===q)return!1}let f=q,g=c-r;for(;g%s===q;)g/=s,++f;let j=(c-r)/s**f;loop:do{let a=await i(c-r,s),b=h(a,j,c);if(b===r||b===c-r)continue;for(let a=1;a<f;a++){if(b=h(b,s,c),b===c-r)continue loop;if(b===r)break}return!1}while(--b);return!0}const q=BigInt(0),r=BigInt(1),s=BigInt(2);return a.abs=c,a.eGcd=d,a.gcd=e,a.isProbablyPrime=f,a.lcm=function(d,f){return d=BigInt(d),f=BigInt(f),c(d*f)/e(d,f)},a.modInv=g,a.modPow=h,a.prime=async function(a,b=16){return new Promise(c=>{let d=[];const e=(e,f)=>{if(e.isPrime){for(let a=0;a<d.length;a++)d[a].terminate();for(;d.length;)d.pop();c(e.value)}else j(a,!0).then(a=>{let c=m(a);try{f.postMessage({rnd:c,iterations:b,id:e.id})}catch(a){}})};{let a=o();for(let b,c=0;c<self.navigator.hardwareConcurrency;c++)b=new Worker(a),b.onmessage=a=>e(a.data,b),d.push(b)}for(let e=0;e<d.length;e++)j(a,!0).then(a=>{let c=m(a);d[e].postMessage({rnd:c,iterations:b,id:e})})})},a.randBetween=i,a.randBits=j,a.randBytes=k,a.toZn=l,a}({}); var bigintCryptoUtils=function(a){'use strict';function c(b){return b=BigInt(b),b>=r?b:-b}function d(c,d){c=BigInt(c),d=BigInt(d);let e=r,f=s,g=s,h=r;for(;c!==r;){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}}function e(d,e){d=c(d),e=c(e);let f=r;for(;!((d|e)&s);)d>>=s,e>>=s,f++;for(;!(d&s);)d>>=s;do{for(;!(e&s);)e>>=s;if(d>e){let a=d;d=e,e=a}e-=d}while(e);return d<<f}async function f(a,b=16){return new Promise((c,d)=>{let e=new Worker(o());e.onmessage=a=>{e.terminate(),c(a.data.isPrime)},e.onmessageerror=a=>{d(a)},e.postMessage({rnd:a,iterations:b,id:0})})}function g(b,a){let c=d(b,a);return c.b===s?l(c.x,a):null}function h(d,e,f){if(f=BigInt(f),d=l(d,f),e=BigInt(e),e<r)return g(h(d,c(e),f),f);let i=s,j=d;for(;0<e;){var k=e%t;e/=t,k==s&&(i*=j,i%=f),j*=j,j%=f}return i}function i(a,b=s){if(a<=b)throw new Error("max must be > min");const c=a-b;let d,e=n(c);do{let a=j(e);d=m(a)}while(d>c);return d+b}function j(a,b=!1){var c=Math.ceil;const d=c(a/8);let e=k(d,!1);if(e[0]&=2**(a%8)-1,b){let b=a%8?2**(a%8-1):128;e[0]|=b}return e}function k(a,b=!1){let c;return c=new Uint8Array(a),self.crypto.getRandomValues(c),b&&(c[0]|=128),c}function l(b,c){return c=BigInt(c),b=BigInt(b)%c,0>b?b+c:b}function m(a){let b=r;for(let c of a.values()){let a=BigInt(c);b=(b<<BigInt(8))+a}return b}function n(b){let c=1;do c++;while((b>>=s)>s);return c}function o(){let a=`'use strict';const _ZERO = BigInt(0);const _ONE = BigInt(1);const _TWO = BigInt(2);const eGcd = ${d.toString()};const modInv = ${g.toString()};const modPow = ${h.toString()};const toZn = ${l.toString()};const randBits = ${j.toString()};const randBytes = ${k.toString()};const randBetween = ${i.toString()};const isProbablyPrime = ${q.toString()};${n.toString()}${m.toString()}`;return a+=`onmessage = ${async function(a){const b=await f(a.data.rnd,a.data.iterations);postMessage({isPrime:b,value:a.data.rnd,id:a.data.id})}.toString()};`,p(a)}function p(a){a=`(() => {${a}})()`;var b=new Blob([a],{type:"text/javascript"});return window.URL.createObjectURL(b)}function q(c,b=16){if(c===t)return!0;if((c&s)===r||c===s)return!1;const e=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597];for(let a=0;a<e.length&&BigInt(e[a])<=c;a++){const b=BigInt(e[a]);if(c===b)return!0;if(c%b===r)return!1}let f=r,g=c-s;for(;g%t===r;)g/=t,++f;let j=(c-s)/t**f;loop:do{let a=i(c-s,t),b=h(a,j,c);if(b===s||b===c-s)continue;for(let a=1;a<f;a++){if(b=h(b,t,c),b===c-s)continue loop;if(b===s)break}return!1}while(--b);return!0}const r=BigInt(0),s=BigInt(1),t=BigInt(2);return a.abs=c,a.eGcd=d,a.gcd=e,a.isProbablyPrime=f,a.lcm=function(d,f){return d=BigInt(d),f=BigInt(f),c(d*f)/e(d,f)},a.modInv=g,a.modPow=h,a.prime=function(a,b=16){return new Promise(c=>{let d=[];const e=(e,f)=>{if(e.isPrime){for(let a=0;a<d.length;a++)d[a].terminate();for(;d.length;)d.pop();c(e.value)}else{let c=j(a,!0),d=m(c);try{f.postMessage({rnd:d,iterations:b,id:e.id})}catch(a){}}};{let a=o();for(let b,c=0;c<self.navigator.hardwareConcurrency;c++)b=new Worker(a),b.onmessage=a=>e(a.data,b),d.push(b)}for(let e=0;e<d.length;e++){let c=j(a,!0),f=m(c);d[e].postMessage({rnd:f,iterations:b,id:e})}})},a.randBetween=i,a.randBits=j,a.randBytes=k,a.toZn=l,a}({});

View File

@ -96,7 +96,7 @@ function gcd(a, b) {
async function isProbablyPrime(w, iterations = 16) { async function isProbablyPrime(w, iterations = 16) {
{ // browser { // browser
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let worker = new Worker(_isProbablyPrimeWorkerURL()); let worker = new Worker(_isProbablyPrimeWorkerUrl());
worker.onmessage = (event) => { worker.onmessage = (event) => {
worker.terminate(); worker.terminate();
@ -189,7 +189,7 @@ function modPow(a, b, n) {
* *
* @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
*/ */
async function prime(bitLength, iterations = 16) { function prime(bitLength, iterations = 16) {
return new Promise((resolve) => { return new Promise((resolve) => {
let workerList = []; let workerList = [];
const _onmessage = (msg, newWorker) => { const _onmessage = (msg, newWorker) => {
@ -203,22 +203,21 @@ async function prime(bitLength, iterations = 16) {
} }
resolve(msg.value); resolve(msg.value);
} else { // if a composite is found, make the worker test another random number } else { // if a composite is found, make the worker test another random number
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
try { try {
newWorker.postMessage({ newWorker.postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': msg.id 'id': msg.id
}); });
} catch (error) { } catch (error) {
// The worker has already terminated. There is nothing to handle here // The worker has already terminated. There is nothing to handle here
} }
});
} }
}; };
{ //browser { //browser
let workerURL = _isProbablyPrimeWorkerURL(); let workerURL = _isProbablyPrimeWorkerUrl();
for (let i = 0; i < self.navigator.hardwareConcurrency; i++) { for (let i = 0; i < self.navigator.hardwareConcurrency; i++) {
let newWorker = new Worker(workerURL); let newWorker = new Worker(workerURL);
newWorker.onmessage = (event) => _onmessage(event.data, newWorker); newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
@ -226,13 +225,12 @@ async function prime(bitLength, iterations = 16) {
} }
} }
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
workerList[i].postMessage({ workerList[i].postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': i 'id': i
});
}); });
} }
}); });
@ -243,15 +241,15 @@ async function prime(bitLength, iterations = 16) {
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} min Returned value will be >= min * @param {bigint} min Returned value will be >= min
* *
* @returns {Promise} A promise that resolves to a cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
async function randBetween(max, min = _ONE) { function randBetween(max, min = _ONE) {
if (max <= min) throw new Error('max must be > min'); if (max <= min) throw new Error('max must be > min');
const interval = max - min; const interval = max - min;
let bitLen = bitLength(interval); let bitLen = bitLength(interval);
let rnd; let rnd;
do { do {
let buf = await randBits(bitLen); let buf = randBits(bitLen);
rnd = fromBuffer(buf); rnd = fromBuffer(buf);
} while (rnd > interval); } while (rnd > interval);
return rnd + min; return rnd + min;
@ -263,11 +261,11 @@ async function randBetween(max, min = _ONE) {
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} forceLength If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} forceLength 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 filled with cryptographically secure random bits * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bits
*/ */
async function randBits(bitLength, forceLength = false) { function randBits(bitLength, forceLength = false) {
const byteLength = Math.ceil(bitLength / 8); const byteLength = Math.ceil(bitLength / 8);
let rndBytes = await randBytes(byteLength, false); let rndBytes = randBytes(byteLength, false);
// Fill with 0's the extra birs // Fill with 0's the extra birs
rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1); rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1);
if (forceLength) { if (forceLength) {
@ -283,21 +281,18 @@ async function randBits(bitLength, forceLength = false) {
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
* *
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bytes
*/ */
async function randBytes(byteLength, forceLength = false) { function randBytes(byteLength, forceLength = false) {
return new Promise((resolve) => { let buf;
let buf; { // browser
buf = new Uint8Array(byteLength);
{ // browser self.crypto.getRandomValues(buf);
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 fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength)
if (forceLength) buf[0] = buf[0] | 128;
buf[0] = buf[0] | 128; return buf;
resolve(buf);
}
});
} }
/** /**
@ -334,11 +329,11 @@ function bitLength(a) {
return bits; return bits;
} }
function _isProbablyPrimeWorkerURL() { function _isProbablyPrimeWorkerUrl() {
// Let's us first add all the required functions // 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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`; 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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`;
const _onmessage = async function (event) { // Let's start once we are called const onmessage = async function (event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>} // event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations); const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations);
postMessage({ postMessage({
@ -347,16 +342,19 @@ function _isProbablyPrimeWorkerURL() {
'id': event.data.id 'id': event.data.id
}); });
}; };
workerCode += `onmessage = ${_onmessage.toString()};`;
workerCode += `onmessage = ${onmessage.toString()};`;
return _workerUrl(workerCode);
}
function _workerUrl(workerCode) {
workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE
var _blob = new Blob([workerCode], { type: 'text/javascript' }); var _blob = new Blob([workerCode], { type: 'text/javascript' });
return window.URL.createObjectURL(_blob); return window.URL.createObjectURL(_blob);
} }
async function _isProbablyPrime(w, iterations = 16) { function _isProbablyPrime(w, iterations = 16) {
/* /*
PREFILTERING. Even values but 2 are not primes, so don't test. 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. 1 is not a prime and the M-R algorithm needs w>1.
@ -657,7 +655,7 @@ async function _isProbablyPrime(w, iterations = 16) {
let m = (w - _ONE) / (_TWO ** a); let m = (w - _ONE) / (_TWO ** a);
loop: do { loop: do {
let b = await randBetween(w - _ONE, _TWO); let b = randBetween(w - _ONE, _TWO);
let z = modPow(b, m, w); let z = modPow(b, m, w);
if (z === _ONE || z === w - _ONE) if (z === _ONE || z === w - _ONE)
continue; continue;

View File

@ -1 +1 @@
function abs(b){return b=BigInt(b),b>=_ZERO?b:-b}function eGcd(c,d){c=BigInt(c),d=BigInt(d);let e=_ZERO,f=_ONE,g=_ONE,h=_ZERO;for(;c!==_ZERO;){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}}function gcd(c,d){c=abs(c),d=abs(d);let e=_ZERO;for(;!((c|d)&_ONE);)c>>=_ONE,d>>=_ONE,e++;for(;!(c&_ONE);)c>>=_ONE;do{for(;!(d&_ONE);)d>>=_ONE;if(c>d){let a=c;c=d,d=a}d-=c}while(d);return c<<e}async function isProbablyPrime(a,b=16){return new Promise((c,d)=>{let e=new Worker(_isProbablyPrimeWorkerURL());e.onmessage=a=>{e.terminate(),c(a.data.isPrime)},e.onmessageerror=a=>{d(a)},e.postMessage({rnd:a,iterations:b,id:0})})}function lcm(c,d){return c=BigInt(c),d=BigInt(d),abs(c*d)/gcd(c,d)}function modInv(b,a){let c=eGcd(b,a);return c.b===_ONE?toZn(c.x,a):null}function modPow(c,d,e){if(e=BigInt(e),c=toZn(c,e),d=BigInt(d),d<_ZERO)return modInv(modPow(c,abs(d),e),e);let f=_ONE,g=c;for(;0<d;){var h=d%_TWO;d/=_TWO,h==_ONE&&(f*=g,f%=e),g*=g,g%=e}return f}async function prime(a,b=16){return new Promise(c=>{let d=[];const e=(e,f)=>{if(e.isPrime){for(let a=0;a<d.length;a++)d[a].terminate();for(;d.length;)d.pop();c(e.value)}else randBits(a,!0).then(a=>{let c=fromBuffer(a);try{f.postMessage({rnd:c,iterations:b,id:e.id})}catch(a){}})};{let a=_isProbablyPrimeWorkerURL();for(let b,c=0;c<self.navigator.hardwareConcurrency;c++)b=new Worker(a),b.onmessage=a=>e(a.data,b),d.push(b)}for(let e=0;e<d.length;e++)randBits(a,!0).then(a=>{let c=fromBuffer(a);d[e].postMessage({rnd:c,iterations:b,id:e})})})}async function randBetween(a,b=_ONE){if(a<=b)throw new Error("max must be > min");const c=a-b;let d,e=bitLength(c);do{let a=await randBits(e);d=fromBuffer(a)}while(d>c);return d+b}async function randBits(a,b=!1){var c=Math.ceil;const d=c(a/8);let e=await randBytes(d,!1);if(e[0]&=2**(a%8)-1,b){let b=a%8?2**(a%8-1):128;e[0]|=b}return e}async function randBytes(a,b=!1){return new Promise(c=>{let d;d=new Uint8Array(a),self.crypto.getRandomValues(d),b&&(d[0]|=128),c(d)})}function toZn(b,c){return c=BigInt(c),b=BigInt(b)%c,0>b?b+c:b}function fromBuffer(a){let b=_ZERO;for(let c of a.values()){let a=BigInt(c);b=(b<<BigInt(8))+a}return b}function bitLength(b){let c=1;do c++;while((b>>=_ONE)>_ONE);return c}function _isProbablyPrimeWorkerURL(){let a=`'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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`;a+=`onmessage = ${async function(a){const b=await isProbablyPrime(a.data.rnd,a.data.iterations);postMessage({isPrime:b,value:a.data.rnd,id:a.data.id})}.toString()};`,a=`(() => {${a}})()`;var b=new Blob([a],{type:"text/javascript"});return window.URL.createObjectURL(b)}async function _isProbablyPrime(c,b=16){if(c===_TWO)return!0;if((c&_ONE)===_ZERO||c===_ONE)return!1;const e=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597];for(let a=0;a<e.length&&BigInt(e[a])<=c;a++){const b=BigInt(e[a]);if(c===b)return!0;if(c%b===_ZERO)return!1}let f=_ZERO,g=c-_ONE;for(;g%_TWO===_ZERO;)g/=_TWO,++f;let h=(c-_ONE)/_TWO**f;loop:do{let a=await randBetween(c-_ONE,_TWO),b=modPow(a,h,c);if(b===_ONE||b===c-_ONE)continue;for(let a=1;a<f;a++){if(b=modPow(b,_TWO,c),b===c-_ONE)continue loop;if(b===_ONE)break}return!1}while(--b);return!0}const _ZERO=BigInt(0),_ONE=BigInt(1),_TWO=BigInt(2);export{abs,eGcd,gcd,isProbablyPrime,lcm,modInv,modPow,prime,randBetween,randBits,randBytes,toZn}; function abs(b){return b=BigInt(b),b>=_ZERO?b:-b}function eGcd(c,d){c=BigInt(c),d=BigInt(d);let e=_ZERO,f=_ONE,g=_ONE,h=_ZERO;for(;c!==_ZERO;){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}}function gcd(c,d){c=abs(c),d=abs(d);let e=_ZERO;for(;!((c|d)&_ONE);)c>>=_ONE,d>>=_ONE,e++;for(;!(c&_ONE);)c>>=_ONE;do{for(;!(d&_ONE);)d>>=_ONE;if(c>d){let a=c;c=d,d=a}d-=c}while(d);return c<<e}async function isProbablyPrime(a,b=16){return new Promise((c,d)=>{let e=new Worker(_isProbablyPrimeWorkerUrl());e.onmessage=a=>{e.terminate(),c(a.data.isPrime)},e.onmessageerror=a=>{d(a)},e.postMessage({rnd:a,iterations:b,id:0})})}function lcm(c,d){return c=BigInt(c),d=BigInt(d),abs(c*d)/gcd(c,d)}function modInv(b,a){let c=eGcd(b,a);return c.b===_ONE?toZn(c.x,a):null}function modPow(c,d,e){if(e=BigInt(e),c=toZn(c,e),d=BigInt(d),d<_ZERO)return modInv(modPow(c,abs(d),e),e);let f=_ONE,g=c;for(;0<d;){var h=d%_TWO;d/=_TWO,h==_ONE&&(f*=g,f%=e),g*=g,g%=e}return f}function prime(a,b=16){return new Promise(c=>{let d=[];const e=(e,f)=>{if(e.isPrime){for(let a=0;a<d.length;a++)d[a].terminate();for(;d.length;)d.pop();c(e.value)}else{let c=randBits(a,!0),d=fromBuffer(c);try{f.postMessage({rnd:d,iterations:b,id:e.id})}catch(a){}}};{let a=_isProbablyPrimeWorkerUrl();for(let b,c=0;c<self.navigator.hardwareConcurrency;c++)b=new Worker(a),b.onmessage=a=>e(a.data,b),d.push(b)}for(let e=0;e<d.length;e++){let c=randBits(a,!0),f=fromBuffer(c);d[e].postMessage({rnd:f,iterations:b,id:e})}})}function randBetween(a,b=_ONE){if(a<=b)throw new Error("max must be > min");const c=a-b;let d,e=bitLength(c);do{let a=randBits(e);d=fromBuffer(a)}while(d>c);return d+b}function randBits(a,b=!1){var c=Math.ceil;const d=c(a/8);let e=randBytes(d,!1);if(e[0]&=2**(a%8)-1,b){let b=a%8?2**(a%8-1):128;e[0]|=b}return e}function randBytes(a,b=!1){let c;return c=new Uint8Array(a),self.crypto.getRandomValues(c),b&&(c[0]|=128),c}function toZn(b,c){return c=BigInt(c),b=BigInt(b)%c,0>b?b+c:b}function fromBuffer(a){let b=_ZERO;for(let c of a.values()){let a=BigInt(c);b=(b<<BigInt(8))+a}return b}function bitLength(b){let c=1;do c++;while((b>>=_ONE)>_ONE);return c}function _isProbablyPrimeWorkerUrl(){let a=`'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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`;return a+=`onmessage = ${async function(a){const b=await isProbablyPrime(a.data.rnd,a.data.iterations);postMessage({isPrime:b,value:a.data.rnd,id:a.data.id})}.toString()};`,_workerUrl(a)}function _workerUrl(a){a=`(() => {${a}})()`;var b=new Blob([a],{type:"text/javascript"});return window.URL.createObjectURL(b)}function _isProbablyPrime(c,b=16){if(c===_TWO)return!0;if((c&_ONE)===_ZERO||c===_ONE)return!1;const e=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597];for(let a=0;a<e.length&&BigInt(e[a])<=c;a++){const b=BigInt(e[a]);if(c===b)return!0;if(c%b===_ZERO)return!1}let f=_ZERO,g=c-_ONE;for(;g%_TWO===_ZERO;)g/=_TWO,++f;let h=(c-_ONE)/_TWO**f;loop:do{let a=randBetween(c-_ONE,_TWO),b=modPow(a,h,c);if(b===_ONE||b===c-_ONE)continue;for(let a=1;a<f;a++){if(b=modPow(b,_TWO,c),b===c-_ONE)continue loop;if(b===_ONE)break}return!1}while(--b);return!0}const _ZERO=BigInt(0),_ONE=BigInt(1),_TWO=BigInt(2);export{abs,eGcd,gcd,isProbablyPrime,lcm,modInv,modPow,prime,randBetween,randBits,randBytes,toZn};

View File

@ -119,7 +119,9 @@ async function isProbablyPrime(w, iterations = 16) {
}); });
}); });
} else { } else {
return _isProbablyPrime(w, iterations); return new Promise((resolve) => {
resolve(_isProbablyPrime(w, iterations));
});
} }
} }
} }
@ -197,13 +199,13 @@ function modPow(a, b, n) {
* *
* @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
*/ */
async function prime(bitLength, iterations = 16) { function prime(bitLength, iterations = 16) {
if (!_useWorkers) { if (!_useWorkers) {
let rnd = _ZERO; let rnd = _ZERO;
do { do {
rnd = fromBuffer(await randBytes(bitLength / 8, true)); rnd = fromBuffer(randBytes(bitLength / 8, true));
} while (! await _isProbablyPrime(rnd, iterations)); } while (!_isProbablyPrime(rnd, iterations));
return rnd; return new Promise((resolve) => { resolve(rnd); });
} }
return new Promise((resolve) => { return new Promise((resolve) => {
let workerList = []; let workerList = [];
@ -218,18 +220,17 @@ async function prime(bitLength, iterations = 16) {
} }
resolve(msg.value); resolve(msg.value);
} else { // if a composite is found, make the worker test another random number } else { // if a composite is found, make the worker test another random number
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
try { try {
newWorker.postMessage({ newWorker.postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': msg.id 'id': msg.id
}); });
} catch (error) { } catch (error) {
// The worker has already terminated. There is nothing to handle here // The worker has already terminated. There is nothing to handle here
} }
});
} }
}; };
{ // Node.js { // Node.js
@ -242,13 +243,12 @@ async function prime(bitLength, iterations = 16) {
} }
} }
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
workerList[i].postMessage({ workerList[i].postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': i 'id': i
});
}); });
} }
}); });
@ -259,15 +259,15 @@ async function prime(bitLength, iterations = 16) {
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} min Returned value will be >= min * @param {bigint} min Returned value will be >= min
* *
* @returns {Promise} A promise that resolves to a cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
async function randBetween(max, min = _ONE) { function randBetween(max, min = _ONE) {
if (max <= min) throw new Error('max must be > min'); if (max <= min) throw new Error('max must be > min');
const interval = max - min; const interval = max - min;
let bitLen = bitLength(interval); let bitLen = bitLength(interval);
let rnd; let rnd;
do { do {
let buf = await randBits(bitLen); let buf = randBits(bitLen);
rnd = fromBuffer(buf); rnd = fromBuffer(buf);
} while (rnd > interval); } while (rnd > interval);
return rnd + min; return rnd + min;
@ -279,11 +279,11 @@ async function randBetween(max, min = _ONE) {
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} forceLength If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} forceLength 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 filled with cryptographically secure random bits * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bits
*/ */
async function randBits(bitLength, forceLength = false) { function randBits(bitLength, forceLength = false) {
const byteLength = Math.ceil(bitLength / 8); const byteLength = Math.ceil(bitLength / 8);
let rndBytes = await randBytes(byteLength, false); let rndBytes = randBytes(byteLength, false);
// Fill with 0's the extra birs // Fill with 0's the extra birs
rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1); rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1);
if (forceLength) { if (forceLength) {
@ -299,24 +299,19 @@ async function randBits(bitLength, forceLength = false) {
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
* *
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bytes
*/ */
async function randBytes(byteLength, forceLength = false) { function randBytes(byteLength, forceLength = false) {
return new Promise((resolve) => { let buf;
let buf; { // node
const crypto = require('crypto');
{ // node buf = Buffer.alloc(byteLength);
const crypto = require('crypto'); crypto.randomFillSync(buf);
buf = Buffer.alloc(byteLength); }
crypto.randomFill(buf, (err, buf) => { // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength if (forceLength)
if (forceLength) buf[0] = buf[0] | 128;
buf[0] = buf[0] | 128; return buf;
resolve(buf);
});
}
});
} }
/** /**
@ -353,7 +348,7 @@ function bitLength(a) {
return bits; return bits;
} }
async function _isProbablyPrime(w, iterations = 16) { function _isProbablyPrime(w, iterations = 16) {
/* /*
PREFILTERING. Even values but 2 are not primes, so don't test. 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. 1 is not a prime and the M-R algorithm needs w>1.
@ -654,7 +649,7 @@ async function _isProbablyPrime(w, iterations = 16) {
let m = (w - _ONE) / (_TWO ** a); let m = (w - _ONE) / (_TWO ** a);
loop: do { loop: do {
let b = await randBetween(w - _ONE, _TWO); let b = randBetween(w - _ONE, _TWO);
let z = modPow(b, m, w); let z = modPow(b, m, w);
if (z === _ONE || z === w - _ONE) if (z === _ONE || z === w - _ONE)
continue; continue;
@ -698,9 +693,9 @@ This node version doesn't support worker_threads. You should enable them in orde
if (_useWorkers) { // node.js with support for workers if (_useWorkers) { // node.js with support for workers
const { parentPort, isMainThread } = require('worker_threads'); const { parentPort, isMainThread } = require('worker_threads');
if (!isMainThread) { // worker if (!isMainThread) { // worker
parentPort.on('message', async function (data) { // Let's start once we are called parentPort.on('message', function (data) { // Let's start once we are called
// data = {rnd: <bigint>, iterations: <number>} // data = {rnd: <bigint>, iterations: <number>}
const isPrime = await _isProbablyPrime(data.rnd, data.iterations); const isPrime = _isProbablyPrime(data.rnd, data.iterations);
parentPort.postMessage({ parentPort.postMessage({
'isPrime': isPrime, 'isPrime': isPrime,
'value': data.rnd, 'value': data.rnd,

View File

@ -117,11 +117,13 @@ export async function isProbablyPrime(w, iterations = 16) {
}); });
}); });
} else { } else {
return _isProbablyPrime(w, iterations); return new Promise((resolve) => {
resolve(_isProbablyPrime(w, iterations));
});
} }
} else { // browser } else { // browser
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let worker = new Worker(_isProbablyPrimeWorkerURL()); let worker = new Worker(_isProbablyPrimeWorkerUrl());
worker.onmessage = (event) => { worker.onmessage = (event) => {
worker.terminate(); worker.terminate();
@ -214,13 +216,13 @@ export function modPow(a, b, n) {
* *
* @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 async function prime(bitLength, iterations = 16) { export function prime(bitLength, iterations = 16) {
if (!process.browser && !_useWorkers) { if (!process.browser && !_useWorkers) {
let rnd = _ZERO; let rnd = _ZERO;
do { do {
rnd = fromBuffer(await randBytes(bitLength / 8, true)); rnd = fromBuffer(randBytes(bitLength / 8, true));
} while (! await _isProbablyPrime(rnd, iterations)); } while (!_isProbablyPrime(rnd, iterations));
return rnd; return new Promise((resolve) => { resolve(rnd); });
} }
return new Promise((resolve) => { return new Promise((resolve) => {
let workerList = []; let workerList = [];
@ -235,22 +237,21 @@ export async function prime(bitLength, iterations = 16) {
} }
resolve(msg.value); resolve(msg.value);
} else { // if a composite is found, make the worker test another random number } else { // if a composite is found, make the worker test another random number
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
try { try {
newWorker.postMessage({ newWorker.postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': msg.id 'id': msg.id
}); });
} catch (error) { } catch (error) {
// The worker has already terminated. There is nothing to handle here // The worker has already terminated. There is nothing to handle here
} }
});
} }
}; };
if (process.browser) { //browser if (process.browser) { //browser
let workerURL = _isProbablyPrimeWorkerURL(); let workerURL = _isProbablyPrimeWorkerUrl();
for (let i = 0; i < self.navigator.hardwareConcurrency; i++) { for (let i = 0; i < self.navigator.hardwareConcurrency; i++) {
let newWorker = new Worker(workerURL); let newWorker = new Worker(workerURL);
newWorker.onmessage = (event) => _onmessage(event.data, newWorker); newWorker.onmessage = (event) => _onmessage(event.data, newWorker);
@ -266,13 +267,12 @@ export async function prime(bitLength, iterations = 16) {
} }
} }
for (let i = 0; i < workerList.length; i++) { for (let i = 0; i < workerList.length; i++) {
randBits(bitLength, true).then((buf) => { let buf = randBits(bitLength, true);
let rnd = fromBuffer(buf); let rnd = fromBuffer(buf);
workerList[i].postMessage({ workerList[i].postMessage({
'rnd': rnd, 'rnd': rnd,
'iterations': iterations, 'iterations': iterations,
'id': i 'id': i
});
}); });
} }
}); });
@ -283,15 +283,15 @@ export async function prime(bitLength, iterations = 16) {
* @param {bigint} max Returned value will be <= max * @param {bigint} max Returned value will be <= max
* @param {bigint} min Returned value will be >= min * @param {bigint} min Returned value will be >= min
* *
* @returns {Promise} A promise that resolves to a cryptographically secure random bigint between [min,max] * @returns {bigint} A cryptographically secure random bigint between [min,max]
*/ */
export async function randBetween(max, min = _ONE) { export function randBetween(max, min = _ONE) {
if (max <= min) throw new Error('max must be > min'); if (max <= min) throw new Error('max must be > min');
const interval = max - min; const interval = max - min;
let bitLen = bitLength(interval); let bitLen = bitLength(interval);
let rnd; let rnd;
do { do {
let buf = await randBits(bitLen); let buf = randBits(bitLen);
rnd = fromBuffer(buf); rnd = fromBuffer(buf);
} while (rnd > interval); } while (rnd > interval);
return rnd + min; return rnd + min;
@ -303,11 +303,11 @@ export async function randBetween(max, min = _ONE) {
* @param {number} bitLength The desired number of random bits * @param {number} bitLength The desired number of random bits
* @param {boolean} forceLength If we want to force the output to have a specific bit length. It basically forces the msb to be 1 * @param {boolean} forceLength 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 filled with cryptographically secure random bits * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bits
*/ */
export async function randBits(bitLength, forceLength = false) { export function randBits(bitLength, forceLength = false) {
const byteLength = Math.ceil(bitLength / 8); const byteLength = Math.ceil(bitLength / 8);
let rndBytes = await randBytes(byteLength, false); let rndBytes = randBytes(byteLength, false);
// Fill with 0's the extra birs // Fill with 0's the extra birs
rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1); rndBytes[0] = rndBytes[0] & (2 ** (bitLength % 8) - 1);
if (forceLength) { if (forceLength) {
@ -323,31 +323,22 @@ export async function randBits(bitLength, forceLength = false) {
* @param {number} byteLength The desired number of random bytes * @param {number} byteLength The desired number of random bytes
* @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 * @param {boolean} forceLength If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1
* *
* @returns {Promise} A promise that resolves to a Buffer/UInt8Array filled with cryptographically secure random bytes * @returns {Buffer|Uint8Array} A Buffer/UInt8Array filled with cryptographically secure random bytes
*/ */
export async function randBytes(byteLength, forceLength = false) { export function randBytes(byteLength, forceLength = false) {
return new Promise((resolve) => { let buf;
let buf; if (!process.browser) { // node
const crypto = require('crypto');
if (!process.browser) { // node buf = Buffer.alloc(byteLength);
const crypto = require('crypto'); crypto.randomFillSync(buf);
buf = Buffer.alloc(byteLength); } else { // browser
crypto.randomFill(buf, (err, buf) => { buf = new Uint8Array(byteLength);
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength self.crypto.getRandomValues(buf);
if (forceLength) }
buf[0] = buf[0] | 128; // If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength)
resolve(buf); buf[0] = buf[0] | 128;
}); return buf;
} else { // browser
buf = new Uint8Array(byteLength);
self.crypto.getRandomValues(buf);
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
if (forceLength)
buf[0] = buf[0] | 128;
resolve(buf);
}
});
} }
/** /**
@ -384,11 +375,11 @@ function bitLength(a) {
return bits; return bits;
} }
function _isProbablyPrimeWorkerURL() { function _isProbablyPrimeWorkerUrl() {
// Let's us first add all the required functions // 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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`; 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 randBytes = ${randBytes.toString()};const randBetween = ${randBetween.toString()};const isProbablyPrime = ${_isProbablyPrime.toString()};${bitLength.toString()}${fromBuffer.toString()}`;
const _onmessage = async function (event) { // Let's start once we are called const onmessage = async function (event) { // Let's start once we are called
// event.data = {rnd: <bigint>, iterations: <number>} // event.data = {rnd: <bigint>, iterations: <number>}
const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations); const isPrime = await isProbablyPrime(event.data.rnd, event.data.iterations);
postMessage({ postMessage({
@ -397,16 +388,19 @@ function _isProbablyPrimeWorkerURL() {
'id': event.data.id 'id': event.data.id
}); });
}; };
workerCode += `onmessage = ${_onmessage.toString()};`;
workerCode += `onmessage = ${onmessage.toString()};`;
return _workerUrl(workerCode);
}
function _workerUrl(workerCode) {
workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE workerCode = `(() => {${workerCode}})()`; // encapsulate IIFE
var _blob = new Blob([workerCode], { type: 'text/javascript' }); var _blob = new Blob([workerCode], { type: 'text/javascript' });
return window.URL.createObjectURL(_blob); return window.URL.createObjectURL(_blob);
} }
async function _isProbablyPrime(w, iterations = 16) { function _isProbablyPrime(w, iterations = 16) {
/* /*
PREFILTERING. Even values but 2 are not primes, so don't test. 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. 1 is not a prime and the M-R algorithm needs w>1.
@ -707,7 +701,7 @@ async function _isProbablyPrime(w, iterations = 16) {
let m = (w - _ONE) / (_TWO ** a); let m = (w - _ONE) / (_TWO ** a);
loop: do { loop: do {
let b = await randBetween(w - _ONE, _TWO); let b = randBetween(w - _ONE, _TWO);
let z = modPow(b, m, w); let z = modPow(b, m, w);
if (z === _ONE || z === w - _ONE) if (z === _ONE || z === w - _ONE)
continue; continue;
@ -751,9 +745,9 @@ This node version doesn't support worker_threads. You should enable them in orde
if (!process.browser && _useWorkers) { // node.js with support for workers if (!process.browser && _useWorkers) { // node.js with support for workers
const { parentPort, isMainThread } = require('worker_threads'); const { parentPort, isMainThread } = require('worker_threads');
if (!isMainThread) { // worker if (!isMainThread) { // worker
parentPort.on('message', async function (data) { // Let's start once we are called parentPort.on('message', function (data) { // Let's start once we are called
// data = {rnd: <bigint>, iterations: <number>} // data = {rnd: <bigint>, iterations: <number>}
const isPrime = await _isProbablyPrime(data.rnd, data.iterations); const isPrime = _isProbablyPrime(data.rnd, data.iterations);
parentPort.postMessage({ parentPort.postMessage({
'isPrime': isPrime, 'isPrime': isPrime,
'value': data.rnd, 'value': data.rnd,