c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

commit 1c6cd7f4061eb00ecc1d5f81114680ecb61eacde
parent 0375b6fa00553baf6f521ff6a43744c1f9fe42da
Author: mauricefisher64 <92736594+mauricefisher64@users.noreply.github.com>
Date:   Thu, 10 Aug 2023 16:03:39 -0400

Fix for overly harsh checks when checking Merkle trees. (#289)

* Fix for overly harsh checks when checking Merkle trees.

* Code simplification

* Fix missing id field from jumbf box reading.

* clippy fixes
Diffstat:
Msdk/src/assertions/bmff_hash.rs | 30++----------------------------
Msdk/src/jumbf/boxes.rs | 95++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
2 files changed, 55 insertions(+), 70 deletions(-)

diff --git a/sdk/src/assertions/bmff_hash.rs b/sdk/src/assertions/bmff_hash.rs @@ -609,33 +609,13 @@ impl BmffHash { } }; - let sample_cnt = mp4.sample_count(mm.local_id).map_err(|_e| { - Error::InvalidAsset("Could not parse BMFF track sample".to_string()) - })?; - + let sample_cnt = track.sample_count(); if sample_cnt == 0 { return Err(Error::InvalidAsset("No samples".to_string())); } let track_id = track.track_id(); - // get the chunk count - let stbl_box = &track.trak.mdia.minf.stbl; - let chunk_cnt = match &stbl_box.stco { - Some(stco) => stco.entries.len(), - None => match &stbl_box.co64 { - Some(co64) => co64.entries.len(), - None => 0, - }, - }; - - // the Merkle count is the number of chunks for timed media - if mm.count != chunk_cnt as u32 { - return Err(Error::HashMismatch( - "Track count does not match Merkle map count".to_string(), - )); - } - // create sample to chunk mapping // create the Merkle tree per samples in a chunk let mut chunk_hash_map: HashMap<u32, Hasher> = HashMap::new(); @@ -684,19 +664,13 @@ impl BmffHash { } } - if chunk_cnt != chunk_hash_map.len() { - return Err(Error::HashMismatch( - "Incorrect number of Merkle trees".to_string(), - )); - } - // finalize leaf hashes let mut leaf_hashes = Vec::new(); for chunk_bmff_mm in &track_to_bmff_merkle_map[&track_id] { match chunk_hash_map.remove(&(chunk_bmff_mm.location + 1)) { Some(h) => { let h = Hasher::finalize(h); - leaf_hashes.push(h.clone()); + leaf_hashes.push(h); } None => { return Err(Error::HashMismatch( diff --git a/sdk/src/jumbf/boxes.rs b/sdk/src/jumbf/boxes.rs @@ -30,6 +30,7 @@ use std::{ io::{Read, Result as IoResult, Seek, SeekFrom, Write}, }; +use byteorder::{BigEndian, ReadBytesExt}; use hex::FromHex; use log::debug; use thiserror::Error; @@ -1910,10 +1911,10 @@ impl BoxReader { reader.read_exact(&mut togs)?; bytes_left -= 1; + let mut sbuf = Vec::with_capacity(64); if togs[0] & 0x03 == 0x03 { // must be requestable and labeled // read label - let mut sbuf = Vec::with_capacity(64); loop { let mut buf = [0; 1]; reader.read_exact(&mut buf)?; @@ -1924,54 +1925,64 @@ impl BoxReader { sbuf.push(buf[0]); } } + } else { + return Err(JumbfParseError::InvalidDescriptionBox); + } - // if there is a signature, we need to read it... - let sig = if togs[0] & 0x08 == 0x08 { - let mut sigbuf: [u8; 32] = [0; 32]; - reader.read_exact(&mut sigbuf)?; - bytes_left -= 32; - Some(sigbuf) - } else { - None - }; - - // read private box if necessary - let private = if togs[0] & 0x10 == 0x10 { - let header = BoxReader::read_header(reader) - .map_err(|_| JumbfParseError::InvalidBoxHeader)?; - if header.size == 0 { - // bad read, - return Err(JumbfParseError::InvalidBoxHeader); - } else if header.size != bytes_left - HEADER_SIZE { - // this means that we started w/o the header... - unread_bytes(reader, HEADER_SIZE)?; - } + // box id + let bxid = if togs[0] & 0x04 == 0x04 { + let idbuf = reader.read_u32::<BigEndian>()?; + bytes_left -= 4; + Some(idbuf) + } else { + None + }; - if header.name == BoxType::SaltHash { - let data_len = header.size - HEADER_SIZE; - let mut buf = vec![0u8; data_len as usize]; - reader.read_exact(&mut buf)?; + // if there is a signature, we need to read it... + let sig = if togs[0] & 0x08 == 0x08 { + let mut sigbuf: [u8; 32] = [0; 32]; + reader.read_exact(&mut sigbuf)?; + bytes_left -= 32; + Some(sigbuf) + } else { + None + }; - bytes_left -= header.size; + // read private box if necessary + let private = if togs[0] & 0x10 == 0x10 { + let header = + BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?; + if header.size == 0 { + // bad read, + return Err(JumbfParseError::InvalidBoxHeader); + } else if header.size != bytes_left - HEADER_SIZE { + // this means that we started w/o the header... + unread_bytes(reader, HEADER_SIZE)?; + } - Some(CAISaltContentBox::new(buf)) - } else { - return Err(JumbfParseError::InvalidBoxHeader); - } - } else { - None - }; + if header.name == BoxType::SaltHash { + let data_len = header.size - HEADER_SIZE; + let mut buf = vec![0u8; data_len as usize]; + reader.read_exact(&mut buf)?; - if bytes_left != HEADER_SIZE { - // make sure we have consumed the entire box + bytes_left -= header.size; + + Some(CAISaltContentBox::new(buf)) + } else { return Err(JumbfParseError::InvalidBoxHeader); } + } else { + None + }; - return Ok(JUMBFDescriptionBox::from( - &uuid, togs[0], sbuf, None, sig, private, - )); + if bytes_left != HEADER_SIZE { + // make sure we have consumed the entire box + return Err(JumbfParseError::InvalidBoxHeader); } - Err(JumbfParseError::InvalidDescriptionBox) + + Ok(JUMBFDescriptionBox::from( + &uuid, togs[0], sbuf, bxid, sig, private, + )) } pub fn read_json_box<R: Read + Seek>( @@ -2192,6 +2203,7 @@ impl BoxReader { // load the description box & create a new superbox from it let jdesc = BoxReader::read_desc_box(reader, jumd_header.size) .map_err(|_| JumbfParseError::UnexpectedEof)?; + if jdesc.label().is_empty() { return Err(JumbfParseError::UnexpectedEof); } @@ -2213,8 +2225,7 @@ impl BoxReader { unread_bytes(reader, HEADER_SIZE)?; // seek back to the beginning of the box let next_box: Box<dyn BMFFBox> = match box_header.name { BoxType::Jumb => Box::new( - BoxReader::read_super_box(reader) - .map_err(|_| JumbfParseError::InvalidJumbBox)?, + BoxReader::read_super_box(reader)?, //.map_err(|_| JumbfParseError::InvalidJumbBox)?, ), BoxType::Json => Box::new( BoxReader::read_json_box(reader, box_header.size)