@succinctlabs/react-native-zcam1 0.4.0-alpha.5 → 0.4.0-alpha.6
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/Zcam1Sdk.podspec +3 -2
- package/android/src/main/java/com/succinctlabs/zcam1sdk/camera/Zcam1CameraService.kt +3 -3
- package/ios/Zcam1Camera.swift +177 -9
- package/ios/Zcam1CameraFilmStyle.swift +18 -2
- package/ios/Zcam1CameraViewManager.m +4 -0
- package/ios/Zcam1DepthData.swift +219 -286
- package/lib/module/NativeZcam1Capture.js.map +1 -1
- package/lib/module/camera.js +49 -4
- package/lib/module/camera.js.map +1 -1
- package/lib/module/capture.js +57 -54
- package/lib/module/capture.js.map +1 -1
- package/lib/module/generated/zcam1_verify_utils.js +74 -5
- package/lib/module/generated/zcam1_verify_utils.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/picker.js +3 -2
- package/lib/module/picker.js.map +1 -1
- package/lib/module/verify.js +1 -0
- package/lib/module/verify.js.map +1 -1
- package/lib/typescript/src/NativeZcam1Capture.d.ts +10 -0
- package/lib/typescript/src/NativeZcam1Capture.d.ts.map +1 -1
- package/lib/typescript/src/camera.d.ts +21 -0
- package/lib/typescript/src/camera.d.ts.map +1 -1
- package/lib/typescript/src/capture.d.ts +9 -3
- package/lib/typescript/src/capture.d.ts.map +1 -1
- package/lib/typescript/src/generated/zcam1_verify_utils.d.ts +127 -4
- package/lib/typescript/src/generated/zcam1_verify_utils.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/picker.d.ts.map +1 -1
- package/lib/typescript/src/verify.d.ts +1 -0
- package/lib/typescript/src/verify.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeZcam1Capture.ts +12 -0
- package/src/camera.tsx +75 -1
- package/src/capture.tsx +81 -67
- package/src/generated/zcam1_verify_utils.ts +86 -5
- package/src/index.ts +8 -1
- package/src/picker.tsx +3 -2
- package/src/verify.tsx +1 -0
package/src/capture.tsx
CHANGED
|
@@ -62,10 +62,15 @@ export type {
|
|
|
62
62
|
* Device registration information including keys, certificate chain, and attestation.
|
|
63
63
|
*/
|
|
64
64
|
export type CaptureInfo = {
|
|
65
|
+
/** Application identifier. On iOS, formatted as `<TEAM_ID>.<BUNDLE_ID>` (e.g. `NLS5R4YCGX.com.example.myapp`). On Android, the app's package name (e.g. `com.example.myapp`). */
|
|
65
66
|
appId: string;
|
|
67
|
+
/** Unique identifier for the device key. */
|
|
66
68
|
deviceKeyId: string;
|
|
69
|
+
/** EC public key used to sign captured content. */
|
|
67
70
|
contentPublicKey: ECKey;
|
|
71
|
+
/** Identifier for the content key. */
|
|
68
72
|
contentKeyId: Uint8Array;
|
|
73
|
+
/** Device attestation blob. */
|
|
69
74
|
attestation: string;
|
|
70
75
|
};
|
|
71
76
|
|
|
@@ -73,7 +78,9 @@ export type CaptureInfo = {
|
|
|
73
78
|
* Configuration settings for device initialization and backend communication.
|
|
74
79
|
*/
|
|
75
80
|
export type Settings = {
|
|
76
|
-
|
|
81
|
+
/** iOS only. The app identifier, formatted as `<TEAM_ID>.<BUNDLE_ID>` (e.g. `NLS5R4YCGX.com.example.myapp`). Required on iOS — omitting it will throw. Ignored on Android, where the package name is derived automatically from the app bundle. */
|
|
82
|
+
appId?: string;
|
|
83
|
+
/** Whether to use the production backend. */
|
|
77
84
|
production: boolean;
|
|
78
85
|
};
|
|
79
86
|
|
|
@@ -96,11 +103,11 @@ export class ZPhoto {
|
|
|
96
103
|
* @returns Device information including keys, certificate chain, and attestation
|
|
97
104
|
*/
|
|
98
105
|
export async function initCapture(settings: Settings): Promise<CaptureInfo> {
|
|
99
|
-
const contentPublicKey = await getContentPublicKey();
|
|
100
106
|
const isSimulator = await isEmulator();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
107
|
+
const appId = getAppId(settings);
|
|
108
|
+
const deviceKeyId = await getAndPersistDeviceKeyId(appId, isSimulator);
|
|
109
|
+
const attestation = await getAndPersistAttestation(deviceKeyId, isSimulator);
|
|
110
|
+
const contentPublicKey = await getContentPublicKey();
|
|
104
111
|
|
|
105
112
|
if (contentPublicKey.kty !== "EC") {
|
|
106
113
|
throw new Error("Only EC public keys are supported");
|
|
@@ -108,74 +115,69 @@ export async function initCapture(settings: Settings): Promise<CaptureInfo> {
|
|
|
108
115
|
|
|
109
116
|
const contentKeyId = getSecureEnclaveKeyId(contentPublicKey);
|
|
110
117
|
|
|
111
|
-
let deviceKeyId = await EncryptedStorage.getItem(`deviceKeyId-${settings.appId}`);
|
|
112
|
-
let attestation = deviceKeyId
|
|
113
|
-
? await EncryptedStorage.getItem(`attestation-${deviceKeyId}`)
|
|
114
|
-
: null;
|
|
115
|
-
|
|
116
|
-
if (deviceKeyId == null || attestation == null) {
|
|
117
|
-
switch (Platform.OS) {
|
|
118
|
-
case "android":
|
|
119
|
-
// On Android, getAttestation() creates the key AND returns the attestation
|
|
120
|
-
// certificate chain in a single call. generateHardwareKey() is iOS-only.
|
|
121
|
-
deviceKeyId = `ZCAM1_ANDROID_DEVICE_${appId}`;
|
|
122
|
-
|
|
123
|
-
if (isSimulator) {
|
|
124
|
-
// Emulator or device without Play Integrity support — use mock attestation.
|
|
125
|
-
console.warn(
|
|
126
|
-
"[ZCAM] Play Integrity not available - using mock attestation. This is for development only.",
|
|
127
|
-
);
|
|
128
|
-
attestation = `SIMULATOR_MOCK_${deviceKeyId}_${Date.now()}`;
|
|
129
|
-
} else {
|
|
130
|
-
attestation = await getAttestation(deviceKeyId, deviceKeyId);
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
133
|
-
|
|
134
|
-
case "ios":
|
|
135
|
-
case "macos":
|
|
136
|
-
// iOS: generate key first, then attest separately
|
|
137
|
-
if (deviceKeyId == null) {
|
|
138
|
-
if (isSimulator) {
|
|
139
|
-
console.warn(
|
|
140
|
-
"[ZCAM] Running in simulator - using mock device key. This is for development only.",
|
|
141
|
-
);
|
|
142
|
-
deviceKeyId = `SIMULATOR_DEVICE_KEY_${Date.now()}`;
|
|
143
|
-
} else {
|
|
144
|
-
deviceKeyId = await generateHardwareKey();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
attestation = await updateRegistration(deviceKeyId!, settings);
|
|
148
|
-
break;
|
|
149
|
-
|
|
150
|
-
default:
|
|
151
|
-
throw new Error(`initCapture: ${Platform.OS} not supported`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
await EncryptedStorage.setItem(`deviceKeyId-${settings.appId}`, deviceKeyId!);
|
|
155
|
-
await EncryptedStorage.setItem(`attestation-${deviceKeyId}`, attestation!);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (deviceKeyId == null) {
|
|
159
|
-
throw new Error("Failed to generate a device key");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
118
|
return {
|
|
163
|
-
appId
|
|
119
|
+
appId: appId!,
|
|
164
120
|
deviceKeyId,
|
|
165
121
|
contentPublicKey,
|
|
166
122
|
contentKeyId,
|
|
167
|
-
attestation
|
|
123
|
+
attestation,
|
|
168
124
|
};
|
|
169
125
|
}
|
|
170
126
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
127
|
+
function getAppId(settings: Settings): string {
|
|
128
|
+
switch (Platform.OS) {
|
|
129
|
+
case "android":
|
|
130
|
+
return getBundleId();
|
|
131
|
+
|
|
132
|
+
case "ios":
|
|
133
|
+
case "macos":
|
|
134
|
+
if (settings.appId === undefined) {
|
|
135
|
+
throw new Error("The appId is required on iOS");
|
|
136
|
+
}
|
|
137
|
+
return settings.appId;
|
|
138
|
+
|
|
139
|
+
default:
|
|
140
|
+
throw new Error(`getAppId: ${Platform.OS} not supported`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function getAndPersistDeviceKeyId(appId: string, isSimulator: boolean): Promise<string> {
|
|
145
|
+
let deviceKeyId = await EncryptedStorage.getItem(`deviceKeyId-${appId}`);
|
|
146
|
+
|
|
147
|
+
if (deviceKeyId) return deviceKeyId;
|
|
148
|
+
|
|
149
|
+
if (isSimulator) {
|
|
150
|
+
console.warn(
|
|
151
|
+
"[ZCAM] Running in simulator - using mock device key. This is for development only.",
|
|
152
|
+
);
|
|
153
|
+
return `SIMULATOR_DEVICE_KEY_${Date.now()}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
switch (Platform.OS) {
|
|
157
|
+
case "android":
|
|
158
|
+
// On Android, getAttestation() creates the key AND returns the attestation
|
|
159
|
+
// certificate chain in a single call. generateHardwareKey() is iOS-only.
|
|
160
|
+
deviceKeyId = `ZCAM1_ANDROID_DEVICE_${appId}`;
|
|
161
|
+
break;
|
|
162
|
+
|
|
163
|
+
case "ios":
|
|
164
|
+
case "macos":
|
|
165
|
+
deviceKeyId = await generateHardwareKey();
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
default:
|
|
169
|
+
throw new Error(`getDeviceKeyId: ${Platform.OS} not supported`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
await EncryptedStorage.setItem(`deviceKeyId-${appId}`, deviceKeyId);
|
|
173
|
+
|
|
174
|
+
return deviceKeyId;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function getAndPersistAttestation(keyId: string, isSimulator: boolean): Promise<string> {
|
|
178
|
+
let attestation = await EncryptedStorage.getItem(`attestation-${keyId}`);
|
|
179
|
+
|
|
180
|
+
if (attestation) return attestation;
|
|
179
181
|
|
|
180
182
|
if (isSimulator) {
|
|
181
183
|
console.warn(
|
|
@@ -184,12 +186,24 @@ export async function updateRegistration(keyId: string, _settings: Settings): Pr
|
|
|
184
186
|
return `SIMULATOR_MOCK_${keyId}_${Date.now()}`;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
|
|
189
|
+
attestation = await getAttestation(keyId, keyId);
|
|
190
|
+
|
|
188
191
|
await EncryptedStorage.setItem(`attestation-${keyId}`, attestation);
|
|
189
192
|
|
|
190
193
|
return attestation;
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Updates device registration by performing attestation with the backend.
|
|
198
|
+
* @param keyId - The hardware key identifier
|
|
199
|
+
* @returns Attestation data and challenge
|
|
200
|
+
*/
|
|
201
|
+
export async function updateRegistration(keyId: string, isSimulator: boolean): Promise<string> {
|
|
202
|
+
await EncryptedStorage.removeItem(`attestation-${keyId}`);
|
|
203
|
+
|
|
204
|
+
return getAndPersistAttestation(keyId, isSimulator);
|
|
205
|
+
}
|
|
206
|
+
|
|
193
207
|
/**
|
|
194
208
|
* Requests camera (and microphone) permissions on Android.
|
|
195
209
|
* No-op on iOS — the system prompts automatically when the camera is accessed.
|
|
@@ -136,7 +136,10 @@ export enum VerifyError_Tags {
|
|
|
136
136
|
Base64 = "Base64",
|
|
137
137
|
Io = "Io",
|
|
138
138
|
Groth16 = "Groth16",
|
|
139
|
+
BindingsNotFound = "BindingsNotFound",
|
|
139
140
|
ProofNotFound = "ProofNotFound",
|
|
141
|
+
MetadataNotFound = "MetadataNotFound",
|
|
142
|
+
SimulatorNotAllowed = "SimulatorNotAllowed",
|
|
140
143
|
PlatformNotSupported = "PlatformNotSupported",
|
|
141
144
|
}
|
|
142
145
|
export const VerifyError = (() => {
|
|
@@ -272,7 +275,7 @@ export const VerifyError = (() => {
|
|
|
272
275
|
return instanceOf(e) && (e as any)[variantOrdinalSymbol] === 6;
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
|
-
class
|
|
278
|
+
class BindingsNotFound extends UniffiError {
|
|
276
279
|
/**
|
|
277
280
|
* @private
|
|
278
281
|
* This field is private and should not be used.
|
|
@@ -284,6 +287,28 @@ export const VerifyError = (() => {
|
|
|
284
287
|
*/
|
|
285
288
|
readonly [variantOrdinalSymbol] = 7;
|
|
286
289
|
|
|
290
|
+
readonly tag = VerifyError_Tags.BindingsNotFound;
|
|
291
|
+
|
|
292
|
+
constructor(message: string) {
|
|
293
|
+
super("VerifyError", "BindingsNotFound", message);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static instanceOf(e: any): e is BindingsNotFound {
|
|
297
|
+
return instanceOf(e) && (e as any)[variantOrdinalSymbol] === 7;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
class ProofNotFound extends UniffiError {
|
|
301
|
+
/**
|
|
302
|
+
* @private
|
|
303
|
+
* This field is private and should not be used.
|
|
304
|
+
*/
|
|
305
|
+
readonly [uniffiTypeNameSymbol]: string = "VerifyError";
|
|
306
|
+
/**
|
|
307
|
+
* @private
|
|
308
|
+
* This field is private and should not be used.
|
|
309
|
+
*/
|
|
310
|
+
readonly [variantOrdinalSymbol] = 8;
|
|
311
|
+
|
|
287
312
|
readonly tag = VerifyError_Tags.ProofNotFound;
|
|
288
313
|
|
|
289
314
|
constructor(message: string) {
|
|
@@ -291,7 +316,51 @@ export const VerifyError = (() => {
|
|
|
291
316
|
}
|
|
292
317
|
|
|
293
318
|
static instanceOf(e: any): e is ProofNotFound {
|
|
294
|
-
return instanceOf(e) && (e as any)[variantOrdinalSymbol] ===
|
|
319
|
+
return instanceOf(e) && (e as any)[variantOrdinalSymbol] === 8;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
class MetadataNotFound extends UniffiError {
|
|
323
|
+
/**
|
|
324
|
+
* @private
|
|
325
|
+
* This field is private and should not be used.
|
|
326
|
+
*/
|
|
327
|
+
readonly [uniffiTypeNameSymbol]: string = "VerifyError";
|
|
328
|
+
/**
|
|
329
|
+
* @private
|
|
330
|
+
* This field is private and should not be used.
|
|
331
|
+
*/
|
|
332
|
+
readonly [variantOrdinalSymbol] = 9;
|
|
333
|
+
|
|
334
|
+
readonly tag = VerifyError_Tags.MetadataNotFound;
|
|
335
|
+
|
|
336
|
+
constructor(message: string) {
|
|
337
|
+
super("VerifyError", "MetadataNotFound", message);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
static instanceOf(e: any): e is MetadataNotFound {
|
|
341
|
+
return instanceOf(e) && (e as any)[variantOrdinalSymbol] === 9;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
class SimulatorNotAllowed extends UniffiError {
|
|
345
|
+
/**
|
|
346
|
+
* @private
|
|
347
|
+
* This field is private and should not be used.
|
|
348
|
+
*/
|
|
349
|
+
readonly [uniffiTypeNameSymbol]: string = "VerifyError";
|
|
350
|
+
/**
|
|
351
|
+
* @private
|
|
352
|
+
* This field is private and should not be used.
|
|
353
|
+
*/
|
|
354
|
+
readonly [variantOrdinalSymbol] = 10;
|
|
355
|
+
|
|
356
|
+
readonly tag = VerifyError_Tags.SimulatorNotAllowed;
|
|
357
|
+
|
|
358
|
+
constructor(message: string) {
|
|
359
|
+
super("VerifyError", "SimulatorNotAllowed", message);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static instanceOf(e: any): e is SimulatorNotAllowed {
|
|
363
|
+
return instanceOf(e) && (e as any)[variantOrdinalSymbol] === 10;
|
|
295
364
|
}
|
|
296
365
|
}
|
|
297
366
|
class PlatformNotSupported extends UniffiError {
|
|
@@ -304,7 +373,7 @@ export const VerifyError = (() => {
|
|
|
304
373
|
* @private
|
|
305
374
|
* This field is private and should not be used.
|
|
306
375
|
*/
|
|
307
|
-
readonly [variantOrdinalSymbol] =
|
|
376
|
+
readonly [variantOrdinalSymbol] = 11;
|
|
308
377
|
|
|
309
378
|
readonly tag = VerifyError_Tags.PlatformNotSupported;
|
|
310
379
|
|
|
@@ -313,7 +382,7 @@ export const VerifyError = (() => {
|
|
|
313
382
|
}
|
|
314
383
|
|
|
315
384
|
static instanceOf(e: any): e is PlatformNotSupported {
|
|
316
|
-
return instanceOf(e) && (e as any)[variantOrdinalSymbol] ===
|
|
385
|
+
return instanceOf(e) && (e as any)[variantOrdinalSymbol] === 11;
|
|
317
386
|
}
|
|
318
387
|
}
|
|
319
388
|
|
|
@@ -328,7 +397,10 @@ export const VerifyError = (() => {
|
|
|
328
397
|
Base64,
|
|
329
398
|
Io,
|
|
330
399
|
Groth16,
|
|
400
|
+
BindingsNotFound,
|
|
331
401
|
ProofNotFound,
|
|
402
|
+
MetadataNotFound,
|
|
403
|
+
SimulatorNotAllowed,
|
|
332
404
|
PlatformNotSupported,
|
|
333
405
|
instanceOf,
|
|
334
406
|
};
|
|
@@ -365,9 +437,18 @@ const FfiConverterTypeVerifyError = (() => {
|
|
|
365
437
|
return new VerifyError.Groth16(FfiConverterString.read(from));
|
|
366
438
|
|
|
367
439
|
case 7:
|
|
368
|
-
return new VerifyError.
|
|
440
|
+
return new VerifyError.BindingsNotFound(FfiConverterString.read(from));
|
|
369
441
|
|
|
370
442
|
case 8:
|
|
443
|
+
return new VerifyError.ProofNotFound(FfiConverterString.read(from));
|
|
444
|
+
|
|
445
|
+
case 9:
|
|
446
|
+
return new VerifyError.MetadataNotFound(FfiConverterString.read(from));
|
|
447
|
+
|
|
448
|
+
case 10:
|
|
449
|
+
return new VerifyError.SimulatorNotAllowed(FfiConverterString.read(from));
|
|
450
|
+
|
|
451
|
+
case 11:
|
|
371
452
|
return new VerifyError.PlatformNotSupported(FfiConverterString.read(from));
|
|
372
453
|
|
|
373
454
|
default:
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ export type {
|
|
|
39
39
|
CaptureFormat,
|
|
40
40
|
FilmStyleEffect,
|
|
41
41
|
FilmStyleRecipe,
|
|
42
|
+
HardwareShutterAction,
|
|
42
43
|
HighlightShadowConfig,
|
|
43
44
|
MonochromeConfig,
|
|
44
45
|
TakePhotoOptions,
|
|
@@ -47,7 +48,13 @@ export type {
|
|
|
47
48
|
} from "./camera";
|
|
48
49
|
export { ZCamera } from "./camera";
|
|
49
50
|
export type { CaptureInfo, DeviceOrientation } from "./capture";
|
|
50
|
-
export {
|
|
51
|
+
export {
|
|
52
|
+
initCapture,
|
|
53
|
+
previewFile,
|
|
54
|
+
requestCameraPermission,
|
|
55
|
+
requestLocationPermission,
|
|
56
|
+
updateRegistration,
|
|
57
|
+
} from "./capture";
|
|
51
58
|
|
|
52
59
|
/**
|
|
53
60
|
* Core cryptographic key types and secure enclave utilities.
|
package/src/picker.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CameraRoll } from "@react-native-camera-roll/camera-roll";
|
|
2
2
|
import { FlashList, useRecyclingState } from "@shopify/flash-list";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
import { Dimensions, Image, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
4
|
+
import { Dimensions, Image, Platform, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
5
5
|
import { createThumbnail } from "react-native-create-thumbnail";
|
|
6
6
|
import { Dirs, FileSystem, Util } from "react-native-file-access";
|
|
7
7
|
|
|
@@ -141,8 +141,9 @@ const ZImageItem = ({
|
|
|
141
141
|
const ext = Util.extname(uri)?.toLowerCase();
|
|
142
142
|
|
|
143
143
|
if (ext === "mov" || ext === "mp4") {
|
|
144
|
+
const url = Platform.OS == "android" ? uri : stripFileProtocol(uri);
|
|
144
145
|
const thumbnail = await createThumbnail({
|
|
145
|
-
url
|
|
146
|
+
url,
|
|
146
147
|
});
|
|
147
148
|
setThumbnail(thumbnail.path);
|
|
148
149
|
}
|
package/src/verify.tsx
CHANGED
|
@@ -57,6 +57,7 @@ export class VerifiableFile {
|
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Verifies the cryptographic proof embedded in the C2PA manifest.
|
|
60
|
+
* @param appId - The app identifier used during capture. On iOS: `TEAM_ID.BUNDLE_ID` (e.g. `NLS5R4YCGX.com.example.myapp`). On Android: the package name (e.g. `com.example.myapp`).
|
|
60
61
|
* @returns True if the proof is valid, false otherwise
|
|
61
62
|
*/
|
|
62
63
|
verifyProof(appId: string): boolean {
|