c2pa_zip/lib.rs
1// Copyright 2026 WritersLogic. All rights reserved.
2// Licensed under the Apache License, Version 2.0 or the MIT license,
3// at your option.
4
5//! C2PA Manifest Store embedding and reading for ZIP-based (OCF-style) documents.
6//!
7//! Implements the ZIP embedding method from the C2PA Technical Specification,
8//! which stores a C2PA Manifest Store as a dedicated entry inside the ZIP
9//! archive at [`ZIP_MANIFEST_PATH`] (`META-INF/content_credential.c2pa`). Many
10//! document formats are ZIP archives with a fixed internal layout — EPUB,
11//! Office Open XML (DOCX/XLSX/PPTX), OpenDocument (ODT/ODS/ODP) and OpenXPS —
12//! and all embed through this single transport.
13//!
14//! Per the specification, the manifest entry is **stored** (compression method
15//! 0), not encrypted, and its general-purpose bit flag is `0`. Embedding
16//! appends the entry before the central directory so existing entries keep
17//! their byte offsets, then rebuilds the central directory and end-of-central-
18//! directory record.
19//!
20//! # Binding inputs
21//!
22//! A ZIP asset is bound with a `c2pa.hash.collection.data` assertion carrying
23//! one entry per member plus the additional `zip_central_directory_hash` field,
24//! which covers the archive's own directory so that an entry added after
25//! signing cannot leave the manifest valid.
26//!
27//! [`collection_members`] and [`central_directory_range`] supply the byte ranges
28//! the specification says to hash. They deliberately do not hash them: locating
29//! those ranges is ZIP parsing, which is this crate's job, while choosing and
30//! running a digest is the caller's, and keeping that split is what lets this
31//! crate stay dependency-free.
32//!
33//! Manifest construction and signing remain out of scope; use the official
34//! [`c2pa`](https://crates.io/crates/c2pa) SDK for those.
35
36mod binding;
37mod error;
38mod reader;
39mod verify;
40mod writer;
41mod zip;
42
43#[cfg(target_arch = "wasm32")]
44mod wasm;
45
46#[cfg(all(feature = "python", not(target_arch = "wasm32")))]
47mod python;
48
49pub use binding::{central_directory_range, collection_members, Member};
50pub use error::Error;
51pub use reader::read_manifest;
52pub use verify::{verify, Compliance};
53pub use writer::{embed_manifest, remove_manifest};
54pub use zip::ZIP_MANIFEST_PATH;