diff --git a/docs/context.md b/docs/context.md new file mode 100644 index 00000000..d4d8ec7f --- /dev/null +++ b/docs/context.md @@ -0,0 +1,513 @@ +# Using Context to configure the SDK + +Use the `Context` class to configure how `Reader`, `Builder`, and other aspects of the SDK operate. + +## What is Context? + +Context encapsulates SDK configuration: + +- **Settings**: Verification options, [`Builder` behavior](#configuring-builder), [`Reader` trust configuration](#configuring-reader), thumbnail configuration, and more. See [Using settings](settings.md) for complete details. +- [**Signer configuration**](#configuring-a-signer): Optional signer credentials and settings that can be stored in the Context for reuse. +- **State isolation**: Each `Context` is independent, allowing different configurations to coexist in the same application. + +### Why use Context? + +`Context` is better than deprecated global/thread-local `Settings` because it: + +- **Makes dependencies explicit**: Configuration is passed directly to `Reader` and `Builder`, not hidden in global state. +- **Enables multiple configurations**: Run different configurations simultaneously. For example, one for development with test certificates, another for production with strict validation. +- **Eliminates thread-local state**: Each `Reader` and `Builder` gets its configuration from the `Context` you pass, avoiding subtle bugs from shared state. +- **Simplifies testing**: Create isolated configurations for tests without worrying about cleanup or interference between them. +- **Improves code clarity**: Reading `Builder(context, manifest)` immediately shows that configuration is being used. + +> [!NOTE] +> The deprecated `c2pa::load_settings(data, format)` still works for backward compatibility but you are encouraged to migrate your code to use `Context`. See [Migrating from thread-local Settings](#migrating-from-thread-local-settings). + +## Creating a Context + +There are several ways to create a `Context`, depending on your needs: + +- [Using SDK default settings](#using-sdk-default-settings) +- [From an inline JSON string](#from-an-inline-json-string) +- [From a Settings object](#from-a-settings-object) +- [Using ContextBuilder](#using-contextbuilder) + +### Using SDK default settings + +The simplest approach is using [SDK default settings](settings.md#default-configuration). + +**When to use:** For quick prototyping, or when you're happy with default behavior (verification enabled, thumbnails enabled at 1024px, and so on). + +```cpp +#include "c2pa.hpp" + +c2pa::Context context; // Uses SDK defaults +``` + +### From an inline JSON string + +You can pass settings to the constructor directly as a JSON string. + +**When to use:** For simple configuration that doesn't need to be shared across the codebase, or when hard-coding settings for a specific purpose (for example, a utility script). + +```cpp +c2pa::Context context(R"({ + "version": 1, + "verify": {"verify_after_sign": true}, + "builder": { + "thumbnail": {"enabled": false}, + "claim_generator_info": {"name": "An app", "version": "0.1.0"} + } +})"); +``` + +### From a Settings object + +You can build a `Settings` object programmatically, then create a `Context` from that. + +**When to use:** For configuration that needs runtime logic (such as conditional settings based on environment), or when you want to build settings incrementally. + +```cpp +c2pa::Settings settings; +settings.set("builder.thumbnail.enabled", "false"); +settings.set("verify.verify_after_sign", "true"); +settings.update(R"({ + "builder": { + "claim_generator_info": {"name": "An app", "version": "0.1.0"} + } +})"); + +c2pa::Context context(settings); +``` + +### Using ContextBuilder + +You can combine multiple configuration sources by using `Context::ContextBuilder`. + +Use **ContextBuilder** when you want to: + +- Load from a file with `with_json_settings_file()`. +- Combine a base `Settings` with environment-specific overrides from a JSON file. +- Apply multiple JSON snippets in a specific order. + +**Don't use ContextBuilder** if you have a single configuration source. In this case, [direct construction from a Settings object](#from-a-settings-object) using `c2pa::Context context(settings)` is simpler and more readable. + +For example: + +```cpp +c2pa::Settings base_settings; +base_settings.set("builder.thumbnail.enabled", "true"); +base_settings.set("builder.thumbnail.long_edge", "1024"); + +auto context = c2pa::Context::ContextBuilder() + .with_settings(base_settings) + .with_json(R"({"verify": {"verify_after_sign": true}})") + .with_json_settings_file("config/overrides.json") + .create_context(); +``` + +> [!IMPORTANT] +> Later configuration overrides earlier configuration. In the example above, if `overrides.json` sets `builder.thumbnail.enabled` to `false`, it will override the `true` value from `base_settings`. + +**ContextBuilder methods** + +| Method | Description | +|--------|-------------| +| `with_settings(settings)` | Apply a `Settings` object. Must be valid (not moved-from). | +| `with_json(json_string)` | Apply settings from a JSON string. Later calls override earlier ones. | +| `with_json_settings_file(path)` | Load and apply settings from a JSON file. Throws `C2paException` if file doesn't exist or is invalid. | +| `create_context()` | Build and return the `Context`. Consumes the builder (it becomes invalid and cannot be reused). | + +## Common configuration patterns + +### Development environment with test certificates + +During development, you often need to trust self-signed or custom CA certificates: + +```cpp +// Load your test root CA +std::string test_ca = read_file("test-ca.pem"); + +c2pa::Context dev_context(R"({ + "version": 1, + "trust": { + "user_anchors": ")" + test_ca + R"(" + }, + "verify": { + "verify_after_reading": true, + "verify_after_sign": true, + "remote_manifest_fetch": false, + "ocsp_fetch": false + }, + "builder": { + "claim_generator_info": {"name": "Dev Build", "version": "dev"}, + "thumbnail": {"enabled": false} + } +})"); +``` + +### Configuration from environment variables + +Adapt configuration based on the runtime environment: + +```cpp +std::string env = std::getenv("ENVIRONMENT") ? std::getenv("ENVIRONMENT") : "dev"; + +c2pa::Settings settings; +if (env == "production") { + settings.update(read_file("config/production.json"), "json"); + settings.set("verify.strict_v1_validation", "true"); +} else { + settings.update(read_file("config/development.json"), "json"); + settings.set("verify.remote_manifest_fetch", "false"); +} + +c2pa::Context context(settings); +``` + +### Layered configuration + +Load base configuration from a file and apply runtime overrides: + +```cpp +auto context = c2pa::Context::ContextBuilder() + .with_json_settings_file("config/base.json") + .with_json_settings_file("config/" + environment + ".json") + .with_json(R"({ + "builder": { + "claim_generator_info": { + "version": ")" + app_version + R"(" + } + } + })") + .create_context(); +``` + +For the full list of settings and defaults, see [Configuring settings](settings.md). + +## Configuring Reader + +Use `Context` to control how `Reader` validates manifests and handles remote resources, including: + +- **Verification behavior**: Whether to verify after reading, check trust, and so on. +- [**Trust configuration**](#trust-configuration): Which certificates to trust when validating signatures. +- [**Network access**](#offline-operation): Whether to fetch remote manifests or OCSP responses. +- **Performance**: Memory thresholds and other core settings. + +> [!IMPORTANT] +> `Context` is used only at construction. `Reader` copies the configuration it needs internally, so the `Context` object does not need to outlive the `Reader`. This means you can safely use temporary point-in-time contexts; for example, as shown below. + +```cpp +c2pa::Reader reader( + c2pa::Context(R"({"verify": {"remote_manifest_fetch": false}})"), + "image.jpg" +); +``` + +### Reading from a file + +```cpp +// Context that disables remote manifest fetch (for offline environments) +c2pa::Context context(R"({ + "version": 1, + "verify": { + "remote_manifest_fetch": false, + "ocsp_fetch": false + } +})"); + +c2pa::Reader reader(context, "image.jpg"); +std::cout << reader.json() << std::endl; +``` + +### Reading from a stream + +```cpp +std::ifstream stream("image.jpg", std::ios::binary); +c2pa::Reader reader(context, "image/jpeg", stream); + +std::cout << reader.json() << std::endl; +``` + +### Trust configuration + +Example of trust configuration in a settings file: + +```json +{ + "version": 1, + "trust": { + "user_anchors": "-----BEGIN CERTIFICATE-----\nMIICEzCCA...\n-----END CERTIFICATE-----", + "trust_config": "1.3.6.1.4.1.311.76.59.1.9\n1.3.6.1.4.1.62558.2.1" + } +} +``` + +**PEM format requirements:** + +- Use literal `\n` characters (as two-character strings) in JSON for line breaks. +- Include the full certificate chain if needed. +- Concatenate multiple certificates into a single string. + +Then load the file in your application as follows: + +```cpp +auto context = c2pa::Context::ContextBuilder() + .with_json_settings_file("dev_trust_config.json") + .create_context(); + +c2pa::Reader reader(context, "signed_asset.jpg"); +``` + +### Full validation + +To configure full validation, with all verification features enabled: + +```cpp +c2pa::Context full_validation_context(R"({ + "verify": { + "verify_after_reading": true, + "verify_trust": true, + "verify_timestamp_trust": true, + "remote_manifest_fetch": true + } +})"); + +c2pa::Reader online_reader(full_validation_context, "asset.jpg"); +``` + +For more information, see [Settings - Verify](settings.md#verify). + +### Offline operation + +To configure `Reader` to work with no network access: + +```cpp +c2pa::Context offline_context(R"({ + "verify": { + "remote_manifest_fetch": false, + "ocsp_fetch": false + } +})"); + +c2pa::Reader offline_reader(offline_context, "local_asset.jpg"); +``` + +For more information, see [Settings - Offline or air-gapped environments](settings.md#offline-or-air-gapped-environments). + + +## Configuring Builder + +`Builder` uses `Context` to control how to create and sign C2PA manifests. The `Context` affects: + +- **Claim generator information**: Application name, version, and metadata embedded in the manifest. +- **Thumbnail generation**: Whether to create thumbnails, size, quality, and format. +- **Action tracking**: Auto-generation of actions like `c2pa.created`, `c2pa.opened`, `c2pa.placed`. +- **Intent**: The purpose of the claim (create, edit, or update). +- **Verification after signing**: Whether to validate the manifest immediately after signing. +- **Signer configuration** (optional): Credentials can be stored in settings for reuse. + + +> [!IMPORTANT] +> The `Context` is used only when constructing the `Builder`. The `Builder` copies the configuration it needs internally, so the `Context` object does not need to outlive the `Builder`. + +### Basic use + +```cpp +c2pa::Context context(R"({ + "version": 1, + "builder": { + "claim_generator_info": { + "name": "An app", + "version": "0.1.0" + }, + "intent": {"Create": "digitalCapture"} + } +})"); + +c2pa::Builder builder(context, manifest_json); + +// Pass signer explicitly at signing time +c2pa::Signer signer("es256", certs, private_key); +builder.sign(source_path, output_path, signer); +``` + +### Controlling thumbnail generation + +```cpp +// Disable thumbnails for faster processing +c2pa::Context no_thumbnails(R"({ + "builder": { + "claim_generator_info": {"name": "Batch Processor"}, + "thumbnail": {"enabled": false} + } +})"); + +// Or customize thumbnail size and quality for mobile +c2pa::Context mobile_thumbnails(R"({ + "builder": { + "claim_generator_info": {"name": "Mobile App"}, + "thumbnail": { + "enabled": true, + "long_edge": 512, + "quality": "low", + "prefer_smallest_format": true + } + } +})"); +``` + +## Configuring a signer + +The `signer` field in settings can specify: +- A **local signer** — certificate and key (paths or PEM strings): + - `signer.local.alg` — e.g. `"ps256"`, `"es256"`, `"ed25519"`. + - `signer.local.sign_cert` — certificate file path or PEM string. + - `signer.local.private_key` — key file path or PEM string. + - `signer.local.tsa_url` — optional TSA URL. +- A **remote signer** — A POST endpoint that receives data to sign and returns the signature: + - `signer.remote.url` — signing service URL. + - `signer.remote.alg`, `signer.remote.sign_cert`, `signer.remote.tsa_url`. + +See [SignerSettings object reference](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#signersettings) for the full property reference. + +You can configure a signer: + +- [From JSON Settings](#from-settings) +- [Explicitly in code](#explicit-signer) + +### From Settings + +Put signer configuration in your JSON or `Settings`: + +```json +{ + "signer": { + "local": { + "alg": "ps256", + "sign_cert": "path/to/cert.pem", + "private_key": "path/to/key.pem", + "tsa_url": "http://timestamp.example.com" + } + } +} +``` + +Then create a `Context` and use it with `Builder`; for example: + +```cpp +c2pa::Context context(settings_json_or_path); +c2pa::Builder builder(context, manifest_json); +// When you call sign(), use a Signer created from your cert/key, +// or the SDK may use the signer from context if the C API supports it. +builder.sign(source_path, dest_path, signer); +``` + +In the C++ API you typically create a `c2pa::Signer` explicitly and pass it to `Builder::sign()`. Settings in the `Context` still control verification, thumbnails, and other builder behavior. + +### Explicit signer + +For full programmatic control, create a `Signer` and pass it to `Builder::sign()`: + +```cpp +c2pa::Signer signer("es256", certs_pem, private_key_pem, "http://timestamp.digicert.com"); +c2pa::Builder builder(context, manifest_json); +builder.sign(source_path, dest_path, signer); +``` + +The `Context` continues to control verification and builder options. The signer is used only for the cryptographic signature. + +## Context lifetime and usage + +Understand how `Context` works to use it properly. + +### Context ownership and lifecycle + +- **Non-copyable, moveable**: `Context` can be moved but not copied. After moving, the source `Context` becomes invalid (`is_valid()` returns `false`). +- **Used at construction only**: When you create a `Reader` or `Builder` with a `Context`, the implementation copies the configuration it needs. The `Context` object does not need to outlive the `Reader` or `Builder` objects. +- **Reusable**: You can reuse the same `Context` to create multiple readers and builders. + +```cpp +c2pa::Context context(settings); + +// All three use the same configuration +c2pa::Builder builder1(context, manifest1); +c2pa::Builder builder2(context, manifest2); +c2pa::Reader reader(context, "image.jpg"); + +// Context can go out of scope, readers/builders still work +``` + +### Multiple contexts for different purposes + +Use different `Context` objects when you need different settings; for example, for development vs. production, or different trust configurations: + +```cpp +c2pa::Context dev_context(dev_settings); +c2pa::Context prod_context(prod_settings); + +// Different builders with different configurations +c2pa::Builder dev_builder(dev_context, manifest); +c2pa::Builder prod_builder(prod_context, manifest); +``` + +### Move semantics + +```cpp +c2pa::Context context1(settings); +c2pa::Context context2 = std::move(context1); + +// context1 is now invalid +assert(!context1.is_valid()); + +// context2 is valid and can be used +c2pa::Builder builder(context2, manifest); +``` + +### Temporary contexts + +Since the context is copied at construction, you can use temporary contexts: + +```cpp +c2pa::Builder builder( + c2pa::Context(R"({"builder": {"thumbnail": {"enabled": false}}})"), + manifest_json +); +// Temporary context destroyed, but builder still has the configuration +``` + +## Migrating from thread-local Settings + +The legacy function `c2pa::load_settings(data, format)` sets thread-local Settings. +This function is deprecated; use `Context` instead. + +| Aspect | load_settings (legacy) | Context | +|--------|------------------------|---------| +| Scope | Global / thread-local | Per Reader/Builder, passed explicitly | +| Multiple configs | Awkward (per-thread) | One context per configuration | +| Testing | Shared global state | Isolated contexts per test | + +**Deprecated:** + +```cpp +// Thread-local settings +std::ifstream config_file("settings.json"); +std::string config((std::istreambuf_iterator(config_file)), std::istreambuf_iterator()); +c2pa::load_settings(config, "json"); +c2pa::Reader reader("image/jpeg", stream); // uses thread-local settings +``` + +**Using current APIs:** + +```cpp +c2pa::Context context(settings_json_string); // or Context(Settings(...)) +c2pa::Reader reader(context, "image/jpeg", stream); +``` + +If you still use `load_settings`, construct `Reader` or `Builder` **without** a context to use the thread-local settings (see [usage.md](usage.md)). Prefer passing a context for new code. + +## See also + +- [Configuring settings](settings.md) — schema, property reference, and examples. +- [Usage](usage.md) — reading and signing with Reader and Builder. +- [CAI settings schema](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/): full schema reference. diff --git a/docs/settings.md b/docs/settings.md new file mode 100644 index 00000000..c3611f09 --- /dev/null +++ b/docs/settings.md @@ -0,0 +1,486 @@ +# Using settings + +You can configure SDK settings using a JSON format that controls many aspects of the library's behavior. +The settings JSON format is the same across all languages in the SDK (Rust, C/C++, Python, and so on). + +This document describes how to use settings in C++. The Settings schema is the same as the [Rust library](https://github.com/contentauth/c2pa-rs); for the complete JSON schema, see the [Settings reference](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/). + +## Using settings with Context + +The recommended approach is to pass settings to a `Context` object and then use the `Context` with `Reader` and `Builder`. This gives you explicit, isolated configuration with no global or thread-local state. For details on creating and using contexts, see [Using Context to configure the SDK](context.md). + +**Legacy approach:** The deprecated `c2pa::load_settings(data, format)` sets thread-local settings. Don't use that approach; instead pass a `Context` (with settings) to `Reader` and `Builder`. See [Using Context with Reader](context.md#using-context-with-reader) and [Using Context with Builder](context.md#using-context-with-builder). + +## Settings API + +Create and configure settings: + +| Method | Description | +|--------|-------------| +| `Settings()` | Create default settings with SDK defaults. | +| `Settings(data, format)` | Parse settings from a string. `format` is `"json"` or `"toml"`. Throws `C2paException` on parse error. | +| `set(path, json_value)` | Set a single value by dot-separated path (e.g. `"verify.verify_after_sign"`). Value must be JSON-encoded. Returns `*this` for chaining. Use this for programmatic configuration. | +| `update(data)` | Merge JSON configuration into existing settings (same as `update(data, "json")`). Later keys override earlier ones. Use this to apply configuration files or JSON strings. | +| `update(data, format)` | Merge configuration from a string; `format` is `"json"` or `"toml"`. | +| `is_valid()` | Returns `true` if the object holds a valid handle (e.g. not moved-from). | + +**Important notes:** + +- Settings are **not copyable**; they are **moveable**. After moving, the source's `is_valid()` is `false`. +- The `set()` and `update()` methods can be chained for sequential configuration. +- When using multiple configuration methods, later calls override earlier ones (last wins). + +## Overview of the Settings structure + +The Settings JSON has this top-level structure: + +```json +{ + "version": 1, + "trust": { ... }, + "cawg_trust": { ... }, + "core": { ... }, + "verify": { ... }, + "builder": { ... }, + "signer": { ... }, + "cawg_x509_signer": { ... } +} +``` + +### Settings format + +Settings can be provided in **JSON** or **TOML**. Use `Settings(data, format)` with `"json"` or `"toml"`, or pass JSON to `Context(json_string)` or `ContextBuilder::with_json()`. JSON is preferred for settings in the C++ SDK. + +```cpp +// JSON +c2pa::Settings settings(R"({"verify": {"verify_after_sign": true}})", "json"); + +// TOML +c2pa::Settings settings(R"( + [verify] + verify_after_sign = true +)", "toml"); + +// Context from JSON string +c2pa::Context context(R"({"verify": {"verify_after_sign": true}})"); +``` + +To load from a file, read the file contents into a string and pass to `Settings` or use `Context::ContextBuilder::with_json_settings_file(path)`. + +## Default configuration + +The settings JSON schema—including the complete default configuration with all properties and their default values—is shared with all languages in the SDK: + +```json +{ + "version": 1, + "builder": { + "claim_generator_info": null, + "created_assertion_labels": null, + "certificate_status_fetch": null, + "certificate_status_should_override": null, + "generate_c2pa_archive": true, + "intent": null, + "actions": { + "all_actions_included": null, + "templates": null, + "actions": null, + "auto_created_action": { + "enabled": true, + "source_type": "empty" + }, + "auto_opened_action": { + "enabled": true, + "source_type": null + }, + "auto_placed_action": { + "enabled": true, + "source_type": null + } + }, + "thumbnail": { + "enabled": true, + "ignore_errors": true, + "long_edge": 1024, + "format": null, + "prefer_smallest_format": true, + "quality": "medium" + }, + }, + "cawg_trust": { + "verify_trust_list": true, + "user_anchors": null, + "trust_anchors": null, + "trust_config": null, + "allowed_list": null + }, + "cawg_x509_signer": null, + "core": { + "merkle_tree_chunk_size_in_kb": null, + "merkle_tree_max_proofs": 5, + "backing_store_memory_threshold_in_mb": 512, + "decode_identity_assertions": true, + "allowed_network_hosts": null + }, + "signer": null, + "trust": { + "user_anchors": null, + "trust_anchors": null, + "trust_config": null, + "allowed_list": null + }, + "verify": { + "verify_after_reading": true, + "verify_after_sign": true, + "verify_trust": true, + "verify_timestamp_trust": true, + "ocsp_fetch": false, + "remote_manifest_fetch": true, + "skip_ingredient_conflict_resolution": false, + "strict_v1_validation": false + } +} +``` + +## Overview of Settings + +For a complete reference to all the Settings properties, see the [SDK object reference - Settings](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema). + +| Property | Description | +|----------|-------------| +| `version` | Settings format version (integer). The default and only supported value is 1. | +| [`builder`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#buildersettings) | Configuration for [Builder](https://contentauth.github.io/c2pa-c/da/db7/classc2pa_1_1Builder.html). | +| [`cawg_trust`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#trust) | Configuration for CAWG trust lists. | +| [`cawg_x509_signer`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#signersettings) | Configuration for the CAWG x.509 signer. | +| [`core`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#core) | Configuration for core features. | +| [`signer`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#signersettings) | Configuration for the base [C2PA signer](https://contentauth.github.io/c2pa-c/d3/da1/classc2pa_1_1Signer.html). | +| [`trust`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#trust) | Configuration for C2PA trust lists. | +| [`verify`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema#verify) | Configuration for verification (validation). | + +The top-level `version` property must be `1`. All other properties are optional. + +For Boolean values, use JSON Booleans `true` and `false`, not the strings `"true"` and `"false"`. + +> [!IMPORTANT] +> If you don't specify a value for a property, the SDK uses the default value. If you specify a value of `null`, the property is explicitly set to `null`, not the default. This distinction is important when you want to override a default behavior. + +### Trust configuration + +The [`trust` properties](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#trust) control which certificates are trusted when validating C2PA manifests. + +- Using `user_anchors`: recommended for development +- Using `allowed_list` (bypass chain validation) +- For team development, you can load trust configuration from a file using `ContextBuilder`; see [Using Context to configure the SDK](context.md#using-contextbuilder) for details. + +| Property | Type | Description | Default | +|----------|------|-------------|---------| +| `trust.allowed_list` | string | Explicitly allowed certificates (PEM format). These certificates are trusted regardless of chain validation. Use for development/testing. | — | +| `trust.trust_anchors` | string | Default trust anchor root certificates (PEM format). **Replaces** the SDK's built-in trust anchors entirely. | — | +| `trust.trust_config` | string | Allowed Extended Key Usage (EKU) OIDs. Controls which certificate purposes are accepted (e.g., document signing: `1.3.6.1.4.1.311.76.59.1.9`). | — | +| `trust.user_anchors` | string | Additional user-provided root certificates (PEM format). Adds custom certificate authorities without replacing the SDK's built-in trust anchors. | — | + +When using self-signed certificates or custom certificate authorities during development, you need to configure trust settings so the SDK can validate your test signatures. + +#### Using `user_anchors` + +For development, you can add your test root CA to the trusted anchors without replacing the SDK's default trust store. +For example: + +```cpp +// Read your test root CA certificate +std::string test_root_ca = R"(-----BEGIN CERTIFICATE----- +MIICEzCCAcWgAwIBAgIUW4fUnS38162x10PCnB8qFsrQuZgwBQYDK2VwMHcxCzAJ +... +-----END CERTIFICATE-----)"; + +c2pa::Context context(R"({ + "version": 1, + "trust": { + "user_anchors": ")" + test_root_ca + R"(" + } +})"); + +c2pa::Reader reader(context, "signed_asset.jpg"); +``` + +#### Using `allowed_list` + +To bypass chain validation, for quick testing, explicitly allow a specific certificate without validating the chain. +For example: + +```cpp +// Read your test signing certificate +std::string test_cert = read_file("test_cert.pem"); + +c2pa::Settings settings; +settings.update(R"({ + "version": 1, + "trust": { + "allowed_list": ")" + test_cert + R"(" + } +})"); + +c2pa::Context context(settings); +c2pa::Reader reader(context, "signed_asset.jpg"); +``` + +### CAWG trust configuration + +The `cawg_trust` properties configure CAWG (Creator Assertions Working Group) validation of identity assertions in C2PA manifests. The `cawg_trust` object has the same properties as [`trust`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#trust). + +> [!NOTE] +> CAWG trust settings are only used when processing identity assertions with X.509 certificates. If your workflow doesn't use CAWG identity assertions, these settings have no effect. + +### Core + +The [`core` properties](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#core) specify core SDK behavior and performance tuning options. + +Use cases: + +- **Performance tuning for large files**: Set `core.backing_store_memory_threshold_in_mb` to `2048` or higher if processing large video files with sufficient RAM. +- **Restricted network environments**: Set `core.allowed_network_hosts` to limit which domains the SDK can contact. + +### Verify + +The [`verify` properties](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#verify) specify how the SDK validates C2PA manifests. These settings affect both reading existing manifests and verifying newly signed content. + +Common use cases include: + +- [Offline or air-gapped environments](#offline-or-air-gapped-environments). +- [Fast development iteration](#fast-development-iteration) with verification disabled. +- [Strict validation](#strict-validation) for certification or compliance testing. + +By default, the following `verify` properties are `true`, which enables verification: + +- `remote_manifest_fetch` - Fetch remote manifests referenced in the asset. Disable in offline or air-gapped environments. +- `verify_after_reading` - Automatically verify manifests when reading assets. Disable only if you want to manually control verification timing. +- `verify_after_sign` - Automatically verify manifests after signing. Recommended to keep enabled to catch signing errors immediately. +- `verify_timestamp_trust` - Verify timestamp authority (TSA) certificates. WARNING: Disabling this setting makes verification non-compliant. +- `verify_trust` - Verify signing certificates against configured trust anchors. WARNING: Disabling this setting makes verification non-compliant. + +> [!WARNING] +> Disabling verification options (changing `true` to `false`) can make verification non-compliant with the C2PA specification. Only modify these settings in controlled environments or when you have specific requirements. + +#### Offline or air-gapped environments + +Set `remote_manifest_fetch` and `ocsp_fetch` to `false` to disable network-dependent verification features: + +```cpp +c2pa::Context context(R"({ + "version": 1, + "verify": { + "remote_manifest_fetch": false, + "ocsp_fetch": false + } +})"); + +c2pa::Reader reader(context, "signed_asset.jpg"); +``` + +See also [Using Context with Reader](context.md#using-context-with-reader). + +#### Fast development iteration + +During active development, you can disable verification for faster iteration: + +```cpp +// WARNING: Only use during development, not in production! +c2pa::Settings dev_settings; +dev_settings.set("verify.verify_after_reading", "false"); +dev_settings.set("verify.verify_after_sign", "false"); + +c2pa::Context dev_context(dev_settings); +``` + +#### Strict validation + +For certification or compliance testing, enable strict validation: + +```cpp +c2pa::Context context(R"({ + "version": 1, + "verify": { + "strict_v1_validation": true, + "ocsp_fetch": true, + "verify_trust": true, + "verify_timestamp_trust": true + } +})"); + +c2pa::Reader reader(context, "asset_to_validate.jpg"); +auto validation_result = reader.json(); +``` + +### Builder + +The [`builder` properties](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#buildersettings) control how the SDK creates and embeds C2PA manifests in assets. + +#### Claim generator information + +The `claim_generator_info` object identifies your application in the C2PA manifest. **Recommended fields:** + +- `name` (string, required): Your application name (e.g., `"My Photo Editor"`) +- `version` (string, recommended): Application version (e.g., `"2.1.0"`) +- `icon` (string, optional): Icon in C2PA format +- `operating_system` (string, optional): OS identifier or `"auto"` to auto-detect + +**Example:** + +```cpp +c2pa::Context context(R"({ + "version": 1, + "builder": { + "claim_generator_info": { + "name": "My Photo Editor", + "version": "2.1.0", + "operating_system": "auto" + } + } +})"); +``` + +#### Thumbnail settings + +The [`builder.thumbnail`](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#thumbnailsettings) properties control automatic thumbnail generation. + +For examples of configuring thumbnails for mobile bandwidth or disabling them for batch processing, see [Controlling thumbnail generation](context.md#controlling-thumbnail-generation). + +#### Action tracking settings + +| Property | Type | Description | Default | +|----------|------|-------------|---------| +| `builder.actions.auto_created_action.enabled` | Boolean | Automatically add a `c2pa.created` action when creating new content. | `true` | +| `builder.actions.auto_created_action.source_type` | string | Source type for the created action. Usually `"empty"` for new content. | `"empty"` | +| `builder.actions.auto_opened_action.enabled` | Boolean | Automatically add a `c2pa.opened` action when opening/reading content. | `true` | +| `builder.actions.auto_placed_action.enabled` | Boolean | Automatically add a `c2pa.placed` action when placing content as an ingredient. | `true` | + +#### Other builder settings + +| Property | Type | Description | Default | +|----------|------|-------------|---------| +| `builder.intent` | object | Claim intent: `{"Create": "digitalCapture"}`, `{"Edit": null}`, or `{"Update": null}`. Describes the purpose of the claim. | `null` | +| `builder.generate_c2pa_archive` | Boolean | Generate content in C2PA archive format. Keep enabled for standard C2PA compliance. | `true` | + +##### Setting Builder intent + +You can use `Context` to set `Builder` intent for different workflows. + +For example, for original digital capture (photos from camera): + +```cpp +c2pa::Context camera_context(R"({ + "version": 1, + "builder": { + "intent": {"Create": "digitalCapture"}, + "claim_generator_info": {"name": "Camera App", "version": "1.0"} + } +})"); +``` + +Or for editing existing content: + +```cpp +c2pa::Context editor_context(R"({ + "version": 1, + "builder": { + "intent": {"Edit": null}, + "claim_generator_info": {"name": "Photo Editor", "version": "2.0"} + } +})"); +``` + +### Signer + +The [`signer` properties](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#signersettings) configure the primary C2PA signer configuration. Set it to `null` if you provide the signer at runtime, or configure as either a **local** or **remote** signer in settings. + +> [!NOTE] +> While you can configure the signer in settings, the typical approach is to pass a `Signer` object directly to the `Builder.sign()` method. Use settings-based signing when you need the same signing configuration across multiple operations or when loading configuration from files. + +#### Local signer + +Use a local signer when you have direct access to the private key and certificate. +For information on all `signer.local` properties, see [signer.local](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#signerlocal) in the SDK object reference. + +**Example: Local signer with ES256** + +```cpp +std::string config = R"({ + "version": 1, + "signer": { + "local": { + "alg": "es256", + "sign_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----", + "tsa_url": "http://timestamp.digicert.com" + } + } +})"; + +c2pa::Context context(config); +c2pa::Builder builder(context, manifest_json); +// Signer is already configured in context +builder.sign(source_path, dest_path); +``` + +#### Remote signer + +Use a remote signer when the private key is stored on a secure signing service (HSM, cloud KMS, and so on). +For information on all `signer.remote` properties, see [signer.remote](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/#signerremote) in the SDK object reference. + +The remote signing service receives a POST request with the data to sign and must return the signature in the expected format. + +For example: + +```cpp +c2pa::Context context(R"({ + "version": 1, + "signer": { + "remote": { + "url": "https://signing-service.example.com/sign", + "alg": "ps256", + "sign_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "tsa_url": "http://timestamp.digicert.com" + } + } +})"); +``` + +### CAWG X.509 signer configuration + +The `cawg_x509_signer` property specifies configuration for identity assertions. This has the same structure as `signer` (can be local or remote). + +**When to use:** If you need to sign identity assertions separately from the main C2PA claim. When both `signer` and `cawg_x509_signer` are configured, the SDK uses a dual signer: + +- Main claim signature comes from `signer` +- Identity assertions are signed with `cawg_x509_signer` + +**Example: Dual signer configuration** + +```cpp +c2pa::Context context(R"({ + "version": 1, + "signer": { + "local": { + "alg": "es256", + "sign_cert": "...", + "private_key": "..." + } + }, + "cawg_x509_signer": { + "local": { + "alg": "ps256", + "sign_cert": "...", + "private_key": "..." + } + } +})"); +``` + +For additional JSON configuration examples (minimal configuration, local/remote signer, development/production configurations), see the [Rust SDK settings examples](https://github.com/contentauth/c2pa-rs/blob/main/docs/settings.md#examples). + +## See also + +- [Using Context to configure the SDK](context.md): how to create and use contexts with settings. +- [Usage](usage.md): reading and signing with `Reader` and `Builder`. +- [Rust SDK settings](https://github.com/contentauth/c2pa-rs/blob/main/docs/settings.md): the shared settings schema, default configuration, and JSON examples (language-independent). +- [CAI settings schema reference](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/): full schema reference. diff --git a/docs/usage.md b/docs/usage.md index ed362b8c..b8a03b4e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -72,7 +72,7 @@ const std::string manifest_json = R"{ The behavior of the SDK can be configured through various [settings](https://opensource.contentauthenticity.org/docs/manifest/json-ref/settings-schema/). SDK settings can be loaded from JSON config files, as well as valid JSON strings directly in the code. -SDK settings are set on the `Context` objects used by the Builder and Reader objects. +SDK settings are set on the `Context` objects used by the Builder and Reader objects. For full details see [Configuring settings](settings.md) and [Configuring the SDK using Context](context.md). NOTE: If you don't specify a value for a property, then the SDK will use the default value. If you specify a value of null, then the property will be set to null, not the default.