Metadata-Version: 2.4
Name: c2pa-health-imaging
Version: 0.0.1
Summary: Custom health imaging provenance for DICOM and NIfTI formats.
Author: C2PA Health Imaging Contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/imagin4care/c2pa-health-imaging
Project-URL: Documentation, https://github.com/imagin4care/c2pa-health-imaging#readme
Project-URL: Repository, https://github.com/imagin4care/c2pa-health-imaging
Project-URL: Changelog, https://github.com/imagin4care/c2pa-health-imaging/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/imagin4care/c2pa-health-imaging/issues
Keywords: dicom,nifti,provenance,cryptography,health-imaging
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Healthcare Industry
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydicom<4,>=3.0.0
Requires-Dist: numpy<3,>=1.24
Requires-Dist: nibabel<6,>=5.0
Requires-Dist: cryptography<47,>=46.0.5
Requires-Dist: typer<1,>=0.12
Provides-Extra: dev
Requires-Dist: pytest<10,>=9.0.3; extra == "dev"
Requires-Dist: pytest-cov<7,>=5.0; extra == "dev"
Requires-Dist: ruff<1,>=0.5.0; extra == "dev"
Requires-Dist: mypy<2,>=1.10; extra == "dev"
Requires-Dist: httpx<1,>=0.27; extra == "dev"
Requires-Dist: fastapi<1,>=0.110; extra == "dev"
Requires-Dist: uvicorn<1,>=0.27; extra == "dev"
Requires-Dist: python-multipart<1,>=0.0.9; extra == "dev"
Requires-Dist: sqlmodel<1,>=0.0.16; extra == "dev"
Provides-Extra: web
Requires-Dist: fastapi<1,>=0.110; extra == "web"
Requires-Dist: uvicorn<1,>=0.27; extra == "web"
Requires-Dist: python-multipart<1,>=0.0.9; extra == "web"
Requires-Dist: sqlmodel<1,>=0.0.16; extra == "web"
Dynamic: license-file

<p align="center">
  <img src="assets/logo/logo_blue-cross_yellow-chain_transparent.png" alt="Health Imaging Provenance Extension logo" width="200">
</p>

# Health Imaging Provenance Extension

Custom cryptographic provenance for **DICOM** and **NIfTI** health imaging formats.

> **Important:** This project implements a **custom provenance manifest** (JSON sidecar / DICOM private block). It is **not** interoperable C2PA (no JUMBF/COSE). Manifest timestamps are **self-asserted** at sign time (`timestamp_kind: self_asserted`), not RFC 3161 TSA timestamps.

## Install

```bash
# From PyPI (after publish)
pip install c2pa-health-imaging

# From source (editable, for development)
pip install -e ".[dev]"
```

The CLI command is **`c2pa-health`** (short for health-imaging provenance). Requires Python 3.10+.

```bash
c2pa-health --help
```

## Binding model

A single **binding profile** produces a deterministic SHA-256 hash over:

- decoded pixel / voxel values (little-endian, C-contiguous — not raw compressed bytes)
- canonical metadata that defines interpretation and geometry (rescale, spacing, affine, etc.)

### DICOM binding profile

DICOM binding always includes **mandatory** keywords required by the Image Pixel Module (e.g. `Rows`, `Columns`, `BitsAllocated`, `PhotometricInterpretation`). Additional keywords can be supplied at sign time; the resolved list is stored in the signed manifest so verification does not depend on local config.

```json
{
  "additional_keywords": ["RescaleSlope", "RescaleIntercept", "PixelSpacing"]
}
```

```bash
c2pa-health sign scan.dcm --key signer.key --cert signer.pem \
  --out signed.dcm \
  --binding-profile ct.json \
  --binding-keyword FrameOfReferenceUID
```

Artifacts signed before binding profiles were introduced verify using the legacy default keyword set when `binding_keywords` is absent from the manifest.

## Signing and verification

| Format | Storage |
|--------|---------|
| DICOM | Private manifest block plus a `DigitalSignaturesSequence` item (custom binding reference, not full PS3.15 MAC) |
| NIfTI | Detached sidecar `<file>.c2pa.json` |

Signing uses **ECDSA P-256** with an **X.509** certificate embedded in the signed artifact.

Verification enforces:

- ECDSA signature over the manifest claim
- Certificate validity window (not before / not after)
- Trusted signer policy (CLI: `--trust-cert` / `--trust-bundle`; web: trusted keys in DB)

## CLI

```bash
# Generate a key pair (optional encryption)
c2pa-health keygen --out-key signer.key --out-cert signer.pem --password 'secret'

# Sign a DICOM or NIfTI file
c2pa-health sign scan.dcm --key signer.key --cert signer.pem --out signed.dcm \
  --process "name=acquire;software=CT Scanner" \
  --process "name=denoise;software=Pipeline"

# Verify with trusted signer certificate
c2pa-health verify signed.dcm --trust-cert signer.pem --json

# Inspect manifest / process chain
c2pa-health inspect volume.nii
```

Exit codes: `0` valid, `1` invalid, `2` cannot verify, `3` untrusted signer.

Process-chain parameters block PHI-like keys (`patient_id`, `accession_number`, etc.).

## Web validator

FastAPI + SQLite validator with **trusted-key enforcement** and API-key auth for admin routes.

```bash
# From repository root (web package is monorepo-local, not pip-installed)
pip install -e ".[web]"
export API_KEY=dev-secret
export REQUIRE_AUTH=true
uvicorn web.backend.app:app --reload --port 8000
```

Or with Docker:

```bash
export API_KEY=change-me-in-production
docker compose up --build
```

- Frontend: http://localhost:8080 (proxies `/api` to backend)
- Health: http://localhost:8080/api/../health via backend internal port

Configure via environment variables:

| Variable | Default | Description |
|----------|---------|-------------|
| `API_KEY` | (empty) | Required when `REQUIRE_AUTH=true` |
| `REQUIRE_AUTH` | `true` | Protect history/trusted-key admin routes |
| `CORS_ORIGINS` | `http://localhost:8080` | Comma-separated allowed origins |
| `MAX_UPLOAD_BYTES` | `536870912` | Max upload size (512 MiB) |
| `DOCS_ENABLED` | `false` | Expose OpenAPI `/docs` |

Uploaded filenames are **not** stored; history uses anonymized `upload_ref` values.

## Tests

```bash
pytest
make lint
make docker-test
```

## Project layout

```
src/c2pa_health_imaging/   Python library (binding, crypto, DICOM, NIfTI, CLI)
web/backend/               FastAPI + SQLite API (monorepo-local)
web/frontend/              React + Vite + Catalyst UI Kit
tests/                     pytest suite
```

## License

MIT — see [LICENSE](LICENSE) and [CHANGELOG](CHANGELOG.md).
