# SignedShot Validator
Verify SignedShot media authenticity proofs. Available as a Rust CLI and Python library.
[](https://pypi.org/project/signedshot/)
[](https://crates.io/crates/signedshot-validator)
[](https://github.com/SignedShot/signedshot-validator/actions/workflows/ci.yml)
## Overview
SignedShot is an open protocol for proving photos and videos haven't been altered since capture. This validator verifies the cryptographic proofs (sidecars) generated by the SignedShot iOS SDK.
## Installation
### Python (PyPI)
```bash
pip install signedshot
```
### Rust (Cargo)
```bash
cargo install signedshot-validator
```
## Python Library
```python
import signedshot
# Validate from files
result = signedshot.validate_files("photo.sidecar.json", "photo.jpg")
print(result.valid) # True/False
print(result.version) # Sidecar format version
print(result.error) # Error message if validation failed
# Capture trust (JWT verification)
trust = result.capture_trust
print(trust["signature_valid"]) # JWT signature verified
print(trust["issuer"]) # API that issued the token
print(trust["publisher_id"]) # Publisher ID
print(trust["device_id"]) # Device ID
print(trust["capture_id"]) # Capture session ID
print(trust["method"]) # Attestation: "sandbox", "app_check", or "app_attest"
print(trust["app_id"]) # App bundle ID (if attested)
print(trust["issued_at"]) # Unix timestamp
# Media integrity (content verification)
integrity = result.media_integrity
print(integrity["content_hash_valid"]) # SHA-256 hash matches
print(integrity["signature_valid"]) # ECDSA signature verified
print(integrity["capture_id_match"]) # Capture IDs match
print(integrity["content_hash"]) # SHA-256 of media
print(integrity["captured_at"]) # ISO8601 timestamp
```
### Validate from Bytes
```python
# Validate from in-memory data
with open("photo.sidecar.json") as f:
sidecar_json = f.read()
with open("photo.jpg", "rb") as f:
media_bytes = f.read()
result = signedshot.validate(sidecar_json, media_bytes)
```
### Validate with Pre-loaded JWKS
```python
# Avoid HTTP call by providing JWKS directly
import requests
jwks = requests.get("https://api.signedshot.io/.well-known/jwks.json").text
result = signedshot.validate_with_jwks(sidecar_json, media_bytes, jwks)
```
### Convert to Dict/JSON
```python
# Get result as dictionary
data = result.to_dict()
# Get result as JSON string
json_str = result.to_json()
```
## CLI Usage
### Validate Media
```bash
# Basic validation
signedshot validate photo.sidecar.json photo.jpg
# Output as JSON
signedshot validate photo.sidecar.json photo.jpg --json
```
### Parse Sidecar (without validation)
```bash
signedshot parse photo.sidecar.json
```
### Example Output
```
Validating sidecar: photo.sidecar.json
Media file: photo.jpg
[OK] Sidecar parsed
[OK] JWT decoded
Issuer: https://api.signedshot.io
Publisher: 9a5b1062-a8fe-4871-bdc1-fe54e96cbf1c
Device: ea5c9bfe-6bbc-4ee2-b82d-0bcfcc185ef1
Capture: ac85dbd2-d8a8-4d0b-9e39-2feef5f7b19f
Method: app_check
App ID: io.signedshot.capture
[OK] JWT signature verified
[OK] Content hash matches
[OK] Media signature verified
[OK] Capture IDs match
✓ VALID - Media authenticity verified
```
## What It Validates
### 1. Capture Trust (JWT)
- Fetches JWKS from issuer (or uses provided keys)
- Verifies ES256 (P-256 ECDSA) signature
- Extracts claims: publisher, device, capture ID, attestation method
### 2. Media Integrity
- Computes SHA-256 hash of media file
- Compares with `content_hash` in sidecar
- Verifies ECDSA signature over integrity data
- Confirms `capture_id` matches JWT
### 3. Cross-Validation
- Ensures capture_id in JWT matches capture_id in media_integrity
- Validates all timestamps and formats
## Building from Source
### Rust CLI
```bash
git clone https://github.com/SignedShot/signedshot-validator.git
cd signedshot-validator
cargo build --release
./target/release/signedshot --help
```
### Python Wheels
```bash
# Install maturin
pip install maturin
# Build wheel
cd python
maturin build --release
# Install locally
pip install ../target/wheels/signedshot-*.whl
```
## Development
```bash
# Format
cargo fmt
# Lint
cargo clippy -- -D warnings
# Test
cargo test
# Build
cargo build --release
```
## Related Repositories
- [signedshot-api](https://github.com/SignedShot/signedshot-api) - Backend API
- [signedshot-ios](https://github.com/SignedShot/signedshot-ios) - iOS SDK
## Links
- [PyPI Package](https://pypi.org/project/signedshot/)
- [Website](https://signedshot.io)
- [Documentation](https://signedshot.io/docs)
- [Interactive Demo](https://signedshot.io/demo)
## License
MIT License - see [LICENSE](LICENSE) for details.