# Dynamic Library Signing Example

This example demonstrates loading a C2PA Rust dynamic library at runtime with libloading, calling exported FFI signing APIs, and writing the signed image output.

The library now supports two signing implementations:
- Standard C2PA signing (`c2pa_sign_content`)
- CAWG identity signing (`c2pa_sign_content_with_cawg_identity`)

## Use As A Library (crates.io)

Other Rust applications can call the exported API directly.

1. Add the dependency:

```zsh
cargo add c2pa-rust-digicert-example
```

2. Call the library function:

```rust
use anyhow::Result;
use c2pa_rust_digicert_example::{
	sign_image_file, SignImageRequest, SigningImplementation,
};

fn main() -> Result<()> {
	let signing_implementation = SigningImplementation::Standard;
	// let signing_implementation = SigningImplementation::CawgIdentity;

	let mut request = SignImageRequest::with_defaults(
		"input/sample.jpg",
		"output",
		"rust_binary/libc2pa_rust.dylib", // use .so on Linux, .dll on Windows
		"", // optional credential override; empty uses internal/default credential
		"your-account-id",
		"", // optional user ID; empty string means not provided
		"your-api-key",
		"https://your-signing-service-url",
	);

	request.roles_csv = "creator,publisher".to_string(); // comma-separated; see Supported Roles section below
	request.signing_implementation = signing_implementation;

	// CAWG mode example:
	// request.smime_credential_id = Some("your-smime-credential-id".to_string());
	// request.user_pin = Some("your-user-pin".to_string());

	let result = sign_image_file(request)?;
	println!("Signed file: {}", result.output_path.display());
	Ok(())
}
```

This library example passes values directly into `SignImageRequest`. The environment variables below are only for the example runner in `examples/load_dylib_sign_image.rs`.
`SigningImplementation::Standard` is the default path, and you only need to switch to `SigningImplementation::CawgIdentity` when you want CAWG signing.

## Environment Variables

Required for example runner (`examples/load_dylib_sign_image.rs`):
- C2PA_IMAGE_INPUT_PATH: path to input image
- C2PA_IMAGE_OUTPUT_FOLDER: folder to save signed image
- C2PA_BINARY_PATH: path to the compiled dynamic library
- C2PA_ACCOUNT_ID: account identifier
- C2PA_API_KEY: signing service API key
- C2PA_SIGNING_SERVICE_URL: signing service URL

Optional:
- C2PA_USER_ID: user identifier (optional; omit or leave empty to sign at account level)
- C2PA_CREDENTIAL_ID: optional override for C2PA credential ID; if omitted, the binary's internal/default credential configuration is used
- C2PA_SIGNING_IMPLEMENTATION: `standard` (default) or `cawg` / `cawg_identity`
- C2PA_ROLES_CSV: comma-separated roles passed to the library (default: `creator`); see Supported Roles below for valid values
- C2PA_SKIP_SSL_VALIDATION (default: true)
- C2PA_IGNORE_EXPIRED_CREDENTIAL_VALIDATION (default: true)
- C2PA_CLAIM_GENERATOR_NAME (default: DigiCert Content Trust Manager)
- C2PA_CLAIM_GENERATOR_VERSION (default: 1.0.0)
- C2PA_TSA_URL (default: http://timestamp.digicert.com)
- C2PA_CAWG_TRAINING_MINING_JSON (only for CAWG identity mode)

Required for CAWG identity mode (`C2PA_SIGNING_IMPLEMENTATION=cawg`):
- C2PA_SMIME_CREDENTIAL_ID: S/MIME credential ID used for CAWG identity assertion signing
- C2PA_USER_PIN: PIN for the S/MIME credential

If any required variable is missing, the example exits with an error before loading the library.

## Input Validation And Secret Handling

`sign_image_file` sanitizes request fields before signing.

- Trims all user-provided string fields.
- Rejects empty values for required fields.
- Rejects control characters in string fields.
- Enforces max lengths on request fields.
- Rejects obvious placeholder secrets for `api_key` and `user_pin` (for example: `changeme`, `default`, `password`, `your_api_key`).

The wrapper requires `smime_credential_id` and `user_pin` when `SigningImplementation::CawgIdentity` is selected.

Use environment variables or a secret manager for sensitive values. Do not hardcode API keys or PINs in source code.

## Supported Roles

The `roles_csv` field (and `C2PA_ROLES_CSV` env var) accepts a comma-separated list of the following role values. Multiple roles can be combined, e.g. `creator,publisher`.

| Role | Maps to CAWG/DC field | Description |
|---|---|---|
| `creator` | `dc:creator` | Primary creator of the content |
| `publisher` | `dc:publisher` | Publisher of the content (only the first value is used) |
| `contributor` | `dc:contributor` | General contributor |
| `editor` | `dc:contributor` | Editor (grouped under contributor) |
| `producer` | `dc:contributor` | Producer (grouped under contributor) |
| `sponsor` | `dc:contributor` | Sponsor (grouped under contributor) |
| `translator` | `dc:contributor` | Translator (grouped under contributor) |

The library fetches the display name for each role from the signing service using the provided `account_id` / `user_id`. An unrecognised role value is rejected with an error.

## How To Run

This section is for running the local example binary from this repository.

1. Build the project in release mode:

```zsh
cargo build --release
```

2. Put the correct dynamic library for your OS inside the rust_binary folder.

Use this file name convention:
- macOS: rust_binary/libc2pa_rust.dylib
- Linux: rust_binary/libc2pa_rust.so
- Windows: rust_binary/c2pa_rust.dll

If you build locally, copy the release artifact into rust_binary:

```zsh
# macOS example
cp target/release/libc2pa_rust.dylib rust_binary/libc2pa_rust.dylib

# Linux example
cp target/release/libc2pa_rust.so rust_binary/libc2pa_rust.so

# Windows example
copy the rust build(c2pa_rust.dll) file to the rust_binary
```

3. Make sure input and output folders exist:

```zsh
mkdir -p input output rust_binary
```

4. Set required environment variables and run the example:

```zsh
C2PA_IMAGE_INPUT_PATH="input/sample.jpg" \
C2PA_BINARY_PATH="rust_binary/libc2pa_rust.dylib" \
C2PA_IMAGE_OUTPUT_FOLDER="output" \
C2PA_ACCOUNT_ID="your_account_id" \
C2PA_USER_ID="your_user_id" \
C2PA_API_KEY="your_api_key" \
C2PA_SIGNING_SERVICE_URL="https://your-signing-service-url" \
cargo run --example load_dylib_sign_image
```

CAWG identity mode example:

```zsh
C2PA_IMAGE_INPUT_PATH="input/sample.jpg" \
C2PA_BINARY_PATH="rust_binary/libc2pa_rust.dylib" \
C2PA_IMAGE_OUTPUT_FOLDER="output" \
C2PA_CREDENTIAL_ID="your_c2pa_credential_id" \
C2PA_SMIME_CREDENTIAL_ID="your_smime_credential_id" \
C2PA_USER_PIN="your_user_pin" \
C2PA_ACCOUNT_ID="your_account_id" \
C2PA_USER_ID="your_user_id" \
C2PA_API_KEY="your_api_key" \
C2PA_SIGNING_SERVICE_URL="https://your-signing-service-url" \
C2PA_SIGNING_IMPLEMENTATION="cawg" \
cargo run --example load_dylib_sign_image
```

Note:
- Set C2PA_BINARY_PATH based on your OS binary name (.dylib/.so/.dll).
- Output naming is mode-aware: standard uses `_signed`, CAWG identity uses `_cawg_signed`.

