c2pa-cpp
C++ API for the C2PA SDK
Loading...
Searching...
No Matches
c2pa.hpp
Go to the documentation of this file.
1// Copyright 2024 Adobe. All rights reserved.
2// This file is licensed to you under the Apache License,
3// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
4// or the MIT license (http://opensource.org/licenses/MIT),
5// at your option.
6// Unless required by applicable law or agreed to in writing,
7// this software is distributed on an "AS IS" BASIS, WITHOUT
8// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
9// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
10// specific language governing permissions and limitations under
11// each license.
12
13/// @file c2pa.hpp
14/// @brief C++ wrapper for the C2PA C library.
15/// @details This is used for creating and verifying C2PA manifests.
16/// This is an early version, and has not been fully tested.
17/// Thread safety is not guaranteed due to the use of errno and etc.
18
19#ifndef C2PA_H
20#define C2PA_H
21
22// Suppress unused function warning for GCC/Clang
23#ifdef __GNUC__
24#pragma GCC diagnostic push
25#pragma GCC diagnostic ignored "-Wunused-function"
26#endif
27
28// Suppress unused function warning for MSVC
29#ifdef _MSC_VER
30#pragma warning(push)
31#pragma warning(disable : 4505)
32#endif
33
34#include <cerrno>
35#include <filesystem>
36#include <fstream>
37#include <functional>
38#include <istream>
39#include <ostream>
40#include <string>
41#include <vector>
42#include <optional>
43#include <memory>
44#include <utility>
45
46#include "c2pa.h"
47
48// NOOP for now, can use later to define static library
49#define C2PA_CPP_API
50
51namespace c2pa
52{
53 /// @typedef SignerInfo
54 /// @brief Type alias for C2paSignerInfo from the C API.
55 typedef C2paSignerInfo SignerInfo;
56
57 // Forward declarations for context types
58 class Settings;
59 class Context;
60 class IContextProvider;
61 class Signer;
62
63 /// @brief Result codes for C API operations (matches C API return convention).
64 enum class OperationResult : int {
65 Success = 0, ///< Operation succeeded
66 Error = -1 ///< Operation failed (check C2paException for details)
67 };
68
69 /// @brief Stream/FFI error codes (maps to errno values used by the C layer).
70 enum class StreamError : int {
71 InvalidArgument = EINVAL,
72 IoError = EIO,
73 NoBufferSpace = ENOBUFS
74 };
75
76 /// @brief Set errno from StreamError and return error sentinel.
77 /// @param e The StreamError value to convert to errno.
78 /// @return OperationResult::Error (-1) for use as C API error return.
79 inline int stream_error_return(StreamError e) noexcept {
80 errno = static_cast<int>(e);
81 return static_cast<int>(OperationResult::Error);
82 }
83
84 /// @brief Exception class for C2pa errors.
85 /// This class is used to throw exceptions for errors encountered by the C2pa library via c2pa_error().
86 class C2PA_CPP_API C2paException : public std::exception
87 {
88 public:
89 /// @brief Default constructor.
90 /// @details Creates an exception and retrieves the error message from the C2PA library.
92
93 /// @brief Construct an exception with a custom error message.
94 /// @param message The error message.
95 explicit C2paException(std::string message);
96
97 ~C2paException() override = default;
98
99 C2paException(const C2paException&) = default;
100
102
104
106
107 /// @brief Get the exception message.
108 /// @return Null-terminated error message string.
109 const char* what() const noexcept override;
110
111 private:
112 std::string message_;
113 };
114
115 /// @brief Interface for types that can provide C2PA context functionality.
116 /// @details This interface can be implemented by external libraries to provide
117 /// custom context implementations (e.g. AdobeContext wrappers).
118 /// Reader and Builder accept a shared_ptr<IContextProvider> and extend
119 /// the provider's lifetime for as long as the Reader/Builder exists.
120 ///
121 /// @par Progress callback lifetime (post-0322d67)
122 /// If the native C2paContext* held by the provider was configured with a progress
123 /// callback, the provider MUST keep the heap-owned ProgressCallbackFunc alive at
124 /// least as long as the native context. The native side stores only a raw pointer
125 /// into that heap block and will call it until the context is freed. Destroy
126 /// order inside the provider: free the native C2paContext* first (stops further
127 /// callback invocations), then release the callback storage. The built-in
128 /// Context class enforces this via member declaration order; external providers
129 /// that adopt a native context built via ContextBuilder::release() must do the
130 /// same.
131 ///
132 /// @par Move semantics
133 /// Move construction and move assignment are defaulted. After move, the moved-from
134 /// object is left in a valid but unspecified state: is_valid() may be false and
135 /// c_context() may return nullptr. Implementations that own a C2paContext* (e.g. Context)
136 /// must set the source's handle to nullptr on move to avoid double-free; callers must
137 /// not use a moved-from provider without checking is_valid() first.
138 ///
139 /// @par Implementation Requirements for is_valid()
140 /// The is_valid() method exists to support implementations that may have:
141 /// - Optional or lazy context initialization
142 /// - Contexts that can be invalidated or moved
143 /// - A "no context" state as part of their lifecycle
144 ///
145 /// @par Why Both c_context() and is_valid()?
146 /// While c_context() can return nullptr, is_valid() provides:
147 /// 1. A boolean check without pointer inspection (yes/no answer for intialization)
148 /// 2. Forward compatibility for implementations with complex context lifecycles (lazy load)
149 ///
150 /// @par Impact on Reader and Builder
151 /// Reader and Builder constructors validate that a provider both exists and
152 /// is_valid() returns true before using c_context(). This ensures that:
153 /// - External implementations cannot be used in an uninitialized state
154 /// - A consistent validation pattern exists across all context-using classes
155 /// - Errors are caught early at construction time rather than during operations
156 ///
157 /// @par Standard Context Implementation
158 /// The built-in Context class always returns true from is_valid() after
159 /// successful construction, as it validates the context pointer in its constructor.
160 /// External implementations may have different invariants.
162 public:
163 virtual ~IContextProvider() noexcept = default;
164
165 /// @brief Get the underlying C2PA context pointer for FFI operations.
166 /// @return Pointer to C2paContext, or nullptr if not available.
167 /// @note Provider retains ownership; pointer valid for provider's lifetime.
168 [[nodiscard]] virtual C2paContext* c_context() const noexcept = 0;
169
170 /// @brief Check if this provider has a valid context.
171 /// @return true if context is available, false otherwise.
172 /// @note For standard Context objects, this always returns true after construction.
173 /// External implementations may return false to indicate uninitialized or
174 /// invalidated state. Reader and Builder constructors check this before use.
175 /// @warning Implementations must ensure is_valid() == true implies c_context() != nullptr.
176 [[nodiscard]] virtual bool is_valid() const noexcept = 0;
177
178 protected:
179 IContextProvider() = default;
180
182 IContextProvider& operator=(const IContextProvider&) = delete;
183 };
184
185 /// @brief (C2PA SDK) Settings configuration object for creating contexts.
186 /// @details Settings can be configured via JSON strings or programmatically
187 /// via set() and update() methods. Once passed to Context::ContextBuilder,
188 /// the settings are copied into the context and the Settings
189 /// object can be reused or discarded.
190 ///
191 /// @par Validity
192 /// Settings uses is_valid() to indicate whether the object holds a valid underlying
193 /// C settings handle. After move-from, is_valid() is false. set(), update(), and callers
194 /// passing this object to the C API must ensure is_valid() is true or check before use.
196 public:
197 /// @brief Create default settings.
199
200 /// @brief Create settings from a configuration string.
201 /// @param data Configuration data in JSON format.
202 /// @param format Format of the data ("json").
203 /// @throws C2paException if parsing fails.
204 Settings(const std::string& data, const std::string& format);
205
206 // Move semantics
207 Settings(Settings&&) noexcept;
208 Settings& operator=(Settings&&) noexcept;
209
210 // Non-copyable
211 Settings(const Settings&) = delete;
212 Settings& operator=(const Settings&) = delete;
213
214 ~Settings() noexcept;
215
216 /// @brief Check if this Settings object is valid (holds a C settings handle).
217 /// @return true if the object can be used (set, update, c_settings, or passed to Context/ContextBuilder).
218 /// @return false if moved-from (or otherwise invalid). Callers must check before use.
219 [[nodiscard]] bool is_valid() const noexcept;
220
221 /// @brief Set a single configuration value by path.
222 /// @param path Dot-separated path to the setting (e.g., "verify.verify_after_sign").
223 /// @param json_value JSON-encoded value to set.
224 /// @return Reference to this Settings for method chaining.
225 /// @throws C2paException if the path or value is invalid.
226 Settings& set(const std::string& path, const std::string& json_value);
227
228 /// @brief Merge configuration from a JSON string (latest configuration wins).
229 /// @param data Configuration data in JSON format.
230 /// @return Reference to this Settings for method chaining.
231 /// @throws C2paException if parsing fails, or if this object is invalid.
232 /// @note This is the recommended overload when configuration is JSON.
233 Settings& update(const std::string& data) { return update(data, "json"); }
234
235 /// @brief Merge configuration from a std::string (latest configuration wins).
236 /// @param data Configuration data in JSON or TOML format.
237 /// @param format Format of the data ("json" or "toml").
238 /// @return Reference to this Settings for method chaining.
239 /// @throws C2paException if parsing fails, or if this object is invalid.
240 Settings& update(const std::string& data, const std::string& format);
241
242 /// @brief Get the raw C FFI settings pointer.
243 /// @return Pointer to C2paSettings when is_valid() is true; nullptr when invalid.
244 /// @note Callers passing this to the C API should check is_valid() first and treat nullptr as invalid.
245 [[nodiscard]] C2paSettings* c_settings() const noexcept;
246
247 private:
248 C2paSettings* settings_ptr;
249 };
250
251 /// @brief Phase values reported to the ProgressCallbackFunc.
252 ///
253 /// @details A scoped C++ mirror of `C2paProgressPhase` from c2pa.h.
254 /// Values are verified at compile time to match the C enum, so any
255 /// future divergence in c2pa-rs will be caught as a build error.
256 ///
257 /// Phases emitted during a typical sign cycle (in order):
258 /// AddingIngredient → Thumbnail → Hashing → Signing → Embedding →
259 /// (if verify_after_sign) VerifyingManifest → VerifyingSignature →
260 /// VerifyingAssetHash → VerifyingIngredient
261 ///
262 /// Phases emitted during reading:
263 /// Reading → VerifyingManifest → VerifyingSignature →
264 /// VerifyingAssetHash → VerifyingIngredient
265 enum class ProgressPhase : uint8_t {
266 Reading = 0,
272 Thumbnail = 6,
273 Hashing = 7,
274 Signing = 8,
275 Embedding = 9,
277 Writing = 11,
278 FetchingOCSP = 12,
280 };
281
282 /// @brief Type alias for the progress callback passed to ContextBuilder::with_progress_callback().
283 ///
284 /// @details The callback is invoked at each major phase of signing and reading operations.
285 /// Returning false from the callback aborts the operation with an
286 /// OperationCancelled error (equivalent to calling Context::cancel()).
287 ///
288 /// @param phase Current operation phase.
289 /// @param step 1-based step index within the phase.
290 /// 0 = indeterminate (use as liveness signal); resets to 1 at each new phase.
291 /// @param total 0 = indeterminate; 1 = single-shot; >1 = determinate (step/total = fraction).
292 /// @return true to continue the operation, false to request cancellation.
293 ///
294 /// @note The callback must not throw. If it throws, the implementation catches the
295 /// exception and reports cancellation to the underlying library (same as returning
296 /// false); the original exception is not propagated. Prefer returning false or
297 /// using Context::cancel() instead of throwing.
298 ///
299 using ProgressCallbackFunc = std::function<bool(ProgressPhase phase, uint32_t step, uint32_t total)>;
300
301 /// @brief C2PA context implementing IContextProvider.
302 /// @details Context objects manage C2PA SDK configuration and state.
303 /// Contexts can be created via direct construction or the ContextBuilder:
304 ///
305 /// Direct construction:
306 /// @code
307 /// c2pa::Context ctx; // default
308 /// c2pa::Context ctx(settings); // from Settings
309 /// c2pa::Context ctx(json); // from JSON string
310 /// @endcode
311 ///
312 /// ContextBuilder (for multi-step configuration):
313 /// @code
314 /// auto ctx = c2pa::Context::ContextBuilder()
315 /// .with_settings(settings)
316 /// .with_json(json)
317 /// .create_context();
318 /// @endcode
319 ///
320 /// Builder and Reader take the context by reference (IContextProvider&).
321 /// The context object must outlive the Builder or Reader instance.
323 public:
324 /// @brief ContextBuilder for creating customized Context instances.
325 /// @details Provides a builder pattern for configuring contexts with multiple settings.
326 /// Note: create_context() consumes the builder.
327 /// @note For most use cases, prefer direct construction via the Context constructors.
328 /// The ContextBuilder is useful when you need to layer multiple configuration
329 /// sources (e.g. with_settings() followed by with_json()).
331 public:
333 ~ContextBuilder() noexcept;
334
335 // Move semantics
337 ContextBuilder& operator=(ContextBuilder&&) noexcept;
338
339 // Non-copyable
341 ContextBuilder& operator=(const ContextBuilder&) = delete;
342
343 /// @brief Check if the builder is in a valid state.
344 /// @return true if the builder can be used, false if moved from.
345 [[nodiscard]] bool is_valid() const noexcept;
346
347 /// @brief Configure with Settings object.
348 /// @param settings Settings to use (will be copied into the context). Must be valid (is_valid() true).
349 /// @return Reference to this ContextBuilder for method chaining.
350 /// @throws C2paException if settings are invalid or settings.is_valid() is false.
351 ContextBuilder& with_settings(const Settings& settings);
352
353 /// @brief Configure settings with JSON string.
354 /// @param json JSON configuration string.
355 /// @return Reference to this ContextBuilder for method chaining.
356 /// @throws C2paException if JSON is invalid.
357 ContextBuilder& with_json(const std::string& json);
358
359 /// @brief Configure settings from a JSON settings file.
360 /// @param settings_path Full path to the JSON settings file.
361 /// @return Reference to this ContextBuilder for method chaining.
362 /// @throws C2paException if file cannot be read or JSON is invalid.
363 ContextBuilder& with_json_settings_file(const std::filesystem::path& settings_path);
364
365 /// @brief Set a Signer on the context being built.
366 /// @details After this call the source Signer object is consumed and must
367 /// not be reused, as it becomes part to the context and tied to it.
368 /// If settings also contain a signer, the programmatic signer
369 /// set through this API will be used for signing.
370 /// @param signer Signer to put into the context.
371 /// @return Reference to this ContextBuilder for method chaining.
372 /// @throws C2paException if the builder or signer is invalid.
373 ContextBuilder& with_signer(Signer&& signer);
374
375 /// @brief Attach a progress callback to the context being built.
376 ///
377 /// @details The callback is invoked at each major phase of signing and
378 /// reading operations performed with the resulting context.
379 /// Return false from the callback to abort the current operation
380 /// with an OperationCancelled error.
381 ///
382 /// Phases emitted during a typical sign cycle (in order):
383 /// VerifyingIngredient → VerifyingManifest → VerifyingSignature →
384 /// VerifyingAssetHash → Thumbnail → Hashing → Signing → Embedding →
385 /// (if verify_after_sign) VerifyingManifest → … → VerifyingIngredient
386 ///
387 /// Phases emitted during reading:
388 /// Reading → VerifyingManifest → VerifyingSignature →
389 /// VerifyingAssetHash → VerifyingIngredient
390 ///
391 /// @param callback A callable matching ProgressCallbackFunc. The callback is
392 /// heap-allocated and owned by the resulting Context. Calling this method
393 /// more than once on the same builder replaces the previous callback.
394 /// The callable must not throw when invoked (see ProgressCallbackFunc).
395 /// @return Reference to this ContextBuilder for method chaining.
396 /// @throws C2paException if the builder is invalid or the C API call fails.
397 ///
398 ContextBuilder& with_progress_callback(ProgressCallbackFunc callback);
399
400 /// @brief Set a custom HTTP resolver callback on the context being built.
401 /// @details The callback is invoked synchronously whenever the SDK needs to
402 /// make an HTTP request (remote manifest fetch, OCSP, timestamp, etc.).
403 /// The callback receives a C2paHttpRequest and must fill in a C2paHttpResponse.
404 /// The response body must be allocated with malloc(); the underlying native library will call free().
405 /// @param user_data Opaque user pointer passed to every callback invocation (may be nullptr).
406 /// @param callback Function pointer matching C2paHttpResolverCallback.
407 /// @return Reference to this ContextBuilder for method chaining.
408 /// @throws C2paException if the resolver could not be set or callback is null.
409 ContextBuilder& with_http_resolver(void* user_data, C2paHttpResolverCallback callback);
410
411 /// @brief Create a Context from the current builder configuration.
412 /// @return A new Context instance.
413 /// @throws C2paException if context creation fails.
414 /// @note This consumes the builder. After calling this, is_valid() returns false.
415 [[nodiscard]] Context create_context();
416
417 /// @brief Result of ContextBuilder::release(): the raw native builder handle
418 /// paired with the heap-owned progress callback (if any).
419 /// @details The caller takes joint ownership. If `callback_owner` is non-null,
420 /// the native side stores a raw pointer into it and will invoke the
421 /// callback until the native context built from `builder` is freed.
422 /// Therefore `callback_owner` must remain alive at least until the
423 /// native context returned by c2pa_context_builder_build(builder) is
424 /// freed via c2pa_free(). Release order: native context first, then
425 /// `callback_owner` goes out of scope.
427 C2paContextBuilder* builder;
428 std::unique_ptr<ProgressCallbackFunc> callback_owner;
429 };
430
431 /// @brief Release ownership of the underlying C2paContextBuilder handle and
432 /// its progress-callback heap block (if any) to the caller.
433 /// @details After this call is_valid() returns false. The previous
434 /// C2paContextBuilder* overload was unsound when a progress callback
435 /// was pending: the C++ builder destructor would free the heap
436 /// ProgressCallbackFunc while the native side still held its pointer,
437 /// yielding a use-after-free on the first progress tick.
438 /// Callers must keep ReleasedBuilder::callback_owner alive for as
439 /// long as the native context built from ReleasedBuilder::builder
440 /// exists.
441 /// @return ReleasedBuilder holding both the native builder and (optionally)
442 /// the owning unique_ptr for the progress callback. Fields are null
443 /// when moved-from.
444 [[nodiscard]] ReleasedBuilder release() noexcept;
445
446 private:
447 // pending_callback_ is declared before context_builder
448 // so destruction (reverse of declaration) frees context_builder first, while
449 // the callback heap block is still live, then releases pending_callback_.
450 std::unique_ptr<ProgressCallbackFunc> pending_callback_;
451 C2paContextBuilder* context_builder;
452 };
453
454 // Direct construction
455 /// @brief Create a Context with default settings.
456 /// @throws C2paException if context creation fails.
458
459 /// @brief Create a Context configured with a Settings object.
460 /// @param settings Settings configuration to apply. Must be valid (settings.is_valid() true).
461 /// @throws C2paException if settings are invalid, settings.is_valid() is false, or context creation fails.
462 explicit Context(const Settings& settings);
463
464 /// @brief Create a Context configured with a JSON string.
465 /// @param json JSON configuration string.
466 /// @throws C2paException if JSON is invalid or context creation fails.
467 explicit Context(const std::string& json);
468
469 /// @brief Create a Context with a Settings object and a Signer.
470 /// @param settings Settings configuration to apply.
471 /// @param signer Signer to move into the context. Consumed after this call.
472 /// The programmatic Signer from the signer parameter
473 /// takes priority over the Signer in settings, so use this API
474 /// when wanting to explicitly set a Signer (or override the Signer in settings).
475 /// @throws C2paException if settings or signer are invalid, or context creation fails.
476 Context(const Settings& settings, Signer&& signer);
477
478 // Non-copyable, moveable
479 Context(const Context&) = delete;
480 Context& operator=(const Context&) = delete;
481 Context(Context&&) noexcept;
482 Context& operator=(Context&&) noexcept;
483
484 ~Context() noexcept override;
485
486 // IContextProvider implementation
487 /// @brief Get the underlying C2PA context pointer.
488 /// @return C2paContext pointer when is_valid() is true; nullptr when moved-from (is_valid() false).
489 /// @note Callers must check is_valid() before using the result; do not pass nullptr to the C API.
490 [[nodiscard]] C2paContext* c_context() const noexcept override;
491
492 /// @brief Check if this Context has a valid context (validity check for context-like types).
493 /// @return true when the object holds a valid C context; false when moved-from.
494 /// @note After move, is_valid() is false and c_context() returns nullptr.
495 [[nodiscard]] bool is_valid() const noexcept override;
496
497 /// @brief Internal constructor from raw FFI pointer (prefer public constructors).
498 /// @param ctx Raw C2paContext pointer — Context takes ownership.
499 /// @throws C2paException if ctx is nullptr.
500 explicit Context(C2paContext* ctx);
501
502 /// @brief "Adopt" a native context together with its heap-owned progress callback.
503 /// @param ctx Native context pointer. Context takes ownership.
504 /// @param callback Heap-owned progress callback whose raw pointer the native
505 /// context may hold. May be null.
506 Context(C2paContext* ctx,
507 std::unique_ptr<ProgressCallbackFunc> callback) noexcept;
508
509 /// @brief Request cancellation of any in-progress operation on this context.
510 ///
511 /// @details Safe to call from another thread while this Context remains valid
512 /// and is not being destroyed or moved concurrently with this call.
513 /// While a signing or reading operation is running on a valid Context,
514 /// the operation is aborted with an OperationCancelled error at the
515 /// next progress checkpoint. Has no effect if no operation is currently
516 /// in progress, or if this object is moved-from (is_valid() is false).
517 ///
518 void cancel() noexcept;
519
520 private:
521 // Member order matters: `context` is declared before `callback_owner_` so
522 // destruction (reverse of declaration) frees the heap callback block after
523 // the native context has been freed.
524 C2paContext* context;
525
526 /// Heap-owned ProgressCallbackFunc, non-null only when the Context was built
527 /// with a progress callback via ContextBuilder::with_progress_callback().
528 /// Destroyed after `context`.
529 std::unique_ptr<ProgressCallbackFunc> callback_owner_;
530 };
531
532 /// @brief Get the version of the C2PA library.
533 /// @return Version string.
534 std::string C2PA_CPP_API version();
535
536 /// @brief Load C2PA settings from a string in a given format.
537 /// @param data The configuration data to load.
538 /// @param format The mimetype of the string.
539 /// @throws C2paException for errors encountered by the C2PA library.
540 /// @deprecated Use Context constructors or Context::ContextBuilder instead for better thread safety.
541 [[deprecated("Use Context::from_json() or Context::from_settings() instead")]]
542 void C2PA_CPP_API load_settings(const std::string& data, const std::string& format);
543
544 /// @defgroup StreamWrappers Stream wrappers for C2PA C API
545 /// @brief C++ stream types that adapt stream types to C2paStream.
546 ///
547 /// The C2PA C API expects a C2paStream with four callbacks: reader, writer, seeker, flusher.
548 /// The contract for each callback is:
549 /// - reader(context, buffer, size): read up to size bytes into buffer; return bytes read, or -1 on error (set errno).
550 /// - writer(context, buffer, size): write size bytes from buffer; return bytes written, or -1 on error (set errno).
551 /// - seeker(context, offset, whence): seek to offset (whence = Start/Current/End); return new position or -1 (set errno).
552 /// - flusher(context): flush; return 0 on success, -1 on error (set errno).
553
554 /// @brief Input stream IStream wrapper for C2paStream.
555 /// @details This class is used to wrap an input stream for use with the C2PA library.
556 class C2PA_CPP_API CppIStream : public C2paStream
557 {
558 public:
559 /// @brief Pointer to the underlying C2paStream.
560 C2paStream *c_stream;
561
562 /// @brief Construct an input stream wrapper from a std::istream-derived object.
563 /// @tparam IStream Type derived from std::istream.
564 /// @param istream The input stream to wrap (must be open and valid).
565 /// @throws C2paException if stream wrapper creation fails.
566 template <typename IStream>
567 explicit CppIStream(IStream &istream) {
568 static_assert(std::is_base_of<std::istream, IStream>::value,
569 "Stream must be derived from std::istream");
570 // Upcast to std::istream* before type erasure; the callbacks cast the context back to std::istream*
571 c_stream = c2pa_create_stream(reinterpret_cast<StreamContext *>(static_cast<std::istream *>(&istream)), reader, seeker, writer, flusher);
572 if (c_stream == nullptr) {
573 throw C2paException("Failed to create input stream wrapper: is stream open and valid?");
574 }
575 }
576
577 CppIStream(const CppIStream &) = delete;
578
579 CppIStream &operator=(const CppIStream &) = delete;
580
581 CppIStream(CppIStream &&) = delete;
582
584
586
587 private:
588 /// @brief Reader callback implementation.
589 /// @param context Stream context pointer.
590 /// @param buffer Buffer to read into.
591 /// @param size Number of bytes to read.
592 /// @return Number of bytes read, or -1 on error (sets errno).
593 static intptr_t reader(StreamContext *context, uint8_t *buffer, intptr_t size);
594
595 /// @brief Writer callback implementation (not used for input streams).
596 /// @param context Stream context pointer.
597 /// @param buffer Buffer to write from.
598 /// @param size Number of bytes to write.
599 /// @return -1 (always fails for input streams).
600 static intptr_t writer(StreamContext *context, const uint8_t *buffer, intptr_t size);
601
602 /// @brief Seeker callback implementation.
603 /// @param context Stream context pointer.
604 /// @param offset Offset to seek to.
605 /// @param whence Seek mode (Start/Current/End).
606 /// @return New stream position, or -1 on error (sets errno).
607 static intptr_t seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence);
608
609 /// @brief Flusher callback implementation (no-op for input streams).
610 /// @param context Stream context pointer.
611 /// @return 0 on success.
612 static intptr_t flusher(StreamContext *context);
613
614 friend class Reader;
615 };
616
617 /// @brief Output stream OStream wrapper for C2paStream.
618 /// @details This class is used to wrap an output stream for use with the C2PA library.
619 class C2PA_CPP_API CppOStream : public C2paStream
620 {
621 public:
622 /// @brief Pointer to the underlying C2paStream.
623 C2paStream *c_stream;
624
625 /// @brief Construct an output stream wrapper from a std::ostream-derived object.
626 /// @tparam OStream Type derived from std::ostream.
627 /// @param ostream The output stream to wrap (must be open and valid).
628 /// @throws C2paException if stream wrapper creation fails.
629 template <typename OStream>
630 explicit CppOStream(OStream &ostream) {
631 static_assert(std::is_base_of<std::ostream, OStream>::value, "Stream must be derived from std::ostream");
632 // Upcast to std::ostream* before type erasure; the callbacks cast the context back to std::ostream*
633 c_stream = c2pa_create_stream(reinterpret_cast<StreamContext *>(static_cast<std::ostream *>(&ostream)), reader, seeker, writer, flusher);
634 if (c_stream == nullptr) {
635 throw C2paException("Failed to create output stream wrapper: is stream open and valid?");
636 }
637 }
638
639 CppOStream(const CppOStream &) = delete;
640
641 CppOStream &operator=(const CppOStream &) = delete;
642
643 CppOStream(CppOStream &&) = delete;
644
646
648
649 private:
650 /// @brief Reader callback implementation (not used for output streams).
651 /// @param context Stream context pointer.
652 /// @param buffer Buffer to read into.
653 /// @param size Number of bytes to read.
654 /// @return -1 (always fails for output streams).
655 static intptr_t reader(StreamContext *context, uint8_t *buffer, intptr_t size);
656
657 /// @brief Writer callback implementation.
658 /// @param context Stream context pointer.
659 /// @param buffer Buffer to write from.
660 /// @param size Number of bytes to write.
661 /// @return Number of bytes written, or -1 on error (sets errno).
662 static intptr_t writer(StreamContext *context, const uint8_t *buffer, intptr_t size);
663
664 /// @brief Seeker callback implementation.
665 /// @param context Stream context pointer.
666 /// @param offset Offset to seek to.
667 /// @param whence Seek mode (Start/Current/End).
668 /// @return New stream position, or -1 on error (sets errno).
669 static intptr_t seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence);
670
671 /// @brief Flusher callback implementation.
672 /// @param context Stream context pointer.
673 /// @return 0 on success, -1 on error (sets errno).
674 static intptr_t flusher(StreamContext *context);
675 };
676
677 /// @brief IOStream Class wrapper for C2paStream.
678 /// @details This class is used to wrap an input/output stream for use with the C2PA library.
679 class C2PA_CPP_API CppIOStream : public C2paStream
680 {
681 public:
682 /// @brief Pointer to the underlying C2paStream.
683 C2paStream *c_stream;
684
685 /// @brief Construct an I/O stream wrapper from a std::iostream-derived object.
686 /// @tparam IOStream Type derived from std::iostream.
687 /// @param iostream The I/O stream to wrap (must be open and valid).
688 /// @throws C2paException if stream wrapper creation fails.
689 template <typename IOStream>
690 CppIOStream(IOStream &iostream) {
691 static_assert(std::is_base_of<std::iostream, IOStream>::value, "Stream must be derived from std::iostream");
692 // Upcast to std::iostream* before type erasure; the callbacks cast the context back to std::iostream*
693 c_stream = c2pa_create_stream(reinterpret_cast<StreamContext *>(static_cast<std::iostream *>(&iostream)), reader, seeker, writer, flusher);
694 if (c_stream == nullptr) {
695 throw C2paException("Failed to create I/O stream wrapper: is stream open and valid?");
696 }
697 }
698
699 CppIOStream(const CppIOStream &) = delete;
700
702
704
706
708
709 private:
710 /// @brief Reader callback implementation.
711 /// @param context Stream context pointer.
712 /// @param buffer Buffer to read into.
713 /// @param size Number of bytes to read.
714 /// @return Number of bytes read, or -1 on error (sets errno).
715 static intptr_t reader(StreamContext *context, uint8_t *buffer, intptr_t size);
716
717 /// @brief Writer callback implementation.
718 /// @param context Stream context pointer.
719 /// @param buffer Buffer to write from.
720 /// @param size Number of bytes to write.
721 /// @return Number of bytes written, or -1 on error (sets errno).
722 static intptr_t writer(StreamContext *context, const uint8_t *buffer, intptr_t size);
723
724 /// @brief Seeker callback implementation.
725 /// @param context Stream context pointer.
726 /// @param offset Offset to seek to.
727 /// @param whence Seek mode (Start/Current/End).
728 /// @return New stream position, or -1 on error (sets errno).
729 static intptr_t seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence);
730
731 /// @brief Flusher callback implementation.
732 /// @param context Stream context pointer.
733 /// @return 0 on success, -1 on error (sets errno).
734 static intptr_t flusher(StreamContext *context);
735 };
736
737 /// @brief Reader class for reading a manifest.
738 /// @details This class is used to read and validate a manifest from a stream or file.
739 /// Resources are managed using RAII; member order ensures cpp_stream (which
740 /// holds a C stream pointing at the ifstream) is destroyed before owned_stream.
742 {
743 private:
744 C2paReader *c2pa_reader;
745 std::unique_ptr<std::ifstream> owned_stream; // Owns file stream when created from path
746 std::unique_ptr<CppIStream> cpp_stream; // Wraps stream for C API; destroyed before owned_stream
747 std::shared_ptr<IContextProvider> context_ref;
748
749 void init_from_context(IContextProvider& context, const std::string &format, std::istream &stream);
750 void init_from_context(IContextProvider& context, const std::filesystem::path &source_path);
751 Reader() : c2pa_reader(nullptr) {}
752
753 public:
754 /// @brief Create a Reader from a context and stream.
755 /// @param context Context provider; used at construction to configure settings.
756 /// @param format The mime format of the stream.
757 /// @param stream The input stream to read from.
758 /// @throws C2paException if context.is_valid() returns false,
759 /// or for other errors encountered by the C2PA library.
760 /// @deprecated Use Reader(std::shared_ptr<IContextProvider>, format, stream) instead.
761 /// The reference overload does not extend the lifetime of the context, which can
762 /// be problematic when progress callbacks fire after the context is destroyed.
763 [[deprecated("Use Reader(std::shared_ptr<IContextProvider>, format, stream) instead. "
764 "The reference overload does not extend the lifetime of the context, which can "
765 "be problematic when progress callbacks fire after the context is destroyed.")]]
766 Reader(IContextProvider& context, const std::string &format, std::istream &stream);
767
768 /// @brief Create a Reader from a context and file path.
769 /// @param context Context provider; used at construction only to configure settings.
770 /// @param source_path The path to the file to read.
771 /// @throws C2paException if context.is_valid() returns false,
772 /// or for other errors encountered by the C2PA library.
773 /// @note Prefer using the streaming APIs if possible.
774 /// @deprecated Use Reader(std::shared_ptr<IContextProvider>, source_path) instead.
775 /// The reference overload does not extend the lifetime of the context, which can
776 /// be problematic when progress callbacks fire after the context is destroyed.
777 [[deprecated("Use Reader(std::shared_ptr<IContextProvider>, source_path) instead. "
778 "The reference overload does not extend the lifetime of the context, which can "
779 "be problematic when progress callbacks fire after the context is destroyed.")]]
780 Reader(IContextProvider& context, const std::filesystem::path &source_path);
781
782 /// @brief Create a Reader from a shared context and stream.
783 /// @details The Reader retains a shared reference to the context, keeping it
784 /// alive for the lifetime of the Reader.
785 /// @param context Shared context provider.
786 /// @param format The mime format of the stream.
787 /// @param stream The input stream to read from.
788 /// @throws C2paException if context is null or context->is_valid() returns false.
789 Reader(std::shared_ptr<IContextProvider> context, const std::string &format, std::istream &stream);
790
791 /// @brief Create a Reader from a shared context and file path.
792 /// @details The Reader retains a shared reference to the context, keeping it
793 /// alive for the lifetime of the Reader.
794 /// @param context Shared context provider.
795 /// @param source_path The path to the file to read.
796 /// @throws C2paException if context is null or context->is_valid() returns false.
797 Reader(std::shared_ptr<IContextProvider> context, const std::filesystem::path &source_path);
798
799 /// @brief Create a Reader from a stream (will use global settings if any loaded).
800 /// @details The validation_status field in the JSON contains validation results.
801 /// @param format The mime format of the stream.
802 /// @param stream The input stream to read from.
803 /// @throws C2paException for errors encountered by the C2PA library.
804 /// @deprecated Use Reader(IContextProvider& context, format, stream) instead.
805 [[deprecated("Use Reader(IContextProvider& context, format, stream) instead")]]
806 Reader(const std::string &format, std::istream &stream);
807
808 /// @brief Create a Reader from a file path (will use global settings if any loaded).
809 /// @param source_path The path to the file to read.
810 /// @throws C2paException for errors encountered by the C2PA library.
811 /// @deprecated Use Reader(IContextProvider& context, source_path) instead.
812 /// @note Prefer using the streaming APIs if possible.
813 [[deprecated("Use Reader(IContextProvider& context, source_path) instead")]]
814 Reader(const std::filesystem::path &source_path);
815
816 /// @brief Try to open a Reader from a context and file path when the asset may lack C2PA data.
817 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
818 /// @throws C2paException for errors other than a missing manifest (e.g. invalid asset).
819 /// @throws std::system_error if the file cannot be opened.
820 /// @deprecated Use from_asset(std::shared_ptr<IContextProvider>, source_path) instead.
821 /// The reference overload does not extend the lifetime of the context, which can
822 /// cause a use-after-free crash when progress callbacks fire after the context is
823 /// destroyed.
824 [[deprecated("Use from_asset(std::shared_ptr<IContextProvider>, source_path) instead. "
825 "The reference overload does not extend the lifetime of the context, which can "
826 "be problematic when progress callbacks fire after the context is destroyed.")]]
827 static std::optional<Reader> from_asset(IContextProvider& context, const std::filesystem::path &source_path);
828
829 /// @brief Try to create a Reader from a context and stream when the asset may lack C2PA data.
830 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
831 /// @throws C2paException for errors other than a missing manifest.
832 /// @deprecated Use from_asset(std::shared_ptr<IContextProvider>, format, stream) instead.
833 /// The reference overload does not extend the lifetime of the context, which can
834 /// cause a use-after-free crash when progress callbacks fire after the context is
835 /// destroyed.
836 [[deprecated("Use from_asset(std::shared_ptr<IContextProvider>, format, stream) instead. "
837 "The reference overload does not extend the lifetime of the context, which can "
838 "be problematic when progress callbacks fire after the context is destroyed.")]]
839 static std::optional<Reader> from_asset(IContextProvider& context, const std::string &format, std::istream &stream);
840
841 /// @brief Try to open a Reader from a shared context and file path when the asset may lack C2PA data.
842 /// @details The Reader retains a shared reference to the context if C2PA data is found.
843 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
844 /// @throws C2paException for errors other than a missing manifest.
845 static std::optional<Reader> from_asset(std::shared_ptr<IContextProvider> context, const std::filesystem::path &source_path);
846
847 /// @brief Try to create a Reader from a shared context and stream when the asset may lack C2PA data.
848 /// @details The Reader retains a shared reference to the context if C2PA data is found.
849 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
850 /// @throws C2paException for errors other than a missing manifest.
851 static std::optional<Reader> from_asset(std::shared_ptr<IContextProvider> context, const std::string &format, std::istream &stream);
852
853 // Non-copyable
854 Reader(const Reader&) = delete;
855
856 Reader& operator=(const Reader&) = delete;
857
858 Reader(Reader&& other) noexcept
859 : c2pa_reader(std::exchange(other.c2pa_reader, nullptr)),
860 owned_stream(std::move(other.owned_stream)),
861 cpp_stream(std::move(other.cpp_stream)),
862 context_ref(std::move(other.context_ref)) {
863 }
864
865 Reader& operator=(Reader&& other) noexcept {
866 if (this != &other) {
867 c2pa_free(c2pa_reader);
868 c2pa_reader = std::exchange(other.c2pa_reader, nullptr);
869 owned_stream = std::move(other.owned_stream);
870 cpp_stream = std::move(other.cpp_stream);
871 context_ref = std::move(other.context_ref);
872 }
873 return *this;
874 }
875
877
878 /// @brief Check if the reader was created from an embedded manifest.
879 /// @return true if the manifest was embedded in the asset, false if external.
880 /// @throws C2paException for errors encountered by the C2PA library.
881 [[nodiscard]] inline bool is_embedded() const {
882 return c2pa_reader_is_embedded(c2pa_reader);
883 }
884
885 /// @brief Returns the remote url of the manifest if this `Reader`
886 /// obtained the manifest remotely
887 /// @return Optional string containing the remote URL, or std::nullopt if manifest was embedded.
888 /// @throws C2paException for errors encountered by the C2PA library.
889 [[nodiscard]] std::optional<std::string> remote_url() const;
890
891 /// @brief Get the manifest as a JSON string.
892 /// @return The manifest as a JSON string.
893 /// @throws C2paException for errors encountered by the C2PA library.
894 std::string json() const;
895
896 /// @brief Get the manifest as a detailed JSON string.
897 /// @details The detailed view more closely resembles the underlying JUMBF
898 /// manifest store. Unlike json(), it exposes the full
899 /// `assertion_store` map for each manifest, including assertions
900 /// that the regular view filters out (such as `c2pa.thumbnail.*`,
901 /// `c2pa.actions.*`, and `c2pa.hash.*`). Use this when building
902 /// redaction URIs or otherwise needing the canonical assertion
903 /// labels embedded in the manifest.
904 /// @return The detailed manifest as a JSON string.
905 /// @throws C2paException for errors encountered by the C2PA library.
906 std::string detailed_json() const;
907
908 /// @brief Get the manifest store as a pretty-printed crJSON string.
909 /// @details crJSON is a standardized JSON format for C2PA manifest data.
910 /// This call is infallible at yields valid empty JSON ("{}") if
911 /// there are no Content Credentials.
912 /// @return The manifest store as a crJSON string.
913 /// @throws C2paException if the underlying C call returns null.
914 std::string crjson() const;
915
916 /// @brief Get a resource from the reader and write it to a file.
917 /// @param uri The URI of the resource.
918 /// @param path The file path to write the resource to.
919 /// @return The number of bytes written.
920 /// @throws C2paException for errors encountered by the C2PA library.
921 /// @note Prefer using the streaming APIs if possible.
922 int64_t get_resource(const std::string &uri, const std::filesystem::path &path);
923
924 /// @brief Get a resource from the reader and write it to an output stream.
925 /// @param uri The URI of the resource.
926 /// @param stream The output stream to write the resource to.
927 /// @return The number of bytes written.
928 /// @throws C2paException for errors encountered by the C2PA library.
929 int64_t get_resource(const std::string &uri, std::ostream &stream);
930
931 /// @brief Get the raw C2paReader pointer.
932 /// @return The raw C2paReader pointer.
933 /// @note This is intended for internal API use and compatibility with C APIs.
934 C2paReader* get_api_internal_raw_reader() const { return c2pa_reader; }
935
936 /// @brief Get a list of mime types that the SDK can read manifests from.
937 /// @return Vector of supported MIME type strings.
938 static std::vector<std::string> supported_mime_types();
939 };
940
941 /// @brief Signer callback function type.
942 /// @details This function type is used to create a callback function for signing.
943 /// The callback receives data to sign and returns the signature.
944 /// @param data The data to sign.
945 /// @return The signature as a vector of bytes.
946 using SignerFunc = std::vector<unsigned char>(const std::vector<unsigned char> &);
947
948 /// @brief Signer class for creating a Signer
949 /// @details This class is used to create a signer from a signing algorithm, certificate, and TSA URI.
950 /// Supports both callback-based and direct signing methods.
952 {
954
955 private:
956 C2paSigner *signer;
957
958 /// @brief Transfers ownership of the underlying C2paSigner pointer out
959 /// of this wrapper, without freeing it.
960 /// @details Used by ContextBuilder::with_signer() to pass the raw pointer
961 /// to c2pa_context_builder_set_signer(), which takes ownership on
962 /// the Rust side via Box::from_raw. After this call the Signer
963 /// wrapper holds nullptr and its destructor is a no-op.
964 /// This is not the same as c2pa_signer_free(), which destroys
965 /// the signer. Similar to std::unique_ptr::release().
966 /// @return Raw C2paSigner pointer, or nullptr if already released.
967 C2paSigner* release() noexcept {
968 return std::exchange(signer, nullptr);
969 }
970
971 /// @brief Validate a TSA URI string.
972 /// @param tsa_uri The TSA URI to validate.
973 /// @return Validated C-string pointer.
974 static const char *validate_tsa_uri(const std::string &tsa_uri);
975
976 /// @brief Validate an optional TSA URI.
977 /// @param tsa_uri The optional TSA URI to validate.
978 /// @return Validated C-string pointer, or nullptr if nullopt.
979 static const char *validate_tsa_uri(const std::optional<std::string> &tsa_uri);
980
981 public:
982 /// @brief Create a Signer from a callback function.
983 /// @param callback The callback function to use for signing.
984 /// @param alg The signing algorithm to use (e.g., C2paSigningAlg::PS256).
985 /// @param sign_cert The signing certificate in PEM format.
986 /// @param tsa_uri The timestamp authority URI for time-stamping.
987 /// @throws C2paException if signer creation fails.
988 Signer(SignerFunc *callback, C2paSigningAlg alg, const std::string &sign_cert, const std::string &tsa_uri);
989
990 /// @brief Create a signer from a Signer pointer and take ownership of that pointer.
991 /// @param c_signer The C2paSigner pointer (must be non-null).
992 Signer(C2paSigner *c_signer) : signer(c_signer) {
993 if (!c_signer) {
994 throw C2paException("Signer can not be null");
995 }
996 }
997
998 /// @brief Create a Signer from signing credentials.
999 /// @param alg Signing algorithm name (e.g., "ps256", "es256").
1000 /// @param sign_cert Signing certificate in PEM format.
1001 /// @param private_key Private key in PEM format.
1002 /// @param tsa_uri Optional timestamp authority URI.
1003 /// @throws C2paException if signer creation fails.
1004 Signer(const std::string &alg, const std::string &sign_cert, const std::string &private_key, const std::optional<std::string> &tsa_uri = std::nullopt);
1005
1006 Signer(const Signer&) = delete;
1007
1008 Signer& operator=(const Signer&) = delete;
1009
1010 /// @brief Move constructor.
1011 /// @param other Signer to move from.
1012 Signer(Signer&& other) noexcept : signer(std::exchange(other.signer, nullptr)) {
1013 }
1014
1015 Signer& operator=(Signer&& other) noexcept {
1016 if (this != &other) {
1017 c2pa_free(signer);
1018 signer = std::exchange(other.signer, nullptr);
1019 }
1020 return *this;
1021 }
1022
1024
1025 /// @brief Get the size to reserve for a signature for this Signer.
1026 /// @return Reserved size for the signature in bytes.
1027 uintptr_t reserve_size();
1028
1029 /// @brief Get the underlying C2paSigner pointer.
1030 /// @return Pointer to the C2paSigner object.
1031 C2paSigner *c2pa_signer() const noexcept;
1032 };
1033
1034 /// @brief Builder class for creating a manifest.
1035 /// @details This class is used to create a manifest from a json std::string and add resources and ingredients to the manifest.
1037 {
1038 private:
1039 C2paBuilder *builder;
1040 std::shared_ptr<IContextProvider> context_ref;
1041
1042 void init_from_context(IContextProvider& context);
1043 void init_from_context(IContextProvider& context, const std::string &manifest_json);
1044
1045 public:
1046 /// @brief Create a Builder from a context with an empty manifest.
1047 /// @param context Context provider; used at construction to configure settings.
1048 /// @throws C2paException if context.is_valid() returns false,
1049 /// or for other errors encountered by the C2PA library.
1050 /// @deprecated Use Builder(std::shared_ptr<IContextProvider>) instead.
1051 /// The reference overload does not extend the lifetime of the context, which can
1052 /// be problematic when progress callbacks fire after the context is destroyed.
1053 [[deprecated("Use Builder(std::shared_ptr<IContextProvider>) instead. "
1054 "The reference overload does not extend the lifetime of the context, which can "
1055 "be problematic when progress callbacks fire after the context is destroyed.")]]
1056 explicit Builder(IContextProvider& context);
1057
1058 /// @brief Create a Builder from a context and manifest JSON string.
1059 /// @param context Context provider; used at construction to configure settings.
1060 /// @param manifest_json The manifest JSON string.
1061 /// @throws C2paException if context.is_valid() returns false,
1062 /// or for other errors encountered by the C2PA library.
1063 /// @deprecated Use Builder(std::shared_ptr<IContextProvider>, manifest_json) instead.
1064 /// The reference overload does not extend the lifetime of the context, which can
1065 /// be problematic when progress callbacks fire after the context is destroyed.
1066 [[deprecated("Use Builder(std::shared_ptr<IContextProvider>, manifest_json) instead. "
1067 "The reference overload does not extend the lifetime of the context, which can "
1068 "be problematic when progress callbacks fire after the context is destroyed.")]]
1069 Builder(IContextProvider& context, const std::string &manifest_json);
1070
1071 /// @brief Create a Builder from a shared context with an empty manifest.
1072 /// @details The Builder retains a shared reference to the context, keeping it
1073 /// alive for the lifetime of the Builder. This is the preferred
1074 /// constructor when the Context may be destroyed before the Builder.
1075 /// @param context Shared context provider.
1076 /// @throws C2paException if context is null or context->is_valid() returns false.
1077 explicit Builder(std::shared_ptr<IContextProvider> context);
1078
1079 /// @brief Create a Builder from a shared context and manifest JSON string.
1080 /// @details The Builder retains a shared reference to the context, keeping it
1081 /// alive for the lifetime of the Builder.
1082 /// @param context Shared context provider.
1083 /// @param manifest_json The manifest JSON string.
1084 /// @throws C2paException if context is null or context->is_valid() returns false.
1085 Builder(std::shared_ptr<IContextProvider> context, const std::string &manifest_json);
1086
1087 /// @brief Create a Builder from a manifest JSON string (will use global settings if any loaded).
1088 /// @param manifest_json The manifest JSON string.
1089 /// @throws C2paException for errors encountered by the C2PA library.
1090 /// @deprecated Use Builder(IContextProvider& context, manifest_json) instead.
1091 [[deprecated("Use Builder(IContextProvider& context, manifest_json) instead")]]
1092 Builder(const std::string &manifest_json);
1093
1094 /// @brief Create a Builder from a raw C FFI builder.
1095 /// @param builder Raw C2paBuilder pointer to wrap.
1096 /// @throws C2paException if builder is nullptr.
1097 explicit Builder(C2paBuilder *builder);
1098
1099 Builder(const Builder&) = delete;
1100
1101 Builder& operator=(const Builder&) = delete;
1102
1103 Builder(Builder&& other) noexcept
1104 : builder(std::exchange(other.builder, nullptr)),
1105 context_ref(std::move(other.context_ref)) {
1106 }
1107
1108 Builder& operator=(Builder&& other) noexcept {
1109 if (this != &other) {
1110 c2pa_free(builder);
1111 builder = std::exchange(other.builder, nullptr);
1112 context_ref = std::move(other.context_ref);
1113 }
1114 return *this;
1115 }
1116
1118
1119 /// @brief Get the underlying C2paBuilder pointer.
1120 /// @return Pointer managed by this wrapper.
1121 C2paBuilder *c2pa_builder() const noexcept;
1122
1123 /// @brief Set or update the manifest definition.
1124 /// @param manifest_json The manifest JSON string.
1125 /// @return Reference to this Builder for method chaining.
1126 /// @throws C2pa::C2paException for errors encountered by the C2PA library.
1127 Builder& with_definition(const std::string &manifest_json);
1128
1129 /// @brief Set the no-embed flag to prevent embedding the manifest in the asset.
1130 /// @details When set, the manifest will be stored externally rather than embedded.
1131 void set_no_embed();
1132
1133 /// @brief Set the remote URL.
1134 /// @param remote_url The remote URL to set.
1135 /// @throws C2paException for errors encountered by the C2PA library.
1136 void set_remote_url(const std::string &remote_url);
1137
1138 /// @brief Set the base path for loading resources from files.
1139 /// @details When set, resources are loaded from files relative to this path.
1140 /// If not set, resources are loaded from memory.
1141 /// @param base_path The base directory path.
1142 /// @throws C2paException for errors encountered by the C2PA library.
1143 /// @deprecated This method is planned to be deprecated in a future release.
1144 /// Usage should be limited and temporary. Use add_resource instead.
1145 void set_base_path(const std::string &base_path);
1146
1147 /// @brief Add a resource to the builder from a stream.
1148 /// @param uri The URI identifier for the resource.
1149 /// @param source The input stream to read the resource from.
1150 /// @throws C2paException for errors encountered by the C2PA library.
1151 void add_resource(const std::string &uri, std::istream &source);
1152
1153 /// @brief Add a resource to the builder from a file.
1154 /// @param uri The URI identifier for the resource.
1155 /// @param source_path The path to the resource file.
1156 /// @throws C2paException for errors encountered by the C2PA library.
1157 /// @note Prefer using the streaming APIs if possible.
1158 void add_resource(const std::string &uri, const std::filesystem::path &source_path);
1159
1160 /// @brief Add an ingredient to the builder from a stream.
1161 /// @param ingredient_json Any fields of the ingredient you want to define.
1162 /// @param format The mime format of the ingredient.
1163 /// @param source The input stream to read the ingredient from.
1164 /// @throws C2paException for errors encountered by the C2PA library.
1165 void add_ingredient(const std::string &ingredient_json, const std::string &format, std::istream &source);
1166
1167 /// @brief Add an ingredient to the builder from a file.
1168 /// @param ingredient_json Any fields of the ingredient you want to define.
1169 /// @param source_path The path to the ingredient file.
1170 /// @throws C2paException for errors encountered by the C2PA library.
1171 /// @note Prefer using the streaming APIs if possible.
1172 void add_ingredient(const std::string &ingredient_json, const std::filesystem::path &source_path);
1173
1174 /// @brief Add an action to the manifest.
1175 /// @param action_json JSON string containing the action data.
1176 /// @throws C2paException for errors encountered by the C2PA library.
1177 void add_action(const std::string &action_json);
1178
1179 /// @brief Set the intent for this Builder, controlling what kind of manifest to create.
1180 /// @param intent The intent type: Create, Edit, or Update.
1181 /// @param digital_source_type Required for Create intent. Describes how the asset was produced.
1182 /// Defaults to Empty.
1183 /// @throws C2paException if the intent cannot be set.
1184 void set_intent(C2paBuilderIntent intent, C2paDigitalSourceType digital_source_type = Empty);
1185
1186 /// @brief Sign an input stream and write the signed data to an output stream.
1187 /// @param format The mime format of the output stream.
1188 /// @param source The input stream to sign.
1189 /// @param dest The output stream to write the signed data to.
1190 /// @param signer The Signer object to use for signing.
1191 /// @return A vector containing the signed manifest bytes.
1192 /// @throws C2paException for errors encountered by the C2PA library.
1193 /// @deprecated Use sign(const string&, std::istream&, std::iostream&, Signer&) instead.
1194 std::vector<unsigned char> sign(const std::string &format, std::istream &source, std::ostream &dest, Signer &signer);
1195
1196 /// @brief Sign an input stream and write the signed data to an I/O stream.
1197 /// @param format The mime format of the output.
1198 /// @param source The input stream to sign.
1199 /// @param dest The I/O stream to write the signed data to.
1200 /// @param signer The Signer object to use for signing.
1201 /// @return A vector containing the signed manifest bytes.
1202 /// @throws C2paException for errors encountered by the C2PA library.
1203 std::vector<unsigned char> sign(const std::string &format, std::istream &source, std::iostream &dest, Signer &signer);
1204
1205 /// @brief Sign a file and write the signed data to an output file.
1206 /// @param source_path The path to the file to sign.
1207 /// @param dest_path The path to write the signed file to.
1208 /// @param signer The signer object to use for signing.
1209 /// @return A vector containing the signed manifest bytes.
1210 /// @throws C2paException for errors encountered by the C2PA library.
1211 /// @note Prefer using the streaming APIs if possible.
1212 std::vector<unsigned char> sign(const std::filesystem::path &source_path, const std::filesystem::path &dest_path, Signer &signer);
1213
1214 /// @brief Sign using the signer from the Builder's Context.
1215 /// @details The Signer may have been set programmatically via
1216 /// ContextBuilder::with_signer(), or configured in settings JSON.
1217 /// If both programmatic and settings signers are present,
1218 /// the programmatic signer takes priority.
1219 /// @param format The mime format of the output.
1220 /// @param source The input stream to sign.
1221 /// @param dest The I/O stream to write the signed data to.
1222 /// @return A vector containing the signed manifest bytes.
1223 /// @throws C2paException if the context has no signer or on other errors.
1224 std::vector<unsigned char> sign(const std::string &format, std::istream &source, std::iostream &dest);
1225
1226 /// @brief Sign a file using the signer from the Builder's Context.
1227 /// @details The signer may have been set programmatically via
1228 /// ContextBuilder::with_signer(), or configured in settings JSON.
1229 /// If both programmatic and settings signers are present,
1230 /// the programmatic signer takes priority.
1231 /// @param source_path The path to the file to sign.
1232 /// @param dest_path The path to write the signed file to.
1233 /// @return A vector containing the signed manifest bytes.
1234 /// @throws C2paException if the context has no signer or on other errors.
1235 std::vector<unsigned char> sign(const std::filesystem::path &source_path, const std::filesystem::path &dest_path);
1236
1237 /// @brief Create a Builder from an archived Builder stream.
1238 /// @param archive The input stream to read the archive from.
1239 /// @return A new Builder instance loaded from the archive.
1240 /// @throws C2paException for errors encountered by the C2PA library.
1241 static Builder from_archive(std::istream &archive);
1242
1243 /// @brief Create a Builder from an archive.
1244 /// @param archive_path The path to the archive file.
1245 /// @return A new Builder instance loaded from the archive.
1246 /// @throws C2paException for errors encountered by the C2PA library.
1247 /// @note Prefer using the streaming APIs if possible.
1248 static Builder from_archive(const std::filesystem::path &archive_path);
1249
1250 /// @brief Load an archive into this builder.
1251 /// @details Replaces the current definition with the archived builder state.
1252 /// @param archive The input stream to read the archive from.
1253 /// @return Reference to this builder for method chaining.
1254 /// @throws C2paException for errors encountered by the C2PA library.
1255 /// @note This allows setting a context before loading the archive, preserving context settings.
1256 Builder& with_archive(std::istream &archive);
1257
1258 /// @brief Write the builder to an archive stream.
1259 /// @param dest The output stream to write the archive to.
1260 /// @throws C2paException for errors encountered by the C2PA library.
1261 void to_archive(std::ostream &dest);
1262
1263 /// @brief Write the builder to an archive file.
1264 /// @param dest_path The path to write the archive file to.
1265 /// @throws C2paException for errors encountered by the C2PA library.
1266 /// @note Prefer using the streaming APIs if possible.
1267 void to_archive(const std::filesystem::path &dest_path);
1268
1269 /// @brief Write a single-ingredient archive for the named ingredient.
1270 /// @param ingredient_id The ingredient's `label` if set, otherwise its `instance_id`,
1271 /// as supplied to add_ingredient.
1272 /// @param dest The output stream to write the ingredient archive to.
1273 /// @note Requires the `generate_c2pa_archive` context setting to be enabled
1274 /// (enabled by default).
1275 /// @throws C2paException for errors encountered by the C2PA library.
1276 void write_ingredient_archive(const std::string &ingredient_id, std::ostream &dest);
1277
1278 /// @brief Add an ingredient to this builder from a per-ingredient archive stream.
1279 /// @param archive The input stream containing the archive produced by write_ingredient_archive.
1280 /// @throws C2paException for errors encountered by the C2PA library.
1281 void add_ingredient_from_archive(std::istream &archive);
1282
1283 /// @brief Create a hashed placeholder from the builder.
1284 /// @param reserved_size The size required for a signature from the intended signer (in bytes).
1285 /// @param format The mime format or extension of the asset.
1286 /// @return A vector containing the hashed placeholder bytes.
1287 /// @throws C2paException for errors encountered by the C2PA library.
1288 std::vector<unsigned char> data_hashed_placeholder(uintptr_t reserved_size, const std::string &format);
1289
1290 /// @brief Sign a Builder using the specified signer and data hash.
1291 /// @param signer The signer to use for signing.
1292 /// @param data_hash The data hash ranges to sign (must contain hashes unless an asset is provided).
1293 /// @param format The mime format for embedding. Use "c2pa" for an unformatted result.
1294 /// @param asset Optional asset to hash according to the data_hash information.
1295 /// @return A vector containing the signed embeddable data.
1296 /// @throws C2paException for errors encountered by the C2PA library.
1297 std::vector<unsigned char> sign_data_hashed_embeddable(Signer &signer, const std::string &data_hash, const std::string &format, std::istream *asset = nullptr);
1298
1299 /// @brief Convert unformatted manifest data to an embeddable format.
1300 /// @param format The format for embedding.
1301 /// @param data Unformatted manifest data from sign_data_hashed_embeddable using "c2pa" format.
1302 /// @return A formatted copy of the data.
1303 static std::vector<unsigned char> format_embeddable(const std::string &format, std::vector<unsigned char> &data);
1304
1305 /// @brief Check if the given format requires a placeholder embedding step.
1306 /// @details Returns false for BoxHash-capable formats when prefer_box_hash is enabled in
1307 /// the context settings (no placeholder needed — hash covers the full asset).
1308 /// Always returns true for BMFF formats (MP4, etc.) regardless of settings.
1309 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1310 /// @return true if placeholder() must be called and embedded before sign_embeddable(); false otherwise.
1311 /// @throws C2paException on error.
1312 bool needs_placeholder(const std::string &format);
1313
1314 /// @brief Create a composed placeholder manifest to embed in the asset.
1315 /// @details The signer (and its reserve size) are obtained from the Builder's Context.
1316 /// For BMFF assets, the placeholder includes a BmffHash assertion with
1317 /// default exclusions for the manifest UUID box.
1318 /// Returns empty bytes for formats that do not need a placeholder (BoxHash).
1319 /// The placeholder size is stored internally so sign_embeddable() returns bytes
1320 /// of exactly the same size, enabling in-place patching.
1321 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1322 /// @return Composed placeholder bytes ready to embed into the asset.
1323 /// @throws C2paException on error.
1324 std::vector<unsigned char> placeholder(const std::string &format);
1325
1326 /// @brief Register the byte ranges where the placeholder was embedded (DataHash workflow).
1327 /// @details Call this after embedding the placeholder bytes into the asset and before
1328 /// update_hash_from_stream(). The exclusions replace the dummy ranges set by
1329 /// placeholder() so the asset hash covers all bytes except the manifest slot.
1330 /// Exclusions are (start, length) pairs in asset byte coordinates.
1331 /// @param exclusions Vector of (start, length) pairs describing the embedded placeholder region.
1332 /// @throws C2paException if no DataHash assertion exists or on other error.
1333 void set_data_hash_exclusions(const std::vector<std::pair<uint64_t, uint64_t>> &exclusions);
1334
1335 /// @brief Compute and store the asset hash by reading a stream.
1336 /// @details Automatically detects the hard binding type from the builder state:
1337 /// - DataHash: uses exclusion ranges already registered via set_data_hash_exclusions().
1338 /// - BmffHash: uses path-based exclusions from the BMFF assertion (UUID box, mdat).
1339 /// - BoxHash: hashes each format-specific box individually.
1340 /// Call set_data_hash_exclusions() before this for DataHash workflows.
1341 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1342 /// @param stream The asset stream to hash. Must include the embedded placeholder bytes.
1343 /// @throws C2paException on error.
1344 void update_hash_from_stream(const std::string &format, std::istream &stream);
1345
1346 /// @brief Sign and return the final manifest bytes, ready for embedding.
1347 /// @details Operates in two modes:
1348 /// - Placeholder mode (after placeholder()): zero-pads the signed manifest to the
1349 /// pre-committed placeholder size, enabling in-place patching of the asset.
1350 /// - Direct mode (no placeholder): returns the actual signed manifest size.
1351 /// Requires a valid hard binding assertion (set via update_hash_from_stream()).
1352 /// The signer is obtained from the Builder's Context.
1353 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1354 /// @return Signed manifest bytes ready to embed into the asset.
1355 /// @throws C2paException on error.
1356 std::vector<unsigned char> sign_embeddable(const std::string &format);
1357
1358 /// @brief Get a list of mime types that the Builder supports.
1359 /// @return Vector of supported MIME type strings.
1360 static std::vector<std::string> supported_mime_types();
1361
1362 private:
1363 explicit Builder(std::istream &archive);
1364 };
1365}
1366
1367// Restore warnings
1368#ifdef __GNUC__
1369#pragma GCC diagnostic pop
1370#endif
1371
1372#ifdef _MSC_VER
1373#pragma warning(pop)
1374#endif
1375
1376#endif // C2PA_H
#define C2PA_CPP_API
Definition c2pa.hpp:49
Builder class for creating a manifest.
Definition c2pa.hpp:1037
Builder(const std::string &manifest_json)
Create a Builder from a manifest JSON string (will use global settings if any loaded).
Builder & operator=(const Builder &)=delete
Builder(Builder &&other) noexcept
Definition c2pa.hpp:1103
Builder(C2paBuilder *builder)
Create a Builder from a raw C FFI builder.
Builder(const Builder &)=delete
Builder(std::shared_ptr< IContextProvider > context, const std::string &manifest_json)
Create a Builder from a shared context and manifest JSON string.
C2paBuilder * c2pa_builder() const noexcept
Get the underlying C2paBuilder pointer.
Builder & operator=(Builder &&other) noexcept
Definition c2pa.hpp:1108
Builder(std::shared_ptr< IContextProvider > context)
Create a Builder from a shared context with an empty manifest.
Builder(IContextProvider &context)
Create a Builder from a context with an empty manifest.
Builder(IContextProvider &context, const std::string &manifest_json)
Create a Builder from a context and manifest JSON string.
Exception class for C2pa errors. This class is used to throw exceptions for errors encountered by the...
Definition c2pa.hpp:87
C2paException & operator=(C2paException &&)=default
~C2paException() override=default
C2paException(const C2paException &)=default
const char * what() const noexcept override
Get the exception message.
C2paException(std::string message)
Construct an exception with a custom error message.
C2paException & operator=(const C2paException &)=default
C2paException(C2paException &&)=default
C2paException()
Default constructor.
ContextBuilder for creating customized Context instances.
Definition c2pa.hpp:330
ReleasedBuilder release() noexcept
Release ownership of the underlying C2paContextBuilder handle and its progress-callback heap block (i...
C2PA context implementing IContextProvider.
Definition c2pa.hpp:322
IOStream Class wrapper for C2paStream.
Definition c2pa.hpp:680
CppIOStream & operator=(const CppIOStream &)=delete
CppIOStream(const CppIOStream &)=delete
CppIOStream & operator=(CppIOStream &&)=delete
C2paStream * c_stream
Pointer to the underlying C2paStream.
Definition c2pa.hpp:683
CppIOStream(IOStream &iostream)
Construct an I/O stream wrapper from a std::iostream-derived object.
Definition c2pa.hpp:690
CppIOStream(CppIOStream &&)=delete
Input stream IStream wrapper for C2paStream.
Definition c2pa.hpp:557
CppIStream & operator=(const CppIStream &)=delete
CppIStream(CppIStream &&)=delete
CppIStream & operator=(CppIStream &&)=delete
C2paStream * c_stream
Pointer to the underlying C2paStream.
Definition c2pa.hpp:560
CppIStream(IStream &istream)
Construct an input stream wrapper from a std::istream-derived object.
Definition c2pa.hpp:567
CppIStream(const CppIStream &)=delete
Output stream OStream wrapper for C2paStream.
Definition c2pa.hpp:620
CppOStream(OStream &ostream)
Construct an output stream wrapper from a std::ostream-derived object.
Definition c2pa.hpp:630
CppOStream(CppOStream &&)=delete
CppOStream & operator=(CppOStream &&)=delete
C2paStream * c_stream
Pointer to the underlying C2paStream.
Definition c2pa.hpp:623
CppOStream & operator=(const CppOStream &)=delete
CppOStream(const CppOStream &)=delete
Interface for types that can provide C2PA context functionality.
Definition c2pa.hpp:161
virtual ~IContextProvider() noexcept=default
Reader class for reading a manifest.
Definition c2pa.hpp:742
Reader(std::shared_ptr< IContextProvider > context, const std::filesystem::path &source_path)
Create a Reader from a shared context and file path.
static std::optional< Reader > from_asset(std::shared_ptr< IContextProvider > context, const std::string &format, std::istream &stream)
Try to create a Reader from a shared context and stream when the asset may lack C2PA data.
bool is_embedded() const
Check if the reader was created from an embedded manifest.
Definition c2pa.hpp:881
std::string json() const
Get the manifest as a JSON string.
static std::optional< Reader > from_asset(IContextProvider &context, const std::string &format, std::istream &stream)
Try to create a Reader from a context and stream when the asset may lack C2PA data.
int64_t get_resource(const std::string &uri, const std::filesystem::path &path)
Get a resource from the reader and write it to a file.
Reader(const std::string &format, std::istream &stream)
Create a Reader from a stream (will use global settings if any loaded).
int64_t get_resource(const std::string &uri, std::ostream &stream)
Get a resource from the reader and write it to an output stream.
Reader(std::shared_ptr< IContextProvider > context, const std::string &format, std::istream &stream)
Create a Reader from a shared context and stream.
static std::optional< Reader > from_asset(IContextProvider &context, const std::filesystem::path &source_path)
Try to open a Reader from a context and file path when the asset may lack C2PA data.
Reader(Reader &&other) noexcept
Definition c2pa.hpp:858
Reader(const Reader &)=delete
std::string crjson() const
Get the manifest store as a pretty-printed crJSON string.
Reader & operator=(const Reader &)=delete
std::optional< std::string > remote_url() const
Returns the remote url of the manifest if this Reader obtained the manifest remotely.
static std::optional< Reader > from_asset(std::shared_ptr< IContextProvider > context, const std::filesystem::path &source_path)
Try to open a Reader from a shared context and file path when the asset may lack C2PA data.
Reader(IContextProvider &context, const std::string &format, std::istream &stream)
Create a Reader from a context and stream.
Reader & operator=(Reader &&other) noexcept
Definition c2pa.hpp:865
std::string detailed_json() const
Get the manifest as a detailed JSON string.
Reader(const std::filesystem::path &source_path)
Create a Reader from a file path (will use global settings if any loaded).
C2paReader * get_api_internal_raw_reader() const
Get the raw C2paReader pointer.
Definition c2pa.hpp:934
Reader(IContextProvider &context, const std::filesystem::path &source_path)
Create a Reader from a context and file path.
static std::vector< std::string > supported_mime_types()
Get a list of mime types that the SDK can read manifests from.
(C2PA SDK) Settings configuration object for creating contexts.
Definition c2pa.hpp:195
Settings & update(const std::string &data)
Merge configuration from a JSON string (latest configuration wins).
Definition c2pa.hpp:233
C2paSettings * c_settings() const noexcept
Get the raw C FFI settings pointer.
Settings(const std::string &data, const std::string &format)
Create settings from a configuration string.
Settings()
Create default settings.
Settings(Settings &&) noexcept
Settings & update(const std::string &data, const std::string &format)
Merge configuration from a std::string (latest configuration wins).
Signer class for creating a Signer.
Definition c2pa.hpp:952
Signer & operator=(Signer &&other) noexcept
Definition c2pa.hpp:1015
uintptr_t reserve_size()
Get the size to reserve for a signature for this Signer.
C2paSigner * c2pa_signer() const noexcept
Get the underlying C2paSigner pointer.
Signer(SignerFunc *callback, C2paSigningAlg alg, const std::string &sign_cert, const std::string &tsa_uri)
Create a Signer from a callback function.
Signer(C2paSigner *c_signer)
Create a signer from a Signer pointer and take ownership of that pointer.
Definition c2pa.hpp:992
Signer & operator=(const Signer &)=delete
Signer(Signer &&other) noexcept
Move constructor.
Definition c2pa.hpp:1012
Signer(const Signer &)=delete
Signer(const std::string &alg, const std::string &sign_cert, const std::string &private_key, const std::optional< std::string > &tsa_uri=std::nullopt)
Create a Signer from signing credentials.
Definition c2pa.hpp:52
OperationResult
Result codes for C API operations (matches C API return convention).
Definition c2pa.hpp:64
@ Success
Operation succeeded.
@ Error
Operation failed (check C2paException for details)
ProgressPhase
Phase values reported to the ProgressCallbackFunc.
Definition c2pa.hpp:265
std::function< bool(ProgressPhase phase, uint32_t step, uint32_t total)> ProgressCallbackFunc
Type alias for the progress callback passed to ContextBuilder::with_progress_callback().
Definition c2pa.hpp:299
int stream_error_return(StreamError e) noexcept
Set errno from StreamError and return error sentinel.
Definition c2pa.hpp:79
C2paSignerInfo SignerInfo
Type alias for C2paSignerInfo from the C API.
Definition c2pa.hpp:55
StreamError
Stream/FFI error codes (maps to errno values used by the C layer).
Definition c2pa.hpp:70
std::vector< unsigned char >(const std::vector< unsigned char > &) SignerFunc
Signer callback function type.
Definition c2pa.hpp:946
Result of ContextBuilder::release(): the raw native builder handle paired with the heap-owned progres...
Definition c2pa.hpp:426
C2paContextBuilder * builder
Definition c2pa.hpp:427
std::unique_ptr< ProgressCallbackFunc > callback_owner
Definition c2pa.hpp:428