usb_forensic/sources/
emdmgmt.rs1use crate::{Attribute, Claim, DeviceKey, HistorySource, Provenance, SourceKind, Value};
11use peripheral_core::emdmgmt::EmdVolume;
12
13pub struct EmdMgmtSource<'a> {
15 volumes: &'a [EmdVolume],
16}
17
18impl<'a> EmdMgmtSource<'a> {
19 #[must_use]
21 pub fn new(volumes: &'a [EmdVolume]) -> Self {
22 Self { volumes }
23 }
24}
25
26fn format_volume_serial(serial: u32) -> String {
29 format!("{:04X}-{:04X}", serial >> 16, serial & 0xFFFF)
30}
31
32impl HistorySource for EmdMgmtSource<'_> {
33 fn claims(&self) -> Vec<Claim> {
34 let mut out = Vec::new();
35 for vol in self.volumes {
36 let serial = format_volume_serial(vol.volume_serial);
37 let device = DeviceKey(serial.clone());
38 let locator = vol
39 .source
40 .key_path
41 .clone()
42 .unwrap_or_else(|| vol.source.file.clone());
43 out.push(Claim {
45 device: device.clone(),
46 attribute: Attribute::VolumeSerial,
47 value: Value::Text(serial),
48 provenance: Provenance {
49 source: SourceKind::EmdMgmt,
50 locator: locator.clone(),
51 },
52 });
53 if !vol.volume_label.is_empty() {
55 out.push(Claim {
56 device,
57 attribute: Attribute::VolumeName,
58 value: Value::Text(vol.volume_label.clone()),
59 provenance: Provenance {
60 source: SourceKind::EmdMgmt,
61 locator,
62 },
63 });
64 }
65 }
66 out
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use peripheral_core::Provenance as PcProvenance;
74
75 fn vol(label: &str, serial: u32) -> EmdVolume {
76 EmdVolume {
77 volume_label: label.to_string(),
78 volume_serial: serial,
79 source: PcProvenance {
80 file: "SOFTWARE".to_string(),
81 line: 0,
82 key_path: Some("…\\EMDMgmt\\x".to_string()),
83 },
84 }
85 }
86
87 #[test]
88 fn labelled_volume_yields_serial_and_name_claims_keyed_by_serial() {
89 let vols = [vol("Authorized USB", 0x5C75_4D3E)];
91 let claims = EmdMgmtSource::new(&vols).claims();
92 assert_eq!(claims.len(), 2);
93 assert_eq!(claims[0].device, DeviceKey("5C75-4D3E".to_string()));
94 assert_eq!(claims[0].attribute, Attribute::VolumeSerial);
95 assert_eq!(claims[0].value, Value::Text("5C75-4D3E".to_string()));
96 assert_eq!(claims[1].attribute, Attribute::VolumeName);
97 assert_eq!(claims[1].value, Value::Text("Authorized USB".to_string()));
98 assert_eq!(claims[1].provenance.source, SourceKind::EmdMgmt);
99 }
100
101 #[test]
102 fn unlabelled_volume_yields_only_the_serial_claim() {
103 let vols = [vol("", 1)];
104 let claims = EmdMgmtSource::new(&vols).claims();
105 assert_eq!(claims.len(), 1);
106 assert_eq!(claims[0].attribute, Attribute::VolumeSerial);
107 assert_eq!(claims[0].value, Value::Text("0000-0001".to_string()));
108 }
109
110 #[test]
111 fn locator_falls_back_to_the_file_without_a_key_path() {
112 let mut v = vol("X", 2);
113 v.source.key_path = None;
114 let vols = [v];
115 let claims = EmdMgmtSource::new(&vols).claims();
116 assert_eq!(claims[0].provenance.locator, "SOFTWARE");
117 }
118}