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