From ff04699b17abd3b9a3434d7b4bdf2f7a98dcbf46 Mon Sep 17 00:00:00 2001 From: LiosK Date: Tue, 1 Aug 2023 21:06:31 +0900 Subject: [PATCH] refactor: defer detection Web Crypto API until creation of Scru128Generator --- CHANGELOG.md | 8 ++++++++ src/index.ts | 49 +++++++++++++++++++++++++------------------------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ccb94..a84fd3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## v3.0.3 - unreleased + +### Maintenance + +- Refactored default random number generator to defer detection Web Crypto API + until creation of `Scru128Generator` +- Fixed wrong test case + ## v3.0.2 - 2023-07-17 Most notably, v3 switches the letter case of generated IDs from uppercase (e.g., diff --git a/src/index.ts b/src/index.ts index dfbc679..443472f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -471,7 +471,7 @@ export class Scru128Generator { /** Returns a 32-bit random unsigned integer. */ nextUint32(): number; }) { - this.rng = randomNumberGenerator || new DefaultRandom(); + this.rng = randomNumberGenerator || getDefaultRandom(); } /** @@ -620,37 +620,38 @@ export class Scru128Generator { /** A global flag to force use of cryptographically strong RNG. */ declare const SCRU128_DENY_WEAK_RNG: boolean; -/** Stores `crypto.getRandomValues()` available in the environment. */ -let getRandomValues: (buffer: Uint32Array) => Uint32Array = (buffer) => { - // fall back on Math.random() unless the flag is set to true - if (typeof SCRU128_DENY_WEAK_RNG !== "undefined" && SCRU128_DENY_WEAK_RNG) { - throw new Error("no cryptographically strong RNG available"); - } - - for (let i = 0; i < buffer.length; i++) { - buffer[i] = - Math.trunc(Math.random() * 0x1_0000) * 0x1_0000 + - Math.trunc(Math.random() * 0x1_0000); +/** Returns the default random number generator available in the environment. */ +const getDefaultRandom = (): { nextUint32(): number } => { + // detect Web Crypto API + if ( + typeof crypto !== "undefined" && + typeof crypto.getRandomValues !== "undefined" + ) { + return new BufferedCryptoRandom(); + } else { + // fall back on Math.random() unless the flag is set to true + if (typeof SCRU128_DENY_WEAK_RNG !== "undefined" && SCRU128_DENY_WEAK_RNG) { + throw new Error("no cryptographically strong RNG available"); + } + return { + nextUint32: (): number => + Math.trunc(Math.random() * 0x1_0000) * 0x1_0000 + + Math.trunc(Math.random() * 0x1_0000), + }; } - return buffer; }; -// detect Web Crypto API -if (typeof crypto !== "undefined" && crypto.getRandomValues) { - getRandomValues = (buffer) => crypto.getRandomValues(buffer); -} - /** - * Wraps `crypto.getRandomValues()` and compatibles to enable buffering; this - * uses a small buffer by default to avoid unbearable throughput decline in some - * environments as well as the waste of time and space for unused values. + * Wraps `crypto.getRandomValues()` to enable buffering; this uses a small + * buffer by default to avoid both unbearable throughput decline in some + * environments and the waste of time and space for unused values. */ -class DefaultRandom { +class BufferedCryptoRandom { private readonly buffer = new Uint32Array(8); - private cursor = Infinity; + private cursor = 0xffff; nextUint32(): number { if (this.cursor >= this.buffer.length) { - getRandomValues(this.buffer); + crypto.getRandomValues(this.buffer); this.cursor = 0; } return this.buffer[this.cursor++];