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

boxes.rs (91876B)


      1 // Copyright 2022 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 
      7 // Unless required by applicable law or agreed to in writing,
      8 // this software is distributed on an "AS IS" BASIS, WITHOUT
      9 // WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
     10 // implied. See the LICENSE-MIT and LICENSE-APACHE files for the
     11 // specific language governing permissions and limitations under
     12 // each license.
     13 
     14 //! This is a library for generating ISO BMFF/JUMBF boxes
     15 //!
     16 //! It is based on the work of Takeru Ohta <phjgt308@gmail.com>
     17 //! and [mse_fmp4](https://github.com/sile/mse_fmp4) and enhanced
     18 //! by Leonard Rosenthol <lrosenth@adobe.com>
     19 //
     20 //!  # References
     21 //!
     22 //!  - [ISO BMFF Byte Stream Format](https://w3c.github.io/media-source/isobmff-byte-stream-format.html)
     23 //!  - [JPEG universal metadata box format](https://www.iso.org/standard/73604.html)
     24 
     25 use std::{
     26     any::Any,
     27     ffi::CString,
     28     fmt,
     29     io::{Read, Result as IoResult, Seek, SeekFrom, Write},
     30 };
     31 
     32 use byteorder::{BigEndian, ReadBytesExt};
     33 use hex::FromHex;
     34 use thiserror::Error;
     35 use tracing::debug;
     36 
     37 use crate::jumbf::{boxio, labels};
     38 
     39 /// `JumbfParseError` enumerates errors detected while parsing JUMBF data structures.
     40 #[derive(Debug, Error)]
     41 pub enum JumbfParseError {
     42     // TODO before merging PR: Add doc comments for these.
     43     // Is there more to say than the description string?
     44     #[error("unexpected end of file")]
     45     UnexpectedEof,
     46 
     47     #[error("invalid box start")]
     48     InvalidBoxStart,
     49 
     50     #[error("invalid box header")]
     51     InvalidBoxHeader,
     52 
     53     #[error("invalid box range")]
     54     InvalidBoxRange,
     55 
     56     #[error("invalid JUMBF header")]
     57     InvalidJumbfHeader,
     58 
     59     #[error("invalid JUMB box")]
     60     InvalidJumbBox,
     61 
     62     #[error("invalid UUID label")]
     63     InvalidUuidValue,
     64 
     65     #[error("invalid JSON box")]
     66     InvalidJsonBox,
     67 
     68     #[error("invalid CBOR box")]
     69     InvalidCborBox,
     70 
     71     #[error("invalid JP2C box")]
     72     InvalidJp2cBox,
     73 
     74     #[error("invalid UUID box")]
     75     InvalidUuidBox,
     76 
     77     #[error("invalid embedded file box")]
     78     InvalidEmbeddedFileBox,
     79 
     80     #[error("invalid box of unknown type")]
     81     InvalidUnknownBox,
     82 
     83     #[error("expected JUMD")]
     84     ExpectedJumdError,
     85 
     86     #[error(transparent)]
     87     IoError(#[from] std::io::Error),
     88 
     89     #[error("assertion salt must be 16 bytes or greater")]
     90     InvalidSalt,
     91 
     92     #[error("invalid JUMD box")]
     93     InvalidDescriptionBox,
     94 }
     95 
     96 /// A specialized `JumbfParseResult` type for JUMBF parsing operations.
     97 pub type JumbfParseResult<T> = std::result::Result<T, JumbfParseError>;
     98 
     99 //-----------------
    100 // ANCHOR ISO BMFF
    101 //-----------------
    102 macro_rules! write_u8 {
    103     ($w:expr, $n:expr) => {{
    104         use byteorder::WriteBytesExt;
    105         $w.write_u8($n)?
    106     }};
    107 }
    108 // macro_rules! write_u16 {
    109 //     ($w:expr, $n:expr) => {{
    110 //         use byteorder::{BigEndian, WriteBytesExt};
    111 //         $w.write_u16::<BigEndian>($n)?;
    112 //     }};
    113 // }
    114 // macro_rules! write_i16 {
    115 //     ($w:expr, $n:expr) => {{
    116 //         use byteorder::{BigEndian, WriteBytesExt};
    117 //         $w.write_i16::<BigEndian>($n)?;
    118 //     }};
    119 // }
    120 // macro_rules! write_u24 {
    121 //     ($w:expr, $n:expr) => {{
    122 //         use byteorder::{BigEndian, WriteBytesExt};
    123 //         $w.write_uint::<BigEndian>($n as u64, 3)?;
    124 //     }};
    125 // }
    126 macro_rules! write_u32 {
    127     ($w:expr, $n:expr) => {{
    128         use byteorder::{BigEndian, WriteBytesExt};
    129         $w.write_u32::<BigEndian>($n)?;
    130     }};
    131 }
    132 // macro_rules! write_i32 {
    133 //     ($w:expr, $n:expr) => {{
    134 //         use byteorder::{BigEndian, WriteBytesExt};
    135 //         $w.write_i32::<BigEndian>($n)?;
    136 //     }};
    137 // }
    138 // macro_rules! write_u64 {
    139 //     ($w:expr, $n:expr) => {{
    140 //         use byteorder::{BigEndian, WriteBytesExt};
    141 //         $w.write_u64::<BigEndian>($n)?;
    142 //     }};
    143 // }
    144 macro_rules! write_all {
    145     ($w:expr, $n:expr) => {
    146         $w.write_all($n)?;
    147     };
    148 }
    149 // macro_rules! write_zeroes {
    150 //     ($w:expr, $n:expr) => {
    151 //         $w.write_all(&[0; $n][..])?;
    152 //     };
    153 // }
    154 // macro_rules! write_box {
    155 //     ($w:expr, $b:expr) => {
    156 //         $b.write_box(&mut $w)?;
    157 //     };
    158 // }
    159 // macro_rules! write_boxes {
    160 //     ($w:expr, $bs:expr) => {
    161 //         for b in $bs {
    162 //             b.write_box(&mut $w)?;
    163 //         }
    164 //     };
    165 // }
    166 macro_rules! box_size {
    167     ($b:expr) => {
    168         $b.box_size()?
    169     };
    170 }
    171 // macro_rules! optional_box_size {
    172 //     ($b:expr) => {
    173 //         if let Some(ref b) = $b.as_ref() {
    174 //             b.box_size()?
    175 //         } else {
    176 //             0
    177 //         }
    178 //     };
    179 // }
    180 macro_rules! boxes_size {
    181     ($b:expr) => {{
    182         let mut size = 0;
    183         for b in $b.iter() {
    184             size += box_size!(b);
    185         }
    186         size
    187     }};
    188 }
    189 
    190 /// ISO BMFF box.
    191 pub trait BMFFBox: Any {
    192     // "Any is the closest thing to reflection there is in Rust"
    193     /// Box type code.
    194     fn box_type(&self) -> &'static [u8; 4];
    195 
    196     /// Box UUID (used by JUMBF)
    197     fn box_uuid(&self) -> &'static str;
    198 
    199     /// Box size.
    200     fn box_size(&self) -> IoResult<u32> {
    201         // if it a real box...
    202         let mut size = if self.box_type() != b"    " { 8 } else { 0 };
    203         size += self.box_payload_size()?;
    204 
    205         Ok(size)
    206     }
    207 
    208     /// Payload size of the box.
    209     fn box_payload_size(&self) -> IoResult<u32>;
    210 
    211     /// Writes the box to the given writer.
    212     fn write_box(&self, writer: &mut dyn Write) -> IoResult<()> {
    213         if self.box_type() != b"    " {
    214             // it's a real box...
    215             write_u32!(writer, self.box_size()?);
    216             write_all!(writer, self.box_type());
    217         }
    218 
    219         self.write_box_payload(writer)?;
    220         Ok(())
    221     }
    222 
    223     /// Writes the payload of the box to the given writer.
    224     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()>;
    225 
    226     // Necessary method to enable conversion between types...
    227     fn as_any(&self) -> &dyn Any;
    228 }
    229 
    230 impl fmt::Debug for dyn BMFFBox {
    231     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    232         f.debug_struct("BMFFBox")
    233             .field("type", self.box_type())
    234             .field("size", &self.box_size())
    235             .finish()
    236     }
    237 }
    238 
    239 //---------------
    240 // SECTION JUMBF
    241 //---------------
    242 pub const JUMB_FOURCC: &str = "6A756D62";
    243 pub const JUMD_FOURCC: &str = "6A756D64";
    244 
    245 // ANCHOR JUMBF superbox
    246 /// JUMBF superbox (ISO 19566-5:2019, Annex A)
    247 #[derive(Debug)]
    248 pub struct JUMBFSuperBox {
    249     desc_box: JUMBFDescriptionBox,
    250     data_boxes: Vec<Box<dyn BMFFBox>>,
    251 }
    252 
    253 impl JUMBFSuperBox {
    254     pub fn new(box_label: &str, a_type: Option<&str>) -> Self {
    255         JUMBFSuperBox {
    256             desc_box: JUMBFDescriptionBox::new(box_label, a_type),
    257             data_boxes: vec![],
    258         }
    259     }
    260 
    261     pub fn from(a_box: JUMBFDescriptionBox) -> Self {
    262         JUMBFSuperBox {
    263             desc_box: a_box,
    264             data_boxes: vec![],
    265         }
    266     }
    267 
    268     // add a data box *WITHOUT* taking ownership of the box
    269     pub fn add_data_box(&mut self, b: Box<dyn BMFFBox>) {
    270         self.data_boxes.push(b)
    271     }
    272 
    273     // getters
    274     pub const fn desc_box(&self) -> &JUMBFDescriptionBox {
    275         &self.desc_box
    276     }
    277 
    278     pub fn data_box_count(&self) -> usize {
    279         self.data_boxes.len()
    280     }
    281 
    282     pub fn data_box(&self, index: usize) -> &dyn BMFFBox {
    283         self.data_boxes[index].as_ref()
    284     }
    285 
    286     pub fn data_box_as_superbox(&self, index: usize) -> Option<&JUMBFSuperBox> {
    287         let da_box = &self.data_boxes[index];
    288         da_box.as_ref().as_any().downcast_ref::<JUMBFSuperBox>()
    289     }
    290 
    291     pub fn data_box_as_json_box(&self, index: usize) -> Option<&JUMBFJSONContentBox> {
    292         let da_box = &self.data_boxes[index];
    293         da_box
    294             .as_ref()
    295             .as_any()
    296             .downcast_ref::<JUMBFJSONContentBox>()
    297     }
    298 
    299     pub fn data_box_as_cbor_box(&self, index: usize) -> Option<&JUMBFCBORContentBox> {
    300         let da_box = &self.data_boxes[index];
    301         da_box
    302             .as_ref()
    303             .as_any()
    304             .downcast_ref::<JUMBFCBORContentBox>()
    305     }
    306 
    307     pub fn data_box_as_jp2c_box(&self, index: usize) -> Option<&JUMBFCodestreamContentBox> {
    308         let da_box = &self.data_boxes[index];
    309         da_box
    310             .as_ref()
    311             .as_any()
    312             .downcast_ref::<JUMBFCodestreamContentBox>()
    313     }
    314 
    315     pub fn data_box_as_uuid_box(&self, index: usize) -> Option<&JUMBFUUIDContentBox> {
    316         let da_box = &self.data_boxes[index];
    317         da_box
    318             .as_ref()
    319             .as_any()
    320             .downcast_ref::<JUMBFUUIDContentBox>()
    321     }
    322 
    323     pub fn data_box_as_embedded_file_content_box(
    324         &self,
    325         index: usize,
    326     ) -> Option<&JUMBFEmbeddedFileContentBox> {
    327         let da_box = &self.data_boxes[index];
    328         da_box
    329             .as_ref()
    330             .as_any()
    331             .downcast_ref::<JUMBFEmbeddedFileContentBox>()
    332     }
    333 
    334     pub fn data_box_as_embedded_media_type_box(
    335         &self,
    336         index: usize,
    337     ) -> Option<&JUMBFEmbeddedFileDescriptionBox> {
    338         let da_box = &self.data_boxes[index];
    339         da_box
    340             .as_ref()
    341             .as_any()
    342             .downcast_ref::<JUMBFEmbeddedFileDescriptionBox>()
    343     }
    344 }
    345 
    346 impl BMFFBox for JUMBFSuperBox {
    347     fn box_type(&self) -> &'static [u8; 4] {
    348         b"jumb"
    349     }
    350 
    351     fn box_uuid(&self) -> &'static str {
    352         JUMB_FOURCC
    353     }
    354 
    355     fn box_payload_size(&self) -> IoResult<u32> {
    356         let mut size = 0;
    357         size += box_size!(self.desc_box);
    358         if !self.data_boxes.is_empty() {
    359             size += boxes_size!(self.data_boxes)
    360         }
    361         Ok(size)
    362     }
    363 
    364     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    365         let res = self.desc_box.write_box(writer);
    366         for b in &self.data_boxes {
    367             b.write_box(writer)?;
    368         }
    369         res
    370     }
    371 
    372     // Necessary method to enable conversion between types...
    373     fn as_any(&self) -> &dyn Any {
    374         self
    375     }
    376 }
    377 
    378 // ANCHOR JUMBF Description box
    379 /// JUMBF Description box (ISO 19566-5:2019, Annex A)
    380 #[derive(Debug)]
    381 pub struct JUMBFDescriptionBox {
    382     box_uuid: [u8; 16],                 // a 128-bit UUID for the type
    383     toggles: u8,                        // bit field for valid values
    384     label: CString,                     // Null terminated UTF-8 string (OPTIONAL)
    385     box_id: Option<u32>,                // user assigned value (OPTIONAL)
    386     signature: Option<[u8; 32]>,        // SHA-256 hash of the payload (OPTIONAL)
    387     private: Option<CAISaltContentBox>, // private salt content box
    388 }
    389 
    390 impl JUMBFDescriptionBox {
    391     /// Makes a new `JUMBFDescriptionBox` instance.
    392     pub fn new(box_label: &str, a_type: Option<&str>) -> Self {
    393         JUMBFDescriptionBox {
    394             box_uuid: match a_type {
    395                 Some(ref t) => <[u8; 16]>::from_hex(t).unwrap_or([0u8; 16]),
    396                 None => [0u8; 16], // init to all zeros
    397             },
    398             toggles: 3, // 0x11 (Requestable + Label Present)
    399             label: CString::new(box_label).unwrap_or_default(),
    400             box_id: None,
    401             signature: None,
    402             private: None,
    403         }
    404     }
    405 
    406     pub fn set_salt(&mut self, salt: Vec<u8>) -> JumbfParseResult<()> {
    407         if salt.len() < 16 {
    408             return Err(JumbfParseError::InvalidSalt);
    409         }
    410 
    411         self.private = Some(CAISaltContentBox::new(salt));
    412         self.toggles = 19; // 0x10011 (Requestable + Label Present + Private)
    413 
    414         Ok(())
    415     }
    416 
    417     pub fn get_salt(&self) -> Option<Vec<u8>> {
    418         self.private.as_ref().map(|saltbox| saltbox.salt.clone())
    419     }
    420 
    421     /// Makes a new `JUMBFDescriptionBox` instance from read in data
    422     pub fn from(
    423         uuid: &[u8; 16],
    424         togs: u8,
    425         box_label: Vec<u8>,
    426         bxid: Option<u32>,
    427         sig: Option<[u8; 32]>,
    428         private: Option<CAISaltContentBox>,
    429     ) -> Self {
    430         let c_string: CString;
    431         unsafe {
    432             c_string = CString::from_vec_unchecked(box_label);
    433         }
    434         JUMBFDescriptionBox {
    435             box_uuid: *uuid,
    436             toggles: togs, // will always be 0x11 (Requestable + Label Present)
    437             label: c_string,
    438             box_id: bxid,
    439             signature: sig,
    440             private,
    441         }
    442     }
    443 
    444     /// getters
    445     pub fn uuid(&self) -> String {
    446         hex::encode(self.box_uuid).to_uppercase()
    447     }
    448 
    449     pub fn label(&self) -> String {
    450         self.label.clone().into_string().unwrap_or_default()
    451     }
    452 }
    453 
    454 impl BMFFBox for JUMBFDescriptionBox {
    455     fn box_type(&self) -> &'static [u8; 4] {
    456         b"jumd"
    457     }
    458 
    459     fn box_uuid(&self) -> &'static str {
    460         JUMD_FOURCC
    461     }
    462 
    463     fn box_payload_size(&self) -> IoResult<u32> {
    464         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
    465         Ok(size as u32)
    466     }
    467 
    468     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    469         write_all!(writer, &self.box_uuid);
    470         write_u8!(writer, self.toggles);
    471 
    472         if self.label.to_str().unwrap_or_default().chars().count() > 0 {
    473             write_all!(writer, self.label.as_bytes_with_nul());
    474         }
    475 
    476         if let Some(x) = self.box_id {
    477             write_u32!(writer, x);
    478         }
    479 
    480         if let Some(x) = self.signature {
    481             write_all!(writer, &x);
    482         }
    483 
    484         if let Some(salt) = &self.private {
    485             salt.write_box(writer)?;
    486         }
    487 
    488         Ok(())
    489     }
    490 
    491     // Necessary method to enable conversion between types...
    492     fn as_any(&self) -> &dyn Any {
    493         self
    494     }
    495 }
    496 
    497 // ANCHOR JUMBF UUIDs
    498 pub const JUMBF_CODESTREAM_UUID: &str = "6579D6FBDBA2446BB2AC1B82FEEB89D1";
    499 pub const JUMBF_JSON_UUID: &str = "6A736F6E00110010800000AA00389B71";
    500 pub const JUMBF_CBOR_UUID: &str = "63626F7200110010800000AA00389B71";
    501 // pub const JUMBF_XML_UUID: &str = "786D6C2000110010800000AA00389B71";
    502 pub const JUMBF_UUID_UUID: &str = "7575696400110010800000AA00389B71";
    503 pub const JUMBF_EMBEDDED_FILE_UUID: &str = "40CB0C32BB8A489DA70B2AD6F47F4369";
    504 // ANCHOR JUMBF Content box
    505 /// JUMBF Content box (ISO 19566-5:2019, Annex B)
    506 #[derive(Debug, Default)]
    507 pub struct JUMBFContentBox;
    508 
    509 impl BMFFBox for JUMBFContentBox {
    510     fn box_type(&self) -> &'static [u8; 4] {
    511         b"jumd"
    512     }
    513 
    514     fn box_uuid(&self) -> &'static str {
    515         "" // base JUMBF boxes don't have any...
    516     }
    517 
    518     fn box_payload_size(&self) -> IoResult<u32> {
    519         Ok(0) // it isn't a real box, just a base class
    520     }
    521 
    522     fn write_box_payload(&self, _writer: &mut dyn Write) -> IoResult<()> {
    523         Ok(())
    524     }
    525 
    526     // Necessary method to enable conversion between types...
    527     fn as_any(&self) -> &dyn Any {
    528         self
    529     }
    530 }
    531 
    532 // ANCHOR JUMB Padding Box
    533 #[derive(Debug, Default)]
    534 pub struct JUMBFPaddingContentBox {
    535     padding: Vec<u8>, // arbitrary number of zero'd bytes...
    536 }
    537 
    538 impl BMFFBox for JUMBFPaddingContentBox {
    539     fn box_type(&self) -> &'static [u8; 4] {
    540         b"free"
    541     }
    542 
    543     fn box_uuid(&self) -> &'static str {
    544         "" // base JUMBF boxes don't have any...
    545     }
    546 
    547     fn box_payload_size(&self) -> IoResult<u32> {
    548         let size = self.padding.len();
    549         Ok(size as u32)
    550     }
    551 
    552     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    553         if !self.padding.is_empty() {
    554             write_all!(writer, &self.padding);
    555         }
    556         Ok(())
    557     }
    558 
    559     // Necessary method to enable conversion between types...
    560     fn as_any(&self) -> &dyn Any {
    561         self
    562     }
    563 }
    564 
    565 impl JUMBFPaddingContentBox {
    566     pub fn new_with_vec(padding: Vec<u8>) -> Self {
    567         JUMBFPaddingContentBox { padding }
    568     }
    569 
    570     // we do not take a vec to ensure the box contains only zeros
    571     pub fn new(box_size: usize) -> Self {
    572         JUMBFPaddingContentBox {
    573             padding: vec![0; box_size],
    574         }
    575     }
    576 }
    577 
    578 // ANCHOR JUMBF JSON Content box
    579 /// JUMBF JSON Content box (ISO 19566-5:2019, Annex B.4)
    580 #[derive(Debug, Default)]
    581 pub struct JUMBFJSONContentBox {
    582     json: Vec<u8>, // arbitrary bunch of bytes...
    583 }
    584 
    585 impl BMFFBox for JUMBFJSONContentBox {
    586     fn box_type(&self) -> &'static [u8; 4] {
    587         b"json"
    588     }
    589 
    590     fn box_uuid(&self) -> &'static str {
    591         JUMBF_JSON_UUID
    592     }
    593 
    594     fn box_payload_size(&self) -> IoResult<u32> {
    595         let size = self.json.len();
    596         Ok(size as u32)
    597     }
    598 
    599     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    600         if !self.json.is_empty() {
    601             write_all!(writer, &self.json);
    602         }
    603         Ok(())
    604     }
    605 
    606     // Necessary method to enable conversion between types...
    607     fn as_any(&self) -> &dyn Any {
    608         self
    609     }
    610 }
    611 
    612 impl JUMBFJSONContentBox {
    613     // the content box takes ownership of the data!
    614     pub fn new(json_in: Vec<u8>) -> Self {
    615         JUMBFJSONContentBox { json: json_in }
    616     }
    617 
    618     // getter
    619     pub const fn json(&self) -> &Vec<u8> {
    620         &self.json
    621     }
    622 }
    623 
    624 pub struct JUMBFCBORContentBox {
    625     cbor: Vec<u8>, // arbitrary bunch of bytes...
    626 }
    627 
    628 impl BMFFBox for JUMBFCBORContentBox {
    629     fn box_type(&self) -> &'static [u8; 4] {
    630         b"cbor"
    631     }
    632 
    633     fn box_uuid(&self) -> &'static str {
    634         JUMBF_CBOR_UUID
    635     }
    636 
    637     fn box_payload_size(&self) -> IoResult<u32> {
    638         let size = self.cbor.len();
    639         Ok(size as u32)
    640     }
    641 
    642     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    643         if !self.cbor.is_empty() {
    644             write_all!(writer, &self.cbor);
    645         }
    646         Ok(())
    647     }
    648 
    649     // Necessary method to enable conversion between types...
    650     fn as_any(&self) -> &dyn Any {
    651         self
    652     }
    653 }
    654 
    655 impl JUMBFCBORContentBox {
    656     // the content box takes ownership of the data!
    657     pub fn new(cbor_in: Vec<u8>) -> Self {
    658         JUMBFCBORContentBox { cbor: cbor_in }
    659     }
    660 
    661     // getter
    662     pub const fn cbor(&self) -> &Vec<u8> {
    663         &self.cbor
    664     }
    665 }
    666 
    667 // ANCHOR JUMBF Codestream Content box
    668 /// JUMBF Codestream Content box (ISO 19566-5:2019, Annex B.2)
    669 #[derive(Debug, Default)]
    670 pub struct JUMBFCodestreamContentBox {
    671     data: Vec<u8>, // arbitrary bunch of bytes...
    672 }
    673 
    674 impl BMFFBox for JUMBFCodestreamContentBox {
    675     fn box_type(&self) -> &'static [u8; 4] {
    676         b"jp2c"
    677     }
    678 
    679     fn box_uuid(&self) -> &'static str {
    680         JUMBF_CODESTREAM_UUID
    681     }
    682 
    683     fn box_payload_size(&self) -> IoResult<u32> {
    684         let size = self.data.len();
    685         Ok(size as u32)
    686     }
    687 
    688     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    689         if !self.data.is_empty() {
    690             write_all!(writer, &self.data);
    691         }
    692         Ok(())
    693     }
    694 
    695     // Necessary method to enable conversion between types...
    696     fn as_any(&self) -> &dyn Any {
    697         self
    698     }
    699 }
    700 
    701 impl JUMBFCodestreamContentBox {
    702     // the content box takes ownership of the data!
    703     pub fn new(data_in: Vec<u8>) -> Self {
    704         JUMBFCodestreamContentBox { data: data_in }
    705     }
    706 
    707     // getter
    708     pub const fn data(&self) -> &Vec<u8> {
    709         &self.data
    710     }
    711 }
    712 
    713 // ANCHOR JUMBF UUID Content box
    714 /// JUMBF UUID Content box (ISO 19566-5:2019, Annex B.5)
    715 #[derive(Debug, Default)]
    716 pub struct JUMBFUUIDContentBox {
    717     uuid: [u8; 16], // a 128-bit UUID for the type
    718     data: Vec<u8>,  // arbitrary bunch of bytes...
    719 }
    720 
    721 impl BMFFBox for JUMBFUUIDContentBox {
    722     fn box_type(&self) -> &'static [u8; 4] {
    723         b"uuid"
    724     }
    725 
    726     fn box_uuid(&self) -> &'static str {
    727         JUMBF_UUID_UUID
    728     }
    729 
    730     fn box_payload_size(&self) -> IoResult<u32> {
    731         let size = 16 /*UUID*/ + self.data.len();
    732         Ok(size as u32)
    733     }
    734 
    735     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    736         if !self.data.is_empty() {
    737             write_all!(writer, &self.uuid);
    738             write_all!(writer, &self.data);
    739         }
    740         Ok(())
    741     }
    742 
    743     // Necessary method to enable conversion between types...
    744     fn as_any(&self) -> &dyn Any {
    745         self
    746     }
    747 }
    748 
    749 impl JUMBFUUIDContentBox {
    750     // the content box takes ownership of the data!
    751     pub fn new(uuid_in: &[u8; 16], data_in: Vec<u8>) -> Self {
    752         let mut u: [u8; 16] = Default::default();
    753         u.copy_from_slice(uuid_in);
    754 
    755         JUMBFUUIDContentBox {
    756             uuid: u,
    757             data: data_in,
    758         }
    759     }
    760 
    761     // getters
    762     pub const fn uuid(&self) -> &[u8; 16] {
    763         &self.uuid
    764     }
    765 
    766     // getter
    767     pub const fn data(&self) -> &Vec<u8> {
    768         &self.data
    769     }
    770 }
    771 
    772 // !SECTION
    773 
    774 //---------------
    775 // SECTION CAI
    776 //---------------
    777 pub const CAI_BLOCK_UUID: &str = "6332706100110010800000AA00389B71"; // c2pa
    778 pub const CAI_STORE_UUID: &str = "63326D6100110010800000AA00389B71"; // c2ma
    779 pub const CAI_UPDATE_MANIFEST_UUID: &str = "6332756D00110010800000AA00389B71"; // c2um
    780 pub const CAI_ASSERTION_STORE_UUID: &str = "6332617300110010800000AA00389B71"; // c2as
    781 pub const CAI_INGREDIENT_STORE_UUID: &str = "6361697300110010800000AA00389B71"; //cais
    782 pub const CAI_JSON_ASSERTION_UUID: &str = "6A736F6E00110010800000AA00389B71"; // json
    783 pub const CAI_CBOR_ASSERTION_UUID: &str = "63626F7200110010800000AA00389B71"; // cbor
    784 pub const CAI_CODESTREAM_ASSERTION_UUID: &str = "6579D6FBDBA2446BB2AC1B82FEEB89D1";
    785 pub const CAI_INGREDIENT_UUID: &str = "6361696E00110010800000AA00389B71"; // cain
    786 pub const CAI_CLAIM_UUID: &str = "6332636C00110010800000AA00389B71"; // c2cl
    787 pub const CAI_SIGNATURE_UUID: &str = "6332637300110010800000AA00389B71"; // c2cs
    788 pub const CAI_EMBEDDED_FILE_UUID: &str = "40CB0C32BB8A489DA70B2AD6F47F4369";
    789 pub const CAI_EMBEDDED_FILE_DESCRIPTION_UUID: &str = "6266646200110010800000AA00389B71"; // bfdb
    790 pub const CAI_EMBEDDED_FILE_DATA_UUID: &str = "6269646200110010800000AA00389B71"; // bidb
    791 pub const CAI_VERIFIABLE_CREDENTIALS_STORE_UUID: &str = "6332766300110010800000AA00389B71"; // c2vc
    792 pub const CAI_UUID_ASSERTION_UUID: &str = "7575696400110010800000AA00389B71"; // uuid
    793 pub const CAI_DATABOXES_STORE_UUID: &str = "6332646200110010800000AA00389B71"; // c2db
    794 
    795 // ANCHOR Salt Content Box
    796 /// Salt Content Box
    797 #[derive(Debug)]
    798 pub struct CAISaltContentBox {
    799     salt: Vec<u8>, // salt data...
    800 }
    801 
    802 impl BMFFBox for CAISaltContentBox {
    803     fn box_type(&self) -> &'static [u8; 4] {
    804         b"c2sh"
    805     }
    806 
    807     fn box_uuid(&self) -> &'static str {
    808         "" // base JUMBF boxes don't have any...
    809     }
    810 
    811     fn box_payload_size(&self) -> IoResult<u32> {
    812         let size = self.salt.len();
    813         Ok(size as u32)
    814     }
    815 
    816     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    817         write_all!(writer, &self.salt);
    818         Ok(())
    819     }
    820 
    821     // Necessary method to enable conversion between types...
    822     fn as_any(&self) -> &dyn Any {
    823         self
    824     }
    825 }
    826 
    827 impl CAISaltContentBox {
    828     pub fn new(data_in: Vec<u8>) -> Self {
    829         CAISaltContentBox { salt: data_in }
    830     }
    831 }
    832 // ANCHOR Signature Content Box
    833 /// Signature Content Box
    834 #[derive(Debug)]
    835 pub struct CAISignatureContentBox {
    836     uuid: [u8; 16],    // a 128-bit UUID
    837     sig_data: Vec<u8>, // signature data...
    838 }
    839 
    840 impl BMFFBox for CAISignatureContentBox {
    841     fn box_type(&self) -> &'static [u8; 4] {
    842         b"uuid"
    843     }
    844 
    845     fn box_uuid(&self) -> &'static str {
    846         "" // base JUMBF boxes don't have any...
    847     }
    848 
    849     fn box_payload_size(&self) -> IoResult<u32> {
    850         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
    851         Ok(size as u32)
    852     }
    853 
    854     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    855         write_all!(writer, &self.uuid);
    856         write_all!(writer, &self.sig_data);
    857         Ok(())
    858     }
    859 
    860     // Necessary method to enable conversion between types...
    861     fn as_any(&self) -> &dyn Any {
    862         self
    863     }
    864 }
    865 
    866 impl CAISignatureContentBox {
    867     pub fn new(data_in: Vec<u8>) -> Self {
    868         CAISignatureContentBox {
    869             uuid: <[u8; 16]>::from_hex(CAI_SIGNATURE_UUID).unwrap_or_default(),
    870             sig_data: data_in,
    871         }
    872     }
    873 }
    874 
    875 // ANCHOR Signature Box
    876 /// Signature Box
    877 #[derive(Debug)]
    878 pub struct CAISignatureBox {
    879     sig_box: JUMBFSuperBox,
    880 }
    881 
    882 impl BMFFBox for CAISignatureBox {
    883     fn box_type(&self) -> &'static [u8; 4] {
    884         b"    "
    885     }
    886 
    887     fn box_uuid(&self) -> &'static str {
    888         CAI_SIGNATURE_UUID
    889     }
    890 
    891     fn box_payload_size(&self) -> IoResult<u32> {
    892         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
    893         Ok(size as u32)
    894     }
    895 
    896     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    897         self.sig_box.write_box(writer)
    898     }
    899 
    900     // Necessary method to enable conversion between types...
    901     fn as_any(&self) -> &dyn Any {
    902         self
    903     }
    904 }
    905 
    906 impl CAISignatureBox {
    907     pub fn new() -> Self {
    908         CAISignatureBox {
    909             sig_box: JUMBFSuperBox::new(labels::SIGNATURE, Some(CAI_SIGNATURE_UUID)),
    910         }
    911     }
    912 
    913     // add a signature content box *WITHOUT* taking ownership of the box
    914     pub fn add_signature(&mut self, b: Box<dyn BMFFBox>) {
    915         self.sig_box.add_data_box(b)
    916     }
    917 }
    918 
    919 impl Default for CAISignatureBox {
    920     fn default() -> Self {
    921         Self::new()
    922     }
    923 }
    924 
    925 // ANCHOR Claim Box
    926 /// Claim Box
    927 #[derive(Debug)]
    928 pub struct CAIClaimBox {
    929     claim_box: JUMBFSuperBox,
    930 }
    931 
    932 impl BMFFBox for CAIClaimBox {
    933     fn box_type(&self) -> &'static [u8; 4] {
    934         b"    "
    935     }
    936 
    937     fn box_uuid(&self) -> &'static str {
    938         CAI_CLAIM_UUID
    939     }
    940 
    941     fn box_payload_size(&self) -> IoResult<u32> {
    942         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
    943         Ok(size as u32)
    944     }
    945 
    946     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    947         self.claim_box.write_box(writer)
    948     }
    949 
    950     // Necessary method to enable conversion between types...
    951     fn as_any(&self) -> &dyn Any {
    952         self
    953     }
    954 }
    955 
    956 impl CAIClaimBox {
    957     pub fn new() -> Self {
    958         CAIClaimBox {
    959             claim_box: JUMBFSuperBox::new(labels::CLAIM, Some(CAI_CLAIM_UUID)),
    960         }
    961     }
    962 
    963     // add a JUMBFCBORContentBox box, with the claim's CBOR
    964     // *WITHOUT* taking ownership of the box
    965     pub fn add_claim(&mut self, b: Box<dyn BMFFBox>) {
    966         self.claim_box.add_data_box(b)
    967     }
    968 }
    969 
    970 impl Default for CAIClaimBox {
    971     fn default() -> Self {
    972         Self::new()
    973     }
    974 }
    975 
    976 // ANCHOR UUID Assertion Box
    977 /// UUID Assertion Box
    978 #[derive(Debug)]
    979 pub struct CAIUUIDAssertionBox {
    980     assertion_box: JUMBFSuperBox,
    981 }
    982 
    983 impl BMFFBox for CAIUUIDAssertionBox {
    984     fn box_type(&self) -> &'static [u8; 4] {
    985         b"    "
    986     }
    987 
    988     fn box_uuid(&self) -> &'static str {
    989         CAI_UUID_ASSERTION_UUID
    990     }
    991 
    992     fn box_payload_size(&self) -> IoResult<u32> {
    993         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
    994         Ok(size as u32)
    995     }
    996 
    997     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
    998         self.assertion_box.write_box(writer)
    999     }
   1000 
   1001     // Necessary method to enable conversion between types...
   1002     fn as_any(&self) -> &dyn Any {
   1003         self
   1004     }
   1005 }
   1006 
   1007 impl CAIUUIDAssertionBox {
   1008     pub fn new(box_label: &str) -> Self {
   1009         CAIUUIDAssertionBox {
   1010             assertion_box: JUMBFSuperBox::new(box_label, Some(CAI_UUID_ASSERTION_UUID)),
   1011         }
   1012     }
   1013 
   1014     // add a JUMBFJSONContentBox box, with the assertion's JSON
   1015     // takes ownership of the JSON
   1016     pub fn add_uuid(&mut self, uuid_str: &str, data: Vec<u8>) -> JumbfParseResult<()> {
   1017         let uuid = hex::decode(uuid_str).map_err(|_e| JumbfParseError::InvalidUuidValue)?;
   1018         if uuid.len() != 16 {
   1019             // the uuid is defined a as 16 bytes
   1020             return Err(JumbfParseError::InvalidUuidValue);
   1021         }
   1022 
   1023         let mut u: [u8; 16] = Default::default();
   1024         u.copy_from_slice(&uuid);
   1025         let assertion_content = JUMBFUUIDContentBox::new(&u, data);
   1026         self.assertion_box.add_data_box(Box::new(assertion_content));
   1027 
   1028         Ok(())
   1029     }
   1030 
   1031     pub fn set_salt(&mut self, salt: Vec<u8>) -> JumbfParseResult<()> {
   1032         self.assertion_box.desc_box.set_salt(salt)
   1033     }
   1034 
   1035     pub fn super_box(&self) -> &dyn BMFFBox {
   1036         &self.assertion_box
   1037     }
   1038 }
   1039 
   1040 // ANCHOR JSON Assertion Box
   1041 /// JSON Assertion Box
   1042 #[derive(Debug)]
   1043 pub struct CAIJSONAssertionBox {
   1044     assertion_box: JUMBFSuperBox,
   1045 }
   1046 
   1047 impl BMFFBox for CAIJSONAssertionBox {
   1048     fn box_type(&self) -> &'static [u8; 4] {
   1049         b"    "
   1050     }
   1051 
   1052     fn box_uuid(&self) -> &'static str {
   1053         CAI_JSON_ASSERTION_UUID
   1054     }
   1055 
   1056     fn box_payload_size(&self) -> IoResult<u32> {
   1057         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1058         Ok(size as u32)
   1059     }
   1060 
   1061     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1062         self.assertion_box.write_box(writer)
   1063     }
   1064 
   1065     // Necessary method to enable conversion between types...
   1066     fn as_any(&self) -> &dyn Any {
   1067         self
   1068     }
   1069 }
   1070 
   1071 impl CAIJSONAssertionBox {
   1072     pub fn new(box_label: &str) -> Self {
   1073         CAIJSONAssertionBox {
   1074             assertion_box: JUMBFSuperBox::new(box_label, Some(CAI_JSON_ASSERTION_UUID)),
   1075         }
   1076     }
   1077 
   1078     // add a JUMBFJSONContentBox box, with the assertion's JSON
   1079     // takes ownership of the JSON
   1080     pub fn add_json(&mut self, json_in: Vec<u8>) {
   1081         let assertion_content = JUMBFJSONContentBox::new(json_in);
   1082         self.assertion_box.add_data_box(Box::new(assertion_content));
   1083     }
   1084 
   1085     pub fn set_salt(&mut self, salt: Vec<u8>) -> JumbfParseResult<()> {
   1086         self.assertion_box.desc_box.set_salt(salt)
   1087     }
   1088 
   1089     pub fn super_box(&self) -> &dyn BMFFBox {
   1090         &self.assertion_box
   1091     }
   1092 }
   1093 
   1094 pub struct CAICBORAssertionBox {
   1095     assertion_box: JUMBFSuperBox,
   1096 }
   1097 
   1098 impl BMFFBox for CAICBORAssertionBox {
   1099     fn box_type(&self) -> &'static [u8; 4] {
   1100         b"    "
   1101     }
   1102 
   1103     fn box_uuid(&self) -> &'static str {
   1104         CAI_CBOR_ASSERTION_UUID
   1105     }
   1106 
   1107     fn box_payload_size(&self) -> IoResult<u32> {
   1108         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1109         Ok(size as u32)
   1110     }
   1111 
   1112     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1113         self.assertion_box.write_box(writer)
   1114     }
   1115 
   1116     // Necessary method to enable conversion between types...
   1117     fn as_any(&self) -> &dyn Any {
   1118         self
   1119     }
   1120 }
   1121 
   1122 impl CAICBORAssertionBox {
   1123     pub fn new(box_label: &str) -> Self {
   1124         CAICBORAssertionBox {
   1125             assertion_box: JUMBFSuperBox::new(box_label, Some(CAI_CBOR_ASSERTION_UUID)),
   1126         }
   1127     }
   1128 
   1129     // add a JUMBFCBORContentBox box, with the assertion's CBOR
   1130     // takes ownership of the CBOR
   1131     pub fn add_cbor(&mut self, cbor_in: Vec<u8>) {
   1132         let assertion_content = JUMBFCBORContentBox::new(cbor_in);
   1133         self.assertion_box.add_data_box(Box::new(assertion_content));
   1134     }
   1135 
   1136     pub fn set_salt(&mut self, salt: Vec<u8>) -> JumbfParseResult<()> {
   1137         self.assertion_box.desc_box.set_salt(salt)
   1138     }
   1139 
   1140     pub fn super_box(&self) -> &dyn BMFFBox {
   1141         &self.assertion_box
   1142     }
   1143 }
   1144 
   1145 // ANCHOR Ingredient Box
   1146 /// Ingedient  Box
   1147 #[derive(Debug)]
   1148 pub struct CAIIngredientBox {
   1149     ingredient_box: JUMBFSuperBox,
   1150 }
   1151 
   1152 impl BMFFBox for CAIIngredientBox {
   1153     fn box_type(&self) -> &'static [u8; 4] {
   1154         b"    "
   1155     }
   1156 
   1157     fn box_uuid(&self) -> &'static str {
   1158         CAI_INGREDIENT_UUID
   1159     }
   1160 
   1161     fn box_payload_size(&self) -> IoResult<u32> {
   1162         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1163         Ok(size as u32)
   1164     }
   1165 
   1166     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1167         self.ingredient_box.write_box(writer)
   1168     }
   1169 
   1170     // Necessary method to enable conversion between types...
   1171     fn as_any(&self) -> &dyn Any {
   1172         self
   1173     }
   1174 }
   1175 
   1176 impl CAIIngredientBox {
   1177     pub fn new(box_label: &str) -> Self {
   1178         CAIIngredientBox {
   1179             ingredient_box: JUMBFSuperBox::new(box_label, Some(CAI_INGREDIENT_UUID)),
   1180         }
   1181     }
   1182 
   1183     // add a JUMBFCodestreamContentBox box, with the codestream data
   1184     //  takes ownership of the data
   1185     pub fn add_data(&mut self, data_in: Vec<u8>) {
   1186         let ingredient_content = JUMBFCodestreamContentBox::new(data_in);
   1187         self.ingredient_box
   1188             .add_data_box(Box::new(ingredient_content));
   1189     }
   1190 }
   1191 
   1192 // ANCHOR Assertion Store
   1193 /// Assertion Store
   1194 #[derive(Debug)]
   1195 pub struct CAIAssertionStore {
   1196     store: JUMBFSuperBox,
   1197 }
   1198 
   1199 impl BMFFBox for CAIAssertionStore {
   1200     fn box_type(&self) -> &'static [u8; 4] {
   1201         b"    "
   1202     }
   1203 
   1204     fn box_uuid(&self) -> &'static str {
   1205         CAI_ASSERTION_STORE_UUID
   1206     }
   1207 
   1208     fn box_payload_size(&self) -> IoResult<u32> {
   1209         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1210         Ok(size as u32)
   1211     }
   1212 
   1213     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1214         self.store.write_box(writer)
   1215     }
   1216 
   1217     // Necessary method to enable conversion between types...
   1218     fn as_any(&self) -> &dyn Any {
   1219         self
   1220     }
   1221 }
   1222 
   1223 impl CAIAssertionStore {
   1224     pub fn new() -> Self {
   1225         CAIAssertionStore {
   1226             store: JUMBFSuperBox::new(labels::ASSERTIONS, Some(CAI_ASSERTION_STORE_UUID)),
   1227         }
   1228     }
   1229 
   1230     pub const fn from(in_box: JUMBFSuperBox) -> Self {
   1231         CAIAssertionStore { store: in_box }
   1232     }
   1233 
   1234     // add an assertion box (of various types) *WITHOUT* taking ownership of the box
   1235     pub fn add_assertion(&mut self, b: Box<dyn BMFFBox>) {
   1236         self.store.add_data_box(b)
   1237     }
   1238 }
   1239 
   1240 impl Default for CAIAssertionStore {
   1241     fn default() -> Self {
   1242         Self::new()
   1243     }
   1244 }
   1245 
   1246 #[derive(Debug)]
   1247 pub struct CAIDataboxStore {
   1248     store: JUMBFSuperBox,
   1249 }
   1250 
   1251 impl BMFFBox for CAIDataboxStore {
   1252     fn box_type(&self) -> &'static [u8; 4] {
   1253         b"    "
   1254     }
   1255 
   1256     fn box_uuid(&self) -> &'static str {
   1257         CAI_DATABOXES_STORE_UUID
   1258     }
   1259 
   1260     fn box_payload_size(&self) -> IoResult<u32> {
   1261         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1262         Ok(size as u32)
   1263     }
   1264 
   1265     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1266         self.store.write_box(writer)
   1267     }
   1268 
   1269     // Necessary method to enable conversion between types...
   1270     fn as_any(&self) -> &dyn Any {
   1271         self
   1272     }
   1273 }
   1274 
   1275 impl CAIDataboxStore {
   1276     pub fn new() -> Self {
   1277         CAIDataboxStore {
   1278             store: JUMBFSuperBox::new(labels::DATABOXES, Some(CAI_DATABOXES_STORE_UUID)),
   1279         }
   1280     }
   1281 
   1282     pub const fn from(in_box: JUMBFSuperBox) -> Self {
   1283         CAIDataboxStore { store: in_box }
   1284     }
   1285 
   1286     // add an assertion box (of various types) *WITHOUT* taking ownership of the box
   1287     pub fn add_databox(&mut self, b: Box<dyn BMFFBox>) {
   1288         self.store.add_data_box(b)
   1289     }
   1290 }
   1291 
   1292 impl Default for CAIDataboxStore {
   1293     fn default() -> Self {
   1294         Self::new()
   1295     }
   1296 }
   1297 
   1298 // ANCHOR Verifiable Credential Store
   1299 /// Ingredients Store
   1300 #[derive(Debug)]
   1301 pub struct CAIVerifiableCredentialStore {
   1302     store: JUMBFSuperBox,
   1303 }
   1304 
   1305 impl BMFFBox for CAIVerifiableCredentialStore {
   1306     fn box_type(&self) -> &'static [u8; 4] {
   1307         b"    "
   1308     }
   1309 
   1310     fn box_uuid(&self) -> &'static str {
   1311         CAI_VERIFIABLE_CREDENTIALS_STORE_UUID
   1312     }
   1313 
   1314     fn box_payload_size(&self) -> IoResult<u32> {
   1315         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1316         Ok(size as u32)
   1317     }
   1318 
   1319     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1320         self.store.write_box(writer)
   1321     }
   1322 
   1323     // Necessary method to enable conversion between types...
   1324     fn as_any(&self) -> &dyn Any {
   1325         self
   1326     }
   1327 }
   1328 
   1329 impl CAIVerifiableCredentialStore {
   1330     pub fn new() -> Self {
   1331         CAIVerifiableCredentialStore {
   1332             store: JUMBFSuperBox::new(
   1333                 labels::CREDENTIALS,
   1334                 Some(CAI_VERIFIABLE_CREDENTIALS_STORE_UUID),
   1335             ),
   1336         }
   1337     }
   1338 
   1339     pub const fn from(in_box: JUMBFSuperBox) -> Self {
   1340         CAIVerifiableCredentialStore { store: in_box }
   1341     }
   1342 
   1343     // add an credential box *WITHOUT* taking ownership of the box
   1344     pub fn add_credential(&mut self, b: Box<dyn BMFFBox>) {
   1345         self.store.add_data_box(b)
   1346     }
   1347 }
   1348 
   1349 impl Default for CAIVerifiableCredentialStore {
   1350     fn default() -> Self {
   1351         Self::new()
   1352     }
   1353 }
   1354 
   1355 // ANCHOR CAI Store
   1356 /// CAI Store
   1357 #[derive(Debug)]
   1358 pub struct CAIStore {
   1359     is_update_manifest: bool,
   1360     store: JUMBFSuperBox,
   1361 }
   1362 
   1363 impl BMFFBox for CAIStore {
   1364     fn box_type(&self) -> &'static [u8; 4] {
   1365         b"    "
   1366     }
   1367 
   1368     fn box_uuid(&self) -> &'static str {
   1369         if self.is_update_manifest {
   1370             CAI_UPDATE_MANIFEST_UUID
   1371         } else {
   1372             CAI_STORE_UUID
   1373         }
   1374     }
   1375 
   1376     fn box_payload_size(&self) -> IoResult<u32> {
   1377         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1378         Ok(size as u32)
   1379     }
   1380 
   1381     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1382         self.store.write_box(writer)
   1383     }
   1384 
   1385     // Necessary method to enable conversion between types...
   1386     fn as_any(&self) -> &dyn Any {
   1387         self
   1388     }
   1389 }
   1390 
   1391 impl CAIStore {
   1392     pub fn new(box_label: &str, update_manifest: bool) -> Self {
   1393         let id = if update_manifest {
   1394             Some(CAI_UPDATE_MANIFEST_UUID)
   1395         } else {
   1396             Some(CAI_STORE_UUID)
   1397         };
   1398         let sbox = JUMBFSuperBox::new(box_label, id);
   1399         CAIStore {
   1400             is_update_manifest: update_manifest,
   1401             store: sbox,
   1402         }
   1403     }
   1404 
   1405     pub fn from(sbox: JUMBFSuperBox) -> Self {
   1406         let update_manifest = sbox.box_uuid() == CAI_UPDATE_MANIFEST_UUID;
   1407 
   1408         CAIStore {
   1409             is_update_manifest: update_manifest,
   1410             store: sbox,
   1411         }
   1412     }
   1413 
   1414     /// add a box (of various types) *WITHOUT* taking ownership of the box
   1415     pub fn add_box(&mut self, b: Box<dyn BMFFBox>) {
   1416         self.store.add_data_box(b)
   1417     }
   1418 
   1419     // getters
   1420     pub const fn super_box(&self) -> &JUMBFSuperBox {
   1421         &self.store
   1422     }
   1423 
   1424     pub const fn desc_box(&self) -> &JUMBFDescriptionBox {
   1425         &self.store.desc_box
   1426     }
   1427 
   1428     pub fn data_box_count(&self) -> usize {
   1429         self.store.data_boxes.len()
   1430     }
   1431 
   1432     pub fn data_box(&self, index: usize) -> &dyn BMFFBox {
   1433         self.store.data_boxes[index].as_ref()
   1434     }
   1435 
   1436     pub fn assertion_store(&self) -> Option<&JUMBFSuperBox> {
   1437         // we REALLY want to return a CAIAssertionStore but can't do to referencing...
   1438         self.store.data_box_as_superbox(0)
   1439     }
   1440 
   1441     pub fn set_salt(&mut self, salt: Vec<u8>) -> JumbfParseResult<()> {
   1442         self.store.desc_box.set_salt(salt)
   1443     }
   1444 }
   1445 
   1446 // ANCHOR CAI Block
   1447 /// CAI Block
   1448 #[derive(Debug)]
   1449 pub struct Cai {
   1450     sbox: JUMBFSuperBox,
   1451 }
   1452 
   1453 impl BMFFBox for Cai {
   1454     fn box_type(&self) -> &'static [u8; 4] {
   1455         b"    "
   1456     }
   1457 
   1458     fn box_uuid(&self) -> &'static str {
   1459         CAI_BLOCK_UUID
   1460     }
   1461 
   1462     fn box_payload_size(&self) -> IoResult<u32> {
   1463         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1464         Ok(size as u32)
   1465     }
   1466 
   1467     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1468         self.sbox.write_box(writer)
   1469     }
   1470 
   1471     // Necessary method to enable conversion between types...
   1472     fn as_any(&self) -> &dyn Any {
   1473         self
   1474     }
   1475 }
   1476 
   1477 impl Cai {
   1478     pub fn new() -> Self {
   1479         Cai {
   1480             sbox: JUMBFSuperBox::new(labels::MANIFEST_STORE, Some(CAI_BLOCK_UUID)),
   1481         }
   1482     }
   1483 
   1484     pub const fn from(in_box: JUMBFSuperBox) -> Self {
   1485         Cai { sbox: in_box }
   1486     }
   1487 
   1488     /// add a box (of various types) *WITHOUT* taking ownership of the box
   1489     pub fn add_box(&mut self, b: Box<dyn BMFFBox>) {
   1490         self.sbox.add_data_box(b)
   1491     }
   1492 
   1493     // getters
   1494     pub const fn super_box(&self) -> &JUMBFSuperBox {
   1495         &self.sbox
   1496     }
   1497 
   1498     pub const fn desc_box(&self) -> &JUMBFDescriptionBox {
   1499         &self.sbox.desc_box
   1500     }
   1501 
   1502     pub fn data_box_count(&self) -> usize {
   1503         self.sbox.data_boxes.len()
   1504     }
   1505 
   1506     pub fn data_box(&self, index: usize) -> &dyn BMFFBox {
   1507         self.sbox.data_boxes[index].as_ref()
   1508     }
   1509 
   1510     pub fn data_box_as_superbox(&self, index: usize) -> Option<&JUMBFSuperBox> {
   1511         let da_box = &self.sbox.data_boxes[index];
   1512         da_box.as_ref().as_any().downcast_ref::<JUMBFSuperBox>()
   1513     }
   1514 
   1515     pub fn store(&self) -> Option<&JUMBFSuperBox> {
   1516         // we REALLY want to return a UpdateManifest but can't do to referencing...
   1517         self.sbox.data_box_as_superbox(0)
   1518     }
   1519 }
   1520 
   1521 impl Default for Cai {
   1522     fn default() -> Self {
   1523         Self::new()
   1524     }
   1525 }
   1526 
   1527 pub struct JumbfEmbeddedFileBox {
   1528     embedding_box: JUMBFSuperBox,
   1529 }
   1530 
   1531 impl BMFFBox for JumbfEmbeddedFileBox {
   1532     fn box_type(&self) -> &'static [u8; 4] {
   1533         b"    "
   1534     }
   1535 
   1536     fn box_uuid(&self) -> &'static str {
   1537         JUMBF_EMBEDDED_FILE_UUID
   1538     }
   1539 
   1540     fn box_payload_size(&self) -> IoResult<u32> {
   1541         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1542         Ok(size as u32)
   1543     }
   1544 
   1545     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1546         self.embedding_box.write_box(writer)
   1547     }
   1548 
   1549     // Necessary method to enable conversion between types...
   1550     fn as_any(&self) -> &dyn Any {
   1551         self
   1552     }
   1553 }
   1554 
   1555 impl JumbfEmbeddedFileBox {
   1556     pub fn new(box_label: &str) -> Self {
   1557         JumbfEmbeddedFileBox {
   1558             embedding_box: JUMBFSuperBox::new(box_label, Some(JUMBF_EMBEDDED_FILE_UUID)),
   1559         }
   1560     }
   1561 
   1562     // add a JUMBFJSONContentBox box, with the claim's JSON
   1563     // *WITHOUT* taking ownership of the box
   1564     pub fn add_data(&mut self, data: Vec<u8>, media_type: String, file_name: Option<String>) {
   1565         // add media type box
   1566         let m = JUMBFEmbeddedFileDescriptionBox::new(media_type, file_name);
   1567         self.embedding_box.add_data_box(Box::new(m));
   1568 
   1569         // add data box
   1570         let d = JUMBFEmbeddedFileContentBox::new(data);
   1571         self.embedding_box.add_data_box(Box::new(d));
   1572     }
   1573 
   1574     pub fn media_type_box(&self) -> Option<&JUMBFEmbeddedFileDescriptionBox> {
   1575         let efd_box = &self.embedding_box.data_boxes[0];
   1576         efd_box
   1577             .as_ref()
   1578             .as_any()
   1579             .downcast_ref::<JUMBFEmbeddedFileDescriptionBox>()
   1580     }
   1581 
   1582     pub fn data_box(&self) -> Option<&JUMBFEmbeddedFileContentBox> {
   1583         let efc_box = &self.embedding_box.data_boxes[1];
   1584         efc_box
   1585             .as_ref()
   1586             .as_any()
   1587             .downcast_ref::<JUMBFEmbeddedFileContentBox>()
   1588     }
   1589 
   1590     pub fn set_salt(&mut self, salt: Vec<u8>) -> JumbfParseResult<()> {
   1591         self.embedding_box.desc_box.set_salt(salt)
   1592     }
   1593 
   1594     pub fn get_salt(&self) -> Option<Vec<u8>> {
   1595         self.embedding_box
   1596             .desc_box
   1597             .private
   1598             .as_ref()
   1599             .map(|saltbox| saltbox.salt.clone())
   1600     }
   1601 
   1602     pub fn super_box(&self) -> &dyn BMFFBox {
   1603         &self.embedding_box
   1604     }
   1605 }
   1606 
   1607 impl Default for JumbfEmbeddedFileBox {
   1608     fn default() -> Self {
   1609         Self::new("")
   1610     }
   1611 }
   1612 #[derive(Debug, Default)]
   1613 pub struct JUMBFEmbeddedFileContentBox {
   1614     data: Vec<u8>, // arbitrary bunch of bytes...
   1615 }
   1616 
   1617 impl BMFFBox for JUMBFEmbeddedFileContentBox {
   1618     fn box_type(&self) -> &'static [u8; 4] {
   1619         b"bidb"
   1620     }
   1621 
   1622     fn box_uuid(&self) -> &'static str {
   1623         CAI_EMBEDDED_FILE_DATA_UUID
   1624     }
   1625 
   1626     fn box_payload_size(&self) -> IoResult<u32> {
   1627         let size = self.data.len();
   1628         Ok(size as u32)
   1629     }
   1630 
   1631     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1632         if !self.data.is_empty() {
   1633             write_all!(writer, &self.data);
   1634         }
   1635         Ok(())
   1636     }
   1637 
   1638     // Necessary method to enable conversion between types...
   1639     fn as_any(&self) -> &dyn Any {
   1640         self
   1641     }
   1642 }
   1643 
   1644 impl JUMBFEmbeddedFileContentBox {
   1645     // the content box takes ownership of the data!
   1646     pub fn new(data_in: Vec<u8>) -> Self {
   1647         JUMBFEmbeddedFileContentBox { data: data_in }
   1648     }
   1649 
   1650     // getter
   1651     pub const fn data(&self) -> &Vec<u8> {
   1652         &self.data
   1653     }
   1654 }
   1655 
   1656 #[derive(Debug)]
   1657 pub struct JUMBFEmbeddedFileDescriptionBox {
   1658     toggles: u8,                // media togles
   1659     media_type: CString,        // file media type
   1660     file_name: Option<CString>, // optional file name
   1661 }
   1662 
   1663 impl BMFFBox for JUMBFEmbeddedFileDescriptionBox {
   1664     fn box_type(&self) -> &'static [u8; 4] {
   1665         b"bfdb"
   1666     }
   1667 
   1668     fn box_uuid(&self) -> &'static str {
   1669         CAI_EMBEDDED_FILE_DESCRIPTION_UUID
   1670     }
   1671 
   1672     fn box_payload_size(&self) -> IoResult<u32> {
   1673         let size = boxio::ByteCounter::calculate(|w| self.write_box_payload(w))?;
   1674         Ok(size as u32)
   1675     }
   1676 
   1677     fn write_box_payload(&self, writer: &mut dyn Write) -> IoResult<()> {
   1678         write_u8!(writer, self.toggles);
   1679         if self.media_type.to_str().unwrap_or_default().chars().count() > 0 {
   1680             write_all!(writer, self.media_type.as_bytes_with_nul());
   1681         }
   1682         /*
   1683         if let Some(name) = &self.file_name {
   1684             if name
   1685                 .to_str()
   1686                 .expect("Incompatible string representation")
   1687                 .chars()
   1688                 .count()
   1689                 > 0
   1690             {
   1691                 write_all!(writer, name.as_bytes_with_nul())
   1692             }
   1693         }
   1694         */
   1695         Ok(())
   1696     }
   1697 
   1698     // Necessary method to enable conversion between types...
   1699     fn as_any(&self) -> &dyn Any {
   1700         self
   1701     }
   1702 }
   1703 
   1704 impl JUMBFEmbeddedFileDescriptionBox {
   1705     pub fn new(media_type: String, file_name: Option<String>) -> Self {
   1706         let mut new_toggles = 0;
   1707 
   1708         let cfile_name = match file_name {
   1709             Some(f) => {
   1710                 new_toggles = 1;
   1711                 Some(CString::new(f).unwrap_or_default())
   1712             }
   1713             None => None,
   1714         };
   1715 
   1716         JUMBFEmbeddedFileDescriptionBox {
   1717             toggles: new_toggles,
   1718             media_type: CString::new(media_type).unwrap_or_default(),
   1719             file_name: cfile_name,
   1720         }
   1721     }
   1722 
   1723     fn to_rust_str(&self, s: &CString) -> String {
   1724         let bytes = s.clone().into_bytes();
   1725 
   1726         let nul_range_end = bytes
   1727             .iter()
   1728             .position(|&c| c == b'\0')
   1729             .unwrap_or(bytes.len());
   1730 
   1731         if let Ok(r_str) = String::from_utf8(bytes[0..nul_range_end].to_vec()) {
   1732             r_str
   1733         } else {
   1734             String::new()
   1735         }
   1736     }
   1737 
   1738     pub fn media_type(&self) -> String {
   1739         self.to_rust_str(&self.media_type)
   1740     }
   1741 
   1742     pub fn file_name(&self) -> Option<String> {
   1743         self.file_name.as_ref().map(|f| self.to_rust_str(f))
   1744     }
   1745 
   1746     /// Makes a new `JUMBFDescriptionBox` instance from read in data
   1747     pub fn from(togs: u8, mt_bytes: Vec<u8>, fn_bytes: Option<Vec<u8>>) -> Self {
   1748         let mt_cstring: CString = unsafe { CString::from_vec_unchecked(mt_bytes) };
   1749         let fn_cstring = fn_bytes.map(|b| unsafe { CString::from_vec_unchecked(b) });
   1750 
   1751         JUMBFEmbeddedFileDescriptionBox {
   1752             toggles: togs,          // media togles
   1753             media_type: mt_cstring, // file media type
   1754             file_name: fn_cstring,  // optional file name
   1755         }
   1756     }
   1757 }
   1758 
   1759 // !SECTION
   1760 
   1761 //---------------
   1762 // SECTION Box Reader
   1763 //---------------
   1764 
   1765 const HEADER_SIZE: u64 = 8;
   1766 const TOGGLE_SIZE: u64 = 1;
   1767 
   1768 /// method for getting the current position
   1769 pub fn current_pos<R: Seek>(seeker: &mut R) -> JumbfParseResult<u64> {
   1770     Ok(seeker.stream_position()?)
   1771 }
   1772 
   1773 /// method for seeking back to the start of the box (header)
   1774 pub fn box_start<R: Seek>(seeker: &mut R) -> JumbfParseResult<u64> {
   1775     Ok(current_pos(seeker).map_err(|_| JumbfParseError::InvalidBoxStart)? - HEADER_SIZE)
   1776 }
   1777 
   1778 /// method for skipping over `size` bytes
   1779 pub fn skip_bytes<S: Seek>(seeker: &mut S, size: u64) -> JumbfParseResult<()> {
   1780     seeker.seek(SeekFrom::Current(size as i64))?;
   1781     Ok(())
   1782 }
   1783 
   1784 /// method for skipping to a specific position (`pos`)
   1785 pub fn skip_bytes_to<S: Seek>(seeker: &mut S, pos: u64) -> JumbfParseResult<()> {
   1786     seeker.seek(SeekFrom::Start(pos))?;
   1787     Ok(())
   1788 }
   1789 
   1790 // method to skip over an entire box
   1791 pub fn skip_box<S: Seek>(seeker: &mut S, size: u64) -> JumbfParseResult<()> {
   1792     let start = box_start(seeker)?;
   1793     skip_bytes_to(seeker, start + size)?;
   1794     Ok(())
   1795 }
   1796 
   1797 /// method for skipping backwards `size` bytes
   1798 pub fn unread_bytes<S: Seek>(seeker: &mut S, size: u64) -> JumbfParseResult<()> {
   1799     let new_loc = -(size as i64);
   1800     seeker.seek(SeekFrom::Current(new_loc))?;
   1801     Ok(())
   1802 }
   1803 
   1804 /// macro for dealing with the type of a BMFF/JUMBF box
   1805 macro_rules! boxtype {
   1806     ($( $name:ident => $value:expr ),*) => {
   1807         #[derive(Debug, Clone, Copy, PartialEq, Eq)]
   1808         pub enum BoxType {
   1809             $( $name, )*
   1810             UnknownBox(u32),
   1811         }
   1812 
   1813         impl From<u32> for BoxType {
   1814             fn from(t: u32) -> BoxType {
   1815                 match t {
   1816                     $( $value => BoxType::$name, )*
   1817                     _ => BoxType::UnknownBox(t),
   1818                 }
   1819             }
   1820         }
   1821 
   1822     }
   1823 }
   1824 
   1825 boxtype! {
   1826     Empty => 0x0000_0000,
   1827     Jumb => 0x6A75_6D62,
   1828     Jumd => 0x6A75_6D64,
   1829     Padding => 0x6672_6565,
   1830     SaltHash => 0x6332_7368,
   1831     Json => 0x6A73_6F6E,
   1832     Uuid => 0x7575_6964,
   1833     Jp2c => 0x6A70_3263,
   1834     Cbor => 0x6362_6F72,
   1835     EmbedMediaDesc => 0x6266_6462,
   1836     EmbedContent => 0x6269_6462
   1837 }
   1838 
   1839 // ANCHOR BlockHeader
   1840 /// class for storing the header of a block
   1841 pub struct BoxHeader {
   1842     pub name: BoxType,
   1843     pub size: u64,
   1844 }
   1845 impl BoxHeader {
   1846     pub const fn new(name: BoxType, size: u64) -> Self {
   1847         Self { name, size }
   1848     }
   1849 }
   1850 
   1851 // ANCHOR BoxReader
   1852 /// class for reading BMFF/JUMBF boxes
   1853 pub struct BoxReader {}
   1854 
   1855 impl BoxReader {
   1856     pub fn read_header<R: Read>(reader: &mut R) -> JumbfParseResult<BoxHeader> {
   1857         // Create and read to buf.
   1858         let mut buf = [0u8; 8]; // 8 bytes for box header.
   1859         let bytes_read = reader.read(&mut buf)?;
   1860 
   1861         if bytes_read == 0 {
   1862             // end of file!
   1863             return Ok(BoxHeader::new(BoxType::Empty, 0));
   1864         }
   1865 
   1866         // Get size.
   1867         let s = buf[0..4]
   1868             .try_into()
   1869             .map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   1870         let size = u32::from_be_bytes(s);
   1871 
   1872         // Get box type string.
   1873         let t = buf[4..8]
   1874             .try_into()
   1875             .map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   1876         let typ = u32::from_be_bytes(t);
   1877 
   1878         // Get large size if size is 1
   1879         if size == 1 {
   1880             reader.read_exact(&mut buf)?;
   1881             let s = buf; //.try_into().unwrap();
   1882             let large_size = u64::from_be_bytes(s);
   1883 
   1884             Ok(BoxHeader {
   1885                 name: BoxType::from(typ),
   1886                 size: large_size,
   1887             })
   1888         } else {
   1889             Ok(BoxHeader {
   1890                 name: BoxType::from(typ),
   1891                 size: size as u64,
   1892             })
   1893         }
   1894     }
   1895 
   1896     pub fn read_desc_box<R: Read + Seek>(
   1897         reader: &mut R,
   1898         size: u64,
   1899     ) -> JumbfParseResult<JUMBFDescriptionBox> {
   1900         let mut bytes_left = size;
   1901         let mut uuid = [0u8; 16]; // 16 bytes for the UUID
   1902         let bytes_read = reader.read(&mut uuid)?;
   1903         if bytes_read == 0 {
   1904             // end of file!
   1905             return Ok(JUMBFDescriptionBox::new("", None));
   1906         }
   1907         bytes_left -= bytes_read as u64;
   1908 
   1909         let mut togs = [0u8]; // 1 byte of toggles
   1910         reader.read_exact(&mut togs)?;
   1911         bytes_left -= 1;
   1912 
   1913         let mut sbuf = Vec::with_capacity(64);
   1914         if togs[0] & 0x03 == 0x03 {
   1915             // must be requestable and labeled
   1916             // read label
   1917             loop {
   1918                 let mut buf = [0; 1];
   1919                 reader.read_exact(&mut buf)?;
   1920                 bytes_left -= 1;
   1921                 if buf[0] == 0x00 {
   1922                     break;
   1923                 } else {
   1924                     sbuf.push(buf[0]);
   1925                 }
   1926             }
   1927         } else {
   1928             return Err(JumbfParseError::InvalidDescriptionBox);
   1929         }
   1930 
   1931         // box id
   1932         let bxid = if togs[0] & 0x04 == 0x04 {
   1933             let idbuf = reader.read_u32::<BigEndian>()?;
   1934             bytes_left -= 4;
   1935             Some(idbuf)
   1936         } else {
   1937             None
   1938         };
   1939 
   1940         // if there is a signature, we need to read it...
   1941         let sig = if togs[0] & 0x08 == 0x08 {
   1942             let mut sigbuf: [u8; 32] = [0; 32];
   1943             reader.read_exact(&mut sigbuf)?;
   1944             bytes_left -= 32;
   1945             Some(sigbuf)
   1946         } else {
   1947             None
   1948         };
   1949 
   1950         // read private box if necessary
   1951         let private = if togs[0] & 0x10 == 0x10 {
   1952             let header =
   1953                 BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   1954             if header.size == 0 {
   1955                 // bad read,
   1956                 return Err(JumbfParseError::InvalidBoxHeader);
   1957             } else if header.size != bytes_left - HEADER_SIZE {
   1958                 // this means that we started w/o the header...
   1959                 unread_bytes(reader, HEADER_SIZE)?;
   1960             }
   1961 
   1962             if header.name == BoxType::SaltHash {
   1963                 let data_len = header.size - HEADER_SIZE;
   1964                 let mut buf = vec![0u8; data_len as usize];
   1965                 reader.read_exact(&mut buf)?;
   1966 
   1967                 bytes_left -= header.size;
   1968 
   1969                 Some(CAISaltContentBox::new(buf))
   1970             } else {
   1971                 return Err(JumbfParseError::InvalidBoxHeader);
   1972             }
   1973         } else {
   1974             None
   1975         };
   1976 
   1977         if bytes_left != HEADER_SIZE {
   1978             // make sure we have consumed the entire box
   1979             return Err(JumbfParseError::InvalidBoxHeader);
   1980         }
   1981 
   1982         Ok(JUMBFDescriptionBox::from(
   1983             &uuid, togs[0], sbuf, bxid, sig, private,
   1984         ))
   1985     }
   1986 
   1987     pub fn read_json_box<R: Read + Seek>(
   1988         reader: &mut R,
   1989         size: u64,
   1990     ) -> JumbfParseResult<JUMBFJSONContentBox> {
   1991         let header =
   1992             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   1993         if header.size == 0 {
   1994             // bad read, return empty box...
   1995             return Ok(JUMBFJSONContentBox::new(Vec::new()));
   1996         } else if header.size != size {
   1997             // this means that we started w/o the header...
   1998             unread_bytes(reader, HEADER_SIZE)?;
   1999         }
   2000 
   2001         let json_len = size - HEADER_SIZE;
   2002         let mut buf = vec![0u8; json_len as usize];
   2003         reader.read_exact(&mut buf)?;
   2004 
   2005         Ok(JUMBFJSONContentBox::new(buf))
   2006     }
   2007 
   2008     pub fn read_cbor_box<R: Read + Seek>(
   2009         reader: &mut R,
   2010         size: u64,
   2011     ) -> JumbfParseResult<JUMBFCBORContentBox> {
   2012         let header =
   2013             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2014         if header.size == 0 {
   2015             // bad read, return empty box...
   2016             return Ok(JUMBFCBORContentBox::new(Vec::new()));
   2017         } else if header.size != size {
   2018             // this means that we started w/o the header...
   2019             unread_bytes(reader, HEADER_SIZE)?;
   2020         }
   2021 
   2022         let cbor_len = size - HEADER_SIZE;
   2023         let mut buf = vec![0u8; cbor_len as usize];
   2024         reader.read_exact(&mut buf)?;
   2025 
   2026         Ok(JUMBFCBORContentBox::new(buf))
   2027     }
   2028 
   2029     pub fn read_padding_box<R: Read + Seek>(
   2030         reader: &mut R,
   2031         size: u64,
   2032     ) -> JumbfParseResult<JUMBFPaddingContentBox> {
   2033         let header =
   2034             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2035         if header.size == 0 {
   2036             // bad read, return empty box...
   2037             return Ok(JUMBFPaddingContentBox::new(0));
   2038         } else if header.size != size {
   2039             // this means that we started w/o the header...
   2040             unread_bytes(reader, HEADER_SIZE)?;
   2041         }
   2042 
   2043         let padding_len = size - HEADER_SIZE;
   2044         let mut buf = vec![0u8; padding_len as usize];
   2045         reader.read_exact(&mut buf)?;
   2046 
   2047         Ok(JUMBFPaddingContentBox::new_with_vec(buf))
   2048     }
   2049 
   2050     pub fn read_jp2c_box<R: Read + Seek>(
   2051         reader: &mut R,
   2052         size: u64,
   2053     ) -> JumbfParseResult<JUMBFCodestreamContentBox> {
   2054         let header =
   2055             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2056         if header.size == 0 {
   2057             // bad read, return empty box...
   2058             return Ok(JUMBFCodestreamContentBox::new(Vec::new()));
   2059         } else if header.size != size {
   2060             // this means that we started w/o the header...
   2061             unread_bytes(reader, HEADER_SIZE)?;
   2062         }
   2063 
   2064         // read the data itself...
   2065         let data_len = size - HEADER_SIZE;
   2066         let mut buf = vec![0u8; data_len as usize];
   2067         reader.read_exact(&mut buf)?;
   2068 
   2069         Ok(JUMBFCodestreamContentBox::new(buf))
   2070     }
   2071 
   2072     pub fn read_uuid_box<R: Read + Seek>(
   2073         reader: &mut R,
   2074         size: u64,
   2075     ) -> JumbfParseResult<JUMBFUUIDContentBox> {
   2076         let header =
   2077             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2078         if header.size == 0 {
   2079             // bad read, return empty box...
   2080             return Ok(JUMBFUUIDContentBox::new(&[0u8; 16], Vec::new()));
   2081         } else if header.size != size {
   2082             // this means that we started w/o the header...
   2083             unread_bytes(reader, HEADER_SIZE)?;
   2084         }
   2085 
   2086         // now read the UUID
   2087         let mut uuid = [0u8; 16]; // 16 bytes of UUID
   2088         reader.read_exact(&mut uuid)?;
   2089 
   2090         // and finally the data itself...
   2091         let data_len = size - HEADER_SIZE - 16 /*UUID*/;
   2092         let mut buf = vec![0u8; data_len as usize];
   2093         reader.read_exact(&mut buf)?;
   2094 
   2095         Ok(JUMBFUUIDContentBox::new(&uuid, buf))
   2096     }
   2097 
   2098     pub fn read_embedded_media_desc_box<R: Read + Seek>(
   2099         reader: &mut R,
   2100         size: u64,
   2101     ) -> JumbfParseResult<JUMBFEmbeddedFileDescriptionBox> {
   2102         let header =
   2103             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2104         if header.size == 0 {
   2105             // bad read, return empty box...
   2106             return Ok(JUMBFEmbeddedFileDescriptionBox::new("".to_string(), None));
   2107         } else if header.size != size {
   2108             // this means that we started w/o the header...
   2109             unread_bytes(reader, HEADER_SIZE)?;
   2110         }
   2111 
   2112         //toggles: u8,                // media togles
   2113         //media_type: CString,        // file media type
   2114         //file_name: Option<CString>, // optional file name
   2115 
   2116         // now read the media_type
   2117         let mut togs = [0u8]; // 1 byte of toggles
   2118         reader.read_exact(&mut togs)?;
   2119 
   2120         // read the data itself...
   2121         let data_len = size - HEADER_SIZE - TOGGLE_SIZE;
   2122         let mut buf = vec![0u8; data_len as usize];
   2123         reader.read_exact(&mut buf)?;
   2124 
   2125         let (media_type, file_name) = match togs[0] {
   2126             1 => {
   2127                 // there may be two c strings in this vec
   2128                 match buf.iter().position(|&x| x == 0) {
   2129                     Some(pos) => {
   2130                         if pos != buf.len() - 1 {
   2131                             (buf, None)
   2132                         } else {
   2133                             let (first, second) = buf.split_at(pos);
   2134                             (first.to_vec(), Some(second.to_vec()))
   2135                         }
   2136                     }
   2137                     None => (buf, None),
   2138                 }
   2139             }
   2140             _ => {
   2141                 // we do not store the trailing 0 on load
   2142                 if buf[buf.len() - 1] == 0 {
   2143                     buf.pop();
   2144                 }
   2145 
   2146                 (buf, None)
   2147             }
   2148         };
   2149 
   2150         Ok(JUMBFEmbeddedFileDescriptionBox::from(
   2151             togs[0], media_type, file_name,
   2152         ))
   2153     }
   2154 
   2155     pub fn read_embedded_content_box<R: Read + Seek>(
   2156         reader: &mut R,
   2157         size: u64,
   2158     ) -> JumbfParseResult<JUMBFEmbeddedFileContentBox> {
   2159         let header =
   2160             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2161         if header.size == 0 {
   2162             // bad read, return empty box...
   2163             return Ok(JUMBFEmbeddedFileContentBox::new(Vec::new()));
   2164         } else if header.size != size {
   2165             // this means that we started w/o the header...
   2166             unread_bytes(reader, HEADER_SIZE)?;
   2167         }
   2168 
   2169         // read data itself...
   2170         let data_len = size - HEADER_SIZE;
   2171         let mut buf = vec![0u8; data_len as usize];
   2172         reader.read_exact(&mut buf)?;
   2173 
   2174         Ok(JUMBFEmbeddedFileContentBox::new(buf))
   2175     }
   2176 
   2177     pub fn read_super_box<R: Read + Seek>(reader: &mut R) -> JumbfParseResult<JUMBFSuperBox> {
   2178         // find out where we're starting...
   2179         let start_pos = current_pos(reader).map_err(|_| JumbfParseError::InvalidBoxRange)?;
   2180 
   2181         // start with the initial jumb
   2182         let jumb_header =
   2183             BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidJumbfHeader)?;
   2184         if jumb_header.name == BoxType::Empty {
   2185             return Err(JumbfParseError::UnexpectedEof);
   2186         } else if jumb_header.name != BoxType::Jumb {
   2187             return Err(JumbfParseError::InvalidJumbfHeader);
   2188         }
   2189 
   2190         // figure out where this particular box ends...
   2191         let dest_pos = start_pos + jumb_header.size;
   2192 
   2193         // now let's load the jumd
   2194         let jumd_header =
   2195             BoxReader::read_header(reader).map_err(|_| JumbfParseError::ExpectedJumdError)?;
   2196         if jumb_header.name == BoxType::Empty {
   2197             return Err(JumbfParseError::UnexpectedEof);
   2198         } else if jumd_header.name != BoxType::Jumd {
   2199             return Err(JumbfParseError::ExpectedJumdError);
   2200         }
   2201 
   2202         // load the description box & create a new superbox from it
   2203         let jdesc = BoxReader::read_desc_box(reader, jumd_header.size)
   2204             .map_err(|_| JumbfParseError::UnexpectedEof)?;
   2205 
   2206         if jdesc.label().is_empty() {
   2207             return Err(JumbfParseError::UnexpectedEof);
   2208         }
   2209         let box_label = jdesc.label();
   2210         debug!(
   2211             "{}",
   2212             format!("START#Label: {box_label:?}" /* jdesc.label() */)
   2213         );
   2214         let mut sbox = JUMBFSuperBox::from(jdesc);
   2215 
   2216         // read each following box and add it to the sbox
   2217         let mut found = true;
   2218         while found {
   2219             let box_header =
   2220                 BoxReader::read_header(reader).map_err(|_| JumbfParseError::InvalidJumbfHeader)?;
   2221             if box_header.name == BoxType::Empty {
   2222                 found = false;
   2223             } else {
   2224                 unread_bytes(reader, HEADER_SIZE)?; // seek back to the beginning of the box
   2225                 let next_box: Box<dyn BMFFBox> = match box_header.name {
   2226                     BoxType::Jumb => Box::new(
   2227                         BoxReader::read_super_box(reader)?, //.map_err(|_| JumbfParseError::InvalidJumbBox)?,
   2228                     ),
   2229                     BoxType::Json => Box::new(
   2230                         BoxReader::read_json_box(reader, box_header.size)
   2231                             .map_err(|_| JumbfParseError::InvalidJsonBox)?,
   2232                     ),
   2233                     BoxType::Cbor => Box::new(
   2234                         BoxReader::read_cbor_box(reader, box_header.size)
   2235                             .map_err(|_| JumbfParseError::InvalidCborBox)?,
   2236                     ),
   2237                     BoxType::Padding => Box::new(
   2238                         BoxReader::read_padding_box(reader, box_header.size)
   2239                             .map_err(|_| JumbfParseError::InvalidCborBox)?,
   2240                     ),
   2241                     BoxType::Jp2c => Box::new(
   2242                         BoxReader::read_jp2c_box(reader, box_header.size)
   2243                             .map_err(|_| JumbfParseError::InvalidJp2cBox)?,
   2244                     ),
   2245 
   2246                     BoxType::Uuid => Box::new(
   2247                         BoxReader::read_uuid_box(reader, box_header.size)
   2248                             .map_err(|_| JumbfParseError::InvalidUuidBox)?,
   2249                     ),
   2250                     BoxType::EmbedMediaDesc => Box::new(
   2251                         BoxReader::read_embedded_media_desc_box(reader, box_header.size)
   2252                             .map_err(|_| JumbfParseError::InvalidEmbeddedFileBox)?,
   2253                     ),
   2254                     BoxType::EmbedContent => Box::new(
   2255                         BoxReader::read_embedded_content_box(reader, box_header.size)
   2256                             .map_err(|_| JumbfParseError::InvalidEmbeddedFileBox)?,
   2257                     ),
   2258                     _ => {
   2259                         debug!("{}", format!("Unknown Boxtype: {:?}", box_header.name));
   2260                         // per the jumbf spec ignore unknown boxes so skip by if possible
   2261                         let header = BoxReader::read_header(reader)
   2262                             .map_err(|_| JumbfParseError::InvalidBoxHeader)?;
   2263                         if header.size == 0 {
   2264                             // bad read, return empty box...
   2265                             return Err(JumbfParseError::InvalidUnknownBox);
   2266                         } else if header.size != box_header.size {
   2267                             // this means that we started w/o the header...
   2268                             unread_bytes(reader, HEADER_SIZE)?;
   2269                         }
   2270 
   2271                         // read data itself...
   2272                         let data_len = box_header.size - HEADER_SIZE;
   2273                         let mut buf = vec![0u8; data_len as usize];
   2274                         reader.read_exact(&mut buf)?;
   2275                         continue;
   2276                     }
   2277                 };
   2278                 sbox.add_data_box(next_box);
   2279             }
   2280 
   2281             // if our current position is past the size, bail out...
   2282             if let Ok(p) = current_pos(reader) {
   2283                 if p >= dest_pos {
   2284                     found = false;
   2285                 }
   2286             }
   2287         }
   2288 
   2289         debug!(
   2290             "{}",
   2291             format!("END#Label: {box_label:?}" /* jdesc.label() */)
   2292         );
   2293 
   2294         // return the filled out sbox
   2295         Ok(sbox)
   2296     }
   2297 }
   2298 
   2299 // !SECTION
   2300 
   2301 //---------------
   2302 // SECTION Tests
   2303 //---------------
   2304 
   2305 #[cfg(test)]
   2306 pub mod tests {
   2307     #![allow(clippy::expect_used)]
   2308     #![allow(clippy::unwrap_used)]
   2309 
   2310     use std::io::Cursor;
   2311 
   2312     use extfmt::*;
   2313 
   2314     use super::*;
   2315 
   2316     // base_len = size (u32) + type (u32)
   2317     // desc_len = base + 16 (UUID type) + 1 (TOGGLE)
   2318     // cont_len = base
   2319     // sig_len  = base + 16 (UUID type)
   2320     const BOX_BASE_LEN: usize = 4 + 4;
   2321     const DESC_BOX_BASE: usize = BOX_BASE_LEN + 16 + 1;
   2322     const CONT_BOX_BASE: usize = BOX_BASE_LEN;
   2323     const SIG_BOX_BASE: usize = BOX_BASE_LEN + 16;
   2324     const EMBED_MEDIA_BASE: usize = BOX_BASE_LEN + 1;
   2325     const EMBED_DATA_BASE: usize = BOX_BASE_LEN;
   2326 
   2327     fn compute_desc_box_size(box_label: &str) -> usize {
   2328         DESC_BOX_BASE + box_label.len() + 1
   2329     }
   2330 
   2331     // len is base + desc (base + len(label) + 1 (null term))
   2332     fn compute_super_box_size(box_label: &str) -> usize {
   2333         let mut len = BOX_BASE_LEN;
   2334         len += compute_desc_box_size(box_label);
   2335         len
   2336     }
   2337 
   2338     fn compute_content_box_size(box_label: &str, data_size: usize) -> usize {
   2339         let content_box_expected_len = CONT_BOX_BASE + data_size;
   2340         let desc_box_expected_len = compute_desc_box_size(box_label);
   2341         BOX_BASE_LEN + desc_box_expected_len + content_box_expected_len
   2342     }
   2343 
   2344     fn compute_signature_box_size(sig_size: usize) -> usize {
   2345         let content_box_expected_len = SIG_BOX_BASE + sig_size;
   2346         let desc_box_expected_len = compute_desc_box_size(labels::SIGNATURE);
   2347         BOX_BASE_LEN + desc_box_expected_len + content_box_expected_len
   2348     }
   2349 
   2350     fn compute_media_type_box_size(media_type: &str, file_name: Option<&str>) -> usize {
   2351         let mut len = EMBED_MEDIA_BASE + media_type.len() + 1;
   2352         if let Some(f) = file_name {
   2353             len += f.len() + 1;
   2354         }
   2355         len
   2356     }
   2357 
   2358     fn compute_embedded_box_size(data_size: usize) -> usize {
   2359         EMBED_DATA_BASE + data_size
   2360     }
   2361 
   2362     fn compute_thumbnail_box_size(
   2363         box_label: &str,
   2364         data_size: usize,
   2365         media_type: &str,
   2366         file_name: Option<&str>,
   2367     ) -> usize {
   2368         let mut len = compute_super_box_size(box_label);
   2369         len += compute_media_type_box_size(media_type, file_name);
   2370         len += compute_embedded_box_size(data_size);
   2371         len
   2372     }
   2373 
   2374     // ANCHOR: DescBox
   2375     #[test]
   2376     fn description_box() {
   2377         let box_label = "test.descbox";
   2378         let jdb = JUMBFDescriptionBox::new(box_label, None);
   2379         let mut mem_box: Vec<u8> = Vec::new();
   2380 
   2381         jdb.write_box(&mut mem_box)
   2382             .expect("Unable to write description box");
   2383 
   2384         println!("DescriptionBox:\t{}", Hexlify(&mem_box));
   2385         assert_eq!(
   2386             format!("{}", Hexlify(&mem_box)),
   2387             "000000266a756d640000000000000000000000000000000003746573742e64657363626f7800"
   2388         );
   2389 
   2390         let expected_len = compute_desc_box_size(box_label);
   2391         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2392     }
   2393 
   2394     // ANCHOR: SuperBox
   2395     #[test]
   2396     fn super_box() {
   2397         let box_label = "test.superbox";
   2398         let jsb = JUMBFSuperBox::new(box_label, None);
   2399         let mut mem_box: Vec<u8> = Vec::new();
   2400 
   2401         jsb.write_box(&mut mem_box)
   2402             .expect("Unable to write superbox");
   2403 
   2404         let expected_len = compute_super_box_size(box_label);
   2405         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2406 
   2407         println!("SuperBox:\t{}", Hexlify(&mem_box));
   2408         assert_eq!(format!("{}", Hexlify(&mem_box)), "0000002f6a756d62000000276a756d640000000000000000000000000000000003746573742e7375706572626f7800");
   2409     }
   2410 
   2411     // ANCHOR: SuperBox + Data Box
   2412     #[test]
   2413     fn super_box_with_one_data_box() {
   2414         let box_label = "test.superbox_databox";
   2415         let mut jsb = JUMBFSuperBox::new(box_label, None);
   2416 
   2417         let data_box_label = "test.databox";
   2418         let jdb = Box::new(JUMBFSuperBox::new(data_box_label, None));
   2419         jsb.add_data_box(jdb);
   2420 
   2421         // now write it and see what we get!!
   2422         let mut mem_box: Vec<u8> = Vec::new();
   2423         jsb.write_box(&mut mem_box)
   2424             .expect("Unable to write superbox");
   2425 
   2426         let data_box_expected_len = compute_super_box_size(data_box_label);
   2427         let expected_len = data_box_expected_len + compute_super_box_size(box_label);
   2428         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2429 
   2430         println!("SuperBox + DataBox:\t{}", Hexlify(&mem_box));
   2431         assert_eq!(format!("{}", Hexlify(&mem_box)), "000000656a756d620000002f6a756d640000000000000000000000000000000003746573742e7375706572626f785f64617461626f78000000002e6a756d62000000266a756d640000000000000000000000000000000003746573742e64617461626f7800");
   2432     }
   2433 
   2434     // ANCHOR: Signature Box
   2435     #[test]
   2436     fn cai_signature_box() {
   2437         let mut sigb = CAISignatureBox::new();
   2438 
   2439         let some_data = String::from("this would normally be binary signature data...");
   2440         let sig_len = some_data.len();
   2441         let sigc = CAISignatureContentBox::new(some_data.into_bytes());
   2442         sigb.add_signature(Box::new(sigc));
   2443 
   2444         let mut mem_box: Vec<u8> = Vec::new();
   2445         sigb.write_box(&mut mem_box)
   2446             .expect("Unable to write CAI Signature");
   2447 
   2448         // expected_len is base + desc_box + content+box
   2449         let expected_len = compute_signature_box_size(sig_len);
   2450         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2451 
   2452         println!("CAISignatureBox:\t{}", Hexlify(&mem_box));
   2453         assert_eq!(format!("{}", Hexlify(&mem_box)), "000000776a756d62000000286a756d646332637300110010800000aa00389b7103633270612e7369676e61747572650000000047757569646332637300110010800000aa00389b717468697320776f756c64206e6f726d616c6c792062652062696e617279207369676e617475726520646174612e2e2e");
   2454     }
   2455 
   2456     // ANCHOR: Claim Box
   2457     #[test]
   2458     fn cai_claim_box() {
   2459         let mut cb = CAIClaimBox::new();
   2460 
   2461         let claim_json = String::from(
   2462             "{
   2463             \"recorder\" : \"Photoshop\",
   2464             \"parent_claim\" : \"self#jumbf=c_tpic_1/c2pa.claim?hl=6E6DD0923B57DCE\",
   2465             \"signature\" : \"self#jumbf=s_adbe_1\",
   2466             \"assertions\" : [
   2467                 \"self#jumbf=as_adbe_1/c2pa.identity?hl=45919681DCCAF6ABAD\",
   2468                 \"self#jumbf=as_adbe_1/c2pa.thumbnail.jpeg?hl=76142BD62363F\"
   2469             ],
   2470             \"redacted_assertions\" : [
   2471                 \"self#jumbf=as_tp_1/c2pa.location.precise\"
   2472             ],
   2473             \"asset_hashes\": []
   2474         }",
   2475         );
   2476 
   2477         let clen = claim_json.len();
   2478         let cjson = JUMBFJSONContentBox::new(claim_json.into_bytes());
   2479         cb.add_claim(Box::new(cjson));
   2480 
   2481         let mut mem_box: Vec<u8> = Vec::new();
   2482         cb.write_box(&mut mem_box)
   2483             .expect("Unable to write CAI Claim");
   2484 
   2485         let expected_len = compute_content_box_size(labels::CLAIM, clen);
   2486         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2487 
   2488         println!("CAIClaimBox:\t{}", Hexlify(&mem_box));
   2489         assert_eq!(format!("{}", Hexlify(&mem_box)), "0000023b6a756d62000000246a756d646332636c00110010800000aa00389b7103633270612e636c61696d000000020f6a736f6e7b0a202020202020202020202020227265636f7264657222203a202250686f746f73686f70222c0a20202020202020202020202022706172656e745f636c61696d22203a202273656c66236a756d62663d635f747069635f312f633270612e636c61696d3f686c3d364536444430393233423537444345222c0a202020202020202020202020227369676e617475726522203a202273656c66236a756d62663d735f616462655f31222c0a20202020202020202020202022617373657274696f6e7322203a205b0a202020202020202020202020202020202273656c66236a756d62663d61735f616462655f312f633270612e6964656e746974793f686c3d343539313936383144434341463641424144222c0a202020202020202020202020202020202273656c66236a756d62663d61735f616462655f312f633270612e7468756d626e61696c2e6a7065673f686c3d37363134324244363233363346220a2020202020202020202020205d2c0a2020202020202020202020202272656461637465645f617373657274696f6e7322203a205b0a202020202020202020202020202020202273656c66236a756d62663d61735f74705f312f633270612e6c6f636174696f6e2e70726563697365220a2020202020202020202020205d2c0a2020202020202020202020202261737365745f686173686573223a205b5d0a20202020202020207d");
   2490     }
   2491 
   2492     // ANCHOR: Location assertion
   2493     #[test]
   2494     fn cai_location_assertion_box() {
   2495         let box_label = "c2pa.location.broad";
   2496         let location = String::from("{ \"location\": \"San Francisco\"}");
   2497         let loc_len = location.len();
   2498 
   2499         let mut cb = CAIJSONAssertionBox::new(box_label);
   2500         cb.add_json(location.into_bytes());
   2501 
   2502         let mut mem_box: Vec<u8> = Vec::new();
   2503         cb.write_box(&mut mem_box)
   2504             .expect("Unable to write location.broad assertion");
   2505 
   2506         let expected_len = compute_content_box_size(box_label, loc_len);
   2507         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2508 
   2509         println!("CAI Broad Location:\t{}", Hexlify(&mem_box));
   2510         assert_eq!(format!("{}", Hexlify(&mem_box)), "0000005b6a756d620000002d6a756d646a736f6e00110010800000aa00389b7103633270612e6c6f636174696f6e2e62726f616400000000266a736f6e7b20226c6f636174696f6e223a202253616e204672616e636973636f227d");
   2511     }
   2512 
   2513     // ANCHOR: Assertion Store
   2514     #[test]
   2515     fn assertion_store() {
   2516         // create the assertion store
   2517         let mut a_store = CAIAssertionStore::new();
   2518 
   2519         // create some assertions & add to the store
   2520         let th_box_label = "c2pa.claim.thumbnail";
   2521         let img = String::from("<image data goes here>");
   2522         let img_len = img.len();
   2523         let mut tb = JumbfEmbeddedFileBox::new(th_box_label);
   2524         tb.add_data(img.into_bytes(), "image/jpeg".to_string(), None);
   2525         a_store.add_assertion(Box::new(tb));
   2526         let tb_len = compute_thumbnail_box_size(th_box_label, img_len, "image/jpeg", None);
   2527 
   2528         let id_box_label = "c2pa.identity";
   2529         let identity = String::from("{ \"uri\": \"did:adobe:lrosenth@adobe.com\"}");
   2530         let id_len = identity.len();
   2531         let mut ib = CAIJSONAssertionBox::new(id_box_label);
   2532         ib.add_json(identity.into_bytes());
   2533         a_store.add_assertion(Box::new(ib));
   2534         let ib_len = compute_content_box_size(id_box_label, id_len);
   2535 
   2536         // write it to memory
   2537         let mut mem_box: Vec<u8> = Vec::new();
   2538         a_store
   2539             .write_box(&mut mem_box)
   2540             .expect("Unable to write assertion store");
   2541 
   2542         // and test the results
   2543         let store_sup_len = compute_super_box_size("c2pa.assertions");
   2544         let expected_len = store_sup_len + tb_len + ib_len;
   2545         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2546 
   2547         println!("CAI Assertion Store:\t{}", Hexlify(&mem_box));
   2548         assert_eq!(format!("{}", Hexlify(&mem_box)), "000000f86a756d62000000296a756d646332617300110010800000aa00389b7103633270612e617373657274696f6e7300000000686a756d620000002e6a756d6440cb0c32bb8a489da70b2ad6f47f436903633270612e636c61696d2e7468756d626e61696c00000000146266646200696d6167652f6a706567000000001e626964623c696d616765206461746120676f657320686572653e0000005f6a756d62000000276a756d646a736f6e00110010800000aa00389b7103633270612e6964656e7469747900000000306a736f6e7b2022757269223a20226469643a61646f62653a6c726f73656e74684061646f62652e636f6d227d");
   2549     }
   2550 
   2551     // ANCHOR: CAI Store
   2552     #[test]
   2553     fn cai_store() {
   2554         // create the CAI store
   2555         let store_label = "cb.adobe_1";
   2556         let mut cai_store = CAIStore::new(store_label, false);
   2557 
   2558         // create the assertion store
   2559         let mut a_store = CAIAssertionStore::new();
   2560 
   2561         // create an assertions & add to the store
   2562         let th_box_label = "c2pa.claim.thumbnail";
   2563         let img = String::from("<image data goes here>");
   2564         let img_len = img.len();
   2565         let mut tb = JumbfEmbeddedFileBox::new(th_box_label);
   2566         tb.add_data(img.into_bytes(), "image/jpeg".to_string(), None);
   2567         a_store.add_assertion(Box::new(tb));
   2568 
   2569         // add the assertion store to the cai store
   2570         cai_store.add_box(Box::new(a_store));
   2571 
   2572         // create a claim & add it to the cai store
   2573         let mut cb = CAIClaimBox::new();
   2574         let claim_json = String::from(
   2575             "{
   2576             \"recorder\" : \"Photoshop\",
   2577             \"signature\" : \"self#jumbf=s_adobe_1\",
   2578             \"assertions\" : [
   2579                 \"self#jumbf=as_adobe_1/c2pa.thumbnail.jpeg?hl=76142BD62363F\"
   2580             ]
   2581         }",
   2582         );
   2583 
   2584         let clen = claim_json.len();
   2585         let cjson = JUMBFJSONContentBox::new(claim_json.into_bytes());
   2586         cb.add_claim(Box::new(cjson));
   2587         cai_store.add_box(Box::new(cb));
   2588 
   2589         // create a signature & add to the cai store
   2590         let mut sigb = CAISignatureBox::new();
   2591         let some_data = String::from("this would normally be binary signature data...");
   2592         let sig_len = some_data.len();
   2593         let sigc = CAISignatureContentBox::new(some_data.into_bytes());
   2594         sigb.add_signature(Box::new(sigc));
   2595         cai_store.add_box(Box::new(sigb));
   2596 
   2597         // write it to memory
   2598         let mut mem_box: Vec<u8> = Vec::new();
   2599         cai_store
   2600             .write_box(&mut mem_box)
   2601             .expect("Unable to write CAI store");
   2602 
   2603         // and test the results
   2604         let cai_store_sup_len = compute_super_box_size(store_label);
   2605         let a_store_sup_len = compute_super_box_size("c2pa.assertions");
   2606         let tb_len = compute_thumbnail_box_size(th_box_label, img_len, "image/jpeg", None);
   2607         let claim_len = compute_content_box_size(labels::CLAIM, clen);
   2608         let sig_box_len = compute_signature_box_size(sig_len);
   2609         let expected_len = cai_store_sup_len + a_store_sup_len + tb_len + claim_len + sig_box_len;
   2610         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2611 
   2612         println!("C2PA Store:\t{}", Hexlify(&mem_box));
   2613         assert_eq!(format!("{}", Hexlify(&mem_box)), "0000024b6a756d62000000246a756d6463326d6100110010800000aa00389b710363622e61646f62655f3100000000996a756d62000000296a756d646332617300110010800000aa00389b7103633270612e617373657274696f6e7300000000686a756d620000002e6a756d6440cb0c32bb8a489da70b2ad6f47f436903633270612e636c61696d2e7468756d626e61696c00000000146266646200696d6167652f6a706567000000001e626964623c696d616765206461746120676f657320686572653e0000010f6a756d62000000246a756d646332636c00110010800000aa00389b7103633270612e636c61696d00000000e36a736f6e7b0a202020202020202020202020227265636f7264657222203a202250686f746f73686f70222c0a202020202020202020202020227369676e617475726522203a202273656c66236a756d62663d735f61646f62655f31222c0a20202020202020202020202022617373657274696f6e7322203a205b0a202020202020202020202020202020202273656c66236a756d62663d61735f61646f62655f312f633270612e7468756d626e61696c2e6a7065673f686c3d37363134324244363233363346220a2020202020202020202020205d0a20202020202020207d000000776a756d62000000286a756d646332637300110010800000aa00389b7103633270612e7369676e61747572650000000047757569646332637300110010800000aa00389b717468697320776f756c64206e6f726d616c6c792062652062696e617279207369676e617475726520646174612e2e2e");
   2614     }
   2615 
   2616     // ANCHOR: CAI block
   2617     #[test]
   2618     fn cai_block() {
   2619         // create the CAI block
   2620         let mut cai_block = Cai::new();
   2621 
   2622         // create the CAI store
   2623         let store_label = "cb.adobe_1";
   2624         let mut cai_store = CAIStore::new(store_label, false);
   2625 
   2626         // create the assertion store
   2627         let mut a_store = CAIAssertionStore::new();
   2628 
   2629         // create an assertions & add to the store
   2630         let loc_box_label = "c2pa.location.broad";
   2631         let location = String::from("{ \"location\": \"Margate City, NJ\"}");
   2632         let loc_len = location.len();
   2633         let mut loc_box = CAIJSONAssertionBox::new(loc_box_label);
   2634         loc_box.add_json(location.into_bytes());
   2635         a_store.add_assertion(Box::new(loc_box));
   2636 
   2637         // add the assertion store to the cai store
   2638         cai_store.add_box(Box::new(a_store));
   2639 
   2640         // create a claim & add it to the cai store
   2641         let mut cb = CAIClaimBox::new();
   2642         let claim_json = String::from(
   2643             "{
   2644             \"recorder\" : \"Photoshop\",
   2645             \"signature\" : \"self#jumbf=s_adobe_1\",
   2646             \"assertions\" : [
   2647                 \"self#jumbf=as_adobe_1/c2pa.location.broad?hl=76142BD62363F\"
   2648             ]
   2649         }",
   2650         );
   2651 
   2652         let clen = claim_json.len();
   2653         let cjson = JUMBFJSONContentBox::new(claim_json.into_bytes());
   2654         cb.add_claim(Box::new(cjson));
   2655         cai_store.add_box(Box::new(cb));
   2656 
   2657         // create a signature & add to the cai store
   2658         let mut sigb = CAISignatureBox::new();
   2659         let some_data = String::from("this would normally be binary signature data...");
   2660         let sig_len = some_data.len();
   2661         let sigc = CAISignatureContentBox::new(some_data.into_bytes());
   2662         sigb.add_signature(Box::new(sigc));
   2663         cai_store.add_box(Box::new(sigb));
   2664 
   2665         // finally add the completed cai store into the cai block
   2666         cai_block.add_box(Box::new(cai_store));
   2667 
   2668         // write it to memory
   2669         let mut mem_box: Vec<u8> = Vec::new();
   2670         cai_block
   2671             .write_box(&mut mem_box)
   2672             .expect("Unable to write CAI block");
   2673 
   2674         // and test the results
   2675         let cai_block_sup_len = compute_super_box_size(labels::MANIFEST_STORE);
   2676         let cai_store_sup_len = compute_super_box_size(store_label);
   2677         let a_store_sup_len = compute_super_box_size("c2pa.assertions");
   2678         let lb_len = compute_content_box_size(loc_box_label, loc_len);
   2679         let claim_len = compute_content_box_size(labels::CLAIM, clen);
   2680         let sig_box_len = compute_signature_box_size(sig_len);
   2681 
   2682         let expected_len = cai_block_sup_len
   2683             + cai_store_sup_len
   2684             + a_store_sup_len
   2685             + lb_len
   2686             + claim_len
   2687             + sig_box_len;
   2688 
   2689         assert_eq!(mem_box.len(), expected_len); // make sure the length is correct
   2690 
   2691         println!("CAI Block:\t{}", Hexlify(&mem_box));
   2692         assert_eq!(format!("{}", Hexlify(&mem_box)), "000002676a756d620000001e6a756d646332706100110010800000aa00389b71036332706100000002416a756d62000000246a756d6463326d6100110010800000aa00389b710363622e61646f62655f31000000008f6a756d62000000296a756d646332617300110010800000aa00389b7103633270612e617373657274696f6e73000000005e6a756d620000002d6a756d646a736f6e00110010800000aa00389b7103633270612e6c6f636174696f6e2e62726f616400000000296a736f6e7b20226c6f636174696f6e223a20224d61726761746520436974792c204e4a227d0000010f6a756d62000000246a756d646332636c00110010800000aa00389b7103633270612e636c61696d00000000e36a736f6e7b0a202020202020202020202020227265636f7264657222203a202250686f746f73686f70222c0a202020202020202020202020227369676e617475726522203a202273656c66236a756d62663d735f61646f62655f31222c0a20202020202020202020202022617373657274696f6e7322203a205b0a202020202020202020202020202020202273656c66236a756d62663d61735f61646f62655f312f633270612e6c6f636174696f6e2e62726f61643f686c3d37363134324244363233363346220a2020202020202020202020205d0a20202020202020207d000000776a756d62000000286a756d646332637300110010800000aa00389b7103633270612e7369676e61747572650000000047757569646332637300110010800000aa00389b717468697320776f756c64206e6f726d616c6c792062652062696e617279207369676e617475726520646174612e2e2e");
   2693     }
   2694 
   2695     // ANCHOR: JUMB BlockReader
   2696     #[test]
   2697     fn jumb_box_reader() {
   2698         const JUMB_TEST: &str = "000000026A756D62";
   2699         let buffer = hex::decode(JUMB_TEST).expect("decode failed");
   2700         let mut buf_reader = Cursor::new(buffer);
   2701         let jumb_header = BoxReader::read_header(&mut buf_reader).unwrap();
   2702         assert_eq!(jumb_header.size, 2);
   2703         assert_eq!(jumb_header.name, BoxType::Jumb);
   2704     }
   2705 
   2706     // ANCHOR: DescriptionBox Reader
   2707     /*
   2708      #[test]
   2709      fn desc_box_reader() {
   2710          const JUMD_DESC: &str =
   2711              "000000256A756D62000000216A756D646332706100110010800000AA00389B7103633270612E763100";
   2712          let buffer = hex::decode(JUMD_DESC).expect("decode failed");
   2713          let mut buf_reader = Cursor::new(buffer);
   2714 
   2715          let jumb_header = BoxReader::read_header(&mut buf_reader).unwrap();
   2716          assert_eq!(jumb_header.size, 0x25);
   2717          assert_eq!(jumb_header.name, BoxType::JumbBox);
   2718 
   2719          let jumd_header = BoxReader::read_header(&mut buf_reader).unwrap();
   2720          assert_eq!(jumd_header.size, 0x21);
   2721          assert_eq!(jumd_header.name, BoxType::JumdBox);
   2722 
   2723          let desc_box = BoxReader::read_desc_box(&mut buf_reader, jumd_header.size).unwrap();
   2724          assert_eq!(desc_box.label(), labels::MANIFEST_STORE);
   2725          assert_eq!(desc_box.uuid(), "6332706100110010800000AA00389B71");
   2726      }
   2727     */
   2728     // ANCHOR: JSON Content Box Reader
   2729     #[test]
   2730     fn json_box_reader() {
   2731         const JSON_BOX: &str ="0000005a6a756d620000002d6a756d646a736f6e00110010800000aa00389b7103633270612e6c6f636174696f6e2e62726f616400000000266a736f6e7b20226c6f636174696f6e223a202253616e204672616e636973636f227d";
   2732 
   2733         let buffer = hex::decode(JSON_BOX).expect("decode failed");
   2734         let mut buf_reader = Cursor::new(buffer);
   2735         let super_box = BoxReader::read_super_box(&mut buf_reader).unwrap();
   2736 
   2737         let desc_box = super_box.desc_box();
   2738         assert_eq!(desc_box.label(), "c2pa.location.broad");
   2739         assert_eq!(desc_box.uuid(), CAI_JSON_ASSERTION_UUID);
   2740         assert_eq!(super_box.data_box_count(), 1);
   2741 
   2742         let json_box = super_box.data_box_as_json_box(0).unwrap();
   2743         assert_eq!(json_box.box_uuid(), JUMBF_JSON_UUID);
   2744         assert_eq!(json_box.json().len(), 30);
   2745     }
   2746 
   2747     #[allow(dead_code)]
   2748     fn check_one_box(
   2749         parent_box: &JUMBFSuperBox,
   2750         index: usize,
   2751         count: usize,
   2752         label: &str,
   2753         uuid: &str,
   2754     ) {
   2755         let superbox = parent_box.data_box_as_superbox(index).unwrap();
   2756         assert_eq!(superbox.box_uuid(), JUMB_FOURCC);
   2757         assert_eq!(superbox.data_box_count(), count);
   2758 
   2759         let desc_box = superbox.desc_box();
   2760         assert_eq!(desc_box.label(), label);
   2761         assert_eq!(desc_box.uuid(), uuid);
   2762     }
   2763 
   2764     // ANCHOR: Full CAI Block Reader
   2765     /*
   2766     #[test]
   2767     fn cai_box_reader() {
   2768         const CAI_BOX: &str ="0000026a6a756d62000000216a756d646332706100110010800000AA00389B71036332706100000002446a756d62000000246a756d6463326D6100110010800000AA00389B710363622e61646f62655f31000000008f6a756d62000000296a756d646332617300110010800000AA00389B7103633270612e617373657274696f6e73000000005e6a756d620000002d6a756d646a736f6e00110010800000aa00389b7103633270612e6c6f636174696f6e2e62726f616400000000296a736f6e7b20226c6f636174696f6e223a20224d61726761746520436974792c204e4a227d000001126a756d62000000276a756d646332636C00110010800000AA00389B7103633270612e636c61696d2e763100000000e36a736f6e7b0a202020202020202020202020227265636f7264657222203a202250686f746f73686f70222c0a202020202020202020202020227369676e617475726522203a202273656c66236a756d62663d735f61646f62655f31222c0a20202020202020202020202022617373657274696f6e7322203a205b0a202020202020202020202020202020202273656c66236a756d62663d61735f61646f62655f312f633270612e6c6f636174696f6e2e62726f61643f686c3d37363134324244363233363346220a2020202020202020202020205d0a20202020202020207d000000776a756d62000000286a756d646332637300110010800000AA00389B7103633270612e7369676e61747572650000000047757569646332637300110010800000AA00389B717468697320776f756c64206e6f726d616c6c792062652062696e617279207369676e617475726520646174612e2e2e";
   2769 
   2770         let buffer = hex::decode(CAI_BOX).expect("decode failed");
   2771         let mut buf_reader = Cursor::new(buffer);
   2772 
   2773         // this loads up all the boxes...
   2774         let super_box = BoxReader::read_super_box(&mut buf_reader).unwrap();
   2775         let cai_block = Cai::from(super_box);
   2776 
   2777         // check the CAI Block
   2778         let desc_box = cai_block.desc_box();
   2779         assert_eq!(desc_box.label(), labels::MANIFEST_STORE);
   2780         assert_eq!(desc_box.uuid(), CAI_BLOCK_UUID);
   2781 
   2782         // it's children are the CAI stores
   2783         // for this test, we only have one...
   2784         assert_eq!(cai_block.data_box_count(), 1);
   2785 
   2786         // retrieve the CAI store & validate it
   2787         // a standard one has 3 children (assertion store, claim & sig)
   2788         check_one_box(&cai_block.super_box(), 0, 3, "cb.adobe_1", CAI_STORE_UUID);
   2789         let cai_store_box = cai_block.store();
   2790 
   2791         // retrieve the assertion store & validate
   2792         check_one_box(
   2793             &cai_store_box,
   2794             0,
   2795             1,
   2796             "c2pa.assertions",
   2797             CAI_ASSERTION_STORE_UUID,
   2798         );
   2799 
   2800         let assertion_store_box = cai_store_box.data_box_as_superbox(0);
   2801 
   2802         // there is only one in our test, but doing a loop on general principle
   2803         let num_assertions = assertion_store_box.data_box_count();
   2804         assert_eq!(num_assertions, 1);
   2805 
   2806         for idx in 0..num_assertions {
   2807             check_one_box(
   2808                 &assertion_store_box,
   2809                 idx,
   2810                 1,
   2811                 "c2pa.location.broad",
   2812                 CAI_JSON_ASSERTION_UUID,
   2813             );
   2814 
   2815             let assertion_box = assertion_store_box.data_box_as_superbox(idx);
   2816             let assertion_desc_box = assertion_box.desc_box();
   2817 
   2818             if assertion_desc_box.uuid() == CAI_JSON_ASSERTION_UUID {
   2819                 let json_box = assertion_box.data_box_as_json_box(0);
   2820                 assert_eq!(json_box.box_uuid(), JUMBF_JSON_UUID);
   2821                 assert_eq!(json_box.json().len(), 33);
   2822             } else if assertion_desc_box.uuid() == CAI_CODESTREAM_ASSERTION_UUID {
   2823                 // this is where we'd validate for a thumbnail if we had one...
   2824             }
   2825         }
   2826 
   2827         // retrieve the claim & validate
   2828         check_one_box(&cai_store_box, 1, 1, "c2pa.claim.v1", CAI_CLAIM_UUID);
   2829         let claim_superbox = cai_store_box.data_box_as_superbox(1);
   2830         let claim_desc_box = claim_superbox.desc_box();
   2831 
   2832         if claim_desc_box.uuid() == CAI_JSON_ASSERTION_UUID {
   2833             // better be, but just in case...
   2834             let json_box = claim_superbox.data_box_as_json_box(0);
   2835             assert_eq!(json_box.box_uuid(), JUMBF_JSON_UUID);
   2836             assert_eq!(json_box.json().len(), 164);
   2837         }
   2838 
   2839         // retrieve the signature & validate
   2840         check_one_box(&cai_store_box, 2, 1, "c2pa.signature", CAI_SIGNATURE_UUID);
   2841         let sig_superbox = cai_store_box.data_box_as_superbox(2);
   2842         let sig_desc_box = sig_superbox.desc_box();
   2843         if sig_desc_box.uuid() == CAI_SIGNATURE_UUID {
   2844             // better be, but just in case...
   2845             let sig_box = sig_superbox.data_box_as_uuid_box(0);
   2846             assert_eq!(sig_box.box_uuid(), JUMBF_UUID_UUID);
   2847             assert_eq!(sig_box.data().len(), 47);
   2848         }
   2849     }
   2850     */
   2851 }
   2852 
   2853 // !SECTION