Skip to content

Commit

Permalink
refactor: defer detection Web Crypto API until creation of Scru128Gen…
Browse files Browse the repository at this point in the history
…erator
  • Loading branch information
LiosK committed Aug 1, 2023
1 parent 3c70b96 commit ff04699
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 24 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.,
Expand Down
49 changes: 25 additions & 24 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down Expand Up @@ -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++];
Expand Down

0 comments on commit ff04699

Please sign in to comment.