Last active
March 10, 2022 07:17
-
-
Save intrnl/13e02c9429de8124ebd131e086ba1e06 to your computer and use it in GitHub Desktop.
Secure random number generators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* https://github.com/EFForg/OpenWireless/blob/master/app/js/diceware.js | |
* @param {number} min | |
* @param {number} max | |
* @returns {number} | |
*/ | |
function random (min, max) { | |
const range = max - min; | |
let rval = 0; | |
const bits = Math.ceil(Math.log2(range)); | |
if (bits > 53) { | |
throw new Error('cannot generate random number larger than 53 bits'); | |
} | |
const bytes = Math.ceil(bits / 8); | |
const mask = (2 ** bits) - 1; | |
const arr = new Uint8Array(bytes); | |
crypto.getRandomValues(arr); | |
let p = (bytes - 1) * 8; | |
for (let i = 0; i < bytes; i++) { | |
rval += arr[i] * (2 ** p); | |
p -= 8; | |
} | |
rval = rval & mask; | |
if (rval >= range) { | |
return random(min, max); | |
} | |
return rval + min; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Cache size must be divisible by 6 because each attempt uses 6 bytes. | |
const randlen = 6 * 1024; | |
const randbuf = new Uint8Array(randlen); | |
let randoffset = randlen; | |
const RAND_MAX = 0xFFFF_FFFF_FFFF; | |
/** | |
* https://github.com/nodejs/node/blob/e8697cf/lib/internal/crypto/random.js#L203 | |
* @param {number} min | |
* @param {number} max | |
* @returns {number} | |
*/ | |
function randomint (min, max) { | |
const range = max - min; | |
if (!(range <= RAND_MAX)) { | |
throw new Error(`${max} - ${min} <= ${RAND_MAX}`); | |
} | |
const limit = RAND_MAX - (RAND_MAX % range); | |
while (true) { | |
if (randoffset >= randlen) { | |
crypto.getRandomValues(randbuf); | |
randoffset = 0; | |
} | |
// byte length = 6 | |
const x = readUint48BE(randbuf, randoffset); | |
randoffset += 6; | |
if (x < limit) { | |
const n = (x % range) + min; | |
return n; | |
} | |
} | |
} | |
function readUint48BE (buf, offset) { | |
const first = buf[offset]; | |
const last = buf[offset + 5]; | |
return ( | |
(first * 2 ** 8 + buf[++offset]) * 2 ** 32 + | |
buf[++offset] * 2 ** 24 + | |
buf[++offset] * 2 ** 16 + | |
buf[++offset] * 2 ** 8 + | |
last | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* https://stackoverflow.com/a/41452318 | |
* @param {number} min | |
* @param {number} max | |
* @returns {number} | |
*/ | |
function randrange (min, max) { | |
const range = max - min; | |
const bytes = Math.ceil(Math.log2(range) / 8); | |
if (range <= 0 || !bytes) { | |
return min; | |
} | |
const maxn = 256 ** bytes; | |
const buf = new Uint8Array(bytes); | |
while (true) { | |
crypto.getRandomValues(buf); | |
let val = 0; | |
for (let idx = 0; idx < bytes; idx++) { | |
val = (val << 8) + buf[idx]; | |
} | |
if (val < maxn - (maxn % range)) { | |
return min + (val % range); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment