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_KEY: u16 = 0x0001;
23pub const VALUE_TYPE_AES_CCM: u16 = 0x0005;
25pub const VALUE_TYPE_EXTERNAL_KEY: u16 = 0x0009;
28pub const VALUE_TYPE_STRETCH: u16 = 0x0003;
30pub const VALUE_TYPE_VMK: u16 = 0x0008;
32
33pub const PROTECTION_PASSWORD: u16 = 0x2000;
35pub const PROTECTION_RECOVERY: u16 = 0x0800;
37pub const PROTECTION_STARTUP_KEY: u16 = 0x0200;
40pub const PROTECTION_CLEAR: u16 = 0x0000;
43
44#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct MetadataEntry {
48 pub entry_type: u16,
50 pub value_type: u16,
52 pub version: u16,
54 pub data: Vec<u8>,
56}
57
58impl MetadataEntry {
59 #[must_use]
66 pub fn parse_sequence(data: &[u8]) -> Vec<MetadataEntry> {
67 let mut out = Vec::new();
68 let mut pos = 0usize;
69 while pos + 8 <= data.len() {
70 let size = le_u16(data, pos) as usize;
71 if size < 8 || pos + size > data.len() {
72 break;
73 }
74 out.push(MetadataEntry {
75 entry_type: le_u16(data, pos + 2),
76 value_type: le_u16(data, pos + 4),
77 version: le_u16(data, pos + 6),
78 data: slice_owned(data, pos + 8, size - 8),
79 });
80 pos += size;
81 }
82 out
83 }
84
85 #[must_use]
87 pub fn nested(&self, offset: usize) -> Vec<MetadataEntry> {
88 let start = offset.min(self.data.len());
89 Self::parse_sequence(&self.data[start..])
90 }
91
92 #[must_use]
94 pub fn is_vmk(&self) -> bool {
95 self.entry_type == ENTRY_TYPE_VMK && self.value_type == VALUE_TYPE_VMK
96 }
97
98 #[must_use]
101 pub fn protection_type(&self) -> Option<u16> {
102 self.is_vmk().then(|| le_u16(&self.data, 26))
103 }
104}
105
106#[derive(Debug, Clone)]
110pub struct FveMetadata {
111 pub encryption_method: u16,
113 pub volume_guid: [u8; 16],
115 pub creation_time: u64,
117 pub entries: Vec<MetadataEntry>,
119 pub encrypted_volume_size: u64,
122 pub volume_header_offset: u64,
124 pub volume_header_size: u64,
126 pub metadata_offsets: [u64; 3],
128 pub metadata_size: u32,
130}
131
132impl FveMetadata {
133 #[must_use]
138 pub fn parse(block: &[u8], bytes_per_sector: u16) -> Option<FveMetadata> {
139 if block.get(0..8) != Some(FVE_SIGNATURE.as_slice()) {
140 return None;
141 }
142
143 let encrypted_volume_size = le_u64(block, 16);
144 let num_volume_header_sectors = le_u32(block, 28);
145 let metadata_offsets = [le_u64(block, 32), le_u64(block, 40), le_u64(block, 48)];
146 let block_volume_header_offset = le_u64(block, 56);
147
148 let mh = 64usize;
150 let metadata_size = le_u32(block, mh);
151 let volume_guid = read_guid(block, mh + 16);
152 let encryption_method = le_u16(block, mh + 36);
153 let creation_time = le_u64(block, mh + 40);
154
155 let entries_start = mh + 48;
157 let entries_end = (mh + metadata_size as usize).min(block.len());
158 let entries = if entries_end > entries_start {
159 MetadataEntry::parse_sequence(&block[entries_start..entries_end])
160 } else {
161 Vec::new()
162 };
163
164 let mut volume_header_offset = block_volume_header_offset;
167 let mut volume_header_size =
168 u64::from(num_volume_header_sectors) * u64::from(bytes_per_sector);
169 if let Some(e) = entries
170 .iter()
171 .find(|e| e.entry_type == ENTRY_TYPE_VOLUME_HEADER)
172 {
173 let bo = le_u64(&e.data, 0);
174 let bs = le_u64(&e.data, 8);
175 if bo != 0 {
176 volume_header_offset = bo;
177 }
178 if bs != 0 {
179 volume_header_size = bs;
180 }
181 }
182
183 Some(FveMetadata {
184 encryption_method,
185 volume_guid,
186 creation_time,
187 entries,
188 encrypted_volume_size,
189 volume_header_offset,
190 volume_header_size,
191 metadata_offsets,
192 metadata_size,
193 })
194 }
195
196 pub fn vmk_entries(&self) -> impl Iterator<Item = &MetadataEntry> {
198 self.entries.iter().filter(|e| e.is_vmk())
199 }
200
201 #[must_use]
203 pub fn protector_types(&self) -> Vec<u16> {
204 self.vmk_entries()
205 .filter_map(MetadataEntry::protection_type)
206 .collect()
207 }
208
209 #[must_use]
211 pub fn fvek_entry(&self) -> Option<&MetadataEntry> {
212 self.entries
213 .iter()
214 .find(|e| e.entry_type == ENTRY_TYPE_FVEK && e.value_type == VALUE_TYPE_AES_CCM)
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 fn entry_bytes(entry_type: u16, value_type: u16, version: u16, data: &[u8]) -> Vec<u8> {
223 let size = (8 + data.len()) as u16;
224 let mut v = Vec::new();
225 v.extend_from_slice(&size.to_le_bytes());
226 v.extend_from_slice(&entry_type.to_le_bytes());
227 v.extend_from_slice(&value_type.to_le_bytes());
228 v.extend_from_slice(&version.to_le_bytes());
229 v.extend_from_slice(data);
230 v
231 }
232
233 #[test]
234 fn parse_sequence_splits_entries() {
235 let mut buf = Vec::new();
236 buf.extend(entry_bytes(0x000f, 0x000f, 1, &[1, 2, 3, 4]));
237 buf.extend(entry_bytes(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, 1, &[9; 20]));
238 let entries = MetadataEntry::parse_sequence(&buf);
239 assert_eq!(entries.len(), 2);
240 assert_eq!(entries[0].entry_type, 0x000f);
241 assert_eq!(entries[0].data, vec![1, 2, 3, 4]);
242 assert!(entries[1].is_vmk());
243 }
244
245 #[test]
246 fn parse_sequence_stops_on_lying_size() {
247 let mut buf = entry_bytes(1, 2, 1, &[]);
249 buf.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); let entries = MetadataEntry::parse_sequence(&buf);
251 assert_eq!(entries.len(), 1);
252 }
253
254 #[test]
255 fn parse_sequence_stops_on_oversize() {
256 let mut buf = Vec::new();
258 buf.extend_from_slice(&100u16.to_le_bytes());
259 buf.extend_from_slice(&[0u8; 6]);
260 assert!(MetadataEntry::parse_sequence(&buf).is_empty());
261 }
262
263 fn build_block(entries: &[Vec<u8>]) -> Vec<u8> {
264 let mut entry_bytes = Vec::new();
265 for e in entries {
266 entry_bytes.extend_from_slice(e);
267 }
268 let metadata_size = 48 + entry_bytes.len();
269 let mut block = vec![0u8; 64 + metadata_size];
270 block[0..8].copy_from_slice(FVE_SIGNATURE);
271 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());
277 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());
280 block[64 + 48..].copy_from_slice(&entry_bytes);
281 block
282 }
283
284 #[test]
285 fn parse_full_block() {
286 let mut vh_data = Vec::new();
288 vh_data.extend_from_slice(&0x0211_0800u64.to_le_bytes()); vh_data.extend_from_slice(&0x0051_5a00u64.to_le_bytes()); let vh = entry_bytes(
291 ENTRY_TYPE_VOLUME_HEADER,
292 ENTRY_TYPE_VOLUME_HEADER,
293 1,
294 &vh_data,
295 );
296
297 let mut vmk_data = vec![0u8; 28];
298 vmk_data[26..28].copy_from_slice(&PROTECTION_PASSWORD.to_le_bytes());
299 let vmk = entry_bytes(ENTRY_TYPE_VMK, VALUE_TYPE_VMK, 1, &vmk_data);
300
301 let block = build_block(&[vh, vmk]);
302 let m = FveMetadata::parse(&block, 512).unwrap();
303 assert_eq!(m.encryption_method, 0x8000);
304 assert_eq!(m.volume_guid, [0xAB; 16]);
305 assert_eq!(m.creation_time, 130_461_864_497_281_120);
306 assert_eq!(m.encrypted_volume_size, 0x0400_0000);
307 assert_eq!(m.volume_header_offset, 0x0211_0800);
308 assert_eq!(m.volume_header_size, 0x0051_5a00); assert_eq!(m.entries.len(), 2);
310 assert_eq!(m.protector_types(), vec![PROTECTION_PASSWORD]);
311 }
312
313 #[test]
314 fn parse_returns_none_without_signature() {
315 let block = vec![0u8; 128];
316 assert!(FveMetadata::parse(&block, 512).is_none());
317 }
318
319 #[test]
320 fn volume_header_size_falls_back_to_sector_count() {
321 let block = build_block(&[]);
323 let m = FveMetadata::parse(&block, 512).unwrap();
324 assert_eq!(m.volume_header_size, 16 * 512);
325 assert_eq!(m.volume_header_offset, 0x0211_0800);
326 }
327
328 #[test]
329 fn truncated_block_does_not_panic() {
330 let mut block = build_block(&[]);
331 block.truncate(70);
332 let _ = FveMetadata::parse(&block, 512);
333 }
334}