// Copyright 2024 Adobe. All rights reserved.
// This file is licensed to you under the Apache License,
// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
// or the MIT license (http://opensource.org/licenses/MIT),
// at your option.
// Unless required by applicable law or agreed to in writing,
// this software is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
// specific language governing permissions and limitations under
// each license.
use std::fmt::{Debug, Formatter};
use crate::{
box_type::SUPER_BOX_TYPE,
debug::*,
parser::{DataBox, DescriptionBox, Error},
};
/// A JUMBF superbox contains a description box and zero or more
/// data boxes, each of which may or may not be a superbox.
#[derive(Clone, Eq, PartialEq)]
pub struct SuperBox<'a> {
/// Description box.
pub desc: DescriptionBox<'a>,
/// Child boxes. (These are referred to in some documentation
/// as "data boxes.")
pub child_boxes: Vec<ChildBox<'a>>,
/// Original box data.
///
/// This the original byte slice that was parsed to create this box.
/// It is preserved in case a future client wishes to re-serialize this
/// box as is.
pub original: &'a [u8],
}
impl<'a> SuperBox<'a> {
/// Parse a byte-slice as a JUMBF superbox, and return a tuple of the parsed
/// super box and the remainder of the input. Children of this superbox
/// which are also superboxes will be parsed recursively without limit.
///
/// The returned object uses zero-copy, and so has the same lifetime as the
/// input.
pub fn from_slice(i: &'a [u8]) -> Result<(Self, &'a [u8]), Error> {
Self::from_slice_with_depth_limit(i, usize::MAX)
}
/// Parse a byte-slice as a JUMBF superbox, and return a tuple of the parsed
/// super box and the remainder of the input. Children of this superbox
/// which are also superboxes will be parsed recursively, to a limit of
/// `depth_limit` nested boxes.
///
/// If `depth_limit` is 0, any child superboxes that are found will be
/// returned as plain [`DataBox`] structs instead.
///
/// The returned object uses zero-copy, and so has the same lifetime as the
/// input.
pub fn from_slice_with_depth_limit(
i: &'a [u8],
depth_limit: usize,
) -> Result<(Self, &'a [u8]), Error> {
let (data_box, i) = DataBox::from_slice(i)?;
let (sbox, _) = Self::from_data_box_with_depth_limit(&data_box, depth_limit)?;
Ok((sbox, i))
}
/// Re-parse a [`DataBox`] as a JUMBF superbox. Children of this
/// superbox which are also superboxes will be parsed recursively without
/// limit.
///
/// If the box is of `jumb` type and has the correct structure, returns
/// a tuple of the new [`SuperBox`] object and the remainder of the input
/// from the box (which should typically be empty).
///
/// Will return an error if the box isn't of `jumb` type.
pub fn from_data_box(data_box: &DataBox<'a>) -> Result<(Self, &'a [u8]), Error> {
Self::from_data_box_with_depth_limit(data_box, usize::MAX)
}
/// Re-parse a [`DataBox`] as a JUMBF superbox. Children of this superbox
/// which are also superboxes will be parsed recursively, to a limit of
/// `depth_limit` nested boxes.
///
/// If the box is of `jumb` type and has the correct structure, returns
/// a tuple of the new [`SuperBox`] object and the remainder of the input
/// from the box (which should typically be empty). If `depth_limit` is 0,
/// any child superboxes that are found will be returned as plain
/// [`DataBox`] structs instead.
///
/// Will return an error if the box isn't of `jumb` type.
pub fn from_data_box_with_depth_limit(
data_box: &DataBox<'a>,
depth_limit: usize,
) -> Result<(Self, &'a [u8]), Error> {
if data_box.tbox != SUPER_BOX_TYPE {
return Err(Error::InvalidSuperBoxType(data_box.tbox));
}
let (desc, i) = DescriptionBox::from_slice(data_box.data)?;
let (child_boxes, i) = boxes_from_slice(i)?;
let child_boxes = child_boxes
.into_iter()
.map(|d| {
if d.tbox == SUPER_BOX_TYPE && depth_limit > 0 {
let (sbox, _) = Self::from_data_box_with_depth_limit(&d, depth_limit - 1)?;
Ok(ChildBox::SuperBox(sbox))
} else {
Ok(ChildBox::DataBox(d))
}
})
.collect::<Result<Vec<ChildBox<'a>>, Error>>()?;
Ok((
Self {
desc,
child_boxes,
original: data_box.original,
},
i,
))
}
/// Find a child superbox of this superbox by label and verify that
/// exactly one such child exists.
///
/// If label contains one or more slash (`/`) characters, the label
/// will be treated as a hierarchical label and this function can then
/// be used to traverse nested data structures.
///
/// Will return `None` if no matching child superbox is found _or_ if
/// more than one matching child superbox is found.
pub fn find_by_label(&self, label: &str) -> Option<&Self> {
let (label, suffix) = match label.split_once('/') {
Some((label, suffix)) => (label, Some(suffix)),
None => (label, None),
};
let matching_children: Vec<&SuperBox> = self
.child_boxes
.iter()
.filter_map(|child_box| match child_box {
ChildBox::SuperBox(sbox) => {
if let Some(sbox_label) = sbox.desc.label {
if sbox_label == label && sbox.desc.requestable {
Some(sbox)
} else {
None
}
} else {
None
}
}
_ => None,
})
.collect();
if let Some(sbox) = matching_children.first() {
if matching_children.len() == 1 {
if let Some(suffix) = suffix {
sbox.find_by_label(suffix)
} else {
Some(sbox)
}
} else {
None
}
} else {
None
}
}
/// If the first child box of this superbox is a data box, return it.
/// Otherwise, return `None`.
///
/// This is a convenience function for the common case where the superbox
/// contains a non-superbox payload that needs to be interpreted further.
pub fn data_box(&'a self) -> Option<&'a DataBox<'a>> {
self.child_boxes
.first()
.and_then(|child_box| match child_box {
ChildBox::DataBox(data_box) => Some(data_box),
_ => None,
})
}
}
impl<'a> Debug for SuperBox<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("SuperBox")
.field("desc", &self.desc)
.field("child_boxes", &self.child_boxes)
.field("original", &DebugByteSlice(self.original))
.finish()
}
}
// Parse boxes from slice until slice is empty.
fn boxes_from_slice(i: &[u8]) -> Result<(Vec<DataBox<'_>>, &[u8]), Error> {
let mut result: Vec<DataBox> = vec![];
let mut i = i;
while !i.is_empty() {
let (data_box, x) = DataBox::from_slice(i)?;
i = x;
result.push(data_box);
}
Ok((result, i))
}
/// This type represents a single box within a superbox,
/// which may itself be a superbox or or a regular box.
///
/// Note that this crate doesn't parse the content or ascribe
/// meaning to any type of box other than superbox (`jumb`) or
/// description box (`jumd`).
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ChildBox<'a> {
/// A superbox.
SuperBox(SuperBox<'a>),
/// Any other kind of box.
DataBox(DataBox<'a>),
}
impl<'a> ChildBox<'a> {
/// If this represents a nested super box, return a reference to that
/// superbox.
pub fn as_super_box(&'a self) -> Option<&'a SuperBox<'a>> {
if let Self::SuperBox(sb) = self {
Some(sb)
} else {
None
}
}
/// If this represents a nested data box, return a reference to that data
/// box.
pub fn as_data_box(&'a self) -> Option<&'a DataBox<'a>> {
if let Self::DataBox(db) = self {
Some(db)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::panic)]
#![allow(clippy::unwrap_used)]
use hex_literal::hex;
use pretty_assertions_sorted::assert_eq;
use crate::{
parser::{ChildBox, DataBox, DescriptionBox, Error, SuperBox},
BoxType,
};
#[test]
fn simple_super_box() {
let jumbf = hex!(
"0000002f" // box size
"6a756d62" // box type = 'jumb'
"00000027" // box size
"6a756d64" // box type = 'jumd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e7375706572626f7800" // label
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.superbox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..47],
},
child_boxes: vec!(),
original: &jumbf,
}
);
assert_eq!(format!("{sbox:#?}"), "SuperBox {\n desc: DescriptionBox {\n uuid: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00],\n label: Some(\n \"test.superbox\",\n ),\n requestable: true,\n id: None,\n hash: None,\n private: None,\n original: 39 bytes starting with [00, 00, 00, 27, 6a, 75, 6d, 64, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00],\n },\n child_boxes: [],\n original: 47 bytes starting with [00, 00, 00, 2f, 6a, 75, 6d, 62, 00, 00, 00, 27, 6a, 75, 6d, 64, 00, 00, 00, 00],\n}");
}
#[test]
fn nested_super_boxes() {
let jumbf = hex!(
"00000065" // box size
"6a756d62" // box type = 'jumb'
"0000002f" // box size
"6a756d64" // box type = 'jumd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e7375706572626f785f64617461626f7800" // label
// ------
"0000002e" // box size
"6a756d62" // box type = 'jumb'
"00000026" // box size
"6a756d64" // box type = 'jumbd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e64617461626f7800"
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.superbox_databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..55],
},
child_boxes: vec!(ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[63..101],
},
child_boxes: vec!(),
original: &jumbf[55..101],
})),
original: &jumbf,
}
);
}
#[test]
fn child_superbox_without_label() {
let jumbf = hex!(
"00000058" // box size
"6a756d62" // box type = 'jumb'
"0000002f" // box size
"6a756d64" // box type = 'jumd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e7375706572626f785f64617461626f7800" // label
// ------
"00000021" // box size
"6a756d62" // box type = 'jumb'
"00000019" // box size
"6a756d64" // box type = 'jumbd'
"00000000000000000000000000000000" // UUID
"00" // toggles
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.superbox_databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..55],
},
child_boxes: vec!(ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: None,
requestable: false,
id: None,
hash: None,
private: None,
original: &jumbf[63..88],
},
child_boxes: vec!(),
original: &jumbf[55..88],
})),
original: &jumbf,
}
);
assert!(sbox.find_by_label("not_there").is_none());
let dbox_as_child = sbox.child_boxes.first().unwrap();
assert!(dbox_as_child.as_data_box().is_none());
assert_eq!(
dbox_as_child.as_super_box().unwrap(),
&SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: None,
requestable: false,
id: None,
hash: None,
private: None,
original: &jumbf[63..88],
},
child_boxes: vec!(),
original: &jumbf[55..88],
}
);
}
#[test]
fn data_box_sample() {
let jumbf = hex!(
"00000077" // box size
"6a756d62" // box type = 'jumb'
"00000028" // box size
"6a756d64" // box type = 'jumd'
"6332637300110010800000aa00389b71" // UUID
"03" // toggles
"633270612e7369676e617475726500" // label
// ----
"00000047" // box size
"75756964" // box type = 'uuid'
"6332637300110010800000aa00389b717468697320776f756c64206e6f726d616c6c792062652062696e617279207369676e617475726520646174612e2e2e" // data (type unknown)
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa.signature"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..48],
},
child_boxes: vec!(ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116, 104,
105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97, 108,
108, 121, 32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115, 105, 103,
110, 97, 116, 117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &jumbf[48..119],
})),
original: &jumbf,
}
);
let uuid_box = sbox.data_box().unwrap();
assert_eq!(uuid_box.offset_within_superbox(&sbox).unwrap(), 56);
let dbox_as_child = sbox.child_boxes.first().unwrap();
assert!(dbox_as_child.as_super_box().is_none());
assert_eq!(
dbox_as_child.as_data_box().unwrap(),
&DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116, 104, 105,
115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97, 108, 108, 121,
32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115, 105, 103, 110, 97, 116,
117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &jumbf[48..119],
}
);
}
#[test]
fn complex_example() {
let jumbf = hex!(
"00000267" // box size
"6a756d62" // box type = 'jumb'
"0000001e" // box size
"6a756d64" // box type = 'jumd'
"6332706100110010800000aa00389b71" // UUID
"03" // toggles
"6332706100" // label = "c2pa"
// ---
"00000241" // box size
"6a756d62" // box type = 'jumb'
"00000024" // box size
"6a756d64" // box type = 'jumd'
"63326d6100110010800000aa00389b71" // UUID
"03" // toggles
"63622e61646f62655f3100" // label = "cb.adobe_1"
// ---
"0000008f" // box size
"6a756d62" // box type = 'jumb'
"00000029" // box size
"6a756d64" // box type = 'jumd'
"6332617300110010800000aa00389b71" // UUID
"03" // toggles
"633270612e617373657274696f6e7300" // label = "c2pa.assertions"
// ---
"0000005e" // box size
"6a756d62" // box type = 'jumb'
"0000002d" // box size
"6a756d64" // box type = 'jumd'
"6a736f6e00110010800000aa00389b71" // UUID
"03" // toggles
"633270612e6c6f636174696f6e2e62726f616400"
// label = "c2pa.location.broad"
// ---
"00000029" // box size
"6a736f6e" // box type = 'json'
"7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d" // payload (JSON)
// ---
"0000010f" // box size
"6a756d62" // box type = 'jumb'
"00000024" // box size
"6a756d64" // box type = 'jumd'
"6332636c00110010800000aa00389b71" // UUID
"03" // toggles
"633270612e636c61696d00" // label = "c2pa.claim"
// ---
"000000e3" // box size
"6a736f6e" // box type = 'json'
"7b0a2020202020202020202020202272"
"65636f7264657222203a202250686f74"
"6f73686f70222c0a2020202020202020"
"20202020227369676e61747572652220"
"3a202273656c66236a756d62663d735f"
"61646f62655f31222c0a202020202020"
"20202020202022617373657274696f6e"
"7322203a205b0a202020202020202020"
"202020202020202273656c66236a756d"
"62663d61735f61646f62655f312f6332"
"70612e6c6f636174696f6e2e62726f61"
"643f686c3d3736313432424436323336"
"3346220a202020202020202020202020"
"5d0a20202020202020207d" // payload (JSON)
// ---
"00000077" // box size
"6a756d62" // box type = 'jumb'
"00000028" // box size
"6a756d64" // box type = 'jumd'
"6332637300110010800000aa00389b71" // UUID
"03" // toggles
"633270612e7369676e617475726500" // label = "c2pa.signature"
// ---
"00000047" // box size
"75756964" // box type = 'uuid'
"6332637300110010800000aa00389b71"
"7468697320776f756c64206e6f726d61"
"6c6c792062652062696e617279207369"
"676e617475726520646174612e2e2e"
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 112, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..38],
},
child_boxes: vec!(ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 109, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("cb.adobe_1"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[46..82],
},
child_boxes: vec!(
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 97, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("c2pa.assertions",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[90..131],
},
child_boxes: vec![ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
106, 115, 111, 110, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56,
155, 113,
],
label: Some("c2pa.location.broad",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[139..184],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 32, 34, 108, 111, 99, 97, 116, 105, 111, 110, 34, 58,
32, 34, 77, 97, 114, 103, 97, 116, 101, 32, 67, 105, 116,
121, 44, 32, 78, 74, 34, 125,
],
original: &jumbf[184..225],
},),],
original: &jumbf[131..225],
},),],
original: &jumbf[82..225],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 108, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("c2pa.claim",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[233..269],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34,
114, 101, 99, 111, 114, 100, 101, 114, 34, 32, 58, 32, 34, 80,
104, 111, 116, 111, 115, 104, 111, 112, 34, 44, 10, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 115, 105, 103, 110, 97,
116, 117, 114, 101, 34, 32, 58, 32, 34, 115, 101, 108, 102, 35,
106, 117, 109, 98, 102, 61, 115, 95, 97, 100, 111, 98, 101, 95,
49, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
34, 97, 115, 115, 101, 114, 116, 105, 111, 110, 115, 34, 32,
58, 32, 91, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 34, 115, 101, 108, 102, 35, 106, 117, 109, 98,
102, 61, 97, 115, 95, 97, 100, 111, 98, 101, 95, 49, 47, 99,
50, 112, 97, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 98,
114, 111, 97, 100, 63, 104, 108, 61, 55, 54, 49, 52, 50, 66,
68, 54, 50, 51, 54, 51, 70, 34, 10, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32,
125,
],
original: &jumbf[269..496],
},),],
original: &jumbf[225..496],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
116, 104, 105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111,
114, 109, 97, 108, 108, 121, 32, 98, 101, 32, 98, 105, 110, 97,
114, 121, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 32,
100, 97, 116, 97, 46, 46, 46,
],
original: &jumbf[544..615],
},),],
original: &jumbf[496..615],
},),
),
original: &jumbf[38..615],
})),
original: &jumbf,
}
);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature"),
Some(&SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116, 104,
105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97, 108,
108, 121, 32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115, 105, 103,
110, 97, 116, 117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &jumbf[544..615],
},),],
original: &jumbf[496..615],
})
);
assert_eq!(sbox.find_by_label("cb.adobe_1x/c2pa.signature"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signaturex"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signature/blah"), None);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature")
.and_then(|sig| sig.data_box()),
Some(&DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116, 104, 105,
115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97, 108, 108, 121,
32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115, 105, 103, 110, 97, 116,
117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &jumbf[544..615],
})
);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature")
.and_then(|sig| sig.data_box())
.and_then(|sig| sig.offset_within_superbox(&sbox))
.unwrap(),
552
);
assert_eq!(sbox.data_box(), None);
}
#[test]
fn error_wrong_box_type() {
let jumbf = hex!(
"00000026" // box size
"6a756d63" // box type = 'jumc' (INCORRECT)
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e64657363626f7800" // label
);
assert_eq!(
SuperBox::from_slice(&jumbf).unwrap_err(),
Error::InvalidSuperBoxType(BoxType(*b"jumc"))
);
}
#[test]
fn find_by_label_avoids_confict() {
let jumbf = hex!(
"00000093" // box size
"6a756d62" // box type = 'jumb'
"0000002f" // box size
"6a756d64" // box type = 'jumd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e7375706572626f785f64617461626f7800" // label
// ------
"0000002e" // box size
"6a756d62" // box type = 'jumb'
"00000026" // box size
"6a756d64" // box type = 'jumbd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e64617461626f7800" // label = "test.databox"
// ------
"0000002e" // box size
"6a756d62" // box type = 'jumb'
"00000026" // box size
"6a756d64" // box type = 'jumbd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e64617461626f7800" // label = "test.databox"
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.superbox_databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..55],
},
child_boxes: vec!(
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[63..101],
},
child_boxes: vec!(),
original: &jumbf[55..101],
}),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[109..147],
},
child_boxes: vec!(),
original: &jumbf[101..147],
})
),
original: &jumbf,
}
);
assert_eq!(sbox.find_by_label("test.databox"), None);
}
#[test]
fn find_by_label_skips_non_requestable_boxes() {
let jumbf = hex!(
"00000093" // box size
"6a756d62" // box type = 'jumb'
"0000002f" // box size
"6a756d64" // box type = 'jumd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e7375706572626f785f64617461626f7800" // label
// ------
"0000002e" // box size
"6a756d62" // box type = 'jumb'
"00000026" // box size
"6a756d64" // box type = 'jumbd'
"00000000000000000000000000000000" // UUID
"02" // toggles
"746573742e64617461626f7800" // label = "test.databox"
// ------
"0000002e" // box size
"6a756d62" // box type = 'jumb'
"00000026" // box size
"6a756d64" // box type = 'jumbd'
"00000000000000000000000000000000" // UUID
"03" // toggles
"746573742e64617461626f7a00" // label = "test.databoz"
);
let (sbox, rem) = SuperBox::from_slice(&jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.superbox_databox"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..55],
},
child_boxes: vec!(
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.databox"),
requestable: false,
id: None,
hash: None,
private: None,
original: &jumbf[63..101],
},
child_boxes: vec!(),
original: &jumbf[55..101],
}),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.databoz"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[109..147],
},
child_boxes: vec!(),
original: &jumbf[101..147],
})
),
original: &jumbf,
}
);
assert_eq!(sbox.find_by_label("test.databox"), None);
assert_eq!(
sbox.find_by_label("test.databoz"),
Some(&SuperBox {
desc: DescriptionBox {
uuid: &[0; 16],
label: Some("test.databoz"),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[109..147],
},
child_boxes: vec!(),
original: &jumbf[101..147],
})
);
}
#[test]
fn parse_c2pa_manifest() {
let jumbf = include_bytes!("../tests/fixtures/C.c2pa");
let (sbox, rem) = SuperBox::from_slice(jumbf).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &hex!("63 32 70 61 00 11 00 10 80 00 00 aa 00 38 9b 71"),
label: Some("c2pa",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[8..38],
},
child_boxes: vec![ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!("63 32 6d 61 00 11 00 10 80 00 00 aa 00 38 9b 71"),
label: Some("contentauth:urn:uuid:021b555e-5e02-4074-b444-43d7919d89b9",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[46..129],
},
child_boxes: vec![
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!("63 32 61 73 00 11 00 10 80 00 00 aa 00 38 9b 71"),
label: Some("c2pa.assertions",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[137..178],
},
child_boxes: vec![
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!(
"40 cb 0c 32 bb 8a 48 9d a7 0b 2a d6 f4 7f 43 69"
),
label: Some("c2pa.thumbnail.claim.jpeg",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[186..237],
},
child_boxes: vec![
ChildBox::DataBox(DataBox {
tbox: BoxType(*b"bfdb"),
data: &jumbf[245..257],
original: &jumbf[237..257],
},),
ChildBox::DataBox(DataBox {
tbox: BoxType(*b"bidb"),
data: &jumbf[265..31976],
original: &jumbf[257..31976],
},),
],
original: &jumbf[178..31976],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!(
"6a 73 6f 6e 00 11 00 10 80 00 00 aa 00 38 9b 71"
),
label: Some("stds.schema-org.CreativeWork",),
requestable: true,
id: None,
hash: None,
private: Some(DataBox {
tbox: BoxType(*b"c2sh"),
data: &jumbf[32046..32062],
original: &jumbf[32038..32062],
},),
original: &jumbf[31984..32062],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &jumbf[32070..32179],
original: &jumbf[32062..32179],
},),],
original: &jumbf[31976..32179],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!(
"63 62 6f 72 00 11 00 10 80 00 00 aa 00 38 9b 71"
),
label: Some("c2pa.actions",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[32187..32225],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"cbor"),
data: &jumbf[32233..32311],
original: &jumbf[32225..32311],
},),],
original: &jumbf[32179..32311],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!(
"63 62 6f 72 00 11 00 10 80 00 00 aa 00 38 9b 71"
),
label: Some("c2pa.hash.data",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[32319..32359],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"cbor"),
data: &jumbf[32367..32482],
original: &jumbf[32359..32482],
},),],
original: &jumbf[32311..32482],
},),
],
original: &jumbf[129..32482],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!("63 32 63 6c 00 11 00 10 80 00 00 aa 00 38 9b 71"),
label: Some("c2pa.claim",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[32490..32526],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"cbor"),
data: &jumbf[32534..33166],
original: &jumbf[32526..33166],
},),],
original: &jumbf[32482..33166],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &hex!("63 32 63 73 00 11 00 10 80 00 00 aa 00 38 9b 71"),
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &jumbf[33174..33214],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"cbor"),
data: &jumbf[33222..46948],
original: &jumbf[33214..46948],
},),],
original: &jumbf[33166..46948],
},),
],
original: &jumbf[38..46948],
},),],
original: &jumbf[0..46948],
}
);
}
mod depth_limit {
use hex_literal::hex;
use pretty_assertions_sorted::assert_eq;
use crate::{
parser::{ChildBox, DataBox, DescriptionBox, SuperBox},
BoxType,
};
const JUMBF: [u8; 615] = hex!(
"00000267" // box size
"6a756d62" // box type = 'jumb'
"0000001e" // box size
"6a756d64" // box type = 'jumd'
"6332706100110010800000aa00389b71" // UUID
"03" // toggles
"6332706100" // label = "c2pa"
// ---
"00000241" // box size
"6a756d62" // box type = 'jumb'
"00000024" // box size
"6a756d64" // box type = 'jumd'
"63326d6100110010800000aa00389b71" // UUID
"03" // toggles
"63622e61646f62655f3100" // label = "cb.adobe_1"
// ---
"0000008f" // box size
"6a756d62" // box type = 'jumb'
"00000029" // box size
"6a756d64" // box type = 'jumd'
"6332617300110010800000aa00389b71" // UUID
"03" // toggles
"633270612e617373657274696f6e7300" // label = "c2pa.assertions"
// ---
"0000005e" // box size
"6a756d62" // box type = 'jumb'
"0000002d" // box size
"6a756d64" // box type = 'jumd'
"6a736f6e00110010800000aa00389b71" // UUID
"03" // toggles
"633270612e6c6f636174696f6e2e62726f616400"
// label = "c2pa.location.broad"
// ---
"00000029" // box size
"6a736f6e" // box type = 'json'
"7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d" // payload (JSON)
// ---
"0000010f" // box size
"6a756d62" // box type = 'jumb'
"00000024" // box size
"6a756d64" // box type = 'jumd'
"6332636c00110010800000aa00389b71" // UUID
"03" // toggles
"633270612e636c61696d00" // label = "c2pa.claim"
// ---
"000000e3" // box size
"6a736f6e" // box type = 'json'
"7b0a2020202020202020202020202272"
"65636f7264657222203a202250686f74"
"6f73686f70222c0a2020202020202020"
"20202020227369676e61747572652220"
"3a202273656c66236a756d62663d735f"
"61646f62655f31222c0a202020202020"
"20202020202022617373657274696f6e"
"7322203a205b0a202020202020202020"
"202020202020202273656c66236a756d"
"62663d61735f61646f62655f312f6332"
"70612e6c6f636174696f6e2e62726f61"
"643f686c3d3736313432424436323336"
"3346220a202020202020202020202020"
"5d0a20202020202020207d" // payload (JSON)
// ---
"00000077" // box size
"6a756d62" // box type = 'jumb'
"00000028" // box size
"6a756d64" // box type = 'jumd'
"6332637300110010800000aa00389b71" // UUID
"03" // toggles
"633270612e7369676e617475726500" // label = "c2pa.signature"
// ---
"00000047" // box size
"75756964" // box type = 'uuid'
"6332637300110010800000aa00389b71"
"7468697320776f756c64206e6f726d61"
"6c6c792062652062696e617279207369"
"676e617475726520646174612e2e2e"
);
#[test]
fn depth_limit_0() {
let (sbox, rem) = SuperBox::from_slice_with_depth_limit(&JUMBF, 0).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 112, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[8..38],
},
child_boxes: vec!(ChildBox::DataBox(DataBox {
tbox: BoxType(*b"jumb"),
original: &JUMBF[38..615],
data: &JUMBF[46..615],
})),
original: &JUMBF,
}
);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signature"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1x/c2pa.signature"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signaturex"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signature/blah"), None);
let data_box = sbox.data_box().unwrap();
assert_eq!(
data_box,
&DataBox {
tbox: BoxType(*b"jumb"),
original: &JUMBF[38..615],
data: &JUMBF[46..615],
}
);
let (nested_box, _) = SuperBox::from_data_box(data_box).unwrap();
assert_eq!(
nested_box,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 109, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("cb.adobe_1"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[46..82],
},
child_boxes: vec!(
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 97, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("c2pa.assertions",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[90..131],
},
child_boxes: vec![ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
106, 115, 111, 110, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56,
155, 113,
],
label: Some("c2pa.location.broad",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[139..184],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 32, 34, 108, 111, 99, 97, 116, 105, 111, 110, 34, 58,
32, 34, 77, 97, 114, 103, 97, 116, 101, 32, 67, 105, 116,
121, 44, 32, 78, 74, 34, 125,
],
original: &JUMBF[184..225],
},),],
original: &JUMBF[131..225],
},),],
original: &JUMBF[82..225],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 108, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("c2pa.claim",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[233..269],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34,
114, 101, 99, 111, 114, 100, 101, 114, 34, 32, 58, 32, 34, 80,
104, 111, 116, 111, 115, 104, 111, 112, 34, 44, 10, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 115, 105, 103, 110, 97,
116, 117, 114, 101, 34, 32, 58, 32, 34, 115, 101, 108, 102, 35,
106, 117, 109, 98, 102, 61, 115, 95, 97, 100, 111, 98, 101, 95,
49, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
34, 97, 115, 115, 101, 114, 116, 105, 111, 110, 115, 34, 32,
58, 32, 91, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 34, 115, 101, 108, 102, 35, 106, 117, 109, 98,
102, 61, 97, 115, 95, 97, 100, 111, 98, 101, 95, 49, 47, 99,
50, 112, 97, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 98,
114, 111, 97, 100, 63, 104, 108, 61, 55, 54, 49, 52, 50, 66,
68, 54, 50, 51, 54, 51, 70, 34, 10, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32,
125,
],
original: &JUMBF[269..496],
},),],
original: &JUMBF[225..496],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
116, 104, 105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111,
114, 109, 97, 108, 108, 121, 32, 98, 101, 32, 98, 105, 110, 97,
114, 121, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 32,
100, 97, 116, 97, 46, 46, 46,
],
original: &JUMBF[544..615],
},),],
original: &JUMBF[496..615],
},),
),
original: &JUMBF[38..615],
}
);
}
#[test]
fn depth_limit_1() {
let (sbox, rem) = SuperBox::from_slice_with_depth_limit(&JUMBF, 1).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 112, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[8..38],
},
child_boxes: vec!(ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 109, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("cb.adobe_1"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[46..82],
},
child_boxes: vec!(
ChildBox::DataBox(DataBox {
tbox: BoxType(*b"jumb"),
original: &JUMBF[82..225],
data: &JUMBF[90..225],
}),
ChildBox::DataBox(DataBox {
tbox: BoxType(*b"jumb"),
original: &JUMBF[225..496],
data: &JUMBF[233..496],
}),
ChildBox::DataBox(DataBox {
tbox: BoxType(*b"jumb"),
original: &JUMBF[496..615],
data: &JUMBF[504..615],
}),
),
original: &JUMBF[38..615],
})),
original: &JUMBF,
}
);
assert!(sbox.find_by_label("cb.adobe_1").is_some());
assert!(sbox.find_by_label("cb.adobe_1/c2pa.signature").is_none());
assert!(sbox.data_box().is_none());
}
#[test]
fn depth_limit_2() {
let (sbox, rem) = SuperBox::from_slice_with_depth_limit(&JUMBF, 2).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 112, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[8..38],
},
child_boxes: vec!(ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 109, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("cb.adobe_1"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[46..82],
},
child_boxes: vec!(
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 97, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113,
],
label: Some("c2pa.assertions",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[90..131],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"jumb"),
data: &JUMBF[139..225],
original: &JUMBF[131..225],
},),],
original: &JUMBF[82..225],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 108, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113,
],
label: Some("c2pa.claim",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[233..269],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
34, 114, 101, 99, 111, 114, 100, 101, 114, 34, 32, 58, 32,
34, 80, 104, 111, 116, 111, 115, 104, 111, 112, 34, 44, 10,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 115,
105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 58, 32, 34,
115, 101, 108, 102, 35, 106, 117, 109, 98, 102, 61, 115,
95, 97, 100, 111, 98, 101, 95, 49, 34, 44, 10, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 97, 115, 115, 101,
114, 116, 105, 111, 110, 115, 34, 32, 58, 32, 91, 10, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
34, 115, 101, 108, 102, 35, 106, 117, 109, 98, 102, 61, 97,
115, 95, 97, 100, 111, 98, 101, 95, 49, 47, 99, 50, 112,
97, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 98, 114,
111, 97, 100, 63, 104, 108, 61, 55, 54, 49, 52, 50, 66, 68,
54, 50, 51, 54, 51, 70, 34, 10, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32,
125,
],
original: &JUMBF[269..496],
},),],
original: &JUMBF[225..496],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113,
],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113, 116, 104, 105, 115, 32, 119, 111, 117, 108, 100, 32,
110, 111, 114, 109, 97, 108, 108, 121, 32, 98, 101, 32, 98,
105, 110, 97, 114, 121, 32, 115, 105, 103, 110, 97, 116,
117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &JUMBF[544..615],
},),],
original: &JUMBF[496..615],
},),
),
original: &JUMBF[38..615],
})),
original: &JUMBF,
}
);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature"),
Some(&SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116,
104, 105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97,
108, 108, 121, 32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115,
105, 103, 110, 97, 116, 117, 114, 101, 32, 100, 97, 116, 97, 46, 46,
46,
],
original: &JUMBF[544..615],
},),],
original: &JUMBF[496..615],
})
);
assert_eq!(sbox.find_by_label("cb.adobe_1x/c2pa.signature"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signaturex"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signature/blah"), None);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature")
.and_then(|sig| sig.data_box()),
Some(&DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116, 104,
105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97, 108,
108, 121, 32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115, 105, 103,
110, 97, 116, 117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &JUMBF[544..615],
})
);
assert_eq!(sbox.data_box(), None);
}
#[test]
fn depth_limit_3() {
let (sbox, rem) = SuperBox::from_slice_with_depth_limit(&JUMBF, 3).unwrap();
assert!(rem.is_empty());
assert_eq!(
sbox,
SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 112, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[8..38],
},
child_boxes: vec!(ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 109, 97, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,
],
label: Some("cb.adobe_1"),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[46..82],
},
child_boxes: vec!(
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 97, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113,
],
label: Some("c2pa.assertions",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[90..131],
},
child_boxes: vec![ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
106, 115, 111, 110, 0, 17, 0, 16, 128, 0, 0, 170, 0,
56, 155, 113,
],
label: Some("c2pa.location.broad",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[139..184],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 32, 34, 108, 111, 99, 97, 116, 105, 111, 110, 34,
58, 32, 34, 77, 97, 114, 103, 97, 116, 101, 32, 67,
105, 116, 121, 44, 32, 78, 74, 34, 125,
],
original: &JUMBF[184..225],
},),],
original: &JUMBF[131..225],
},),],
original: &JUMBF[82..225],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 108, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113,
],
label: Some("c2pa.claim",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[233..269],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"json"),
data: &[
123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
34, 114, 101, 99, 111, 114, 100, 101, 114, 34, 32, 58, 32,
34, 80, 104, 111, 116, 111, 115, 104, 111, 112, 34, 44, 10,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 115,
105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 58, 32, 34,
115, 101, 108, 102, 35, 106, 117, 109, 98, 102, 61, 115,
95, 97, 100, 111, 98, 101, 95, 49, 34, 44, 10, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 97, 115, 115, 101,
114, 116, 105, 111, 110, 115, 34, 32, 58, 32, 91, 10, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
34, 115, 101, 108, 102, 35, 106, 117, 109, 98, 102, 61, 97,
115, 95, 97, 100, 111, 98, 101, 95, 49, 47, 99, 50, 112,
97, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 98, 114,
111, 97, 100, 63, 104, 108, 61, 55, 54, 49, 52, 50, 66, 68,
54, 50, 51, 54, 51, 70, 34, 10, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32,
125,
],
original: &JUMBF[269..496],
},),],
original: &JUMBF[225..496],
},),
ChildBox::SuperBox(SuperBox {
desc: DescriptionBox {
uuid: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113,
],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155,
113, 116, 104, 105, 115, 32, 119, 111, 117, 108, 100, 32,
110, 111, 114, 109, 97, 108, 108, 121, 32, 98, 101, 32, 98,
105, 110, 97, 114, 121, 32, 115, 105, 103, 110, 97, 116,
117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &JUMBF[544..615],
},),],
original: &JUMBF[496..615],
},),
),
original: &JUMBF[38..615],
})),
original: &JUMBF,
}
);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature"),
Some(&SuperBox {
desc: DescriptionBox {
uuid: &[99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113,],
label: Some("c2pa.signature",),
requestable: true,
id: None,
hash: None,
private: None,
original: &JUMBF[504..544],
},
child_boxes: vec![ChildBox::DataBox(DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116,
104, 105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97,
108, 108, 121, 32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115,
105, 103, 110, 97, 116, 117, 114, 101, 32, 100, 97, 116, 97, 46, 46,
46,
],
original: &JUMBF[544..615],
},),],
original: &JUMBF[496..615],
})
);
assert_eq!(sbox.find_by_label("cb.adobe_1x/c2pa.signature"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signaturex"), None);
assert_eq!(sbox.find_by_label("cb.adobe_1/c2pa.signature/blah"), None);
assert_eq!(
sbox.find_by_label("cb.adobe_1/c2pa.signature")
.and_then(|sig| sig.data_box()),
Some(&DataBox {
tbox: BoxType(*b"uuid"),
data: &[
99, 50, 99, 115, 0, 17, 0, 16, 128, 0, 0, 170, 0, 56, 155, 113, 116, 104,
105, 115, 32, 119, 111, 117, 108, 100, 32, 110, 111, 114, 109, 97, 108,
108, 121, 32, 98, 101, 32, 98, 105, 110, 97, 114, 121, 32, 115, 105, 103,
110, 97, 116, 117, 114, 101, 32, 100, 97, 116, 97, 46, 46, 46,
],
original: &JUMBF[544..615],
})
);
assert_eq!(sbox.data_box(), None);
}
}
}