npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rolobits/attestation-photo-mobile

v1.3.0

Published

Atomic C2PA camera SDK for React Native

Readme

Every photo taken through this SDK is signed by the device's tamper-resistant hardware, Secure Enclave on iOS and StrongBox/TEE on Android, and embedded with a C2PA manifest before the file is ever written to disk. The resulting JPEG can be verified with any standard C2PA tool.

Install

1. npm

npm install @rolobits/attestation-photo-mobile

| Peer dependency | Version | |---|---| | react | >= 18 | | react-native | >= 0.73 |

2. Rust toolchain, one-time setup

# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Add mobile targets
rustup target add aarch64-apple-ios        # iOS device
rustup target add aarch64-apple-ios-sim    # iOS simulator (Apple Silicon)
rustup target add aarch64-linux-android    # Android

# Android builds require cargo-ndk
cargo install cargo-ndk

3. iOS

The Rust static library is compiled automatically via a CocoaPods script phase. No manual Xcode linking required.

cd ios && pod install

4. Android

The Rust shared library is compiled automatically via a Gradle task. Just build your app as usual.

5. Permissions

| Permission | iOS Info.plist | Android AndroidManifest.xml | Required? | |---|---|---|---| | Camera | NSCameraUsageDescription | android.permission.CAMERA | Yes | | Location | NSLocationWhenInUseUsageDescription | android.permission.ACCESS_FINE_LOCATION | Only with includeLocation: true |

Quick start

This library is headless, it handles attestation and signing, you bring your own camera (react-native-vision-camera, expo-camera, or anything that produces a JPEG path). See the example app for a full implementation.

import { useRef, useCallback } from "react";
import { Camera, useCameraDevice, useCameraPermission } from "react-native-vision-camera";
import { useAttestedCapture, saveToGallery } from "@rolobits/attestation-photo-mobile";

function CaptureScreen() {
  const cameraRef = useRef<Camera>(null);
  const device = useCameraDevice("back");
  const { hasPermission, requestPermission } = useCameraPermission();

  const { signPhoto, status, isReady } = useAttestedCapture({
    includeLocation: true,               // embed GPS in the C2PA manifest
    appName: "My App",                   // shown in Content Credentials
    nonce: "server-challenge-token",     // replay prevention
  });

  const onPressCapture = useCallback(async () => {
    // 1. Take a photo with VisionCamera
    const rawPhoto = await cameraRef.current!.takePhoto();

    // 2. Sign & embed the C2PA manifest — that's it
    const signed = await signPhoto(rawPhoto.path);

    // 3. (Optional) save to gallery
    await saveToGallery({ filePath: signed.path });
  }, [signPhoto]);

  return <Camera ref={cameraRef} device={device!} isActive photo />;
}

useAttestedCapture handles everything behind the scenes: hardware key provisioning, device integrity checks, location prefetch & refresh, file:// prefix stripping, and error wrapping into typed AttestedCameraError codes.

useAttestedCapture options

| Option | Type | Default | Description | |---|---|---|---| | includeLocation | boolean | false | Embed GPS coordinates in the C2PA manifest. | | requireTrustedHardware | boolean | true | Reject signing on devices without Secure Enclave / StrongBox / TEE. | | appName | string | "Attestation Mobile" | App name shown in C2PA Content Credentials. | | nonce | string | — | Server challenge token for replay prevention. |

useAttestedCapture return value

| Property | Type | Description | |---|---|---| | signPhoto | (photoPath: string) => Promise<SignedPhoto> | Sign a photo at the given path. Strips file:// prefix automatically. Rejects with AttestedCameraError on trust or signing failures. | | status | AttestationStatus \| null | Current attestation status (null while loading on mount). | | isReady | boolean | true once key provisioning and status check have completed. |

Lower-level functions

For advanced use cases where you need full control over each step, every function the hook uses is also exported individually:

| Function | Returns | Description | |---|---|---| | getAttestationStatus() | Promise<AttestationStatus> | Check device integrity and hardware trust level. | | ensureHardwareKey() | Promise<{ trustLevel }> | Provision the hardware-backed signing key. Idempotent. | | captureAndSignAtomic(params) | Promise<SignedPhoto> | Hash, sign, and embed a C2PA manifest into a JPEG. | | saveToGallery(params) | Promise<{ uri }> | Save a file to the device photo gallery. | | hashPhotoAtPath(params) | Promise<{ sha256Hex }> | Compute the SHA-256 hash of a photo file. | | signPayload(params) | Promise<{ signatureBase64, trustLevel }> | Sign arbitrary base64 data with the hardware key. | | prefetchLocation() | Promise<{ latitude, longitude } \| null> | Pre-fetch GPS location in background for faster captures. |

SignedPhoto

The object returned by captureAndSignAtomic():

interface SignedPhoto {
  path: string;                    // File path to the signed JPEG
  signature: string;               // SHA-256 hex of the original asset
  algorithm: "ECDSA_P256_SHA256";  // Signing algorithm used
  manifestFormat: "c2pa-jumbf";    // Always JUMBF
  trustLevel: PlatformTrustLevel;  // "secure_enclave" | "strongbox" | "tee" | "software_fallback"
  embeddedManifest?: boolean;      // true when real C2PA manifest is embedded
  metadata: CaptureMetadata;       // Device model, OS, timestamp, nonce, etc.
}

Verifying output

Upload the output JPEG to verify.contentauthenticity.org or use the CLI:

cargo install c2patool
c2patool verify output.jpg

The verifier will show a valid signature with an unknown signer. This is expected for self-signed certificates. See Limitations below.

Error codes

| Code | When | |---|---| | E_COMPROMISED_DEVICE | Device shows signs of jailbreak or root. Capture is blocked. | | E_NO_TRUSTED_HARDWARE | No Secure Enclave, StrongBox, or TEE available and requireTrustedHardware is true. | | E_ATTESTATION_FAILED | Hardware key provisioning failed. | | E_CAPTURE_FAILED | Camera capture or file I/O failed. | | E_SIGNING_FAILED | Hardware signing operation rejected. | | E_C2PA_EMBED_FAILED | C2PA manifest could not be built or embedded. |

Limitations

  • Self-signed certificates — The signing key has a self-signed X.509 certificate with no CA chain. Verifiers will report an unknown signer. Tamper detection works, but attribution requires a CA integration (not yet implemented).
  • Camera injection — On a compromised device, an attacker could feed synthetic frames to the camera API. The SDK signs whatever the camera returns.
  • Heuristic root detection — Jailbreak/root detection uses basic signals. Sophisticated root hides are not detected. Consider layering with Play Integrity (Android) or App Attest (iOS) for higher assurance.

Platform requirements

| Platform | Minimum | Recommended | |---|---|---| | iOS | 14.0 | 16.0+ | | Android | API 28 / Android 9 | API 30+ / Android 11+ for higher StrongBox availability | | Rust | 1.75.0 | stable | | Node | 18 | 22 |

License

MIT