1use crate::bytes::{le_u16, le_u32, le_u64, read_guid, slice_owned};
10
11const FVE_SIGNATURE: &[u8; 8] = b"-FVE-FS-";
12
13pub const ENTRY_TYPE_VMK: u16 = 0x0002;
15pub const ENTRY_TYPE_FVEK: u16 = 0x0003;
17pub const ENTRY_TYPE_VOLUME_HEADER: u16 = 0x000f;
19
20pub const VALUE_TYPE_AES_CCM: u16 = 0x0005;
22pub const VALUE_TYPE_STRETCH: u16 = 0x0003;
24pub const VALUE_TYPE_VMK: u16 = 0x0008;
26
27pub const PROTECTION_PASSWORD: u16 = 0x2000;
29pub const PROTECTION_RECOVERY: u16 = 0x0800;
31
32#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct MetadataEntry {
36 pub entry_type: u16,
38 pub value_type: u16,
40 pub version: u16,
42 pub data: Vec<u8>,
44}
45
46impl MetadataEntry {
47 #[must_use]
54 pub fn parse_sequence(data: &[u8]) -> Vec<MetadataEntry> {
55 let mut out = Vec::new();
56 let mut pos = 0usize;
57 while pos + 8 <= data.len() {
58 let size = le_u16(data, pos) as usize;
59 if size < 8 || pos + size > data.len() {
60 break;
61 }
62 out.push(MetadataEntry {
63 entry_type: le_u16(data, pos + 2),
64 value_type: le_u16(data, pos + 4),
65 version: le_u16(data, pos + 6),
66 data: slice_owned(data, pos + 8, size - 8),
67 });
68 pos += size;
69 }
70 out
71 }
72
73 #[must_use]
75 pub fn nested(&self, offset: usize) -> Vec<MetadataEntry> {
76 let start = offset.min(self.data.len());
77 Self::parse_sequence(&self.data[start..])
78 }
79
80 #[must_use]
82 pub fn is_vmk(&self) -> bool {
83 self.entry_type == ENTRY_TYPE_VMK && self.value_type == VALUE_TYPE_VMK
84 }
85
86 #[must_use]
89 pub fn protection_type(&self) -> Option<u16> {
90 self.is_vmk().then(|| le_u16(&self.data, 26))
91 }
92}
93
94#[derive(Debug, Clone)]
98pub struct FveMetadata {
99 pub encryption_method: u16,
101 pub volume_guid: [u8; 16],
103 pub creation_time: u64,
105 pub entries: Vec<MetadataEntry>,
107 pub encrypted_volume_size: u64,
110 pub volume_header_offset: u64,
112 pub volume_header_size: u64,
114 pub metadata_offsets: [u64; 3],
116 pub metadata_size: u32,
118}
119
120impl FveMetadata {
121 #[must_use]
126 pub fn parse(block: &[u8], bytes_per_sector: u16) -> Option<FveMetadata> {
127 if block.get(0..8) != Some(FVE_SIGNATURE.as_slice()) {
128 return None;
129 }
130
131 let encrypted_volume_size = le_u64(block, 16);
132 let num_volume_header_sectors = le_u32(block, 28);
133 let metadata_offsets = [le_u64(block, 32), le_u64(block, 40), le_u64(block, 48)];
134 let block_volume_header_offset = le_u64(block, 56);
135
136 let mh = 64usize;
138 let metadata_size = le_u32(block, mh);
139 let volume_guid = read_guid(block, mh + 16);
140 let encryption_method = le_u16(block, mh + 36);
141 let creation_time = le_u64(block, mh + 40);
142
143 let entries_start = mh + 48;
145 let entries_end = (mh + metadata_size as usize).min(block.len());
146 let entries = if entries_end > entries_start {
147 MetadataEntry::parse_sequence(&block[entries_start..entries_end])
148 } else {
149 Vec::new()
150 };
151
152 let mut volume_header_offset = block_volume_header_offset;
155 let mut volume_header_size =
156 u64::from(num_volume_header_sectors) * u64::from(bytes_per_sector);
157 if let Some(e) = entries
158 .iter()
159 .find(|e| e.entry_type == ENTRY_TYPE_VOLUME_HEADER)
160 {
161 let bo = le_u64(&e.data, 0);
162 let bs = le_u64(&e.data, 8);
163 if bo != 0 {
164 volume_header_offset = bo;
165 }
166 if bs != 0 {
167 volume_header_size = bs;
168 }
169 }
170
171 Some(FveMetadata {
172 encryption_method,
173 volume_guid,
174 creation_time,
175 entries,
176 encrypted_volume_size,
177 volume_header_offset,
178 volume_header_size,
179 metadata_offsets,
180 metadata_size,
181 })
182 }
183
184 pub fn vmk_entries(&self) -> impl Iterator<Item = &MetadataEntry> {
186 self.entries.iter().filter(|e| e.is_vmk())
187 }
188
189 #[must_use]
191 pub fn protector_types(&self) -> Vec<u16> {
192 self.vmk_entries()
193 .filter_map(MetadataEntry::protection_type)
194 .collect()
195 }
196
197 #[must_use]
199 pub fn fvek_entry(&self) -> Option<&MetadataEntry> {
200 self.entries
201 .iter()
202 .find(|e| e.entry_type == ENTRY_TYPE_FVEK && e.value_type == VALUE_TYPE_AES_CCM)
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 fn entry_bytes(entry_type: u16, value_type: u16, version: u16, data: &[u8]) -> Vec<u8> {
211 let size = (8 + data.len()) as u16;
212 let mut v = Vec::new();
213 v.extend_from_slice(&size.to_le_bytes());
214 v.extend_from_slice(&entry_type.to_le_bytes());
215 v.extend_from_slice(&value_type.to_le_bytes());
216 v.extend_from_slice(&version.to_le_bytes());
217 v.extend_from_slice(data);
218 v
219 }
220
221 #[test]
222 fn parse_sequence_splits_entries() {
223 let mut buf = Vec::new();
224 buf.extend(entry_bytes(0x000f, 0x000f, 1, &[1, 2, 3, 4]));
225 buf.extend(entry_bytes(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, 1, &[9; 20]));
226 let entries = MetadataEntry::parse_sequence(&buf);
227 assert_eq!(entries.len(), 2);
228 assert_eq!(entries[0].entry_type, 0x000f);
229 assert_eq!(entries[0].data, vec![1, 2, 3, 4]);
230 assert!(entries[1].is_vmk());
231 }
232
233 #[test]
234 fn parse_sequence_stops_on_lying_size() {
235 let mut buf = entry_bytes(1, 2, 1, &[]);
237 buf.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); let entries = MetadataEntry::parse_sequence(&buf);
239 assert_eq!(entries.len(), 1);
240 }
241
242 #[test]
243 fn parse_sequence_stops_on_oversize() {
244 let mut buf = Vec::new();
246 buf.extend_from_slice(&100u16.to_le_bytes());
247 buf.extend_from_slice(&[0u8; 6]);
248 assert!(MetadataEntry::parse_sequence(&buf).is_empty());
249 }
250
251 fn build_block(entries: &[Vec<u8>]) -> Vec<u8> {
252 let mut entry_bytes = Vec::new();
253 for e in entries {
254 entry_bytes.extend_from_slice(e);
255 }
256 let metadata_size = 48 + entry_bytes.len();
257 let mut block = vec![0u8; 64 + metadata_size];
258 block[0..8].copy_from_slice(FVE_SIGNATURE);
259 block[10..12].copy_from_slice(&2u16.to_le_bytes()); block[16..24].copy_from_slice(&0x0400_0000u64.to_le_bytes()); block[28..32].copy_from_slice(&16u32.to_le_bytes()); block[56..64].copy_from_slice(&0x0211_0800u64.to_le_bytes()); block[64..68].copy_from_slice(&(metadata_size as u32).to_le_bytes());
265 block[64 + 16..64 + 32].copy_from_slice(&[0xAB; 16]); block[64 + 36..64 + 38].copy_from_slice(&0x8000u16.to_le_bytes()); block[64 + 40..64 + 48].copy_from_slice(&130_461_864_497_281_120u64.to_le_bytes());
268 block[64 + 48..].copy_from_slice(&entry_bytes);
269 block
270 }
271
272 #[test]
273 fn parse_full_block() {
274 let mut vh_data = Vec::new();
276 vh_data.extend_from_slice(&0x0211_0800u64.to_le_bytes()); vh_data.extend_from_slice(&0x0051_5a00u64.to_le_bytes()); let vh = entry_bytes(
279 ENTRY_TYPE_VOLUME_HEADER,
280 ENTRY_TYPE_VOLUME_HEADER,
281 1,
282 &vh_data,
283 );
284
285 let mut vmk_data = vec![0u8; 28];
286 vmk_data[26..28].copy_from_slice(&PROTECTION_PASSWORD.to_le_bytes());
287 let vmk = entry_bytes(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, 1, &vmk_data);
288
289 let block = build_block(&[vh, vmk]);
290 let m = FveMetadata::parse(&block, 512).unwrap();
291 assert_eq!(m.encryption_method, 0x8000);
292 assert_eq!(m.volume_guid, [0xAB; 16]);
293 assert_eq!(m.creation_time, 130_461_864_497_281_120);
294 assert_eq!(m.encrypted_volume_size, 0x0400_0000);
295 assert_eq!(m.volume_header_offset, 0x0211_0800);
296 assert_eq!(m.volume_header_size, 0x0051_5a00); assert_eq!(m.entries.len(), 2);
298 assert_eq!(m.protector_types(), vec![PROTECTION_PASSWORD]);
299 }
300
301 #[test]
302 fn parse_returns_none_without_signature() {
303 let block = vec![0u8; 128];
304 assert!(FveMetadata::parse(&block, 512).is_none());
305 }
306
307 #[test]
308 fn volume_header_size_falls_back_to_sector_count() {
309 let block = build_block(&[]);
311 let m = FveMetadata::parse(&block, 512).unwrap();
312 assert_eq!(m.volume_header_size, 16 * 512);
313 assert_eq!(m.volume_header_offset, 0x0211_0800);
314 }
315
316 #[test]
317 fn truncated_block_does_not_panic() {
318 let mut block = build_block(&[]);
319 block.truncate(70);
320 let _ = FveMetadata::parse(&block, 512);
321 }
322}