Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,33 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Checkout c2pa-rs (main)
uses: actions/checkout@v4
with:
repository: contentauth/c2pa-rs
ref: main
path: c2pa-rs

- name: Configure c2pa-rs main source build
shell: bash
run: |
git -C c2pa-rs log -1 --format='c2pa-rs main: %H %s'
cargo --version
rustc --version
echo "C2PA_BUILD_FROM_SOURCE=ON" >> "$GITHUB_ENV"
echo "C2PA_RS_PATH=${GITHUB_WORKSPACE//\\//}/c2pa-rs" >> "$GITHUB_ENV"

- name: Check cmake
run: cmake --version

- name: Check Ninja
run: ninja --version

- name: Configure
run: cmake -S . -B build -G Ninja

- name: Setup MSVC toolchain on Windows
if: matrix.os == 'windows-latest'
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }}

- name: Print make version and path
run: |
Expand Down
14 changes: 7 additions & 7 deletions docs/context-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ c2pa::Reader reader(context, "image.jpg");
- **Improves code clarity**: `Builder(context, manifest)` shows configuration is being used

> [!NOTE]
> The deprecated `c2pa::load_settings(data, format)` still works but you should migrate to `Context`. **Never mix the two approaches**.
> `c2pa::load_settings(data, format)` has been removed on this nightly-compatibility branch. Pass settings explicitly through a `Context`.
> See [Migrating from deprecated APIs](#migrating-from-deprecated-apis).

### Context lifecycle
Expand Down Expand Up @@ -883,11 +883,11 @@ c2pa::Context context(R"({

## Migrating from deprecated APIs

The SDK introduced Context-based APIs to replace constructors and functions that relied on thread-local state. The older APIs still compile but produce deprecation warnings. This section covers each deprecation that involves passing a `Context`, and explains the `Builder::sign` overloads.
The SDK introduced Context-based APIs to replace constructors and functions that relied on thread-local state. On this nightly-compatibility branch, `load_settings` has been removed. The context-free `Reader` and `Builder` constructors still compile with deprecation warnings, but create a new default Context rather than reading thread-local settings. This section covers migration to explicit contexts and the `Builder::sign` overloads.

### Quick reference

| Deprecated API | Replacement |
| Removed or deprecated API | Replacement |
|---|---|
| `load_settings(data, format)` | [`Context` constructors or `ContextBuilder`](#replacing-load_settings) |
| `Reader(format, stream)` | [`Reader(shared_ptr<IContextProvider>, format, stream)`](#adding-a-context-parameter-to-reader-and-builder) |
Expand All @@ -898,15 +898,15 @@ The SDK introduced Context-based APIs to replace constructors and functions that

### Replacing load_settings

`c2pa::load_settings(data, format)` sets thread-local settings that `Reader` and `Builder` pick up implicitly. Replace it with a `Context` that you pass explicitly.
The removed `c2pa::load_settings(data, format)` used to set thread-local settings that `Reader` and `Builder` picked up implicitly. Replace it with a `Context` that you pass explicitly.

| 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:**
**Removed (old code):**

```cpp
std::ifstream config_file("settings.json");
Expand All @@ -922,11 +922,11 @@ 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 parameter to continue using the thread-local settings. Prefer passing a context for new code.
There is no thread-local settings fallback. Context-free constructors use default settings; pass a context whenever configuration matters.

### Adding a context parameter to Reader and Builder

The following constructors are deprecated because they rely on thread-local settings:
The following constructors are deprecated because they do not accept explicit configuration:

- `Reader(const std::string& format, std::istream& stream)`
- `Reader(const std::filesystem::path& source_path)`
Expand Down
18 changes: 18 additions & 0 deletions docs/embeddable-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ The embeddable signing API provides direct, fine-grained control over embedding
4. Sign the claim.
5. Patch the manifest in place.

## Migrating from the removed data-hashed APIs

On this nightly-compatibility branch, `data_hashed_placeholder(reserve_size, format)`
and `sign_data_hashed_embeddable(signer, data_hash, format, asset)` have been removed.
The supported workflow is:

1. Attach the signer to the Context before constructing the Builder.
2. Call `placeholder(format)` and embed the returned bytes in the asset.
3. Call `set_data_hash_exclusions()` with the embedded placeholder's byte offset and length.
4. Call `update_hash_from_stream(format, asset)` to hash the asset.
5. Call `sign_embeddable(format)` and overwrite the placeholder with its result.

The new workflow obtains the signature reserve size from the Context signer and
computes the hash from the supplied stream; it does not accept the old precomputed
DataHash JSON argument. Do not change the asset outside the excluded placeholder
region after hashing. `format_embeddable(format, data)` remains available for raw
manifest bytes and now uses the supported `c2pa_builder_compose_manifest` API.

## Why use the embeddable API

The standard `Builder::sign()` handles the full pipeline internally:
Expand Down
12 changes: 3 additions & 9 deletions docs/intents.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ You can set the intent on a `Builder` instance by:
- [Using Context](#using-context)
- [Using `set_intent` on the `Builder`](#using-set_intent-on-builder)

Don't set intent using the deprecated `load_settings()` function. For existing code, see [Context and Settings - Migrating from deprecated APIs](context-settings.md#migrating-from-deprecated-apis).
The `load_settings()` function has been removed on this nightly-compatibility branch. For existing code, see [Context and Settings - Migrating from deprecated APIs](context-settings.md#migrating-from-deprecated-apis).

### Using Context

Expand Down Expand Up @@ -112,11 +112,7 @@ flowchart TD
Check --> |No| CheckCtx{Was a Context with
builder.intent provided?}
CheckCtx --> |Yes| UseCtx["Use Context intent"]
CheckCtx --> |No| CheckGlobal{Was load_settings called
with builder.intent?}
CheckGlobal --> |Yes| UseGlobal["Use global intent
(deprecated)"]
CheckGlobal --> |No| NoIntent["No intent set.
CheckCtx --> |No| NoIntent["No intent set.
Caller must define actions
manually in manifest JSON."]
```
Expand Down Expand Up @@ -171,8 +167,7 @@ The `Builder` intent controls what `Builder` does with the source stream (source
```mermaid
flowchart TD
Intent["Intent
(via Context, set_intent,
or load_settings)"] --> Q{Intent type?}
(via Context or set_intent)"] --> Q{Intent type?}
Q --> |CREATE| CreateFlow["No parent allowed
Source stream is new content"]
Q --> |EDIT or UPDATE| EditFlow{Was a parentOf ingredient
Expand Down Expand Up @@ -485,4 +480,3 @@ c2pa::Builder builder(context, R"({})");
builder.set_intent(Update);
builder.sign("signed_asset.jpg", "updated_asset.jpg", signer);
```

38 changes: 7 additions & 31 deletions include/c2pa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -533,14 +533,6 @@ namespace c2pa
/// @return Version string.
std::string C2PA_CPP_API version();

/// @brief Load C2PA settings from a string in a given format.
/// @param data The configuration data to load.
/// @param format The mimetype of the string.
/// @throws C2paException for errors encountered by the C2PA library.
/// @deprecated Use Context constructors or Context::ContextBuilder instead for better thread safety.
[[deprecated("Use Context::from_json() or Context::from_settings() instead")]]
void C2PA_CPP_API load_settings(const std::string& data, const std::string& format);

/// @defgroup StreamWrappers Stream wrappers for C2PA C API
/// @brief C++ stream types that adapt stream types to C2paStream.
///
Expand All @@ -553,7 +545,7 @@ namespace c2pa

/// @brief Input stream IStream wrapper for C2paStream.
/// @details This class is used to wrap an input stream for use with the C2PA library.
class C2PA_CPP_API CppIStream : public C2paStream
class C2PA_CPP_API CppIStream
{
public:
/// @brief Pointer to the underlying C2paStream.
Expand Down Expand Up @@ -616,7 +608,7 @@ namespace c2pa

/// @brief Output stream OStream wrapper for C2paStream.
/// @details This class is used to wrap an output stream for use with the C2PA library.
class C2PA_CPP_API CppOStream : public C2paStream
class C2PA_CPP_API CppOStream
{
public:
/// @brief Pointer to the underlying C2paStream.
Expand Down Expand Up @@ -676,7 +668,7 @@ namespace c2pa

/// @brief IOStream Class wrapper for C2paStream.
/// @details This class is used to wrap an input/output stream for use with the C2PA library.
class C2PA_CPP_API CppIOStream : public C2paStream
class C2PA_CPP_API CppIOStream
{
public:
/// @brief Pointer to the underlying C2paStream.
Expand Down Expand Up @@ -847,7 +839,7 @@ namespace c2pa
std::istream& image_stream,
const std::vector<uint8_t>& manifest_jumbf);

/// @brief Create a Reader from a stream (will use global settings if any loaded).
/// @brief Create a Reader from a stream using a new default Context.
/// @details The validation_status field in the JSON contains validation results.
/// @param format The mime format of the stream.
/// @param stream The input stream to read from.
Expand All @@ -856,7 +848,7 @@ namespace c2pa
[[deprecated("Use Reader(IContextProvider& context, format, stream) instead")]]
Reader(const std::string &format, std::istream &stream);

/// @brief Create a Reader from a file path (will use global settings if any loaded).
/// @brief Create a Reader from a file path using a new default Context.
/// @param source_path The path to the file to read.
/// @throws C2paException for errors encountered by the C2PA library.
/// @deprecated Use Reader(IContextProvider& context, source_path) instead.
Expand Down Expand Up @@ -1172,7 +1164,7 @@ namespace c2pa
/// @throws C2paException if context is null or context->is_valid() returns false.
Builder(std::shared_ptr<IContextProvider> context, const std::string &manifest_json);

/// @brief Create a Builder from a manifest JSON string (will use global settings if any loaded).
/// @brief Create a Builder from a manifest JSON string using a new default Context.
/// @param manifest_json The manifest JSON string.
/// @throws C2paException for errors encountered by the C2PA library.
/// @deprecated Use Builder(IContextProvider& context, manifest_json) instead.
Expand Down Expand Up @@ -1368,25 +1360,9 @@ namespace c2pa
/// @throws C2paException for errors encountered by the C2PA library.
void add_ingredient_from_archive(std::istream &archive);

/// @brief Create a hashed placeholder from the builder.
/// @param reserved_size The size required for a signature from the intended signer (in bytes).
/// @param format The mime format or extension of the asset.
/// @return A vector containing the hashed placeholder bytes.
/// @throws C2paException for errors encountered by the C2PA library.
std::vector<unsigned char> data_hashed_placeholder(uintptr_t reserved_size, const std::string &format);

/// @brief Sign a Builder using the specified signer and data hash.
/// @param signer The signer to use for signing.
/// @param data_hash The data hash ranges to sign (must contain hashes unless an asset is provided).
/// @param format The mime format for embedding. Use "c2pa" for an unformatted result.
/// @param asset Optional asset to hash according to the data_hash information.
/// @return A vector containing the signed embeddable data.
/// @throws C2paException for errors encountered by the C2PA library.
std::vector<unsigned char> sign_data_hashed_embeddable(Signer &signer, const std::string &data_hash, const std::string &format, std::istream *asset = nullptr);

/// @brief Convert unformatted manifest data to an embeddable format.
/// @param format The format for embedding.
/// @param data Unformatted manifest data from sign_data_hashed_embeddable using "c2pa" format.
/// @param data Unformatted manifest data in "application/c2pa" format.
/// @return A formatted copy of the data.
static std::vector<unsigned char> format_embeddable(const std::string &format, std::vector<unsigned char> &data);

Expand Down
28 changes: 14 additions & 14 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,30 @@ if(C2PA_BUILD_FROM_SOURCE)
find_program(CARGO_EXECUTABLE cargo PATHS "$ENV{HOME}/.cargo/bin" REQUIRED)
message(STATUS "Found cargo at: ${CARGO_EXECUTABLE}")

set(C2PA_RUST_ARTIFACTS "${C2PA_LIB_NAME}" "c2pa.h")
if(WIN32)
list(APPEND C2PA_RUST_ARTIFACTS "${C2PA_IMPLIB_NAME}")
endif()
set(C2PA_RUST_SOURCES ${C2PA_RUST_ARTIFACTS})
list(TRANSFORM C2PA_RUST_SOURCES PREPEND "${C2PA_RS_PATH}/target/release/")
set(C2PA_RUST_OUTPUTS ${C2PA_RUST_ARTIFACTS})
list(TRANSFORM C2PA_RUST_OUTPUTS PREPEND "${C2PA_RUST_BUILD_DIR}/")

# Build the Rust c2pa_c_ffi library
add_custom_command(
OUTPUT "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}"
OUTPUT ${C2PA_RUST_OUTPUTS}
COMMAND ${CARGO_EXECUTABLE} build --release --package c2pa-c-ffi --no-default-features --features "rust_native_crypto, file_io, http, add_thumbnails"
COMMAND ${CMAKE_COMMAND} -E copy
"${C2PA_RS_PATH}/target/release/${C2PA_LIB_NAME}"
"${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${C2PA_RUST_SOURCES}
"${C2PA_RUST_BUILD_DIR}"
WORKING_DIRECTORY "${C2PA_RS_PATH}"
COMMENT "Building c2pa_c from source using Cargo"
VERBATIM
)

# Create a custom target that depends on the library
add_custom_target(build_c2pa_c ALL
DEPENDS "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}"
)

# Copy the generated c2pa.h header
add_custom_command(
TARGET build_c2pa_c POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${C2PA_RS_PATH}/target/release/c2pa.h"
"${C2PA_RUST_BUILD_DIR}/c2pa.h"
COMMENT "Copying c2pa.h header"
DEPENDS ${C2PA_RUST_OUTPUTS}
)

set(C2PA_C_LIB "${C2PA_RUST_BUILD_DIR}/${C2PA_LIB_NAME}")
Expand Down
38 changes: 5 additions & 33 deletions src/c2pa_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,8 @@ namespace c2pa
Builder::Builder(const std::string &manifest_json)
: builder(nullptr)
{
builder = c2pa_builder_from_json(manifest_json.c_str());
if (builder == nullptr)
{
throw C2paException();
}
Context context;
init_from_context(context, manifest_json);
}

/// @brief Create a Builder from an archive.
Expand Down Expand Up @@ -394,33 +391,6 @@ namespace c2pa
}
}

std::vector<unsigned char> Builder::data_hashed_placeholder(uintptr_t reserve_size, const std::string &format)
{
const std::string normalized_format = detail::normalize_format(format);

const unsigned char *c2pa_manifest_bytes = nullptr;
auto result = c2pa_builder_data_hashed_placeholder(builder, reserve_size, normalized_format.c_str(), &c2pa_manifest_bytes);
return detail::to_byte_vector(c2pa_manifest_bytes, result);
}

std::vector<unsigned char> Builder::sign_data_hashed_embeddable(Signer &signer, const std::string &data_hash, const std::string &format, std::istream *asset)
{
const std::string normalized_format = detail::normalize_format(format);

int64_t result;
const unsigned char *c2pa_manifest_bytes = nullptr;
if (asset)
{
CppIStream c_asset(*asset);
result = c2pa_builder_sign_data_hashed_embeddable(builder, signer.c2pa_signer(), data_hash.c_str(), normalized_format.c_str(), c_asset.c_stream, &c2pa_manifest_bytes);
}
else
{
result = c2pa_builder_sign_data_hashed_embeddable(builder, signer.c2pa_signer(), data_hash.c_str(), normalized_format.c_str(), nullptr, &c2pa_manifest_bytes);
}
return detail::to_byte_vector(c2pa_manifest_bytes, result);
}

bool Builder::needs_placeholder(const std::string &format)
{
const std::string resolved_format = detail::resolve_format(format);
Expand Down Expand Up @@ -487,7 +457,9 @@ namespace c2pa
const std::string normalized_format = detail::normalize_format(format);

const unsigned char *c2pa_manifest_bytes = nullptr;
auto result = c2pa_format_embeddable(normalized_format.c_str(), data.data(), data.size(), &c2pa_manifest_bytes);
Context context;
Builder composer(context);
auto result = c2pa_builder_compose_manifest(composer.c2pa_builder(), normalized_format.c_str(), data.data(), data.size(), &c2pa_manifest_bytes);
return detail::to_byte_vector(c2pa_manifest_bytes, result);
}

Expand Down
14 changes: 0 additions & 14 deletions src/c2pa_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,4 @@ namespace c2pa
return detail::c_string_to_string(c2pa_version());
}

/// Loads C2PA settings from a std::string in a given format.
/// @param data the std::string to load.
/// @param format the mime format of the string.
/// @throws a C2pa::C2paException for errors encountered by the C2PA library.
[[deprecated("Use Settings on Builder and Reader instead")]]
void load_settings(const std::string &data, const std::string &format)
{
auto result = c2pa_load_settings(data.c_str(), format.c_str());
if (result != 0)
{
throw c2pa::C2paException();
}
}

} // namespace c2pa
Loading
Loading