# 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 signing_implementation = SigningImplementation::StandardFromPath;      // heap-optimized, requires lib ≥ v1.1.0
	// let signing_implementation = SigningImplementation::CawgIdentityFromPath; // heap-optimized CAWG, requires lib ≥ v1.1.0

	let mut request = SignImageRequest::with_defaults(
		"input/sample.jpg",
		"output",
		"rust_binary/libc2pa_rust.dylib", // use .so on Linux, .dll on Windows
		"your-account-id",
		"your-api-key",
		"https://your-signing-service-url",
	);

	// Optionally set user_id if signing at user level:
	// request.user_id = "your-user-id".to_string();

	request.roles_csv = "creator,publisher".to_string(); // standard: creator, publisher, contributor only; 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`.
`SigningImplementation::Standard` is the default. Switch to `SigningImplementation::CawgIdentity` for CAWG signing. The `StandardFromPath` and `CawgIdentityFromPath` variants are heap-optimized alternatives that read input and write output via file paths — preferred for large files or videos. Both path-based variants require library ≥ v1.1.0.

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` or `SigningImplementation::CawgIdentityFromPath` 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 accepts a comma-separated list of role values. The set of accepted roles depends on the signing mode.

### Standard Signing (`Standard`, `StandardFromPath`)

Only the following Dublin Core roles are accepted. Multiple roles can be combined, e.g. `creator,publisher`.

| Role | Maps to 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 |

### CAWG Identity Signing (`CawgIdentity`, `CawgIdentityFromPath`)

All of the standard roles above plus the following additional roles are accepted. Validated roles are written to `cawg.identity.signer_payload.role` using CAWG names (e.g. `cawg.creator`, `cawg.editor`).

| Role | CAWG identity field | Description |
|---|---|---|
| `creator` | `cawg.creator` | Primary creator of the content |
| `publisher` | `cawg.publisher` | Publisher of the content |
| `contributor` | `cawg.contributor` | General contributor |
| `editor` | `cawg.editor` | Editor |
| `producer` | `cawg.producer` | Producer |
| `sponsor` | `cawg.sponsor` | Sponsor |
| `translator` | `cawg.translator` | Translator |

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:

```
cargo run
```

Note:
- Set binary_path based on your OS binary name (.dylib/.so/.dll).
- Output naming is mode-aware: `Standard` and `StandardFromPath` use `_signed`; `CawgIdentity` and `CawgIdentityFromPath` use `_cawg_signed`.
- `C2PA_SIGNING_IMPLEMENTATION` accepts: `standard`, `cawg` / `cawg_identity`, `standard_from_path`, `cawg_from_path` / `cawg_identity_from_path`.

