randBitsSync
This commit is contained in:
parent
66b4d7fad1
commit
c52d54f66b
110
README.md
110
README.md
|
@ -226,6 +226,116 @@ Finds the smallest positive element that is congruent to a in modulo n
|
||||||
| a | <code>number</code> \| <code>bigint</code> | An integer |
|
| a | <code>number</code> \| <code>bigint</code> | An integer |
|
||||||
| n | <code>number</code> \| <code>bigint</code> | The modulo |
|
| n | <code>number</code> \| <code>bigint</code> | The modulo |
|
||||||
|
|
||||||
|
<a name="isProbablyPrime"></a>
|
||||||
|
|
||||||
|
### isProbablyPrime(w, [iterations]) ⇒ <code>Promise.<boolean></code>
|
||||||
|
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**: <code>Promise.<boolean></code> - A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| w | <code>number</code> \| <code>bigint</code> | | An integer to be tested for primality |
|
||||||
|
| [iterations] | <code>number</code> | <code>16</code> | The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3 |
|
||||||
|
|
||||||
|
<a name="prime"></a>
|
||||||
|
|
||||||
|
### prime(bitLength, [iterations]) ⇒ <code>Promise.<bigint></code>
|
||||||
|
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**: <code>Promise.<bigint></code> - A promise that resolves to a bigint probable prime of bitLength bits.
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| bitLength | <code>number</code> | | The required bit length for the generated prime |
|
||||||
|
| [iterations] | <code>number</code> | <code>16</code> | The number of iterations for the Miller-Rabin Probabilistic Primality Test |
|
||||||
|
|
||||||
|
<a name="primeSync"></a>
|
||||||
|
|
||||||
|
### primeSync(bitLength, [iterations]) ⇒ <code>bigint</code>
|
||||||
|
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**: <code>bigint</code> - A bigint probable prime of bitLength bits.
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| bitLength | <code>number</code> | | The required bit length for the generated prime |
|
||||||
|
| [iterations] | <code>number</code> | <code>16</code> | The number of iterations for the Miller-Rabin Probabilistic Primality Test |
|
||||||
|
|
||||||
|
<a name="randBetween"></a>
|
||||||
|
|
||||||
|
### randBetween(max, [min]) ⇒ <code>bigint</code>
|
||||||
|
Returns a cryptographically secure random integer between [min,max]
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>bigint</code> - A cryptographically secure random bigint between [min,max]
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| max | <code>bigint</code> | | Returned value will be <= max |
|
||||||
|
| [min] | <code>bigint</code> | <code>BigInt(1)</code> | Returned value will be >= min |
|
||||||
|
|
||||||
|
<a name="randBits"></a>
|
||||||
|
|
||||||
|
### randBits(bitLength, [forceLength]) ⇒ <code>Promise.<(Buffer\|Uint8Array)></code>
|
||||||
|
Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>Promise.<(Buffer\|Uint8Array)></code> - A Promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| bitLength | <code>number</code> | | The desired number of random bits |
|
||||||
|
| [forceLength] | <code>boolean</code> | <code>false</code> | If we want to force the output to have a specific bit length. It basically forces the msb to be 1 |
|
||||||
|
|
||||||
|
<a name="randBitsSync"></a>
|
||||||
|
|
||||||
|
### randBitsSync(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()
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>Buffer</code> \| <code>Uint8Array</code> - A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bits
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| bitLength | <code>number</code> | | The desired number of random bits |
|
||||||
|
| [forceLength] | <code>boolean</code> | <code>false</code> | If we want to force the output to have a specific bit length. It basically forces the msb to be 1 |
|
||||||
|
|
||||||
|
<a name="randBytes"></a>
|
||||||
|
|
||||||
|
### randBytes(byteLength, [forceLength]) ⇒ <code>Promise.<(Buffer\|Uint8Array)></code>
|
||||||
|
Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>Promise.<(Buffer\|Uint8Array)></code> - A promise that resolves to a Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| byteLength | <code>number</code> | | The desired number of random bytes |
|
||||||
|
| [forceLength] | <code>boolean</code> | <code>false</code> | If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 |
|
||||||
|
|
||||||
|
<a name="randBytesSync"></a>
|
||||||
|
|
||||||
|
### randBytesSync(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()
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
**Returns**: <code>Buffer</code> \| <code>Uint8Array</code> - A Buffer/UInt8Array (Node.js/Browser) filled with cryptographically secure random bytes
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| byteLength | <code>number</code> | | The desired number of random bytes |
|
||||||
|
| [forceLength] | <code>boolean</code> | <code>false</code> | If we want to force the output to have a bit length of 8*byteLength. It basically forces the msb to be 1 |
|
||||||
|
|
||||||
<a name="egcdReturn"></a>
|
<a name="egcdReturn"></a>
|
||||||
|
|
||||||
### egcdReturn : <code>Object</code>
|
### egcdReturn : <code>Object</code>
|
||||||
|
|
|
@ -8,17 +8,19 @@ const pkgJson = require('../package.json')
|
||||||
const rootDir = path.join(__dirname, '..')
|
const rootDir = path.join(__dirname, '..')
|
||||||
|
|
||||||
const template = path.join(rootDir, pkgJson.directories.src, 'doc', 'readme-template.md')
|
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 = {
|
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' }),
|
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"
|
'heading-depth': 3, // The initial heading depth. For example, with a value of 2 the top-level markdown headings look like "## The heading"
|
||||||
'global-index-format': 'none' // none, grouped, table, dl.
|
'global-index-format': 'none' // none, grouped, table, dl.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jsdoc2md.clear().then(() => {
|
||||||
const readmeContents = jsdoc2md.renderSync(options)
|
const readmeContents = jsdoc2md.renderSync(options)
|
||||||
|
|
||||||
const readmeFile = path.join(rootDir, 'README.md')
|
const readmeFile = path.join(rootDir, 'README.md')
|
||||||
|
|
||||||
fs.writeFileSync(readmeFile, readmeContents)
|
fs.writeFileSync(readmeFile, readmeContents)
|
||||||
|
})
|
||||||
|
|
|
@ -29,7 +29,7 @@ module.exports = [
|
||||||
input: input,
|
input: input,
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
file: path.join(dstDir, 'index.browser.mod.js'),
|
file: path.join(rootDir, pkgJson.browser),
|
||||||
format: 'esm'
|
format: 'esm'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -46,7 +46,17 @@ module.exports = [
|
||||||
{
|
{
|
||||||
file: path.join(dstDir, 'index.browser.bundle.js'),
|
file: path.join(dstDir, 'index.browser.bundle.js'),
|
||||||
format: 'iife',
|
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'),
|
file: path.join(dstDir, 'index.browser.bundle.mod.js'),
|
||||||
|
@ -59,10 +69,6 @@ module.exports = [
|
||||||
}),
|
}),
|
||||||
resolve({
|
resolve({
|
||||||
browser: true
|
browser: true
|
||||||
}),
|
|
||||||
terser({
|
|
||||||
// mangle: false,
|
|
||||||
// compress: false
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -65,7 +65,7 @@ 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
|
||||||
const buf = randBits(bitLength, true)
|
const buf = randBitsSync(bitLength, true)
|
||||||
const rnd = fromBuffer(buf)
|
const rnd = fromBuffer(buf)
|
||||||
try {
|
try {
|
||||||
newWorker.postMessage({
|
newWorker.postMessage({
|
||||||
|
@ -89,7 +89,7 @@ function prime (bitLength, iterations = 16) {
|
||||||
}
|
}
|
||||||
/* eslint-enable no-lone-blocks */
|
/* eslint-enable no-lone-blocks */
|
||||||
for (let i = 0; i < workerList.length; i++) {
|
for (let i = 0; i < workerList.length; i++) {
|
||||||
const buf = randBits(bitLength, true)
|
const buf = randBitsSync(bitLength, true)
|
||||||
const rnd = fromBuffer(buf)
|
const rnd = fromBuffer(buf)
|
||||||
workerList[i].postMessage({
|
workerList[i].postMessage({
|
||||||
rnd: rnd,
|
rnd: rnd,
|
||||||
|
@ -131,12 +131,40 @@ function randBetween (max, min = 1n) {
|
||||||
const bitLen = bitLength(interval)
|
const bitLen = bitLength(interval)
|
||||||
let rnd
|
let rnd
|
||||||
do {
|
do {
|
||||||
const buf = randBits(bitLen)
|
const buf = randBitsSync(bitLen)
|
||||||
rnd = fromBuffer(buf)
|
rnd = fromBuffer(buf)
|
||||||
} while (rnd > interval)
|
} while (rnd > interval)
|
||||||
return rnd + min
|
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<Buffer | Uint8Array>} 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()
|
* 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
|
* @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) {
|
if (bitLength < 1) {
|
||||||
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)
|
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) {
|
function randBytes (byteLength, forceLength = false) {
|
||||||
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
||||||
|
|
||||||
let buf
|
|
||||||
/* eslint-disable no-lone-blocks */
|
/* eslint-disable no-lone-blocks */
|
||||||
{ // browser
|
{ // browser
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
buf = new Uint8Array(byteLength)
|
const buf = new Uint8Array(byteLength)
|
||||||
self.crypto.getRandomValues(buf)
|
self.crypto.getRandomValues(buf)
|
||||||
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
resolve(buf)
|
resolve(buf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/* eslint-disable no-lone-blocks */
|
/* eslint-enable no-lone-blocks */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -200,15 +227,16 @@ function randBytes (byteLength, forceLength = false) {
|
||||||
function randBytesSync (byteLength, forceLength = false) {
|
function randBytesSync (byteLength, forceLength = false) {
|
||||||
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
||||||
|
|
||||||
let buf
|
/* eslint-disable no-lone-blocks */
|
||||||
{ // browser
|
{ // browser
|
||||||
buf = new Uint8Array(byteLength)
|
const buf = new Uint8Array(byteLength)
|
||||||
self.crypto.getRandomValues(buf)
|
self.crypto.getRandomValues(buf)
|
||||||
}
|
|
||||||
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-lone-blocks */
|
||||||
|
}
|
||||||
|
|
||||||
/* HELPER FUNCTIONS */
|
/* HELPER FUNCTIONS */
|
||||||
|
|
||||||
|
@ -223,7 +251,7 @@ function fromBuffer (buf) {
|
||||||
|
|
||||||
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 ${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
|
const onmessage = async function (event) { // Let's start once we are called
|
||||||
// event.data = {rnd: <bigint>, iterations: <number>}
|
// event.data = {rnd: <bigint>, iterations: <number>}
|
||||||
|
@ -581,4 +609,4 @@ function _isProbablyPrime (w, iterations = 16) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export { isProbablyPrime, prime, primeSync, randBetween, randBits, randBytes, randBytesSync }
|
export { isProbablyPrime, prime, primeSync, randBetween, randBits, randBitsSync, randBytes, randBytesSync }
|
||||||
|
|
|
@ -82,7 +82,7 @@ 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
|
||||||
const buf = randBits(bitLength, true)
|
const buf = randBitsSync(bitLength, true)
|
||||||
const rnd = fromBuffer(buf)
|
const rnd = fromBuffer(buf)
|
||||||
try {
|
try {
|
||||||
newWorker.postMessage({
|
newWorker.postMessage({
|
||||||
|
@ -107,7 +107,7 @@ function prime (bitLength, iterations = 16) {
|
||||||
}
|
}
|
||||||
/* eslint-enable no-lone-blocks */
|
/* eslint-enable no-lone-blocks */
|
||||||
for (let i = 0; i < workerList.length; i++) {
|
for (let i = 0; i < workerList.length; i++) {
|
||||||
const buf = randBits(bitLength, true)
|
const buf = randBitsSync(bitLength, true)
|
||||||
const rnd = fromBuffer(buf)
|
const rnd = fromBuffer(buf)
|
||||||
workerList[i].postMessage({
|
workerList[i].postMessage({
|
||||||
rnd: rnd,
|
rnd: rnd,
|
||||||
|
@ -149,12 +149,40 @@ function randBetween (max, min = 1n) {
|
||||||
const bitLen = bigintModArith.bitLength(interval)
|
const bitLen = bigintModArith.bitLength(interval)
|
||||||
let rnd
|
let rnd
|
||||||
do {
|
do {
|
||||||
const buf = randBits(bitLen)
|
const buf = randBitsSync(bitLen)
|
||||||
rnd = fromBuffer(buf)
|
rnd = fromBuffer(buf)
|
||||||
} while (rnd > interval)
|
} while (rnd > interval)
|
||||||
return rnd + min
|
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<Buffer | Uint8Array>} 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()
|
* 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
|
* @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) {
|
if (bitLength < 1) {
|
||||||
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)
|
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) {
|
function randBytes (byteLength, forceLength = false) {
|
||||||
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
||||||
|
|
||||||
let buf
|
|
||||||
/* eslint-disable no-lone-blocks */
|
/* eslint-disable no-lone-blocks */
|
||||||
{ // node
|
{ // node
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
buf = Buffer.alloc(byteLength)
|
const buf = Buffer.alloc(byteLength)
|
||||||
return crypto.randomFill(buf, function (resolve) {
|
return crypto.randomFill(buf, function (resolve) {
|
||||||
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
resolve(buf)
|
resolve(buf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/* eslint-disable no-lone-blocks */
|
/* eslint-enable no-lone-blocks */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -218,16 +245,17 @@ function randBytes (byteLength, forceLength = false) {
|
||||||
function randBytesSync (byteLength, forceLength = false) {
|
function randBytesSync (byteLength, forceLength = false) {
|
||||||
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
||||||
|
|
||||||
let buf
|
/* eslint-disable no-lone-blocks */
|
||||||
{ // node
|
{ // node
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
buf = Buffer.alloc(byteLength)
|
const buf = Buffer.alloc(byteLength)
|
||||||
crypto.randomFillSync(buf)
|
crypto.randomFillSync(buf)
|
||||||
}
|
|
||||||
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-lone-blocks */
|
||||||
|
}
|
||||||
|
|
||||||
/* HELPER FUNCTIONS */
|
/* 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
|
let _useWorkers = true // The following is just to check whether Node.js can use workers
|
||||||
|
/* eslint-disable no-lone-blocks */
|
||||||
{ // Node.js
|
{ // Node.js
|
||||||
_useWorkers = (function _workers () {
|
_useWorkers = (function _workers () {
|
||||||
try {
|
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
|
if (_useWorkers) { // node.js with support for workers
|
||||||
const { parentPort, isMainThread } = require('worker_threads')
|
const { parentPort, isMainThread } = require('worker_threads')
|
||||||
|
@ -671,5 +701,6 @@ exports.prime = prime
|
||||||
exports.primeSync = primeSync
|
exports.primeSync = primeSync
|
||||||
exports.randBetween = randBetween
|
exports.randBetween = randBetween
|
||||||
exports.randBits = randBits
|
exports.randBits = randBits
|
||||||
|
exports.randBitsSync = randBitsSync
|
||||||
exports.randBytes = randBytes
|
exports.randBytes = randBytes
|
||||||
exports.randBytesSync = randBytesSync
|
exports.randBytesSync = randBytesSync
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"/test/browser/",
|
"/test/browser/",
|
||||||
"/lib/index.browser.bundle.js",
|
"/lib/index.browser.bundle.js",
|
||||||
"/lib/index.browser.bundle.mod.js"
|
"/lib/index.browser.bundle.min.mod.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -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 | bigint} w An integer to be tested for primality
|
||||||
* @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
|
* @param {number} [iterations = 16] The number of iterations for the primality test. The value shall be consistent with Table C.1, C.2 or C.3
|
||||||
*
|
*
|
||||||
* @return {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
|
* @returns {Promise<boolean>} A promise that resolves to a boolean that is either true (a probably prime number) or false (definitely composite)
|
||||||
*/
|
*/
|
||||||
export async function isProbablyPrime (w, iterations = 16) {
|
export function isProbablyPrime (w, iterations = 16) {
|
||||||
if (typeof w === 'number') {
|
if (typeof w === 'number') {
|
||||||
w = BigInt(w)
|
w = BigInt(w)
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ export 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
|
||||||
const buf = randBits(bitLength, true)
|
const buf = randBitsSync(bitLength, true)
|
||||||
const rnd = fromBuffer(buf)
|
const rnd = fromBuffer(buf)
|
||||||
try {
|
try {
|
||||||
newWorker.postMessage({
|
newWorker.postMessage({
|
||||||
|
@ -130,7 +130,7 @@ export function prime (bitLength, iterations = 16) {
|
||||||
}
|
}
|
||||||
/* eslint-enable no-lone-blocks */
|
/* eslint-enable no-lone-blocks */
|
||||||
for (let i = 0; i < workerList.length; i++) {
|
for (let i = 0; i < workerList.length; i++) {
|
||||||
const buf = randBits(bitLength, true)
|
const buf = randBitsSync(bitLength, true)
|
||||||
const rnd = fromBuffer(buf)
|
const rnd = fromBuffer(buf)
|
||||||
workerList[i].postMessage({
|
workerList[i].postMessage({
|
||||||
rnd: rnd,
|
rnd: rnd,
|
||||||
|
@ -172,12 +172,40 @@ export function randBetween (max, min = 1n) {
|
||||||
const bitLen = bitLength(interval)
|
const bitLen = bitLength(interval)
|
||||||
let rnd
|
let rnd
|
||||||
do {
|
do {
|
||||||
const buf = randBits(bitLen)
|
const buf = randBitsSync(bitLen)
|
||||||
rnd = fromBuffer(buf)
|
rnd = fromBuffer(buf)
|
||||||
} while (rnd > interval)
|
} while (rnd > interval)
|
||||||
return rnd + min
|
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<Buffer | Uint8Array>} 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()
|
* 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
|
* @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) {
|
if (bitLength < 1) {
|
||||||
throw new RangeError(`bitLength MUST be > 0 and it is ${bitLength}`)
|
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) {
|
export function randBytes (byteLength, forceLength = false) {
|
||||||
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
||||||
|
|
||||||
let buf
|
|
||||||
/* eslint-disable no-lone-blocks */
|
/* eslint-disable no-lone-blocks */
|
||||||
if (!process.browser) { // node
|
if (!process.browser) { // node
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
buf = Buffer.alloc(byteLength)
|
const buf = Buffer.alloc(byteLength)
|
||||||
return crypto.randomFill(buf, function (resolve) {
|
return crypto.randomFill(buf, function (resolve) {
|
||||||
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
|
@ -228,14 +255,14 @@ export function randBytes (byteLength, forceLength = false) {
|
||||||
})
|
})
|
||||||
} else { // browser
|
} else { // browser
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
buf = new Uint8Array(byteLength)
|
const buf = new Uint8Array(byteLength)
|
||||||
self.crypto.getRandomValues(buf)
|
self.crypto.getRandomValues(buf)
|
||||||
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
// If fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
resolve(buf)
|
resolve(buf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/* eslint-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) {
|
export function randBytesSync (byteLength, forceLength = false) {
|
||||||
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
if (byteLength < 1) { throw new RangeError(`byteLength MUST be > 0 and it is ${byteLength}`) }
|
||||||
|
|
||||||
let buf
|
/* eslint-disable no-lone-blocks */
|
||||||
if (!process.browser) { // node
|
if (!process.browser) { // node
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
buf = Buffer.alloc(byteLength)
|
const buf = Buffer.alloc(byteLength)
|
||||||
crypto.randomFillSync(buf)
|
crypto.randomFillSync(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 fixed length is required we put the first bit to 1 -> to get the necessary bitLength
|
||||||
if (forceLength) { buf[0] = buf[0] | 128 }
|
if (forceLength) { buf[0] = buf[0] | 128 }
|
||||||
return buf
|
return buf
|
||||||
|
} else { // browser
|
||||||
|
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 */
|
/* HELPER FUNCTIONS */
|
||||||
|
@ -276,7 +307,7 @@ function fromBuffer (buf) {
|
||||||
|
|
||||||
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 ${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
|
const onmessage = async function (event) { // Let's start once we are called
|
||||||
// event.data = {rnd: <bigint>, iterations: <number>}
|
// event.data = {rnd: <bigint>, iterations: <number>}
|
||||||
|
@ -635,6 +666,7 @@ function _isProbablyPrime (w, iterations = 16) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let _useWorkers = true // The following is just to check whether Node.js can use workers
|
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
|
if (!process.browser) { // Node.js
|
||||||
_useWorkers = (function _workers () {
|
_useWorkers = (function _workers () {
|
||||||
try {
|
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
|
if (!process.browser && _useWorkers) { // node.js with support for workers
|
||||||
const { parentPort, isMainThread } = require('worker_threads')
|
const { parentPort, isMainThread } = require('worker_threads')
|
||||||
|
|
|
@ -39,6 +39,15 @@ export function primeSync(bitLength: number, iterations?: number): bigint;
|
||||||
* @returns {bigint} A cryptographically secure random bigint between [min,max]
|
* @returns {bigint} A cryptographically secure random bigint between [min,max]
|
||||||
*/
|
*/
|
||||||
export function randBetween(max: bigint, min?: bigint): bigint;
|
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<Buffer | Uint8Array>} 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<Uint8Array | Buffer>;
|
||||||
/**
|
/**
|
||||||
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
|
* Secure random bits for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
|
||||||
*
|
*
|
||||||
|
@ -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
|
* @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()
|
* Secure random bytes for both node and browsers. Node version uses crypto.randomFill() and browser one self.crypto.getRandomValues()
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue