Examples of code. If a webview doesn't support Web Workers, they are not used

This commit is contained in:
juanelas 2020-04-08 17:09:52 +02:00
parent 49bd34b7e9
commit 0524f41a3f
11 changed files with 111 additions and 175 deletions

View File

@ -2,7 +2,7 @@
# bigint-crypto-utils
Utils for working with cryptography using native JS ([ES-2020](https://tc39.es/ecma262/#sec-bigint-objects)) implementation of BigInt. It includes some extra functions to work with modular arithmetic along with secure random numbers and a fast strong probable prime generator/tester (parallelized multi-threaded Miller-Rabin primality test). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). In the latter case, for multi-threaded primality tests, you should use Node.js v11 or newer or enable at runtime with `node --experimental-worker` with Node.js version >= 10.5.0 and < 11.
Utils for working with cryptography using native JS ([ES-2020](https://tc39.es/ecma262/#sec-bigint-objects)) implementation of BigInt. It includes some extra functions to work with modular arithmetic along with secure random numbers and a fast strong probable prime generator/tester (parallelized multi-threaded Miller-Rabin primality tests if workers are supported). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0).
> The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html).** Many platforms provide native support for cryptography, such as [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
@ -56,14 +56,14 @@ Import your module as :
</body>
```
And you could use it like in the following:
An example of usage could be:
```javascript
/* Stage 3 BigInts with value 666 can be declared as BigInt('666')
or the shorter new no-so-linter-friendly syntax 666n.
Notice that you can also pass a number, e.g. BigInt(666), but it is not
recommended since values over 2**53 - 1 won't be safe but no warning will
be raised.
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
@ -95,6 +95,9 @@ primeTesting()
```
You can find examples in the [examples folder of the repository](https://github.com/juanelas/bigint-crypto-utils/tree/master/examples).
## API reference documentation
### Functions

View File

@ -23,12 +23,12 @@ const dstFileName = path.join(dstDir, 'index.html')
const template = fs.readFileSync(templatePath, 'utf-8')
const bundleFile = path.join(rootDir, pkgJson.directories.lib, 'index.browser.bundle.mod.js')
const testsJs = `
<script type="module">
import * as _pkg from '${path.relative(templatePath, bundleFile)}'
window._pkg = _pkg;
import './tests.js';
mocha.run();
</script>`
<script type="module">
import * as _pkg from '${path.relative(templatePath, bundleFile)}'
window._pkg = _pkg
import './tests.js'
mocha.run()
</script>`
fs.writeFileSync(dstFileName,
template.replace(/{{TESTS}}/g, testsJs).replace(/{{PKG_NAME}}/g, pkgName).replace(/{{MOCHA_VERSION}}/g, mochaVersion).replace(/{{CHAI_VERSION}}/g, chaiVersion)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -260,6 +260,14 @@ function isProbablyPrime (w, iterations = 16) {
*/
function prime (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) }
if (!_useWorkers) { // If there is no support for workers
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
} while (!_isProbablyPrime(rnd, iterations))
return new Promise((resolve) => { resolve(rnd) })
}
return new Promise((resolve) => {
const workerList = []
const _onmessage = (msg, newWorker) => {
@ -318,7 +326,7 @@ function prime (bitLength, iterations = 16) {
* @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}`) }
if (bitLength < 1) throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
@ -417,9 +425,9 @@ function randBytes (byteLength, forceLength = false) {
{ // browser
return new Promise(function (resolve) {
const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf)
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 }
if (forceLength) buf[0] = buf[0] | 128
resolve(buf)
})
}
@ -440,7 +448,7 @@ function randBytesSync (byteLength, forceLength = false) {
/* eslint-disable no-lone-blocks */
{ // browser
const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf)
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
@ -489,7 +497,8 @@ 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 }
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.
@ -749,11 +758,8 @@ function _isProbablyPrime (w, iterations = 16) {
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
}
if (w === p) return true
else if (w % p === 0n) return false
}
/*
@ -785,38 +791,27 @@ function _isProbablyPrime (w, iterations = 16) {
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 }
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 }
if (z === d) break
if (z === 1n) return false
j++
}
if (z !== d) {
return false
}
if (z !== d) return false
} while (--iterations)
return true
}
let _useWorkers = false // The following is just to check whether we can use workers
/* eslint-disable no-lone-blocks */
{ // Native JS
if (self.Worker) _useWorkers = true
}
export { abs, bitLength, eGcd, gcd, isProbablyPrime, lcm, max, min, modInv, modPow, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync, toZn }

View File

@ -271,7 +271,7 @@ function isProbablyPrime (w, iterations = 16) {
function prime (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) }
if (!_useWorkers) {
if (!_useWorkers) { // If there is no support for workers
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
@ -337,7 +337,7 @@ function prime (bitLength, iterations = 16) {
* @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}`) }
if (bitLength < 1) throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
@ -438,7 +438,7 @@ function randBytes (byteLength, forceLength = false) {
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 }
if (forceLength) buf[0] = buf[0] | 128
resolve(buf)
})
}
@ -484,7 +484,8 @@ 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 }
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.
@ -744,11 +745,8 @@ function _isProbablyPrime (w, iterations = 16) {
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
}
if (w === p) return true
else if (w % p === 0n) return false
}
/*
@ -780,55 +778,36 @@ function _isProbablyPrime (w, iterations = 16) {
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 }
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 }
if (z === d) break
if (z === 1n) return false
j++
}
if (z !== d) {
return false
}
if (z !== d) return false
} while (--iterations)
return true
}
let _useWorkers = true // The following is just to check whether Node.js can use workers
let _useWorkers = false // The following is just to check whether we can use workers
/* eslint-disable no-lone-blocks */
{ // Node.js
_useWorkers = (function _workers () {
try {
require.resolve('worker_threads')
return true
} catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
try {
require.resolve('worker_threads')
_useWorkers = true
} catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
return false
}
})()
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
_useWorkers = true
}
}
/* eslint-enable no-lone-blocks */

