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