1use {
8 crate::{format::XarChecksum, Error, XarResult},
9 base64::{engine::general_purpose::STANDARD as STANDARD_ENGINE, Engine},
10 digest::DynDigest,
11 serde::Deserialize,
12 std::{
13 fmt::{Display, Formatter},
14 io::{Read, Write},
15 ops::{Deref, DerefMut},
16 },
17 x509_certificate::{CapturedX509Certificate, X509CertificateError},
18 xml::{
19 common::XmlVersion,
20 writer::{EmitterConfig, EventWriter, XmlEvent},
21 },
22};
23
24#[derive(Clone, Debug, Deserialize)]
26#[serde(deny_unknown_fields)]
27pub struct TableOfContents {
28 toc: XarToC,
29}
30
31impl Deref for TableOfContents {
32 type Target = XarToC;
33
34 fn deref(&self) -> &Self::Target {
35 &self.toc
36 }
37}
38
39impl DerefMut for TableOfContents {
40 fn deref_mut(&mut self) -> &mut Self::Target {
41 &mut self.toc
42 }
43}
44
45impl TableOfContents {
46 pub fn from_reader(reader: impl Read) -> XarResult<Self> {
48 Ok(serde_xml_rs::from_reader(reader)?)
49 }
50
51 pub fn files(&self) -> XarResult<Vec<(String, File)>> {
57 let mut files = self
58 .toc
59 .files
60 .iter()
61 .map(|f| f.files(None))
62 .collect::<XarResult<Vec<_>>>()?
63 .into_iter()
64 .flat_map(|x| x.into_iter())
65 .collect::<Vec<_>>();
66
67 files.sort_by(|a, b| a.1.id.cmp(&b.1.id));
68
69 Ok(files)
70 }
71
72 pub fn to_xml(&self) -> XarResult<Vec<u8>> {
73 let mut emitter = EmitterConfig::new().create_writer(std::io::BufWriter::new(vec![]));
74 self.write_xml(&mut emitter)?;
75
76 emitter
77 .into_inner()
78 .into_inner()
79 .map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))
80 }
81
82 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>) -> XarResult<()> {
83 writer.write(XmlEvent::StartDocument {
84 version: XmlVersion::Version10,
85 encoding: Some("UTF-8"),
86 standalone: None,
87 })?;
88
89 writer.write(XmlEvent::start_element("xar"))?;
90 writer.write(XmlEvent::start_element("toc"))?;
91
92 writer.write(XmlEvent::start_element("creation-time"))?;
93 writer.write(XmlEvent::characters(&self.creation_time))?;
94 writer.write(XmlEvent::end_element())?;
95
96 self.checksum.write_xml(writer)?;
97
98 if let Some(sig) = &self.signature {
99 sig.write_xml(writer, "signature")?;
100 }
101 if let Some(sig) = &self.x_signature {
102 sig.write_xml(writer, "x-signature")?;
103 }
104
105 for file in &self.files {
106 file.write_xml(writer)?;
107 }
108
109 writer.write(XmlEvent::end_element().name("toc"))?;
110 writer.write(XmlEvent::end_element().name("xar"))?;
111
112 Ok(())
113 }
114}
115
116#[derive(Clone, Debug, Deserialize)]
118#[serde(deny_unknown_fields, rename_all = "kebab-case")]
119pub struct XarToC {
120 pub creation_time: String,
121 pub checksum: Checksum,
122 #[serde(rename = "file")]
123 pub files: Vec<File>,
124 pub signature: Option<Signature>,
125 pub x_signature: Option<Signature>,
126}
127
128impl XarToC {
129 pub fn signatures(&self) -> Vec<&Signature> {
131 let mut res = vec![];
132 if let Some(sig) = &self.signature {
133 res.push(sig);
134 }
135 if let Some(sig) = &self.x_signature {
136 res.push(sig);
137 }
138
139 res
140 }
141
142 pub fn find_signature(&self, style: SignatureStyle) -> Option<&Signature> {
144 self.signatures().into_iter().find(|sig| sig.style == style)
145 }
146
147 pub fn visit_files_mut(&mut self, cb: &dyn Fn(&mut File)) {
148 for file in self.files.iter_mut() {
149 cb(file);
150 file.visit_files_mut(cb);
151 }
152 }
153}
154
155#[derive(Clone, Debug, Deserialize)]
156#[serde(deny_unknown_fields)]
157pub struct Checksum {
158 pub style: ChecksumType,
160
161 pub offset: u64,
163
164 pub size: u64,
166}
167
168impl Checksum {
169 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>) -> XarResult<()> {
170 writer.write(XmlEvent::start_element("checksum").attr("style", &self.style.to_string()))?;
171 writer.write(XmlEvent::start_element("offset"))?;
172 writer.write(XmlEvent::characters(&format!("{}", self.offset)))?;
173 writer.write(XmlEvent::end_element())?;
174 writer.write(XmlEvent::start_element("size"))?;
175 writer.write(XmlEvent::characters(&format!("{}", self.size)))?;
176 writer.write(XmlEvent::end_element())?;
177 writer.write(XmlEvent::end_element().name("checksum"))?;
178
179 Ok(())
180 }
181}
182
183#[derive(Clone, Copy, Debug, Deserialize)]
184#[serde(rename_all = "lowercase")]
185pub enum ChecksumType {
186 #[serde(alias = "NONE")]
187 None,
188 #[serde(alias = "SHA1")]
189 Sha1,
190 #[serde(alias = "SHA256")]
191 Sha256,
192 #[serde(alias = "SHA512")]
193 Sha512,
194 #[serde(alias = "MD5")]
195 Md5,
196}
197
198impl Display for ChecksumType {
199 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
200 match self {
201 Self::None => f.write_str("none"),
202 Self::Sha1 => f.write_str("sha1"),
203 Self::Sha256 => f.write_str("sha256"),
204 Self::Sha512 => f.write_str("sha512"),
205 Self::Md5 => f.write_str("md5"),
206 }
207 }
208}
209
210impl TryFrom<XarChecksum> for ChecksumType {
211 type Error = Error;
212
213 fn try_from(v: XarChecksum) -> Result<Self, Self::Error> {
214 match v {
215 XarChecksum::None => Ok(Self::None),
216 XarChecksum::Sha1 => Ok(Self::Sha1),
217 XarChecksum::Md5 => Ok(Self::Md5),
218 XarChecksum::Sha256 => Ok(Self::Sha256),
219 XarChecksum::Sha512 => Ok(Self::Sha512),
220 XarChecksum::Other(_) => Err(Error::Unsupported("unknown checksum type")),
221 }
222 }
223}
224
225impl From<ChecksumType> for XarChecksum {
226 fn from(v: ChecksumType) -> Self {
227 match v {
228 ChecksumType::None => Self::None,
229 ChecksumType::Sha1 => Self::Sha1,
230 ChecksumType::Sha256 => Self::Sha256,
231 ChecksumType::Sha512 => Self::Sha512,
232 ChecksumType::Md5 => Self::Md5,
233 }
234 }
235}
236
237impl ChecksumType {
238 pub fn digest_data(&self, data: &[u8]) -> XarResult<Vec<u8>> {
240 let mut h: Box<dyn DynDigest> = match self {
241 Self::None => return Err(Error::Unsupported("cannot digest None checksum")),
242 Self::Md5 => Box::<md5::Md5>::default(),
243 Self::Sha1 => Box::<sha1::Sha1>::default(),
244 Self::Sha256 => Box::<sha2::Sha256>::default(),
245 Self::Sha512 => Box::<sha2::Sha512>::default(),
246 };
247
248 h.update(data);
249
250 Ok(h.finalize().to_vec())
251 }
252}
253
254#[derive(Clone, Debug, Deserialize)]
255#[serde(deny_unknown_fields)]
256pub struct File {
257 pub id: u64,
258 pub ctime: Option<String>,
259 pub mtime: Option<String>,
260 pub atime: Option<String>,
261 #[serde(rename = "name")]
266 pub names: Vec<String>,
267 #[serde(rename = "type")]
268 pub file_type: FileType,
269 pub mode: Option<String>,
270 pub deviceno: Option<u32>,
271 pub inode: Option<u64>,
272 pub uid: Option<u32>,
273 pub gid: Option<u32>,
274 pub user: Option<String>,
275 pub group: Option<String>,
276 pub size: Option<u64>,
277 pub data: Option<FileData>,
278 pub ea: Option<Ea>,
279 #[serde(rename = "FinderCreateTime")]
280 pub finder_create_time: Option<FinderCreateTime>,
281 #[serde(default, rename = "file")]
282 pub files: Vec<File>,
283}
284
285impl File {
286 pub fn files(&self, directory: Option<&str>) -> XarResult<Vec<(String, File)>> {
287 let name = self
288 .names
289 .iter()
290 .last()
291 .ok_or(Error::TableOfContentsCorrupted("missing file name"))?;
292
293 let full_path = if let Some(d) = directory {
294 format!("{d}/{name}")
295 } else {
296 name.clone()
297 };
298
299 let mut files = vec![(full_path.clone(), self.clone())];
300
301 for f in &self.files {
302 files.extend(f.files(Some(&full_path))?);
303 }
304
305 Ok(files)
306 }
307
308 pub fn visit_files_mut(&mut self, cb: &dyn Fn(&mut File)) {
309 for f in self.files.iter_mut() {
310 cb(f);
311 f.visit_files_mut(cb)
312 }
313 }
314
315 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>) -> XarResult<()> {
316 writer.write(XmlEvent::start_element("file").attr("id", &format!("{}", self.id)))?;
317
318 if let Some(data) = &self.data {
319 data.write_xml(writer)?;
320 }
321
322 if let Some(fct) = &self.finder_create_time {
323 writer.write(XmlEvent::start_element("FinderCreateTime"))?;
324
325 writer.write(XmlEvent::start_element("nanoseconds"))?;
326 writer.write(XmlEvent::characters(&format!("{}", fct.nanoseconds)))?;
327 writer.write(XmlEvent::end_element())?;
328
329 writer.write(XmlEvent::start_element("time"))?;
330 writer.write(XmlEvent::characters(&fct.time))?;
331 writer.write(XmlEvent::end_element())?;
332
333 writer.write(XmlEvent::end_element())?;
334 }
335
336 if let Some(time) = &self.ctime {
337 writer.write(XmlEvent::start_element("ctime"))?;
338 writer.write(XmlEvent::characters(time))?;
339 writer.write(XmlEvent::end_element())?;
340 }
341
342 if let Some(time) = &self.mtime {
343 writer.write(XmlEvent::start_element("mtime"))?;
344 writer.write(XmlEvent::characters(time))?;
345 writer.write(XmlEvent::end_element())?;
346 }
347
348 if let Some(time) = &self.atime {
349 writer.write(XmlEvent::start_element("atime"))?;
350 writer.write(XmlEvent::characters(time))?;
351 writer.write(XmlEvent::end_element())?;
352 }
353
354 if let Some(v) = &self.group {
355 writer.write(XmlEvent::start_element("group"))?;
356 writer.write(XmlEvent::characters(v))?;
357 writer.write(XmlEvent::end_element())?;
358 }
359
360 if let Some(v) = &self.gid {
361 writer.write(XmlEvent::start_element("gid"))?;
362 writer.write(XmlEvent::characters(&format!("{v}")))?;
363 writer.write(XmlEvent::end_element())?;
364 }
365
366 if let Some(v) = &self.user {
367 writer.write(XmlEvent::start_element("user"))?;
368 writer.write(XmlEvent::characters(v))?;
369 writer.write(XmlEvent::end_element())?;
370 }
371
372 if let Some(v) = &self.uid {
373 writer.write(XmlEvent::start_element("uid"))?;
374 writer.write(XmlEvent::characters(&format!("{v}")))?;
375 writer.write(XmlEvent::end_element())?;
376 }
377
378 if let Some(v) = &self.mode {
379 writer.write(XmlEvent::start_element("mode"))?;
380 writer.write(XmlEvent::characters(v))?;
381 writer.write(XmlEvent::end_element())?;
382 }
383
384 if let Some(v) = &self.deviceno {
385 writer.write(XmlEvent::start_element("deviceno"))?;
386 writer.write(XmlEvent::characters(&format!("{v}")))?;
387 writer.write(XmlEvent::end_element())?;
388 }
389
390 if let Some(v) = &self.inode {
391 writer.write(XmlEvent::start_element("inode"))?;
392 writer.write(XmlEvent::characters(&format!("{v}")))?;
393 writer.write(XmlEvent::end_element())?;
394 }
395
396 if let Some(ea) = &self.ea {
397 ea.write_xml(writer)?;
398 }
399
400 writer.write(XmlEvent::start_element("type"))?;
401 writer.write(XmlEvent::characters(&self.file_type.to_string()))?;
402 writer.write(XmlEvent::end_element())?;
403
404 for name in &self.names {
405 writer.write(XmlEvent::start_element("name"))?;
406 writer.write(XmlEvent::characters(name))?;
407 writer.write(XmlEvent::end_element())?;
408 }
409
410 for file in &self.files {
411 file.write_xml(writer)?;
412 }
413
414 writer.write(XmlEvent::end_element().name("file"))?;
415
416 Ok(())
417 }
418}
419
420#[derive(Clone, Copy, Debug, Deserialize)]
421#[serde(rename_all = "lowercase")]
422pub enum FileType {
423 File,
424 Directory,
425 HardLink,
426 Link,
427}
428
429impl Display for FileType {
430 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
431 match self {
432 FileType::File => f.write_str("file"),
433 FileType::Directory => f.write_str("directory"),
434 FileType::HardLink => f.write_str("hardlink"),
435 FileType::Link => f.write_str("symlink"),
436 }
437 }
438}
439
440#[derive(Clone, Debug, Deserialize)]
441#[serde(deny_unknown_fields, rename_all = "kebab-case")]
442pub struct FileData {
443 pub offset: u64,
444 pub size: u64,
445 pub length: u64,
446 pub extracted_checksum: FileChecksum,
447 pub archived_checksum: FileChecksum,
448 pub encoding: FileEncoding,
449}
450
451impl FileData {
452 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>) -> XarResult<()> {
453 writer.write(XmlEvent::start_element("data"))?;
454
455 writer.write(XmlEvent::start_element("length"))?;
456 writer.write(XmlEvent::characters(&format!("{}", self.length)))?;
457 writer.write(XmlEvent::end_element())?;
458
459 writer.write(XmlEvent::start_element("offset"))?;
460 writer.write(XmlEvent::characters(&format!("{}", self.offset)))?;
461 writer.write(XmlEvent::end_element())?;
462
463 writer.write(XmlEvent::start_element("size"))?;
464 writer.write(XmlEvent::characters(&format!("{}", self.size)))?;
465 writer.write(XmlEvent::end_element())?;
466
467 writer.write(XmlEvent::start_element("encoding").attr("style", &self.encoding.style))?;
468 writer.write(XmlEvent::end_element())?;
469
470 self.extracted_checksum
471 .write_xml(writer, "extracted-checksum")?;
472 self.archived_checksum
473 .write_xml(writer, "archived-checksum")?;
474
475 writer.write(XmlEvent::end_element().name("data"))?;
476
477 Ok(())
478 }
479}
480
481#[derive(Clone, Debug, Deserialize)]
482#[serde(deny_unknown_fields)]
483pub struct FileChecksum {
484 pub style: ChecksumType,
485 #[serde(rename = "$value")]
486 pub checksum: String,
487}
488
489impl FileChecksum {
490 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>, name: &str) -> XarResult<()> {
491 writer.write(XmlEvent::start_element(name).attr("style", &self.style.to_string()))?;
492 writer.write(XmlEvent::characters(&self.checksum))?;
493 writer.write(XmlEvent::end_element())?;
494
495 Ok(())
496 }
497}
498
499#[derive(Clone, Debug, Deserialize)]
500#[serde(deny_unknown_fields)]
501pub struct FileEncoding {
502 pub style: String,
503}
504
505#[derive(Clone, Debug, Deserialize)]
506#[serde(deny_unknown_fields, rename_all = "kebab-case")]
507pub struct Ea {
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub id: Option<u64>,
510 pub name: String,
511 pub offset: u64,
512 pub size: u64,
513 pub length: u64,
514 pub extracted_checksum: FileChecksum,
515 pub archived_checksum: FileChecksum,
516 pub encoding: FileEncoding,
517}
518
519impl Ea {
520 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>) -> XarResult<()> {
521 let mut ea = XmlEvent::start_element("ea");
522
523 let id = self.id.map(|x| format!("{}", x));
524
525 if let Some(id) = &id {
526 ea = ea.attr("id", id.as_str());
527 }
528
529 writer.write(ea)?;
530
531 writer.write(XmlEvent::start_element("name"))?;
532 writer.write(XmlEvent::characters(&self.name))?;
533 writer.write(XmlEvent::end_element())?;
534
535 writer.write(XmlEvent::start_element("offset"))?;
536 writer.write(XmlEvent::characters(&format!("{}", self.offset)))?;
537 writer.write(XmlEvent::end_element())?;
538
539 writer.write(XmlEvent::start_element("size"))?;
540 writer.write(XmlEvent::characters(&format!("{}", self.size)))?;
541 writer.write(XmlEvent::end_element())?;
542
543 writer.write(XmlEvent::start_element("length"))?;
544 writer.write(XmlEvent::characters(&format!("{}", self.length)))?;
545 writer.write(XmlEvent::end_element())?;
546
547 self.extracted_checksum
548 .write_xml(writer, "extracted-checksum")?;
549 self.archived_checksum
550 .write_xml(writer, "archived-checksum")?;
551
552 writer.write(XmlEvent::start_element("encoding").attr("style", &self.encoding.style))?;
553 writer.write(XmlEvent::end_element())?;
554
555 writer.write(XmlEvent::end_element())?;
556
557 Ok(())
558 }
559}
560
561#[derive(Clone, Debug, Deserialize)]
562#[serde(deny_unknown_fields)]
563pub struct FinderCreateTime {
564 pub nanoseconds: u64,
565 pub time: String,
566}
567
568#[derive(Clone, Debug, Deserialize)]
569#[serde(deny_unknown_fields)]
570pub struct Signature {
571 pub style: SignatureStyle,
572 pub offset: u64,
573 pub size: u64,
574 #[serde(rename = "KeyInfo")]
575 pub key_info: KeyInfo,
576}
577
578impl Signature {
579 pub fn x509_certificates(&self) -> XarResult<Vec<CapturedX509Certificate>> {
581 self.key_info.x509_certificates()
582 }
583
584 pub fn write_xml<W: Write>(&self, writer: &mut EventWriter<W>, name: &str) -> XarResult<()> {
585 writer.write(XmlEvent::start_element(name).attr("style", &self.style.to_string()))?;
586
587 writer.write(XmlEvent::start_element("offset"))?;
588 writer.write(XmlEvent::characters(&format!("{}", &self.offset)))?;
589 writer.write(XmlEvent::end_element())?;
590
591 writer.write(XmlEvent::start_element("size"))?;
592 writer.write(XmlEvent::characters(&format!("{}", &self.size)))?;
593 writer.write(XmlEvent::end_element())?;
594
595 writer.write(
596 XmlEvent::start_element("KeyInfo").ns("", "http://www.w3.org/2000/09/xmldsig#"),
597 )?;
598 writer.write(XmlEvent::start_element("X509Data"))?;
599
600 for cert in &self.key_info.x509_data.x509_certificate {
601 writer.write(XmlEvent::start_element("X509Certificate"))?;
602 writer.write(XmlEvent::characters(cert))?;
603 writer.write(XmlEvent::end_element())?;
604 }
605
606 writer.write(XmlEvent::end_element().name("X509Data"))?;
607 writer.write(XmlEvent::end_element().name("KeyInfo"))?;
608
609 writer.write(XmlEvent::end_element().name(name))?;
610
611 Ok(())
612 }
613}
614
615#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
616#[serde(rename_all = "UPPERCASE")]
617pub enum SignatureStyle {
618 Cms,
620
621 Rsa,
623}
624
625impl Display for SignatureStyle {
626 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
627 match self {
628 Self::Cms => f.write_str("CMS"),
629 Self::Rsa => f.write_str("RSA"),
630 }
631 }
632}
633
634#[derive(Clone, Debug, Deserialize)]
635#[serde(deny_unknown_fields)]
636pub struct KeyInfo {
637 #[serde(rename = "X509Data")]
638 pub x509_data: X509Data,
639}
640
641impl KeyInfo {
642 pub fn from_certificates<'a>(
644 certs: impl Iterator<Item = &'a CapturedX509Certificate>,
645 ) -> XarResult<Self> {
646 Ok(Self {
647 x509_data: X509Data {
648 x509_certificate: certs
649 .map(|cert| {
650 let der = cert.encode_der()?;
651 let s = STANDARD_ENGINE.encode(der);
652
653 let mut lines = vec![];
654
655 let mut remaining = s.as_str();
656
657 loop {
658 if remaining.len() > 72 {
659 let res = remaining.split_at(72);
660 lines.push(res.0);
661 remaining = res.1;
662 } else {
663 lines.push(remaining);
664 break;
665 }
666 }
667
668 Ok(lines.join("\n"))
669 })
670 .collect::<XarResult<Vec<_>>>()?,
671 },
672 })
673 }
674
675 pub fn x509_certificates(&self) -> XarResult<Vec<CapturedX509Certificate>> {
677 self.x509_data.x509_certificates()
678 }
679}
680
681#[derive(Clone, Debug, Deserialize)]
682#[serde(deny_unknown_fields)]
683pub struct X509Data {
684 #[serde(rename = "X509Certificate")]
685 pub x509_certificate: Vec<String>,
686}
687
688impl X509Data {
689 pub fn x509_certificates(&self) -> XarResult<Vec<CapturedX509Certificate>> {
691 Ok(self
692 .x509_certificate
693 .iter()
694 .map(|data| {
695 let data = format!(
698 "-----BEGIN CERTIFICATE-----\r\n{data}\r\n-----END CERTIFICATE-----\r\n"
699 );
700
701 CapturedX509Certificate::from_pem(data)
702 })
703 .collect::<Result<Vec<_>, X509CertificateError>>()?)
704 }
705}
706
707#[cfg(test)]
708mod test {
709 use super::*;
710
711 #[test]
712 fn file_checksum_serialize() -> XarResult<()> {
713 let xml = "<checksum style=\"sha1\">checksum</checksum>";
714
715 let _: FileChecksum = serde_xml_rs::from_reader(std::io::BufReader::new(xml.as_bytes()))?;
716
717 let xml = "<checksum style=\"SHA1\">checksum</checksum>";
719 let _: FileChecksum = serde_xml_rs::from_reader(std::io::BufReader::new(xml.as_bytes()))?;
720
721 Ok(())
722 }
723}