xref: /expo/packages/expo-random/src/Random.ts (revision ca5301c7)
1import { toByteArray } from 'base64-js';
2import { UnavailabilityError } from 'expo-modules-core';
3
4import ExpoRandom from './ExpoRandom';
5
6function assertByteCount(value: any, methodName: string): void {
7  if (
8    typeof value !== 'number' ||
9    isNaN(value) ||
10    Math.floor(value) < 0 ||
11    Math.floor(value) > 1024
12  ) {
13    throw new TypeError(
14      `expo-random: ${methodName}(${value}) expected a valid number from range 0...1024`
15    );
16  }
17}
18
19// @needsAudit
20/**
21 * Generates completely random bytes using native implementations. The `byteCount` property
22 * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.
23 * Falls back to `Math.random` during development to prevent issues with React Native Debugger.
24 * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.
25 * @return An array of random bytes with the same length as the `byteCount`.
26 */
27export function getRandomBytes(byteCount: number): Uint8Array {
28  assertByteCount(byteCount, 'getRandomBytes');
29  const validByteCount = Math.floor(byteCount);
30  if (__DEV__) {
31    if (!global.nativeCallSyncHook || global.__REMOTEDEV__) {
32      // remote javascript debugging is enabled
33      const array = new Uint8Array(validByteCount);
34      for (let i = 0; i < validByteCount; i++) {
35        array[i] = Math.floor(Math.random() * 256);
36      }
37      return array;
38    }
39  }
40  if (ExpoRandom.getRandomBytes) {
41    return ExpoRandom.getRandomBytes(validByteCount);
42  } else if (ExpoRandom.getRandomBase64String) {
43    const base64 = ExpoRandom.getRandomBase64String(validByteCount);
44    return toByteArray(base64);
45  } else {
46    throw new UnavailabilityError('expo-random', 'getRandomBytes');
47  }
48}
49
50// @needsAudit
51/**
52 * Generates completely random bytes using native implementations. The `byteCount` property
53 * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.
54 * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.
55 * @return A promise that fulfills with an array of random bytes with the same length as the `byteCount`.
56 */
57export async function getRandomBytesAsync(byteCount: number): Promise<Uint8Array> {
58  assertByteCount(byteCount, 'getRandomBytesAsync');
59  const validByteCount = Math.floor(byteCount);
60  if (ExpoRandom.getRandomBytesAsync) {
61    return await ExpoRandom.getRandomBytesAsync(validByteCount);
62  } else if (ExpoRandom.getRandomBase64StringAsync) {
63    const base64 = await ExpoRandom.getRandomBase64StringAsync(validByteCount);
64    return toByteArray(base64);
65  } else {
66    throw new UnavailabilityError('expo-random', 'getRandomBytesAsync');
67  }
68}
69