View File

@ -47,6 +47,7 @@
"BigInt",
"Blob",
"postMessage",
"crypto",
"self",
"Worker"
],

View File

@ -2,7 +2,7 @@
# bigint-crypto-utils
Utils for working with cryptography using native JS ([ES-2020](https://tc39.es/ecma262/#sec-bigint-objects)) implementation of BigInt. It includes some extra functions to work with modular arithmetic along with secure random numbers and a fast strong probable prime generator/tester (parallelized multi-threaded Miller-Rabin primality test). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). In the latter case, for multi-threaded primality tests, you should use Node.js v11 or newer or enable at runtime with `node --experimental-worker` with Node.js version >= 10.5.0 and < 11.
Utils for working with cryptography using native JS ([ES-2020](https://tc39.es/ecma262/#sec-bigint-objects)) implementation of BigInt. It includes some extra functions to work with modular arithmetic along with secure random numbers and a fast strong probable prime generator/tester (parallelized multi-threaded Miller-Rabin primality tests if workers are supported). It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0).
> The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html).** Many platforms provide native support for cryptography, such as [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html).
@ -56,14 +56,14 @@ Import your module as :
</body>
```
And you could use it like in the following:
An example of usage could be:
```javascript
/* Stage 3 BigInts with value 666 can be declared as BigInt('666')
or the shorter new no-so-linter-friendly syntax 666n.
Notice that you can also pass a number, e.g. BigInt(666), but it is not
recommended since values over 2**53 - 1 won't be safe but no warning will
be raised.
/* A BigInt with value 666 can be declared calling the bigint constructor as
BigInt('666') or with the shorter 666n.
Notice that you can also pass a number to the constructor, e.g. BigInt(666).
However, it is not recommended since values over 2**53 - 1 won't be safe but
no warning will be raised.
*/
const a = BigInt('5')
const b = BigInt('2')
@ -95,6 +95,9 @@ primeTesting()
```
You can find examples in the [examples folder of the repository](https://github.com/juanelas/bigint-crypto-utils/tree/master/examples).
## API reference documentation
{{>main}}

View File

@ -78,7 +78,7 @@ export function isProbablyPrime (w, iterations = 16) {
export function prime (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) }
if (!process.browser && !_useWorkers) {
if (!_useWorkers) { // If there is no support for workers
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
@ -151,7 +151,7 @@ export function prime (bitLength, iterations = 16) {
* @returns {bigint} A bigint probable prime of bitLength bits.
*/
export function primeSync (bitLength, iterations = 16) {
if (bitLength < 1) { throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`) }
if (bitLength < 1) throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)
let rnd = 0n
do {
rnd = fromBuffer(randBitsSync(bitLength, true))
@ -252,15 +252,15 @@ export function randBytes (byteLength, forceLength = false) {
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 }
if (forceLength) buf[0] = buf[0] | 128
resolve(buf)
})
} else { // browser
return new Promise(function (resolve) {
const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf)
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 }
if (forceLength) buf[0] = buf[0] | 128
resolve(buf)
})
}
@ -288,7 +288,7 @@ export function randBytesSync (byteLength, forceLength = false) {
return buf
} else { // browser
const buf = new Uint8Array(byteLength)
self.crypto.getRandomValues(buf)
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
@ -337,7 +337,8 @@ 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 }
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.
@ -597,11 +598,8 @@ function _isProbablyPrime (w, iterations = 16) {
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
}
if (w === p) return true
else if (w % p === 0n) return false
}
/*
@ -633,55 +631,38 @@ function _isProbablyPrime (w, iterations = 16) {
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 }
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 }
if (z === d) break
if (z === 1n) return false
j++
}
if (z !== d) {
return false
}
if (z !== d) return false
} while (--iterations)
return true
}
let _useWorkers = true // The following is just to check whether Node.js can use workers
let _useWorkers = false // The following is just to check whether we can use workers
/* eslint-disable no-lone-blocks */
if (!process.browser) { // Node.js
_useWorkers = (function _workers () {
try {
require.resolve('worker_threads')
return true
} catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
try {
require.resolve('worker_threads')
_useWorkers = true
} catch (e) {
console.log(`[bigint-crypto-utils] WARNING:
This node version doesn't support worker_threads. You should enable them in order to greatly speedup the generation of big prime numbers.
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
return false
}
})()
· With Node >=11 it is enabled by default (consider upgrading).
· With Node 10, starting with 10.5.0, you can enable worker_threads at runtime executing node --experimental-worker `)
_useWorkers = true
}
} else { // Native JS
if (self.Worker) _useWorkers = true
}
/* eslint-enable no-lone-blocks */

View File

@ -12,12 +12,12 @@
<div id="mocha"></div>
<script>mocha.setup('bdd'); mocha.setup({ timeout: 90000 });</script>
<script type="module">
import * as _pkg from '../../../lib/index.browser.bundle.mod.js'
window._pkg = _pkg;
import './tests.js';
mocha.run();
</script>
<script type="module">
import * as _pkg from '../../../lib/index.browser.bundle.mod.js'
window._pkg = _pkg
import './tests.js'
mocha.run()
</script>
</body>
</html>

View File

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Primes</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="syncPrime">result of primeSync() will show up here</div>
<div id="asyncPrime">result of prime() will show up here</div>
<script src="../../lib/index.browser.bundle.iife.js"></script>
<script>
(async () => {
document.getElementById(
"syncPrime"
).innerHTML = bigintCryptoUtils.primeSync(123);
document.getElementById(
"asyncPrime"
).innerHTML = await bigintCryptoUtils.prime(123);
})();
</script>
</body>
</html>