@symbiote-native/crypto 0.0.1
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/LICENSE +21 -0
- package/README.md +226 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +4 -0
- package/build/core/crypto.d.ts +37 -0
- package/build/core/crypto.js +132 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +9 -0
- package/build/core/native-module.js +3 -0
- package/build/core/types.d.ts +24 -0
- package/build/core/types.js +18 -0
- package/build/react/index.d.ts +1 -0
- package/build/react/index.js +5 -0
- package/build/vue/index.d.ts +1 -0
- package/build/vue/index.js +4 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/crypto.d.ts +37 -0
- package/build-ngc/core/crypto.js +133 -0
- package/build-ngc/core/crypto.js.map +1 -0
- package/build-ngc/core/index.d.ts +2 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +9 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +24 -0
- package/build-ngc/core/types.js +19 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +4 -0
- package/src/core/crypto.test.ts +245 -0
- package/src/core/crypto.ts +165 -0
- package/src/core/index.ts +18 -0
- package/src/core/native-module.ts +26 -0
- package/src/core/types.ts +32 -0
- package/src/react/index.ts +5 -0
- package/src/vue/index.ts +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 A. Prokopenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# @symbiote-native/crypto
|
|
2
|
+
|
|
3
|
+
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
+
[`expo-crypto`](https://github.com/expo/expo/tree/main/packages/expo-crypto) — cryptographically
|
|
5
|
+
secure random bytes, `randomUUID`, and string/buffer digest hashing — usable from **every**
|
|
6
|
+
adapter, React, Vue, and Angular, not just React. Like `@symbiote-native/local-auth`, every
|
|
7
|
+
function here is a plain sync/async call with no per-instance state or event stream, so there is
|
|
8
|
+
no hook/composable/service to wrap — the React, Vue, and Angular entry points are plain
|
|
9
|
+
re-exports of the same `core`. AES encryption (`expo-crypto`'s `aes/` subfolder) is out of scope
|
|
10
|
+
for this pass.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @symbiote-native/crypto
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`expo-crypto` and `expo-modules-core` come along as regular dependencies, pinned to exact
|
|
19
|
+
versions — never install either yourself, and never add the `expo` meta-package to your project
|
|
20
|
+
(it bundles its own Metro/Babel pipeline, which conflicts with this project's own).
|
|
21
|
+
|
|
22
|
+
## Required one-time step: native autolinking wiring
|
|
23
|
+
|
|
24
|
+
Unlike a plain RN native module, `expo-crypto`'s native code is discovered by
|
|
25
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism — this needs wiring
|
|
26
|
+
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
27
|
+
package with zero further changes:
|
|
28
|
+
|
|
29
|
+
| Platform | Touches |
|
|
30
|
+
|---|---|
|
|
31
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
32
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
33
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
34
|
+
| Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
35
|
+
|
|
36
|
+
Full mechanics live in the `symbiote-expo-native-module` project skill. Reference
|
|
37
|
+
implementation: `examples/expo-react/ios/Podfile` and
|
|
38
|
+
`examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt`.
|
|
39
|
+
|
|
40
|
+
No platform permission strings are needed for this package — random-byte generation and
|
|
41
|
+
digest hashing touch no protected device capability.
|
|
42
|
+
|
|
43
|
+
## Shape
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
src/core/ getRandomBytes / getRandomBytesAsync / getRandomValues / randomUUID /
|
|
47
|
+
digestStringAsync / digest, plus CryptoDigestAlgorithm, CryptoEncoding, the
|
|
48
|
+
option/result types, and the CryptoError validation-error class.
|
|
49
|
+
native-module.ts resolves the native module via expo-modules-core's
|
|
50
|
+
requireNativeModule.
|
|
51
|
+
src/react/ @symbiote-native/crypto/react — export * from '../core'
|
|
52
|
+
src/vue/ @symbiote-native/crypto/vue — export * from '../core'
|
|
53
|
+
src/angular/ @symbiote-native/crypto/angular — export * from '../core'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
No per-adapter lifecycle wrapper exists because there's nothing to subscribe to or clean up —
|
|
57
|
+
each adapter entry is a single-file re-export.
|
|
58
|
+
|
|
59
|
+
## Use it
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// React
|
|
63
|
+
import { useEffect, useState } from 'react';
|
|
64
|
+
import { Pressable, Text, View } from '@symbiote-native/react';
|
|
65
|
+
import {
|
|
66
|
+
CryptoDigestAlgorithm,
|
|
67
|
+
digestStringAsync,
|
|
68
|
+
randomUUID,
|
|
69
|
+
} from '@symbiote-native/crypto/react';
|
|
70
|
+
|
|
71
|
+
function CryptoScreen() {
|
|
72
|
+
const [uuid, setUuid] = useState('');
|
|
73
|
+
const [hash, setHash] = useState('');
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
setUuid(randomUUID());
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const handleHash = () => {
|
|
80
|
+
digestStringAsync(CryptoDigestAlgorithm.SHA256, 'Confirm it is you').then(setHash);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<View>
|
|
85
|
+
<Text>UUID: {uuid}</Text>
|
|
86
|
+
<Pressable onPress={handleHash}>
|
|
87
|
+
<Text>Hash a string</Text>
|
|
88
|
+
</Pressable>
|
|
89
|
+
{hash && <Text>SHA-256: {hash}</Text>}
|
|
90
|
+
</View>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```vue
|
|
96
|
+
<!-- Vue -->
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import { onMounted, ref } from 'vue';
|
|
99
|
+
import { Pressable, Text, View } from '@symbiote-native/vue';
|
|
100
|
+
import {
|
|
101
|
+
CryptoDigestAlgorithm,
|
|
102
|
+
digestStringAsync,
|
|
103
|
+
randomUUID,
|
|
104
|
+
} from '@symbiote-native/crypto/vue';
|
|
105
|
+
|
|
106
|
+
const uuid = ref('');
|
|
107
|
+
const hash = ref('');
|
|
108
|
+
|
|
109
|
+
onMounted(() => {
|
|
110
|
+
uuid.value = randomUUID();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
function handleHash(): void {
|
|
114
|
+
void digestStringAsync(CryptoDigestAlgorithm.SHA256, 'Confirm it is you').then(value => {
|
|
115
|
+
hash.value = value;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<View>
|
|
122
|
+
<Text>UUID: {{ uuid }}</Text>
|
|
123
|
+
<Pressable @press="handleHash">
|
|
124
|
+
<Text>Hash a string</Text>
|
|
125
|
+
</Pressable>
|
|
126
|
+
<Text v-if="hash">SHA-256: {{ hash }}</Text>
|
|
127
|
+
</View>
|
|
128
|
+
</template>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
// Angular
|
|
133
|
+
import { Component, signal } from '@angular/core';
|
|
134
|
+
import { Pressable, Text, View } from '@symbiote-native/angular';
|
|
135
|
+
import {
|
|
136
|
+
CryptoDigestAlgorithm,
|
|
137
|
+
digestStringAsync,
|
|
138
|
+
randomUUID,
|
|
139
|
+
} from '@symbiote-native/crypto/angular';
|
|
140
|
+
|
|
141
|
+
@Component({
|
|
142
|
+
standalone: true,
|
|
143
|
+
imports: [Pressable, Text, View],
|
|
144
|
+
template: `
|
|
145
|
+
<View>
|
|
146
|
+
<Text>UUID: {{ uuid() }}</Text>
|
|
147
|
+
<Pressable (press)="handleHash()">
|
|
148
|
+
<Text>Hash a string</Text>
|
|
149
|
+
</Pressable>
|
|
150
|
+
@if (hash()) {
|
|
151
|
+
<Text>SHA-256: {{ hash() }}</Text>
|
|
152
|
+
}
|
|
153
|
+
</View>
|
|
154
|
+
`,
|
|
155
|
+
})
|
|
156
|
+
export class CryptoScreen {
|
|
157
|
+
readonly uuid = signal(randomUUID());
|
|
158
|
+
readonly hash = signal('');
|
|
159
|
+
|
|
160
|
+
handleHash(): void {
|
|
161
|
+
digestStringAsync(CryptoDigestAlgorithm.SHA256, 'Confirm it is you').then(value =>
|
|
162
|
+
this.hash.set(value),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
There's no per-instance service to `inject()` in the Angular case — every function is a plain
|
|
169
|
+
free function off the core package.
|
|
170
|
+
|
|
171
|
+
## API
|
|
172
|
+
|
|
173
|
+
Free sync/async functions, no event stream, no per-instance state:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
getRandomBytes(byteCount: number): Uint8Array
|
|
177
|
+
getRandomBytesAsync(byteCount: number): Promise<Uint8Array>
|
|
178
|
+
getRandomValues<T extends ITypedArray>(typedArray: T): T
|
|
179
|
+
randomUUID(): string
|
|
180
|
+
digestStringAsync(
|
|
181
|
+
algorithm: CryptoDigestAlgorithm,
|
|
182
|
+
data: string,
|
|
183
|
+
options?: ICryptoDigestOptions,
|
|
184
|
+
): Promise<IDigest>
|
|
185
|
+
digest(algorithm: CryptoDigestAlgorithm, data: BufferSource): Promise<ArrayBuffer>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Plus `CryptoDigestAlgorithm` (`SHA1`/`SHA256`/`SHA384`/`SHA512`/`MD2`\*/`MD4`\*/`MD5`, \*iOS
|
|
189
|
+
only), `CryptoEncoding` (`HEX`/`BASE64`), `ICryptoDigestOptions`, `IDigest`, `ITypedArray` (and its
|
|
190
|
+
`IUintBasedTypedArray`/`IIntBasedTypedArray` halves), and the `CryptoError` validation-error class
|
|
191
|
+
— ported from upstream's `Crypto.types.ts`/`Crypto.ts`, renamed with this repo's `I`-prefix
|
|
192
|
+
convention for exported types (`ts-js-best-practices`).
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { digestStringAsync, randomUUID } from '@symbiote-native/crypto';
|
|
196
|
+
// or the framework-scoped entry points — identical surface, re-exported verbatim:
|
|
197
|
+
import { randomUUID } from '@symbiote-native/crypto/react';
|
|
198
|
+
import { randomUUID } from '@symbiote-native/crypto/vue';
|
|
199
|
+
import { randomUUID } from '@symbiote-native/crypto/angular';
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Notes
|
|
203
|
+
|
|
204
|
+
- `getRandomBytes`/`getRandomBytesAsync` validate `byteCount` is a number in `0`-`1024`
|
|
205
|
+
(inclusive), throwing a plain `TypeError` otherwise, and floor a fractional count — matching
|
|
206
|
+
upstream exactly. This port skips upstream's `__DEV__`/remote-debugger `Math.random` fallback:
|
|
207
|
+
that's a React Native debugging-tool concern, not applicable to this package's native-call
|
|
208
|
+
path.
|
|
209
|
+
- `digestStringAsync` validates `algorithm`/`data`/`options.encoding` and throws `CryptoError` (a
|
|
210
|
+
`TypeError` subclass, `code: 'ERR_CRYPTO'`) on an invalid value — mirroring upstream's own
|
|
211
|
+
`CryptoError`.
|
|
212
|
+
- `digest` prefers a native `digestAsync` when present; otherwise it allocates a fixed-size
|
|
213
|
+
output buffer (sized via the `digestLengths` lookup table) and calls the sync native `digest`.
|
|
214
|
+
|
|
215
|
+
## Test it
|
|
216
|
+
|
|
217
|
+
No Fabric/Descriptor angle at all — every function here is a pure sync/async-function surface,
|
|
218
|
+
never a view or per-instance state. Tests inject a fake native-module object in place of the
|
|
219
|
+
real `requireNativeModule` resolution (`src/core/crypto.test.ts`, `vitest`) — no
|
|
220
|
+
`installFabric()`, no ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
221
|
+
[README](../../README.md) for the project's testing model).
|
|
222
|
+
|
|
223
|
+
Native autolinking wiring is expected to land in the four Expo canary apps
|
|
224
|
+
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`)
|
|
225
|
+
following the same recipe already proven for `@symbiote-native/local-auth`/`@symbiote-native/sensors`
|
|
226
|
+
— demo-screen wiring for this package is a follow-up, not part of this pass.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CryptoDigestAlgorithm } from './types';
|
|
2
|
+
import type { ICryptoDigestOptions, IDigest, ITypedArray } from './types';
|
|
3
|
+
export declare class CryptoError extends TypeError {
|
|
4
|
+
code: string;
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Generates completely random bytes using native implementations. `byteCount` must be within
|
|
9
|
+
* `0`-`1024` (inclusive), anything else throws a `TypeError`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getRandomBytes(byteCount: number): Uint8Array;
|
|
12
|
+
/**
|
|
13
|
+
* Generates completely random bytes using native implementations. `byteCount` must be within
|
|
14
|
+
* `0`-`1024` (inclusive), anything else throws a `TypeError`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getRandomBytesAsync(byteCount: number): Promise<Uint8Array>;
|
|
17
|
+
/**
|
|
18
|
+
* Fills the provided `TypedArray` with cryptographically secure random values in place, and
|
|
19
|
+
* returns it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getRandomValues<T extends ITypedArray>(typedArray: T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Returns a unique identifier based on the V4 UUID spec (RFC4122), using cryptographically
|
|
24
|
+
* secure random values.
|
|
25
|
+
*/
|
|
26
|
+
export declare function randomUUID(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Generates a digest of the supplied `data` string with the provided digest `algorithm`,
|
|
29
|
+
* formatted as one of `CryptoEncoding` (defaults to `HEX`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function digestStringAsync(algorithm: CryptoDigestAlgorithm, data: string, options?: ICryptoDigestOptions): Promise<IDigest>;
|
|
32
|
+
/**
|
|
33
|
+
* Generates a digest of the supplied `TypedArray`/`ArrayBuffer` of bytes with the provided digest
|
|
34
|
+
* `algorithm`. Prefers the native async `digestAsync` when present; otherwise falls back to
|
|
35
|
+
* allocating a fixed-size output buffer (sized via `digestLengths`) and the sync `digest`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function digest(algorithm: CryptoDigestAlgorithm, data: BufferSource): Promise<ArrayBuffer>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { expoCrypto } from './native-module';
|
|
3
|
+
import { CryptoDigestAlgorithm, CryptoEncoding } from './types';
|
|
4
|
+
const NATIVE_MODULE_NAME = 'expo-crypto';
|
|
5
|
+
const MIN_BYTE_COUNT = 0;
|
|
6
|
+
const MAX_BYTE_COUNT = 1024;
|
|
7
|
+
export class CryptoError extends TypeError {
|
|
8
|
+
code = 'ERR_CRYPTO';
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(`expo-crypto: ${message}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function assertByteCount(value, methodName) {
|
|
14
|
+
if (typeof value !== 'number' ||
|
|
15
|
+
Number.isNaN(value) ||
|
|
16
|
+
Math.floor(value) < MIN_BYTE_COUNT ||
|
|
17
|
+
Math.floor(value) > MAX_BYTE_COUNT) {
|
|
18
|
+
throw new TypeError(`expo-crypto: ${methodName}(${value}) expected a valid number from range ${MIN_BYTE_COUNT}...${MAX_BYTE_COUNT}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function assertAlgorithm(algorithm) {
|
|
22
|
+
if (!Object.values(CryptoDigestAlgorithm).includes(algorithm)) {
|
|
23
|
+
throw new CryptoError(`Invalid algorithm provided. Expected one of: CryptoDigestAlgorithm.${Object.keys(CryptoDigestAlgorithm).join(', CryptoDigestAlgorithm.')}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function assertData(data) {
|
|
27
|
+
if (typeof data !== 'string') {
|
|
28
|
+
throw new CryptoError('Invalid data provided. Expected a string.');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function assertEncoding(encoding) {
|
|
32
|
+
if (!Object.values(CryptoEncoding).includes(encoding)) {
|
|
33
|
+
throw new CryptoError(`Invalid encoding provided. Expected one of: CryptoEncoding.${Object.keys(CryptoEncoding).join(', CryptoEncoding.')}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generates completely random bytes using native implementations. `byteCount` must be within
|
|
38
|
+
* `0`-`1024` (inclusive), anything else throws a `TypeError`.
|
|
39
|
+
*/
|
|
40
|
+
export function getRandomBytes(byteCount) {
|
|
41
|
+
assertByteCount(byteCount, 'getRandomBytes');
|
|
42
|
+
const validByteCount = Math.floor(byteCount);
|
|
43
|
+
if (!expoCrypto.getRandomValues) {
|
|
44
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getRandomBytes');
|
|
45
|
+
}
|
|
46
|
+
const byteArray = new Uint8Array(validByteCount);
|
|
47
|
+
expoCrypto.getRandomValues(byteArray);
|
|
48
|
+
return byteArray;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generates completely random bytes using native implementations. `byteCount` must be within
|
|
52
|
+
* `0`-`1024` (inclusive), anything else throws a `TypeError`.
|
|
53
|
+
*/
|
|
54
|
+
export async function getRandomBytesAsync(byteCount) {
|
|
55
|
+
assertByteCount(byteCount, 'getRandomBytesAsync');
|
|
56
|
+
const validByteCount = Math.floor(byteCount);
|
|
57
|
+
if (!expoCrypto.getRandomValues) {
|
|
58
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getRandomBytesAsync');
|
|
59
|
+
}
|
|
60
|
+
const byteArray = new Uint8Array(validByteCount);
|
|
61
|
+
expoCrypto.getRandomValues(byteArray);
|
|
62
|
+
return byteArray;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Fills the provided `TypedArray` with cryptographically secure random values in place, and
|
|
66
|
+
* returns it.
|
|
67
|
+
*/
|
|
68
|
+
export function getRandomValues(typedArray) {
|
|
69
|
+
if (!expoCrypto.getRandomValues) {
|
|
70
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getRandomValues');
|
|
71
|
+
}
|
|
72
|
+
expoCrypto.getRandomValues(typedArray);
|
|
73
|
+
return typedArray;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns a unique identifier based on the V4 UUID spec (RFC4122), using cryptographically
|
|
77
|
+
* secure random values.
|
|
78
|
+
*/
|
|
79
|
+
export function randomUUID() {
|
|
80
|
+
if (!expoCrypto.randomUUID) {
|
|
81
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'randomUUID');
|
|
82
|
+
}
|
|
83
|
+
return expoCrypto.randomUUID();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generates a digest of the supplied `data` string with the provided digest `algorithm`,
|
|
87
|
+
* formatted as one of `CryptoEncoding` (defaults to `HEX`).
|
|
88
|
+
*/
|
|
89
|
+
export async function digestStringAsync(algorithm, data, options = { encoding: CryptoEncoding.HEX }) {
|
|
90
|
+
if (!expoCrypto.digestStringAsync) {
|
|
91
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'digestStringAsync');
|
|
92
|
+
}
|
|
93
|
+
assertAlgorithm(algorithm);
|
|
94
|
+
assertData(data);
|
|
95
|
+
assertEncoding(options.encoding);
|
|
96
|
+
return expoCrypto.digestStringAsync(algorithm, data, options);
|
|
97
|
+
}
|
|
98
|
+
// Fixed output length (in bytes) per digest algorithm — used only as the sync fallback's
|
|
99
|
+
// preallocated output buffer size when the native module has no digestAsync.
|
|
100
|
+
const digestLengths = {
|
|
101
|
+
[CryptoDigestAlgorithm.SHA1]: 20,
|
|
102
|
+
[CryptoDigestAlgorithm.SHA256]: 32,
|
|
103
|
+
[CryptoDigestAlgorithm.SHA384]: 48,
|
|
104
|
+
[CryptoDigestAlgorithm.SHA512]: 64,
|
|
105
|
+
[CryptoDigestAlgorithm.MD2]: 16,
|
|
106
|
+
[CryptoDigestAlgorithm.MD4]: 16,
|
|
107
|
+
[CryptoDigestAlgorithm.MD5]: 16,
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Generates a digest of the supplied `TypedArray`/`ArrayBuffer` of bytes with the provided digest
|
|
111
|
+
* `algorithm`. Prefers the native async `digestAsync` when present; otherwise falls back to
|
|
112
|
+
* allocating a fixed-size output buffer (sized via `digestLengths`) and the sync `digest`.
|
|
113
|
+
*/
|
|
114
|
+
export function digest(algorithm, data) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
try {
|
|
117
|
+
if (typeof expoCrypto.digestAsync === 'function') {
|
|
118
|
+
resolve(expoCrypto.digestAsync(algorithm, data));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (!expoCrypto.digest) {
|
|
122
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'digest');
|
|
123
|
+
}
|
|
124
|
+
const output = new Uint8Array(digestLengths[algorithm]);
|
|
125
|
+
expoCrypto.digest(algorithm, output, data);
|
|
126
|
+
resolve(output.buffer);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
reject(error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { CryptoError, getRandomBytes, getRandomBytesAsync, getRandomValues, randomUUID, digestStringAsync, digest, } from './crypto';
|
|
2
|
+
export { CryptoDigestAlgorithm, CryptoEncoding, type ICryptoDigestOptions, type IDigest, type IUintBasedTypedArray, type IIntBasedTypedArray, type ITypedArray, } from './types';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CryptoDigestAlgorithm, ICryptoDigestOptions, IDigest, ITypedArray } from './types';
|
|
2
|
+
export type INativeCryptoModule = {
|
|
3
|
+
getRandomValues(typedArray: ITypedArray): void;
|
|
4
|
+
randomUUID(): string;
|
|
5
|
+
digest(algorithm: CryptoDigestAlgorithm, output: Uint8Array, data: BufferSource): void;
|
|
6
|
+
digestStringAsync?(algorithm: CryptoDigestAlgorithm, data: string, options: ICryptoDigestOptions): Promise<IDigest>;
|
|
7
|
+
digestAsync?(algorithm: CryptoDigestAlgorithm, data: BufferSource): Promise<ArrayBuffer>;
|
|
8
|
+
};
|
|
9
|
+
export declare const expoCrypto: INativeCryptoModule;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare enum CryptoDigestAlgorithm {
|
|
2
|
+
SHA1 = "SHA-1",
|
|
3
|
+
SHA256 = "SHA-256",
|
|
4
|
+
SHA384 = "SHA-384",
|
|
5
|
+
SHA512 = "SHA-512",
|
|
6
|
+
/** @platform ios */
|
|
7
|
+
MD2 = "MD2",
|
|
8
|
+
/** @platform ios */
|
|
9
|
+
MD4 = "MD4",
|
|
10
|
+
MD5 = "MD5"
|
|
11
|
+
}
|
|
12
|
+
export declare enum CryptoEncoding {
|
|
13
|
+
HEX = "hex",
|
|
14
|
+
/** Has trailing padding. Does not wrap lines. Does not have a trailing newline. */
|
|
15
|
+
BASE64 = "base64"
|
|
16
|
+
}
|
|
17
|
+
export type ICryptoDigestOptions = {
|
|
18
|
+
/** Format the digest is returned in. */
|
|
19
|
+
encoding: CryptoEncoding;
|
|
20
|
+
};
|
|
21
|
+
export type IDigest = string;
|
|
22
|
+
export type IUintBasedTypedArray = Uint8Array | Uint16Array | Uint32Array;
|
|
23
|
+
export type IIntBasedTypedArray = Int8Array | Int16Array | Int32Array;
|
|
24
|
+
export type ITypedArray = IUintBasedTypedArray | IIntBasedTypedArray;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export var CryptoDigestAlgorithm;
|
|
2
|
+
(function (CryptoDigestAlgorithm) {
|
|
3
|
+
CryptoDigestAlgorithm["SHA1"] = "SHA-1";
|
|
4
|
+
CryptoDigestAlgorithm["SHA256"] = "SHA-256";
|
|
5
|
+
CryptoDigestAlgorithm["SHA384"] = "SHA-384";
|
|
6
|
+
CryptoDigestAlgorithm["SHA512"] = "SHA-512";
|
|
7
|
+
/** @platform ios */
|
|
8
|
+
CryptoDigestAlgorithm["MD2"] = "MD2";
|
|
9
|
+
/** @platform ios */
|
|
10
|
+
CryptoDigestAlgorithm["MD4"] = "MD4";
|
|
11
|
+
CryptoDigestAlgorithm["MD5"] = "MD5";
|
|
12
|
+
})(CryptoDigestAlgorithm || (CryptoDigestAlgorithm = {}));
|
|
13
|
+
export var CryptoEncoding;
|
|
14
|
+
(function (CryptoEncoding) {
|
|
15
|
+
CryptoEncoding["HEX"] = "hex";
|
|
16
|
+
/** Has trailing padding. Does not wrap lines. Does not have a trailing newline. */
|
|
17
|
+
CryptoEncoding["BASE64"] = "base64";
|
|
18
|
+
})(CryptoEncoding || (CryptoEncoding = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @symbiote-native/crypto/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// Upstream ships free sync/async functions and two enums, no per-instance state and no event
|
|
3
|
+
// stream — there is nothing for a hook to wrap, so this is a plain re-export, mirroring
|
|
4
|
+
// packages/local-auth/src/react/index.ts.
|
|
5
|
+
export * from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @symbiote-native/crypto/angular: the Angular entry over the framework-agnostic core. Same
|
|
2
|
+
// reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
|
|
3
|
+
// service, so this is a plain re-export.
|
|
4
|
+
export * from '../core';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,0FAA0F;AAC1F,yCAAyC;AACzC,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CryptoDigestAlgorithm } from './types';
|
|
2
|
+
import type { ICryptoDigestOptions, IDigest, ITypedArray } from './types';
|
|
3
|
+
export declare class CryptoError extends TypeError {
|
|
4
|
+
code: string;
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Generates completely random bytes using native implementations. `byteCount` must be within
|
|
9
|
+
* `0`-`1024` (inclusive), anything else throws a `TypeError`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getRandomBytes(byteCount: number): Uint8Array;
|
|
12
|
+
/**
|
|
13
|
+
* Generates completely random bytes using native implementations. `byteCount` must be within
|
|
14
|
+
* `0`-`1024` (inclusive), anything else throws a `TypeError`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getRandomBytesAsync(byteCount: number): Promise<Uint8Array>;
|
|
17
|
+
/**
|
|
18
|
+
* Fills the provided `TypedArray` with cryptographically secure random values in place, and
|
|
19
|
+
* returns it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getRandomValues<T extends ITypedArray>(typedArray: T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Returns a unique identifier based on the V4 UUID spec (RFC4122), using cryptographically
|
|
24
|
+
* secure random values.
|
|
25
|
+
*/
|
|
26
|
+
export declare function randomUUID(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Generates a digest of the supplied `data` string with the provided digest `algorithm`,
|
|
29
|
+
* formatted as one of `CryptoEncoding` (defaults to `HEX`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function digestStringAsync(algorithm: CryptoDigestAlgorithm, data: string, options?: ICryptoDigestOptions): Promise<IDigest>;
|
|
32
|
+
/**
|
|
33
|
+
* Generates a digest of the supplied `TypedArray`/`ArrayBuffer` of bytes with the provided digest
|
|
34
|
+
* `algorithm`. Prefers the native async `digestAsync` when present; otherwise falls back to
|
|
35
|
+
* allocating a fixed-size output buffer (sized via `digestLengths`) and the sync `digest`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function digest(algorithm: CryptoDigestAlgorithm, data: BufferSource): Promise<ArrayBuffer>;
|