Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Verify SDK

The Verify SDK (@succinctlabs/react-native-zcam1) validates the authenticity of C2PA-signed photos with embedded proofs.

Installation

npm i @succinctlabs/react-native-zcam1
cd ios && pod install

Core Concepts

The Verify SDK:

  • Extracts C2PA manifests from photos
  • Verifies the photo hash matches the manifest
  • Validates bindings (Apple App Attest signatures) for photos with succinct.bindings
  • Validates ZK proofs against a pinned Apple Root CA for photos with succinct.proof
  • Extracts capture metadata (device info, camera settings, etc.)
  • Confirms the photo was taken on a genuine iOS device

API Reference

VerifiableFile

Class for verifying photo authenticity.

import { VerifiableFile } from "@succinctlabs/react-native-zcam1";
 
const file = new VerifiableFile(photoUri);
Constructor:
  • uriOrPath - File path or URI to the photo

VerifiableFile.verifyBindings()

Verifies the Apple App Attest bindings for photos that have succinct.bindings but haven't been proven yet. This is useful for verifying photos before proof generation or when using bindings-only verification.

const isBindingsValid: boolean = file.verifyBindings(appAttestProduction);
Parameters:
  • appAttestProduction - true if the photo was captured in production mode, false for development

Returns: true if the bindings are valid

Note: This method works for photos with succinct.bindings. For photos with succinct.proof, use verifyProof() instead.

VerifiableFile.verifyProof()

Parameters:
  • appId - The app identifier

Validates the embedded ZK proof against the photo hash and Apple Root CA.

const isProofValid: boolean = file.verifyProof();

Returns: true if the proof is valid

Throws: Error if the photo doesn't contain a succinct.proof assertion

VerifiableFile.dataHash()

Returns the file's content hash as recorded in the active C2PA manifest.

const hash: string | undefined = file.dataHash();

Returns: The manifest data hash (base64-encoded string), or undefined if not available

VerifiableFile.captureMetadata()

Returns the capture metadata from the C2PA manifest, containing device info and camera settings recorded at capture time.

import { CaptureMetadata } from "@succinctlabs/react-native-zcam1";
 
const metadata: CaptureMetadata | null = file.captureMetadata();

Returns: The capture metadata object, or null if not present

Metadata includes:
  • action - The capture action type
  • when - Timestamp of capture
  • parameters - Photo or video capture settings:
    • Photo fields:
      • deviceMake, deviceModel
      • softwareVersion
      • xResolution, yResolution
      • orientation
      • iso, exposureTime
      • focalLength, depthOfField
      • authenticityData:
        • isJailBroken
        • isLocationSpoofingAvailable
      • depthData (when available):
        • width, height
        • pixelFormat
        • accuracy
        • statistics:
          • min, max, mean, stdDev
          • validPixelCount, sampleStride
    • Video fields:
      • deviceMake, deviceModel
      • softwareVersion
      • format
      • hasAudio
      • durationSeconds
      • fileSizeBytes
      • width, height
      • rotationDegrees
      • frameRate
      • videoCodec
      • audioCodec
      • audioSampleRate
      • audioChannels
      • authenticityData:
        • isJailBroken
        • isLocationSpoofingAvailable

Complete Example

import { useState } from "react";
import { View, Button, Text, StyleSheet } from "react-native";
import { VerifiableFile } from "@succinctlabs/react-native-zcam1";
 
export function VerifyScreen({ photoUri }: { photoUri: string }) {
  const [status, setStatus] = useState<string>();
 
  const handleVerify = () => {
    try {
      const file = new VerifiableFile(photoUri);
      const appId = "TEAM_ID.com.example.app";
      const isValid = file.verifyProof(appId);
      setStatus(isValid ? "Authentic" : "Invalid proof");
    } catch (err: any) {
      setStatus(`Error: ${err.message}`);
    }
  };
 
  return (
    <View style={styles.container}>
      <Button title="Verify Photo" onPress={handleVerify} />
      {status && <Text style={styles.result}>{status}</Text>}
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: { flex: 1, padding: 16 },
  result: { marginTop: 16, fontSize: 18 },
});

What Verification Checks

CheckWhat It ValidatesUse Case
verifyBindings(production)Apple App Attest signature is valid and corresponds to the photo hashFor photos with succinct.bindings (before proof generation)
verifyProof(appId)ZK proof is valid and chains to the Apple Root CAFor photos with succinct.proof (after proof generation)

Both methods internally compute and verify the file's content hash against the C2PA manifest.

Verification Flow:

For photos with succinct.bindings:

  • Call verifyBindings(production) to verify the Apple App Attest signature and file integrity

For photos with succinct.proof:

  • Call verifyProof(appId) to verify the ZK proof and file integrity

Notes

  • Verification is performed locally—no network required
  • Use verifyBindings() for photos with succinct.bindings (before proof generation)
  • Use verifyProof() for photos with succinct.proof (after proof generation)
  • Photos with only succinct.bindings will fail verifyProof() (they haven't been proven yet)
  • Photos with succinct.proof can use either verification method, but verifyProof() provides stronger guarantees
  • The Apple Root CA is pinned in the SDK; proofs must chain to this root
  • captureMetadata() provides access to device and camera settings recorded at capture time