dmg_oxide/
koly.rs

1use anyhow::Result;
2use byteorder::{ReadBytesExt, WriteBytesExt, BE};
3use std::io::{Read, Seek, SeekFrom, Write};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub struct UdifChecksum {
7    pub r#type: u32,
8    pub size: u32,
9    pub data: [u8; 128],
10}
11
12impl Default for UdifChecksum {
13    fn default() -> Self {
14        Self {
15            r#type: 2,
16            size: 32,
17            data: [0; 128],
18        }
19    }
20}
21
22impl UdifChecksum {
23    pub fn new(crc32: u32) -> Self {
24        let mut data = [0; 128];
25        data[..4].copy_from_slice(&crc32.to_be_bytes());
26        Self {
27            data,
28            ..Default::default()
29        }
30    }
31
32    pub fn from_bytes(bytes: &[u8]) -> Self {
33        Self::new(crc32fast::hash(bytes))
34    }
35
36    pub fn read_from<R: Read>(r: &mut R) -> Result<Self> {
37        let r#type = r.read_u32::<BE>()?;
38        let size = r.read_u32::<BE>()?;
39        let mut data = [0; 128];
40        r.read_exact(&mut data)?;
41        Ok(Self { r#type, size, data })
42    }
43
44    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
45        w.write_u32::<BE>(self.r#type)?;
46        w.write_u32::<BE>(self.size)?;
47        w.write_all(&self.data)?;
48        Ok(())
49    }
50}
51
52impl From<UdifChecksum> for u32 {
53    fn from(checksum: UdifChecksum) -> Self {
54        let mut data = [0; 4];
55        data.copy_from_slice(&checksum.data[..4]);
56        u32::from_be_bytes(data)
57    }
58}
59
60const KOLY_SIZE: i64 = 512;
61
62/// DMG trailer describing file content.
63///
64/// This is the main structure defining a DMG.
65#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub struct KolyTrailer {
67    // "koly" signature: [u8; 4],
68    pub version: u32,
69    //header_size: u32,
70    pub flags: u32,
71    pub running_data_fork_offset: u64,
72    pub data_fork_offset: u64,
73    pub data_fork_length: u64,
74    pub rsrc_fork_offset: u64,
75    pub rsrc_fork_length: u64,
76    pub segment_number: u32,
77    pub segment_count: u32,
78    pub segment_id: [u8; 16],
79    pub data_fork_digest: UdifChecksum,
80    pub plist_offset: u64,
81    pub plist_length: u64,
82    pub reserved1: [u8; 64],
83    pub code_signature_offset: u64,
84    pub code_signature_size: u64,
85    pub reserved2: [u8; 40],
86    pub main_digest: UdifChecksum,
87    pub image_variant: u32,
88    pub sector_count: u64,
89    pub reserved3: [u8; 12],
90}
91
92impl Default for KolyTrailer {
93    fn default() -> Self {
94        Self {
95            version: 4,
96            flags: 1,
97            running_data_fork_offset: 0,
98            data_fork_offset: 0,
99            data_fork_length: 0,
100            rsrc_fork_offset: 0,
101            rsrc_fork_length: 0,
102            segment_number: 1,
103            segment_count: 1,
104            segment_id: [0; 16],
105            data_fork_digest: UdifChecksum::default(),
106            plist_offset: 0,
107            plist_length: 0,
108            reserved1: [0; 64],
109            code_signature_offset: 0,
110            code_signature_size: 0,
111            reserved2: [0; 40],
112            main_digest: UdifChecksum::default(),
113            image_variant: 1,
114            sector_count: 0,
115            reserved3: [0; 12],
116        }
117    }
118}
119
120impl KolyTrailer {
121    pub fn new(
122        bytes: u64,
123        sectors: u64,
124        plist_offset: u64,
125        plist_length: u64,
126        data_digest: u32,
127        main_digest: u32,
128    ) -> Self {
129        let mut segment_id = [0; 16];
130        getrandom::getrandom(&mut segment_id).unwrap();
131        Self {
132            data_fork_length: bytes,
133            sector_count: sectors,
134            plist_offset,
135            plist_length,
136            data_fork_digest: UdifChecksum::new(data_digest),
137            main_digest: UdifChecksum::new(main_digest),
138            segment_id,
139            ..Default::default()
140        }
141    }
142
143    /// Construct an instance by reading from a seekable reader.
144    ///
145    /// The trailer is the final 512 bytes of the seekable stream.
146    pub fn read_from<R: Read + Seek>(r: &mut R) -> Result<Self> {
147        r.seek(SeekFrom::End(-KOLY_SIZE))?;
148
149        let mut signature = [0; 4];
150        r.read_exact(&mut signature)?;
151        anyhow::ensure!(&signature == b"koly");
152        let version = r.read_u32::<BE>()?;
153        let header_size = r.read_u32::<BE>()?;
154        anyhow::ensure!(header_size == 512);
155        let flags = r.read_u32::<BE>()?;
156        let running_data_fork_offset = r.read_u64::<BE>()?;
157        let data_fork_offset = r.read_u64::<BE>()?;
158        let data_fork_length = r.read_u64::<BE>()?;
159        let rsrc_fork_offset = r.read_u64::<BE>()?;
160        let rsrc_fork_length = r.read_u64::<BE>()?;
161        let segment_number = r.read_u32::<BE>()?;
162        let segment_count = r.read_u32::<BE>()?;
163        let mut segment_id = [0; 16];
164        r.read_exact(&mut segment_id)?;
165        let data_fork_digest = UdifChecksum::read_from(r)?;
166        let plist_offset = r.read_u64::<BE>()?;
167        let plist_length = r.read_u64::<BE>()?;
168        let mut reserved1 = [0; 64];
169        r.read_exact(&mut reserved1)?;
170        let code_signature_offset = r.read_u64::<BE>()?;
171        let code_signature_size = r.read_u64::<BE>()?;
172        let mut reserved2 = [0; 40];
173        r.read_exact(&mut reserved2)?;
174        let main_digest = UdifChecksum::read_from(r)?;
175        let image_variant = r.read_u32::<BE>()?;
176        let sector_count = r.read_u64::<BE>()?;
177        let mut reserved3 = [0; 12];
178        r.read_exact(&mut reserved3)?;
179        Ok(Self {
180            version,
181            flags,
182            running_data_fork_offset,
183            data_fork_offset,
184            data_fork_length,
185            rsrc_fork_offset,
186            rsrc_fork_length,
187            segment_number,
188            segment_count,
189            segment_id,
190            data_fork_digest,
191            plist_offset,
192            plist_length,
193            reserved1,
194            code_signature_offset,
195            code_signature_size,
196            reserved2,
197            main_digest,
198            image_variant,
199            sector_count,
200            reserved3,
201        })
202    }
203
204    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
205        w.write_all(b"koly")?;
206        w.write_u32::<BE>(self.version)?;
207        w.write_u32::<BE>(KOLY_SIZE as u32)?;
208        w.write_u32::<BE>(self.flags)?;
209        w.write_u64::<BE>(self.running_data_fork_offset)?;
210        w.write_u64::<BE>(self.data_fork_offset)?;
211        w.write_u64::<BE>(self.data_fork_length)?;
212        w.write_u64::<BE>(self.rsrc_fork_offset)?;
213        w.write_u64::<BE>(self.rsrc_fork_length)?;
214        w.write_u32::<BE>(self.segment_number)?;
215        w.write_u32::<BE>(self.segment_count)?;
216        w.write_all(&self.segment_id)?;
217        self.data_fork_digest.write_to(w)?;
218        w.write_u64::<BE>(self.plist_offset)?;
219        w.write_u64::<BE>(self.plist_length)?;
220        w.write_all(&self.reserved1)?;
221        w.write_u64::<BE>(self.code_signature_offset)?;
222        w.write_u64::<BE>(self.code_signature_size)?;
223        w.write_all(&self.reserved2)?;
224        self.main_digest.write_to(w)?;
225        w.write_u32::<BE>(self.image_variant)?;
226        w.write_u64::<BE>(self.sector_count)?;
227        w.write_all(&self.reserved3)?;
228        Ok(())
229    }
230}