summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal/crypto/_randomBytes.ts
blob: e1dc5c5ac8fc5ddf33152a95d464a03c415dc58b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials

import { Buffer } from "node:buffer";

export const MAX_RANDOM_VALUES = 65536;
export const MAX_SIZE = 4294967295;

function generateRandomBytes(size: number) {
  if (size > MAX_SIZE) {
    throw new RangeError(
      `The value of "size" is out of range. It must be >= 0 && <= ${MAX_SIZE}. Received ${size}`,
    );
  }

  const bytes = Buffer.allocUnsafe(size);

  //Work around for getRandomValues max generation
  if (size > MAX_RANDOM_VALUES) {
    for (let generated = 0; generated < size; generated += MAX_RANDOM_VALUES) {
      globalThis.crypto.getRandomValues(
        bytes.slice(generated, generated + MAX_RANDOM_VALUES),
      );
    }
  } else {
    globalThis.crypto.getRandomValues(bytes);
  }

  return bytes;
}

/**
 * @param size Buffer length, must be equal or greater than zero
 */
export default function randomBytes(size: number): Buffer;
export default function randomBytes(
  size: number,
  cb?: (err: Error | null, buf?: Buffer) => void,
): void;
export default function randomBytes(
  size: number,
  cb?: (err: Error | null, buf?: Buffer) => void,
): Buffer | void {
  if (typeof cb === "function") {
    let err: Error | null = null, bytes: Buffer;
    try {
      bytes = generateRandomBytes(size);
    } catch (e) {
      //NodeJS nonsense
      //If the size is out of range it will throw sync, otherwise throw async
      if (
        e instanceof RangeError &&
        e.message.includes('The value of "size" is out of range')
      ) {
        throw e;
      } else if (e instanceof Error) {
        err = e;
      } else {
        err = new Error("[non-error thrown]");
      }
    }
    setTimeout(() => {
      if (err) {
        cb(err);
      } else {
        cb(null, bytes);
      }
    }, 0);
  } else {
    return generateRandomBytes(size);
  }
}