expo-crypto 11.0.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 12.1.0 — 2022-12-30
14
+
15
+ ### 🎉 New features
16
+
17
+ - Added a `randomUUID` method to get a random UUIDv4 string. ([#20274](https://github.com/expo/expo/pull/20274) by [@aleqsio](https://github.com/aleqsio))
18
+ - Added a `getRandomValues` method to fill typed arrays. ([#20257](https://github.com/expo/expo/pull/20257) by [@aleqsio](https://github.com/aleqsio))
19
+ - Ported over `getRandomBytes`, `getRandomBytesAsync` methods from `expo-random`. ([#20217](https://github.com/expo/expo/pull/20217) by [@aleqsio](https://github.com/aleqsio))
20
+
21
+ ## 12.0.0 — 2022-10-25
22
+
23
+ ### 🛠 Breaking changes
24
+
25
+ - Bumped iOS deployment target to 13.0 and deprecated support for iOS 12. ([#18873](https://github.com/expo/expo/pull/18873) by [@tsapeta](https://github.com/tsapeta))
26
+
13
27
  ## 11.0.0 — 2022-07-07
14
28
 
15
29
  ### 🎉 New features
package/README.md CHANGED
@@ -4,7 +4,7 @@ Provides cryptography primitives.
4
4
 
5
5
  # API documentation
6
6
 
7
- - [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/crypto.md)
7
+ - [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/crypto.mdx)
8
8
  - [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/crypto/)
9
9
 
10
10
  # Installation in managed Expo projects
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '11.0.0'
6
+ version = '12.1.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -74,7 +74,7 @@ android {
74
74
  minSdkVersion safeExtGet("minSdkVersion", 21)
75
75
  targetSdkVersion safeExtGet("targetSdkVersion", 31)
76
76
  versionCode 25
77
- versionName "11.0.0"
77
+ versionName "12.1.0"
78
78
  }
79
79
  lintOptions {
80
80
  abortOnError false
@@ -3,14 +3,31 @@ package expo.modules.crypto
3
3
  import android.util.Base64
4
4
  import expo.modules.kotlin.modules.Module
5
5
  import expo.modules.kotlin.modules.ModuleDefinition
6
+ import expo.modules.kotlin.typedarray.TypedArray
6
7
  import java.security.MessageDigest
8
+ import java.security.SecureRandom
9
+ import java.util.UUID
7
10
 
8
11
  class CryptoModule : Module() {
12
+ private val secureRandom by lazy { SecureRandom() }
13
+
9
14
  override fun definition() = ModuleDefinition {
10
15
  Name("ExpoCrypto")
11
16
 
12
17
  Function("digestString", this@CryptoModule::digestString)
13
18
  AsyncFunction("digestStringAsync", this@CryptoModule::digestString)
19
+ Function("getRandomBase64String", this@CryptoModule::getRandomBase64String)
20
+ AsyncFunction("getRandomBase64StringAsync", this@CryptoModule::getRandomBase64String)
21
+ Function("getRandomValues", this@CryptoModule::getRandomValues)
22
+ Function("randomUUID") {
23
+ UUID.randomUUID().toString()
24
+ }
25
+ }
26
+
27
+ private fun getRandomBase64String(randomByteCount: Int): String {
28
+ val output = ByteArray(randomByteCount)
29
+ secureRandom.nextBytes(output)
30
+ return Base64.encodeToString(output, Base64.NO_WRAP)
14
31
  }
15
32
 
16
33
  private fun digestString(algorithm: DigestAlgorithm, data: String, options: DigestOptions): String {
@@ -30,4 +47,10 @@ class CryptoModule : Module() {
30
47
  }
31
48
  }
32
49
  }
50
+
51
+ private fun getRandomValues(typedArray: TypedArray) {
52
+ val array = ByteArray(typedArray.byteLength)
53
+ secureRandom.nextBytes(array)
54
+ typedArray.write(array, typedArray.byteOffset, typedArray.byteLength)
55
+ }
33
56
  }
@@ -1,6 +1,8 @@
1
1
  package expo.modules.crypto
2
2
 
3
- enum class DigestAlgorithm(val value: String) {
3
+ import expo.modules.kotlin.types.Enumerable
4
+
5
+ enum class DigestAlgorithm(val value: String) : Enumerable {
4
6
  MD5("MD5"),
5
7
  SHA1("SHA-1"),
6
8
  SHA256("SHA-256"),
@@ -2,12 +2,13 @@ package expo.modules.crypto
2
2
 
3
3
  import expo.modules.kotlin.records.Field
4
4
  import expo.modules.kotlin.records.Record
5
+ import expo.modules.kotlin.types.Enumerable
5
6
 
6
7
  class DigestOptions : Record {
7
8
  @Field
8
9
  var encoding: Encoding = Encoding.HEX
9
10
 
10
- enum class Encoding(val value: String) {
11
+ enum class Encoding(val value: String) : Enumerable {
11
12
  HEX("hex"),
12
13
  BASE64("base64")
13
14
  }
package/build/Crypto.d.ts CHANGED
@@ -1,5 +1,21 @@
1
+ import { UintBasedTypedArray, IntBasedTypedArray } from 'expo-modules-core';
1
2
  import { CryptoDigestAlgorithm, CryptoDigestOptions, Digest } from './Crypto.types';
2
3
  export * from './Crypto.types';
4
+ /**
5
+ * Generates completely random bytes using native implementations. The `byteCount` property
6
+ * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.
7
+ * Falls back to `Math.random` during development to prevent issues with React Native Debugger.
8
+ * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.
9
+ * @return An array of random bytes with the same length as the `byteCount`.
10
+ */
11
+ export declare function getRandomBytes(byteCount: number): Uint8Array;
12
+ /**
13
+ * Generates completely random bytes using native implementations. The `byteCount` property
14
+ * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.
15
+ * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.
16
+ * @return A promise that fulfills with an array of random bytes with the same length as the `byteCount`.
17
+ */
18
+ export declare function getRandomBytesAsync(byteCount: number): Promise<Uint8Array>;
3
19
  /**
4
20
  * The `digestStringAsync()` method of `Crypto` generates a digest of the supplied `data` string with the provided digest `algorithm`.
5
21
  * A digest is a short fixed-length value derived from some variable-length input. **Cryptographic digests** should exhibit _collision-resistance_,
@@ -21,4 +37,30 @@ export * from './Crypto.types';
21
37
  * ```
22
38
  */
23
39
  export declare function digestStringAsync(algorithm: CryptoDigestAlgorithm, data: string, options?: CryptoDigestOptions): Promise<Digest>;
40
+ /**
41
+ * The `getRandomValues()` method of `Crypto` fills a provided `TypedArray` with cryptographically secure random values.
42
+ *
43
+ * @param typedArray An integer based [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) to fill with cryptographically secure random values. It modifies the input array in place.
44
+ * @return The input array filled with cryptographically secure random values.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const byteArray = new Uint8Array(16);
49
+ * Crypto.getRandomValues(byteArray);
50
+ * console.log('Your lucky bytes: ' + byteArray);
51
+ * ```
52
+ */
53
+ export declare function getRandomValues<T extends IntBasedTypedArray | UintBasedTypedArray>(typedArray: T): T;
54
+ /**
55
+ * The `randomUUID()` method returns a unique identifier based on the V4 UUID spec (RFC4122).
56
+ * It uses cryptographically secure random values to generate the UUID.
57
+ *
58
+ * @return A string containing a newly generated UUIDv4 identifier
59
+ * @example
60
+ * ```ts
61
+ * const UUID = Crypto.randomUUID();
62
+ * console.log('Your UUID: ' + UUID);
63
+ * ```
64
+ */
65
+ export declare function randomUUID(): any;
24
66
  //# sourceMappingURL=Crypto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../src/Crypto.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAkB,mBAAmB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGpG,cAAc,gBAAgB,CAAC;AAqC/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAsD,GAC9D,OAAO,CAAC,MAAM,CAAC,CAUjB"}
1
+ {"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../src/Crypto.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEjG,OAAO,EAAE,qBAAqB,EAAkB,mBAAmB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAKpG,cAAc,gBAAgB,CAAC;AAW/B;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAqB5D;AAGD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAWhF;AA0CD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAsD,GAC9D,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,kBAAkB,GAAG,mBAAmB,EAChF,UAAU,EAAE,CAAC,GACZ,CAAC,CAGH;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,QAEzB"}
package/build/Crypto.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { toByteArray } from 'base64-js';
1
2
  import { UnavailabilityError } from 'expo-modules-core';
2
3
  import { CryptoDigestAlgorithm, CryptoEncoding } from './Crypto.types';
3
4
  import ExpoCrypto from './ExpoCrypto';
@@ -8,6 +9,67 @@ class CryptoError extends TypeError {
8
9
  super(`expo-crypto: ${message}`);
9
10
  }
10
11
  }
12
+ // @needsAudit
13
+ /**
14
+ * Generates completely random bytes using native implementations. The `byteCount` property
15
+ * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.
16
+ * Falls back to `Math.random` during development to prevent issues with React Native Debugger.
17
+ * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.
18
+ * @return An array of random bytes with the same length as the `byteCount`.
19
+ */
20
+ export function getRandomBytes(byteCount) {
21
+ assertByteCount(byteCount, 'getRandomBytes');
22
+ const validByteCount = Math.floor(byteCount);
23
+ if (__DEV__) {
24
+ if (!global.nativeCallSyncHook || global.__REMOTEDEV__) {
25
+ // remote javascript debugging is enabled
26
+ const array = new Uint8Array(validByteCount);
27
+ for (let i = 0; i < validByteCount; i++) {
28
+ array[i] = Math.floor(Math.random() * 256);
29
+ }
30
+ return array;
31
+ }
32
+ }
33
+ if (ExpoCrypto.getRandomBytes) {
34
+ return ExpoCrypto.getRandomBytes(validByteCount);
35
+ }
36
+ else if (ExpoCrypto.getRandomBase64String) {
37
+ const base64 = ExpoCrypto.getRandomBase64String(validByteCount);
38
+ return toByteArray(base64);
39
+ }
40
+ else {
41
+ throw new UnavailabilityError('expo-crypto', 'getRandomBytes');
42
+ }
43
+ }
44
+ // @needsAudit
45
+ /**
46
+ * Generates completely random bytes using native implementations. The `byteCount` property
47
+ * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.
48
+ * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.
49
+ * @return A promise that fulfills with an array of random bytes with the same length as the `byteCount`.
50
+ */
51
+ export async function getRandomBytesAsync(byteCount) {
52
+ assertByteCount(byteCount, 'getRandomBytesAsync');
53
+ const validByteCount = Math.floor(byteCount);
54
+ if (ExpoCrypto.getRandomBytesAsync) {
55
+ return await ExpoCrypto.getRandomBytesAsync(validByteCount);
56
+ }
57
+ else if (ExpoCrypto.getRandomBase64StringAsync) {
58
+ const base64 = await ExpoCrypto.getRandomBase64StringAsync(validByteCount);
59
+ return toByteArray(base64);
60
+ }
61
+ else {
62
+ throw new UnavailabilityError('expo-crypto', 'getRandomBytesAsync');
63
+ }
64
+ }
65
+ function assertByteCount(value, methodName) {
66
+ if (typeof value !== 'number' ||
67
+ isNaN(value) ||
68
+ Math.floor(value) < 0 ||
69
+ Math.floor(value) > 1024) {
70
+ throw new TypeError(`expo-crypto: ${methodName}(${value}) expected a valid number from range 0...1024`);
71
+ }
72
+ }
11
73
  function assertAlgorithm(algorithm) {
12
74
  if (!Object.values(CryptoDigestAlgorithm).includes(algorithm)) {
13
75
  throw new CryptoError(`Invalid algorithm provided. Expected one of: CryptoDigestAlgorithm.${Object.keys(CryptoDigestAlgorithm).join(', AlgCryptoDigestAlgorithmorithm.')}`);
@@ -53,4 +115,35 @@ export async function digestStringAsync(algorithm, data, options = { encoding: C
53
115
  assertEncoding(options.encoding);
54
116
  return await ExpoCrypto.digestStringAsync(algorithm, data, options);
55
117
  }
118
+ /**
119
+ * The `getRandomValues()` method of `Crypto` fills a provided `TypedArray` with cryptographically secure random values.
120
+ *
121
+ * @param typedArray An integer based [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) to fill with cryptographically secure random values. It modifies the input array in place.
122
+ * @return The input array filled with cryptographically secure random values.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const byteArray = new Uint8Array(16);
127
+ * Crypto.getRandomValues(byteArray);
128
+ * console.log('Your lucky bytes: ' + byteArray);
129
+ * ```
130
+ */
131
+ export function getRandomValues(typedArray) {
132
+ ExpoCrypto.getRandomValues(typedArray);
133
+ return typedArray;
134
+ }
135
+ /**
136
+ * The `randomUUID()` method returns a unique identifier based on the V4 UUID spec (RFC4122).
137
+ * It uses cryptographically secure random values to generate the UUID.
138
+ *
139
+ * @return A string containing a newly generated UUIDv4 identifier
140
+ * @example
141
+ * ```ts
142
+ * const UUID = Crypto.randomUUID();
143
+ * console.log('Your UUID: ' + UUID);
144
+ * ```
145
+ */
146
+ export function randomUUID() {
147
+ return ExpoCrypto.randomUUID();
148
+ }
56
149
  //# sourceMappingURL=Crypto.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Crypto.js","sourceRoot":"","sources":["../src/Crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAA+B,MAAM,gBAAgB,CAAC;AACpG,OAAO,UAAU,MAAM,cAAc,CAAC;AAEtC,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAY,SAAQ,SAAS;IACjC,IAAI,GAAG,YAAY,CAAC;IAEpB,YAAY,OAAe;QACzB,KAAK,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC;IACnC,CAAC;CACF;AAED,SAAS,eAAe,CAAC,SAAgC;IACvD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAC7D,MAAM,IAAI,WAAW,CACnB,sEAAsE,MAAM,CAAC,IAAI,CAC/E,qBAAqB,CACtB,CAAC,IAAI,CAAC,mCAAmC,CAAC,EAAE,CAC9C,CAAC;KACH;AACH,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,MAAM,IAAI,WAAW,CAAC,2CAA2C,CAAC,CAAC;KACpE;AACH,CAAC;AAED,SAAS,cAAc,CAAC,QAAwB;IAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACrD,MAAM,IAAI,WAAW,CACnB,8DAA8D,MAAM,CAAC,IAAI,CACvE,cAAc,CACf,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAC9B,CAAC;KACH;AACH,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAgC,EAChC,IAAY,EACZ,UAA+B,EAAE,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE;IAE/D,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE;QACjC,MAAM,IAAI,mBAAmB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;KACnE;IAED,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3B,UAAU,CAAC,IAAI,CAAC,CAAC;IACjB,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEjC,OAAO,MAAM,UAAU,CAAC,iBAAiB,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport { CryptoDigestAlgorithm, CryptoEncoding, CryptoDigestOptions, Digest } from './Crypto.types';\nimport ExpoCrypto from './ExpoCrypto';\n\nexport * from './Crypto.types';\n\nclass CryptoError extends TypeError {\n code = 'ERR_CRYPTO';\n\n constructor(message: string) {\n super(`expo-crypto: ${message}`);\n }\n}\n\nfunction assertAlgorithm(algorithm: CryptoDigestAlgorithm): void {\n if (!Object.values(CryptoDigestAlgorithm).includes(algorithm)) {\n throw new CryptoError(\n `Invalid algorithm provided. Expected one of: CryptoDigestAlgorithm.${Object.keys(\n CryptoDigestAlgorithm\n ).join(', AlgCryptoDigestAlgorithmorithm.')}`\n );\n }\n}\n\nfunction assertData(data: string): void {\n if (typeof data !== 'string') {\n throw new CryptoError(`Invalid data provided. Expected a string.`);\n }\n}\n\nfunction assertEncoding(encoding: CryptoEncoding): void {\n if (!Object.values(CryptoEncoding).includes(encoding)) {\n throw new CryptoError(\n `Invalid encoding provided. Expected one of: CryptoEncoding.${Object.keys(\n CryptoEncoding\n ).join(', CryptoEncoding.')}`\n );\n }\n}\n\n// @needsAudit\n/**\n * The `digestStringAsync()` method of `Crypto` generates a digest of the supplied `data` string with the provided digest `algorithm`.\n * A digest is a short fixed-length value derived from some variable-length input. **Cryptographic digests** should exhibit _collision-resistance_,\n * meaning that it's very difficult to generate multiple inputs that have equal digest values.\n * You can specify the returned string format as one of `CryptoEncoding`. By default, the resolved value will be formatted as a `HEX` string.\n * On web, this method can only be called from a secure origin (https) otherwise an error will be thrown.\n *\n * @param algorithm The cryptographic hash function to use to transform a block of data into a fixed-size output.\n * @param data The value that will be used to generate a digest.\n * @param options Format of the digest string. Defaults to: `CryptoDigestOptions.HEX`.\n * @return Return a Promise which fulfills with a value representing the hashed input.\n *\n * @example\n * ```ts\n * const digest = await Crypto.digestStringAsync(\n * Crypto.CryptoDigestAlgorithm.SHA512,\n * '🥓 Easy to Digest! 💙'\n * );\n * ```\n */\nexport async function digestStringAsync(\n algorithm: CryptoDigestAlgorithm,\n data: string,\n options: CryptoDigestOptions = { encoding: CryptoEncoding.HEX }\n): Promise<Digest> {\n if (!ExpoCrypto.digestStringAsync) {\n throw new UnavailabilityError('expo-crypto', 'digestStringAsync');\n }\n\n assertAlgorithm(algorithm);\n assertData(data);\n assertEncoding(options.encoding);\n\n return await ExpoCrypto.digestStringAsync(algorithm, data, options);\n}\n"]}
1
+ {"version":3,"file":"Crypto.js","sourceRoot":"","sources":["../src/Crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAA2C,MAAM,mBAAmB,CAAC;AAEjG,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAA+B,MAAM,gBAAgB,CAAC;AACpG,OAAO,UAAU,MAAM,cAAc,CAAC;AAItC,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAY,SAAQ,SAAS;IACjC,IAAI,GAAG,YAAY,CAAC;IAEpB,YAAY,OAAe;QACzB,KAAK,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC;IACnC,CAAC;CACF;AAED,cAAc;AACd;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,eAAe,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,OAAO,EAAE;QACX,IAAI,CAAC,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,aAAa,EAAE;YACtD,yCAAyC;YACzC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC;YAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE;gBACvC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;aAC5C;YACD,OAAO,KAAK,CAAC;SACd;KACF;IACD,IAAI,UAAU,CAAC,cAAc,EAAE;QAC7B,OAAO,UAAU,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;KAClD;SAAM,IAAI,UAAU,CAAC,qBAAqB,EAAE;QAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAChE,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;KAC5B;SAAM;QACL,MAAM,IAAI,mBAAmB,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;KAChE;AACH,CAAC;AAED,cAAc;AACd;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,SAAiB;IACzD,eAAe,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,mBAAmB,EAAE;QAClC,OAAO,MAAM,UAAU,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC;KAC7D;SAAM,IAAI,UAAU,CAAC,0BAA0B,EAAE;QAChD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,0BAA0B,CAAC,cAAc,CAAC,CAAC;QAC3E,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;KAC5B;SAAM;QACL,MAAM,IAAI,mBAAmB,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC;KACrE;AACH,CAAC;AAED,SAAS,eAAe,CAAC,KAAU,EAAE,UAAkB;IACrD,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,KAAK,CAAC;QACZ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,EACxB;QACA,MAAM,IAAI,SAAS,CACjB,gBAAgB,UAAU,IAAI,KAAK,+CAA+C,CACnF,CAAC;KACH;AACH,CAAC;AAED,SAAS,eAAe,CAAC,SAAgC;IACvD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAC7D,MAAM,IAAI,WAAW,CACnB,sEAAsE,MAAM,CAAC,IAAI,CAC/E,qBAAqB,CACtB,CAAC,IAAI,CAAC,mCAAmC,CAAC,EAAE,CAC9C,CAAC;KACH;AACH,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,MAAM,IAAI,WAAW,CAAC,2CAA2C,CAAC,CAAC;KACpE;AACH,CAAC;AAED,SAAS,cAAc,CAAC,QAAwB;IAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACrD,MAAM,IAAI,WAAW,CACnB,8DAA8D,MAAM,CAAC,IAAI,CACvE,cAAc,CACf,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAC9B,CAAC;KACH;AACH,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAgC,EAChC,IAAY,EACZ,UAA+B,EAAE,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE;IAE/D,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE;QACjC,MAAM,IAAI,mBAAmB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;KACnE;IAED,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3B,UAAU,CAAC,IAAI,CAAC,CAAC;IACjB,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEjC,OAAO,MAAM,UAAU,CAAC,iBAAiB,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAa;IAEb,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,UAAU,CAAC,UAAU,EAAE,CAAC;AACjC,CAAC","sourcesContent":["import { toByteArray } from 'base64-js';\nimport { UnavailabilityError, UintBasedTypedArray, IntBasedTypedArray } from 'expo-modules-core';\n\nimport { CryptoDigestAlgorithm, CryptoEncoding, CryptoDigestOptions, Digest } from './Crypto.types';\nimport ExpoCrypto from './ExpoCrypto';\n\ndeclare const global: any;\n\nexport * from './Crypto.types';\n\nclass CryptoError extends TypeError {\n code = 'ERR_CRYPTO';\n\n constructor(message: string) {\n super(`expo-crypto: ${message}`);\n }\n}\n\n// @needsAudit\n/**\n * Generates completely random bytes using native implementations. The `byteCount` property\n * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.\n * Falls back to `Math.random` during development to prevent issues with React Native Debugger.\n * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.\n * @return An array of random bytes with the same length as the `byteCount`.\n */\nexport function getRandomBytes(byteCount: number): Uint8Array {\n assertByteCount(byteCount, 'getRandomBytes');\n const validByteCount = Math.floor(byteCount);\n if (__DEV__) {\n if (!global.nativeCallSyncHook || global.__REMOTEDEV__) {\n // remote javascript debugging is enabled\n const array = new Uint8Array(validByteCount);\n for (let i = 0; i < validByteCount; i++) {\n array[i] = Math.floor(Math.random() * 256);\n }\n return array;\n }\n }\n if (ExpoCrypto.getRandomBytes) {\n return ExpoCrypto.getRandomBytes(validByteCount);\n } else if (ExpoCrypto.getRandomBase64String) {\n const base64 = ExpoCrypto.getRandomBase64String(validByteCount);\n return toByteArray(base64);\n } else {\n throw new UnavailabilityError('expo-crypto', 'getRandomBytes');\n }\n}\n\n// @needsAudit\n/**\n * Generates completely random bytes using native implementations. The `byteCount` property\n * is a `number` indicating the number of bytes to generate in the form of a `Uint8Array`.\n * @param byteCount - A number within the range from `0` to `1024`. Anything else will throw a `TypeError`.\n * @return A promise that fulfills with an array of random bytes with the same length as the `byteCount`.\n */\nexport async function getRandomBytesAsync(byteCount: number): Promise<Uint8Array> {\n assertByteCount(byteCount, 'getRandomBytesAsync');\n const validByteCount = Math.floor(byteCount);\n if (ExpoCrypto.getRandomBytesAsync) {\n return await ExpoCrypto.getRandomBytesAsync(validByteCount);\n } else if (ExpoCrypto.getRandomBase64StringAsync) {\n const base64 = await ExpoCrypto.getRandomBase64StringAsync(validByteCount);\n return toByteArray(base64);\n } else {\n throw new UnavailabilityError('expo-crypto', 'getRandomBytesAsync');\n }\n}\n\nfunction assertByteCount(value: any, methodName: string): void {\n if (\n typeof value !== 'number' ||\n isNaN(value) ||\n Math.floor(value) < 0 ||\n Math.floor(value) > 1024\n ) {\n throw new TypeError(\n `expo-crypto: ${methodName}(${value}) expected a valid number from range 0...1024`\n );\n }\n}\n\nfunction assertAlgorithm(algorithm: CryptoDigestAlgorithm): void {\n if (!Object.values(CryptoDigestAlgorithm).includes(algorithm)) {\n throw new CryptoError(\n `Invalid algorithm provided. Expected one of: CryptoDigestAlgorithm.${Object.keys(\n CryptoDigestAlgorithm\n ).join(', AlgCryptoDigestAlgorithmorithm.')}`\n );\n }\n}\n\nfunction assertData(data: string): void {\n if (typeof data !== 'string') {\n throw new CryptoError(`Invalid data provided. Expected a string.`);\n }\n}\n\nfunction assertEncoding(encoding: CryptoEncoding): void {\n if (!Object.values(CryptoEncoding).includes(encoding)) {\n throw new CryptoError(\n `Invalid encoding provided. Expected one of: CryptoEncoding.${Object.keys(\n CryptoEncoding\n ).join(', CryptoEncoding.')}`\n );\n }\n}\n\n// @needsAudit\n/**\n * The `digestStringAsync()` method of `Crypto` generates a digest of the supplied `data` string with the provided digest `algorithm`.\n * A digest is a short fixed-length value derived from some variable-length input. **Cryptographic digests** should exhibit _collision-resistance_,\n * meaning that it's very difficult to generate multiple inputs that have equal digest values.\n * You can specify the returned string format as one of `CryptoEncoding`. By default, the resolved value will be formatted as a `HEX` string.\n * On web, this method can only be called from a secure origin (https) otherwise an error will be thrown.\n *\n * @param algorithm The cryptographic hash function to use to transform a block of data into a fixed-size output.\n * @param data The value that will be used to generate a digest.\n * @param options Format of the digest string. Defaults to: `CryptoDigestOptions.HEX`.\n * @return Return a Promise which fulfills with a value representing the hashed input.\n *\n * @example\n * ```ts\n * const digest = await Crypto.digestStringAsync(\n * Crypto.CryptoDigestAlgorithm.SHA512,\n * '🥓 Easy to Digest! 💙'\n * );\n * ```\n */\nexport async function digestStringAsync(\n algorithm: CryptoDigestAlgorithm,\n data: string,\n options: CryptoDigestOptions = { encoding: CryptoEncoding.HEX }\n): Promise<Digest> {\n if (!ExpoCrypto.digestStringAsync) {\n throw new UnavailabilityError('expo-crypto', 'digestStringAsync');\n }\n\n assertAlgorithm(algorithm);\n assertData(data);\n assertEncoding(options.encoding);\n\n return await ExpoCrypto.digestStringAsync(algorithm, data, options);\n}\n\n/**\n * The `getRandomValues()` method of `Crypto` fills a provided `TypedArray` with cryptographically secure random values.\n *\n * @param typedArray An integer based [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) to fill with cryptographically secure random values. It modifies the input array in place.\n * @return The input array filled with cryptographically secure random values.\n *\n * @example\n * ```ts\n * const byteArray = new Uint8Array(16);\n * Crypto.getRandomValues(byteArray);\n * console.log('Your lucky bytes: ' + byteArray);\n * ```\n */\nexport function getRandomValues<T extends IntBasedTypedArray | UintBasedTypedArray>(\n typedArray: T\n): T {\n ExpoCrypto.getRandomValues(typedArray);\n return typedArray;\n}\n\n/**\n * The `randomUUID()` method returns a unique identifier based on the V4 UUID spec (RFC4122).\n * It uses cryptographically secure random values to generate the UUID.\n *\n * @return A string containing a newly generated UUIDv4 identifier\n * @example\n * ```ts\n * const UUID = Crypto.randomUUID();\n * console.log('Your UUID: ' + UUID);\n * ```\n */\nexport function randomUUID() {\n return ExpoCrypto.randomUUID();\n}\n"]}
@@ -42,11 +42,11 @@ export declare enum CryptoEncoding {
42
42
  */
43
43
  BASE64 = "base64"
44
44
  }
45
- export declare type CryptoDigestOptions = {
45
+ export type CryptoDigestOptions = {
46
46
  /**
47
47
  * Format the digest is returned in.
48
48
  */
49
49
  encoding: CryptoEncoding;
50
50
  };
51
- export declare type Digest = string;
51
+ export type Digest = string;
52
52
  //# sourceMappingURL=Crypto.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Crypto.types.d.ts","sourceRoot":"","sources":["../src/Crypto.types.ts"],"names":[],"mappings":"AACA;;GAEG;AACH,oBAAY,qBAAqB;IAC/B;;OAEG;IACH,IAAI,UAAU;IACd;;OAEG;IACH,MAAM,YAAY;IAClB;;OAEG;IACH,MAAM,YAAY;IAClB;;OAEG;IACH,MAAM,YAAY;IAClB;;;OAGG;IACH,GAAG,QAAQ;IACX;;;OAGG;IACH,GAAG,QAAQ;IACX;;;;OAIG;IACH,GAAG,QAAQ;CACZ;AAGD,oBAAY,cAAc;IACxB,GAAG,QAAQ;IACX;;OAEG;IACH,MAAM,WAAW;CAClB;AAGD,oBAAY,mBAAmB,GAAG;IAChC;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAGF,oBAAY,MAAM,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"Crypto.types.d.ts","sourceRoot":"","sources":["../src/Crypto.types.ts"],"names":[],"mappings":"AACA;;GAEG;AACH,oBAAY,qBAAqB;IAC/B;;OAEG;IACH,IAAI,UAAU;IACd;;OAEG;IACH,MAAM,YAAY;IAClB;;OAEG;IACH,MAAM,YAAY;IAClB;;OAEG;IACH,MAAM,YAAY;IAClB;;;OAGG;IACH,GAAG,QAAQ;IACX;;;OAGG;IACH,GAAG,QAAQ;IACX;;;;OAIG;IACH,GAAG,QAAQ;CACZ;AAGD,oBAAY,cAAc;IACxB,GAAG,QAAQ;IACX;;OAEG;IACH,MAAM,WAAW;CAClB;AAGD,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAGF,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC"}
@@ -1,7 +1,12 @@
1
+ import { TypedArray } from 'expo-modules-core';
1
2
  import { CryptoDigestAlgorithm, CryptoDigestOptions } from './Crypto.types';
2
3
  declare const _default: {
3
4
  readonly name: string;
4
5
  digestStringAsync(algorithm: CryptoDigestAlgorithm, data: string, options: CryptoDigestOptions): Promise<string>;
6
+ getRandomBytes(length: number): Uint8Array;
7
+ getRandomBytesAsync(length: number): Promise<Uint8Array>;
8
+ getRandomValues(typedArray: TypedArray): TypedArray;
9
+ randomUUID(): string;
5
10
  };
6
11
  export default _default;
7
12
  //# sourceMappingURL=ExpoCrypto.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoCrypto.web.d.ts","sourceRoot":"","sources":["../src/ExpoCrypto.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAkB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;;;iCAO7E,qBAAqB,QAC1B,MAAM,WACH,mBAAmB,GAC3B,QAAQ,MAAM,CAAC;;AARpB,wBAyBE"}
1
+ {"version":3,"file":"ExpoCrypto.web.d.ts","sourceRoot":"","sources":["../src/ExpoCrypto.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,qBAAqB,EAAkB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;;;iCAS7E,qBAAqB,QAC1B,MAAM,WACH,mBAAmB,GAC3B,QAAQ,MAAM,CAAC;2BAiBK,MAAM,GAAG,UAAU;gCAIR,MAAM,GAAG,QAAQ,UAAU,CAAC;gCAIlC,UAAU;;;AAjCxC,wBAuCE"}
@@ -1,5 +1,6 @@
1
1
  import { CodedError } from 'expo-modules-core';
2
2
  import { CryptoEncoding } from './Crypto.types';
3
+ const getCrypto = () => window.crypto ?? window.msCrypto;
3
4
  export default {
4
5
  get name() {
5
6
  return 'ExpoCrypto';
@@ -19,6 +20,20 @@ export default {
19
20
  }
20
21
  throw new CodedError('ERR_CRYPTO_DIGEST', 'Invalid encoding type provided.');
21
22
  },
23
+ getRandomBytes(length) {
24
+ const array = new Uint8Array(length);
25
+ return getCrypto().getRandomValues(array);
26
+ },
27
+ async getRandomBytesAsync(length) {
28
+ const array = new Uint8Array(length);
29
+ return getCrypto().getRandomValues(array);
30
+ },
31
+ getRandomValues(typedArray) {
32
+ return getCrypto().getRandomValues(typedArray);
33
+ },
34
+ randomUUID() {
35
+ return getCrypto().randomUUID();
36
+ },
22
37
  };
23
38
  function hexString(buffer) {
24
39
  const byteArray = new Uint8Array(buffer);
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoCrypto.web.js","sourceRoot":"","sources":["../src/ExpoCrypto.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAyB,cAAc,EAAuB,MAAM,gBAAgB,CAAC;AAE5F,eAAe;IACb,IAAI,IAAI;QACN,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,KAAK,CAAC,iBAAiB,CACrB,SAAgC,EAChC,IAAY,EACZ,OAA4B;QAE5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAClB,MAAM,IAAI,UAAU,CAClB,wBAAwB,EACxB,sEAAsE,CACvE,CAAC;SACH;QACD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,QAAQ,KAAK,cAAc,CAAC,GAAG,EAAE;YAC3C,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;SAC9B;aAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,cAAc,CAAC,MAAM,EAAE;YACrD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;SACjE;QACD,MAAM,IAAI,UAAU,CAAC,mBAAmB,EAAE,iCAAiC,CAAC,CAAC;IAC/E,CAAC;CACF,CAAC;AAEF,SAAS,SAAS,CAAC,MAAmB;IACpC,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAEzC,MAAM,QAAQ,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/C,OAAO,aAAa,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC","sourcesContent":["import { CodedError } from 'expo-modules-core';\n\nimport { CryptoDigestAlgorithm, CryptoEncoding, CryptoDigestOptions } from './Crypto.types';\n\nexport default {\n get name(): string {\n return 'ExpoCrypto';\n },\n async digestStringAsync(\n algorithm: CryptoDigestAlgorithm,\n data: string,\n options: CryptoDigestOptions\n ): Promise<string> {\n if (!crypto.subtle) {\n throw new CodedError(\n 'ERR_CRYPTO_UNAVAILABLE',\n 'Access to the WebCrypto API is restricted to secure origins (https).'\n );\n }\n const encoder = new TextEncoder();\n const buffer = encoder.encode(data);\n const hashedData = await crypto.subtle.digest(algorithm, buffer);\n if (options.encoding === CryptoEncoding.HEX) {\n return hexString(hashedData);\n } else if (options.encoding === CryptoEncoding.BASE64) {\n return btoa(String.fromCharCode(...new Uint8Array(hashedData)));\n }\n throw new CodedError('ERR_CRYPTO_DIGEST', 'Invalid encoding type provided.');\n },\n};\n\nfunction hexString(buffer: ArrayBuffer): string {\n const byteArray = new Uint8Array(buffer);\n\n const hexCodes = [...byteArray].map((value) => {\n const hexCode = value.toString(16);\n const paddedHexCode = hexCode.padStart(2, '0');\n return paddedHexCode;\n });\n\n return hexCodes.join('');\n}\n"]}
1
+ {"version":3,"file":"ExpoCrypto.web.js","sourceRoot":"","sources":["../src/ExpoCrypto.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAc,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAyB,cAAc,EAAuB,MAAM,gBAAgB,CAAC;AAE5F,MAAM,SAAS,GAAG,GAAW,EAAE,CAAC,MAAM,CAAC,MAAM,IAAK,MAAc,CAAC,QAAQ,CAAC;AAE1E,eAAe;IACb,IAAI,IAAI;QACN,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,KAAK,CAAC,iBAAiB,CACrB,SAAgC,EAChC,IAAY,EACZ,OAA4B;QAE5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAClB,MAAM,IAAI,UAAU,CAClB,wBAAwB,EACxB,sEAAsE,CACvE,CAAC;SACH;QACD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,QAAQ,KAAK,cAAc,CAAC,GAAG,EAAE;YAC3C,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;SAC9B;aAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,cAAc,CAAC,MAAM,EAAE;YACrD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;SACjE;QACD,MAAM,IAAI,UAAU,CAAC,mBAAmB,EAAE,iCAAiC,CAAC,CAAC;IAC/E,CAAC;IACD,cAAc,CAAC,MAAc;QAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QACrC,OAAO,SAAS,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,mBAAmB,CAAC,MAAc;QACtC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QACrC,OAAO,SAAS,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IACD,eAAe,CAAC,UAAsB;QACpC,OAAO,SAAS,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;IACD,UAAU;QACR,OAAO,SAAS,EAAE,CAAC,UAAU,EAAE,CAAC;IAClC,CAAC;CACF,CAAC;AAEF,SAAS,SAAS,CAAC,MAAmB;IACpC,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAEzC,MAAM,QAAQ,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/C,OAAO,aAAa,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC","sourcesContent":["import { CodedError, TypedArray } from 'expo-modules-core';\n\nimport { CryptoDigestAlgorithm, CryptoEncoding, CryptoDigestOptions } from './Crypto.types';\n\nconst getCrypto = (): Crypto => window.crypto ?? (window as any).msCrypto;\n\nexport default {\n get name(): string {\n return 'ExpoCrypto';\n },\n async digestStringAsync(\n algorithm: CryptoDigestAlgorithm,\n data: string,\n options: CryptoDigestOptions\n ): Promise<string> {\n if (!crypto.subtle) {\n throw new CodedError(\n 'ERR_CRYPTO_UNAVAILABLE',\n 'Access to the WebCrypto API is restricted to secure origins (https).'\n );\n }\n const encoder = new TextEncoder();\n const buffer = encoder.encode(data);\n const hashedData = await crypto.subtle.digest(algorithm, buffer);\n if (options.encoding === CryptoEncoding.HEX) {\n return hexString(hashedData);\n } else if (options.encoding === CryptoEncoding.BASE64) {\n return btoa(String.fromCharCode(...new Uint8Array(hashedData)));\n }\n throw new CodedError('ERR_CRYPTO_DIGEST', 'Invalid encoding type provided.');\n },\n getRandomBytes(length: number): Uint8Array {\n const array = new Uint8Array(length);\n return getCrypto().getRandomValues(array);\n },\n async getRandomBytesAsync(length: number): Promise<Uint8Array> {\n const array = new Uint8Array(length);\n return getCrypto().getRandomValues(array);\n },\n getRandomValues(typedArray: TypedArray) {\n return getCrypto().getRandomValues(typedArray);\n },\n randomUUID() {\n return getCrypto().randomUUID();\n },\n};\n\nfunction hexString(buffer: ArrayBuffer): string {\n const byteArray = new Uint8Array(buffer);\n\n const hexCodes = [...byteArray].map((value) => {\n const hexCode = value.toString(16);\n const paddedHexCode = hexCode.padStart(2, '0');\n return paddedHexCode;\n });\n\n return hexCodes.join('');\n}\n"]}
@@ -10,7 +10,27 @@ public class CryptoModule: Module {
10
10
  AsyncFunction("digestStringAsync", digestString)
11
11
 
12
12
  Function("digestString", digestString)
13
+
14
+ AsyncFunction("getRandomBase64StringAsync", getRandomBase64String)
15
+
16
+ Function("getRandomBase64String", getRandomBase64String)
17
+
18
+ Function("getRandomValues", getRandomValues)
19
+
20
+ Function("randomUUID") {
21
+ UUID().uuidString
22
+ }
23
+ }
24
+ }
25
+
26
+ private func getRandomBase64String(length: Int) throws -> String {
27
+ var bytes = [UInt8](repeating: 0, count: length)
28
+ let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
29
+
30
+ guard status == errSecSuccess else {
31
+ throw FailedGeneratingRandomBytesException(status)
13
32
  }
33
+ return Data(bytes).base64EncodedString()
14
34
  }
15
35
 
16
36
  private func digestString(algorithm: DigestAlgorithm, str: String, options: DigestOptions) throws -> String {
@@ -33,8 +53,27 @@ private func digestString(algorithm: DigestAlgorithm, str: String, options: Dige
33
53
  }
34
54
  }
35
55
 
56
+ private func getRandomValues(array: TypedArray) throws -> TypedArray {
57
+ let status = SecRandomCopyBytes(
58
+ kSecRandomDefault,
59
+ array.byteLength,
60
+ array.rawPointer
61
+ )
62
+
63
+ guard status == errSecSuccess else {
64
+ throw FailedGeneratingRandomBytesException(status)
65
+ }
66
+ return array
67
+ }
68
+
36
69
  private class LossyConversionException: Exception {
37
70
  override var reason: String {
38
71
  "Unable to convert given string without losing some information"
39
72
  }
40
73
  }
74
+
75
+ private class FailedGeneratingRandomBytesException: GenericException<OSStatus> {
76
+ override var reason: String {
77
+ "Generating random bytes has failed with OSStatus code: \(param)"
78
+ }
79
+ }
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
- s.platform = :ios, '12.0'
13
+ s.platform = :ios, '13.0'
14
14
  s.swift_version = '5.4'
15
15
  s.source = { git: 'https://github.com/expo/expo.git' }
16
16
  s.static_framework = true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-crypto",
3
- "version": "11.0.0",
3
+ "version": "12.1.0",
4
4
  "description": "Expo universal module for crypto",
5
5
  "main": "build/Crypto.js",
6
6
  "types": "build/Crypto.d.ts",
@@ -38,12 +38,14 @@
38
38
  "jest": {
39
39
  "preset": "expo-module-scripts"
40
40
  },
41
- "dependencies": {},
41
+ "dependencies": {
42
+ "base64-js": "^1.3.0"
43
+ },
42
44
  "devDependencies": {
43
- "expo-module-scripts": "^2.0.0"
45
+ "expo-module-scripts": "^3.0.0"
44
46
  },
45
47
  "peerDependencies": {
46
48
  "expo": "*"
47
49
  },
48
- "gitHead": "e893ff2b01e108cf246cec02318c0df9d6bc603c"
50
+ "gitHead": "ba80e8181b79d06e00a245653727f4eaeb80420e"
49
51
  }
package/src/Crypto.ts CHANGED
@@ -1,8 +1,11 @@
1
- import { UnavailabilityError } from 'expo-modules-core';
1
+ import { toByteArray } from 'base64-js';
2
+ import { UnavailabilityError, UintBasedTypedArray, IntBasedTypedArray } from 'expo-modules-core';
2
3
 
3
4
  import { CryptoDigestAlgorithm, CryptoEncoding, CryptoDigestOptions, Digest } from './Crypto.types';
4
5
  import ExpoCrypto from './ExpoCrypto';
5
6
 
7
+ declare const global: any;
8
+
6
9
  export * from './Crypto.types';
7
10
 
8
11
  class CryptoError extends TypeError {
@@ -13,6 +16,70 @@ class CryptoError extends TypeError {
13
16
  }
14
17
  }
15
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
+ */
27
+ export 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 (ExpoCrypto.getRandomBytes) {
41
+ return ExpoCrypto.getRandomBytes(validByteCount);
42
+ } else if (ExpoCrypto.getRandomBase64String) {
43
+ const base64 = ExpoCrypto.getRandomBase64String(validByteCount);
44
+ return toByteArray(base64);
45
+ } else {
46
+ throw new UnavailabilityError('expo-crypto', '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
+ */
57
+ export async function getRandomBytesAsync(byteCount: number): Promise<Uint8Array> {
58
+ assertByteCount(byteCount, 'getRandomBytesAsync');
59
+ const validByteCount = Math.floor(byteCount);
60
+ if (ExpoCrypto.getRandomBytesAsync) {
61
+ return await ExpoCrypto.getRandomBytesAsync(validByteCount);
62
+ } else if (ExpoCrypto.getRandomBase64StringAsync) {
63
+ const base64 = await ExpoCrypto.getRandomBase64StringAsync(validByteCount);
64
+ return toByteArray(base64);
65
+ } else {
66
+ throw new UnavailabilityError('expo-crypto', 'getRandomBytesAsync');
67
+ }
68
+ }
69
+
70
+ function assertByteCount(value: any, methodName: string): void {
71
+ if (
72
+ typeof value !== 'number' ||
73
+ isNaN(value) ||
74
+ Math.floor(value) < 0 ||
75
+ Math.floor(value) > 1024
76
+ ) {
77
+ throw new TypeError(
78
+ `expo-crypto: ${methodName}(${value}) expected a valid number from range 0...1024`
79
+ );
80
+ }
81
+ }
82
+
16
83
  function assertAlgorithm(algorithm: CryptoDigestAlgorithm): void {
17
84
  if (!Object.values(CryptoDigestAlgorithm).includes(algorithm)) {
18
85
  throw new CryptoError(
@@ -75,3 +142,38 @@ export async function digestStringAsync(
75
142
 
76
143
  return await ExpoCrypto.digestStringAsync(algorithm, data, options);
77
144
  }
145
+
146
+ /**
147
+ * The `getRandomValues()` method of `Crypto` fills a provided `TypedArray` with cryptographically secure random values.
148
+ *
149
+ * @param typedArray An integer based [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) to fill with cryptographically secure random values. It modifies the input array in place.
150
+ * @return The input array filled with cryptographically secure random values.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const byteArray = new Uint8Array(16);
155
+ * Crypto.getRandomValues(byteArray);
156
+ * console.log('Your lucky bytes: ' + byteArray);
157
+ * ```
158
+ */
159
+ export function getRandomValues<T extends IntBasedTypedArray | UintBasedTypedArray>(
160
+ typedArray: T
161
+ ): T {
162
+ ExpoCrypto.getRandomValues(typedArray);
163
+ return typedArray;
164
+ }
165
+
166
+ /**
167
+ * The `randomUUID()` method returns a unique identifier based on the V4 UUID spec (RFC4122).
168
+ * It uses cryptographically secure random values to generate the UUID.
169
+ *
170
+ * @return A string containing a newly generated UUIDv4 identifier
171
+ * @example
172
+ * ```ts
173
+ * const UUID = Crypto.randomUUID();
174
+ * console.log('Your UUID: ' + UUID);
175
+ * ```
176
+ */
177
+ export function randomUUID() {
178
+ return ExpoCrypto.randomUUID();
179
+ }
@@ -1,7 +1,9 @@
1
- import { CodedError } from 'expo-modules-core';
1
+ import { CodedError, TypedArray } from 'expo-modules-core';
2
2
 
3
3
  import { CryptoDigestAlgorithm, CryptoEncoding, CryptoDigestOptions } from './Crypto.types';
4
4
 
5
+ const getCrypto = (): Crypto => window.crypto ?? (window as any).msCrypto;
6
+
5
7
  export default {
6
8
  get name(): string {
7
9
  return 'ExpoCrypto';
@@ -27,6 +29,20 @@ export default {
27
29
  }
28
30
  throw new CodedError('ERR_CRYPTO_DIGEST', 'Invalid encoding type provided.');
29
31
  },
32
+ getRandomBytes(length: number): Uint8Array {
33
+ const array = new Uint8Array(length);
34
+ return getCrypto().getRandomValues(array);
35
+ },
36
+ async getRandomBytesAsync(length: number): Promise<Uint8Array> {
37
+ const array = new Uint8Array(length);
38
+ return getCrypto().getRandomValues(array);
39
+ },
40
+ getRandomValues(typedArray: TypedArray) {
41
+ return getCrypto().getRandomValues(typedArray);
42
+ },
43
+ randomUUID() {
44
+ return getCrypto().randomUUID();
45
+ },
30
46
  };
31
47
 
32
48
  function hexString(buffer: ArrayBuffer): string {