From 8270c6a034436c94a6d0ebf4dd1d958191441a85 Mon Sep 17 00:00:00 2001 From: Kornel Date: Mon, 23 Dec 2024 17:22:47 +0000 Subject: [PATCH] Skip signed files or remove C2PA metadata --- src/error.rs | 4 ++++ src/headers.rs | 32 ++++++++++++++++++++++++++++++++ src/main.rs | 6 +++++- src/png/mod.rs | 9 +++++++++ tests/files/c2pa-signed.png | Bin 0 -> 67753 bytes tests/lib.rs | 13 +++++++++++++ 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/files/c2pa-signed.png diff --git a/src/error.rs b/src/error.rs index 919c77f0..21ce6ab5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,7 @@ pub enum PngError { ChunkMissing(&'static str), InvalidDepthForType(BitDepth, ColorType), IncorrectDataLength(usize, usize), + C2PAMetadataPreventsChanges, Other(Box), } @@ -41,6 +42,9 @@ impl fmt::Display for PngError { "Data length {} does not match the expected length {}", l1, l2 ), + PngError::C2PAMetadataPreventsChanges => f.write_str( + "The image contains C2PA manifest that would be invalidated by any file changes", + ), PngError::Other(ref s) => f.write_str(s), } } diff --git a/src/headers.rs b/src/headers.rs index d869199a..92c949ac 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -75,6 +75,8 @@ pub struct Chunk { #[derive(Debug, PartialEq, Eq, Clone)] pub enum StripChunks { /// None + /// + /// ...except caBX chunk if it contains a C2PA.org signature. None, /// Remove specific chunks Strip(IndexSet<[u8; 4]>), @@ -111,6 +113,36 @@ pub struct RawChunk<'a> { pub data: &'a [u8], } +impl RawChunk<'_> { + // Is it a chunk for C2PA/CAI JUMBF metadata + pub(crate) fn is_c2pa(&self) -> bool { + if self.name == *b"caBX" { + if let Some((b"jumb", data)) = parse_jumbf_box(self.data) { + if let Some((b"jumd", data)) = parse_jumbf_box(data) { + if data.get(..4) == Some(b"c2pa") { + return true; + } + } + } + } + false + } +} + +fn parse_jumbf_box(data: &[u8]) -> Option<(&[u8], &[u8])> { + if data.len() < 8 { + return None; + } + let (len, rest) = data.split_at(4); + let len = u32::from_be_bytes(len.try_into().unwrap()) as usize; + if len < 8 || len > data.len() { + return None; + } + let (box_name, data) = rest.split_at(4); + let data = data.get(..len - 8)?; + Some((box_name, data)) +} + pub fn parse_next_chunk<'a>( byte_data: &'a [u8], byte_offset: &mut usize, diff --git a/src/main.rs b/src/main.rs index 5d73be32..1b8d4781 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ use clap::ArgMatches; mod cli; use indexmap::IndexSet; use log::{error, warn, Level, LevelFilter}; -use oxipng::{Deflaters, InFile, Options, OutFile, RowFilter, StripChunks}; +use oxipng::{Deflaters, InFile, Options, OutFile, PngError, RowFilter, StripChunks}; use rayon::prelude::*; use crate::cli::DISPLAY_CHUNKS; @@ -88,6 +88,10 @@ fn main() -> ExitCode { // PNG files, and return an error for them. // We don't really want to return an error code for those files. Ok(_) => OptimizationResult::Ok, + Err(e @ PngError::C2PAMetadataPreventsChanges) => { + warn!("{input}: {e}"); + OptimizationResult::Skipped + } Err(e) => { error!("{input}: {e}"); OptimizationResult::Failed diff --git a/src/png/mod.rs b/src/png/mod.rs index cd2d605b..08ef264d 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -114,6 +114,15 @@ impl PngData { } _ => { if opts.strip.keep(&chunk.name) { + if chunk.is_c2pa() { + // StripChunks::None is the default value, so to keep optimizing by default, + // interpret it as stripping the C2PA metadata. + // The C2PA metadata is invalidated if the file changes, so it shouldn't be kept. + if opts.strip == StripChunks::None { + continue; + } + return Err(PngError::C2PAMetadataPreventsChanges); + } aux_chunks.push(Chunk { name: chunk.name, data: chunk.data.to_owned(), diff --git a/tests/files/c2pa-signed.png b/tests/files/c2pa-signed.png new file mode 100644 index 0000000000000000000000000000000000000000..b70b781d7ac10bd71a2059e88dd96286764fe92c GIT binary patch literal 67753 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�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