1type BoxedError = Box<dyn std::error::Error>;
2
3#[derive(Default)]
9pub struct Keybagv5ClassKey {
10 pub uuid: Keybagv5Item,
11 pub class: Keybagv5Item,
12 pub key_type: Keybagv5Item,
13 pub wrap: Keybagv5Item,
14 pub wrapped_key: Keybagv5Item,
15 pub pbky: Option<Keybagv5Item>,
16}
17impl std::fmt::Debug for Keybagv5ClassKey {
18 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
19 f.debug_struct("Keybagv5Item")
20 .field("UUID", &self.uuid)
21 .field("Key Class", &self.class)
22 .field("Key Type", &self.key_type)
23 .field("Wrap", &self.wrap)
24 .field("Wrapped Key", &self.wrapped_key)
25 .field("PBKY", &self.pbky)
26 .finish()
27 }
28}
29
30#[derive(Default)]
34pub struct Keybagv5Metadata {
35 pub uuid: Keybagv5Item,
36 pub hmac: Keybagv5Item,
37 pub wrap: Keybagv5Item,
38 pub salt: Keybagv5Item,
39 pub iter: Keybagv5Item,
40 pub grce: Keybagv5Item,
41 pub cfgf: Keybagv5Item,
42 pub tkmt: Keybagv5Item,
43 pub usid: Keybagv5Item,
44 pub grid: Keybagv5Item,
45}
46impl std::fmt::Debug for Keybagv5Metadata {
47 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
48 f.debug_struct("Keybagv5Item")
49 .field("UUID", &self.uuid)
50 .field("HMAC", &self.hmac)
51 .field("WRAP", &self.wrap)
52 .field("SALT", &self.salt)
53 .field("ITER", &self.iter)
54 .field("GRCE", &self.grce)
55 .field("CFGF", &self.cfgf)
56 .field("TKMT", &self.tkmt)
57 .field("USID", &self.usid)
58 .field("GRID", &self.grid)
59 .finish()
60 }
61}
62
63#[derive(Default, Clone)]
67pub struct Keybagv5Item {
68 pub tag: String,
70 pub len: u32,
72 pub data: Vec<u8>,
74}
75
76impl Keybagv5Item {
77 fn data_as_u32(&self) -> Result<u32, BoxedError> {
78 if self.len > 4 {
79 Err(format!("Data length [{}] is too large to convert to u32", self.len).into())
81 } else {
82 let tmp = self.data.as_slice();
83 Ok(u32::from_be_bytes(tmp.try_into()?))
84 }
85 }
86}
87
88impl std::fmt::Debug for Keybagv5Item {
89 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90 f.debug_struct("Keybagv5Item")
91 .field("tag", &self.tag)
92 .field("len", &format_args!("{} ({:#02X?})", &self.len, &self.len))
93 .field("data", &format_args!("{:02X?}", &self.data))
94 .finish()
95 }
96}
97
98const KB_TAG_LEN: usize = 4;
99const KB_SZ_LEN: usize = KB_TAG_LEN;
100const KB_EXCLUDED_LEN: usize = 36;
101
102#[derive(Default)]
109pub struct Keybagv5 {
110 pos: usize,
111 pub len: u32,
112 pub kb_type: Keybagv5Item,
113 pub kb_vers: Keybagv5Item,
114 pub metadata: Keybagv5Metadata,
115 pub class_keys: Vec<Keybagv5ClassKey>,
116 pub sig: Keybagv5Item,
117}
118
119impl Keybagv5 {
120 pub fn parse(raw: &[u8]) -> Result<Keybagv5, BoxedError> {
121 let mut kb = Keybagv5::default();
123
124 let tag = kb.parse_tag(raw)?;
128 kb.pos += KB_TAG_LEN;
129
130 match "DATA" == tag {
133 true => {
134 kb.len = kb.parse_tag_len(raw)?;
135 kb.pos += KB_SZ_LEN;
136 }
137 false => return Err("DATA tag not found in bytes provided".into()),
138 }
139
140 kb.kb_vers = kb.parse_item(raw)?;
141 if kb.get_vers()? != 5 {
142 let msg = format!(
143 "Only Keybag version 5 supported. Version {} found...",
144 kb.get_vers()?
145 );
146 return Err(msg.into());
147 }
148
149 kb.kb_type = kb.parse_item(raw)?;
150 let item_meta_uuid = kb.parse_item(raw)?;
154 kb.metadata = Keybagv5Metadata {
155 uuid: item_meta_uuid,
156 hmac: kb.parse_item(raw)?,
157 wrap: kb.parse_item(raw)?,
158 salt: kb.parse_item(raw)?,
159 iter: kb.parse_item(raw)?,
160 grce: kb.parse_item(raw)?,
161 cfgf: kb.parse_item(raw)?,
162 tkmt: kb.parse_item(raw)?,
163 usid: kb.parse_item(raw)?,
164 grid: kb.parse_item(raw)?,
165 };
166
167 while kb.pos - (KB_TAG_LEN + KB_SZ_LEN) != kb.get_len() {
174 let ck = kb.parse_key_class(raw)?;
175 kb.class_keys.push(ck);
176 }
177
178 kb.sig = kb.parse_item(raw)?;
180
181 Ok(kb)
182 }
183
184 #[inline(always)]
185 pub fn get_len(&self) -> usize {
187 self.len as usize
188 }
189
190 pub fn get_vers(&self) -> Result<u32, BoxedError> {
192 self.kb_vers.data_as_u32()
193 }
194
195 pub fn get_type(&self) -> Result<u32, BoxedError> {
198 self.kb_type.data_as_u32()
199 }
200
201 #[inline(always)]
203 fn peek_tag<'a>(&'a self, raw: &'a [u8]) -> Result<String, BoxedError> {
204 self.parse_tag(raw)
205 }
206
207 fn parse_tag<'a>(&'a self, raw: &'a [u8]) -> Result<String, BoxedError> {
208 if self.pos + KB_TAG_LEN < KB_EXCLUDED_LEN + self.get_len() {
209 let tag = std::str::from_utf8(&raw[self.pos..(self.pos + KB_TAG_LEN)])?;
210 Ok(tag.to_owned())
211 } else {
212 Err("Number of bytes requested larger than Keybag size".into())
213 }
214 }
215
216 fn parse_tag_len<'a>(&'a self, raw: &'a [u8]) -> Result<u32, BoxedError> {
217 if self.pos + KB_TAG_LEN < KB_EXCLUDED_LEN + self.get_len() {
218 Ok(u32::from_be_bytes(
219 raw[self.pos..(self.pos + KB_SZ_LEN)].try_into()?,
220 ))
221 } else {
222 Err("Number of bytes requested larger than Keybag size".into())
223 }
224 }
225
226 fn parse_item(&mut self, raw: &[u8]) -> Result<Keybagv5Item, BoxedError> {
227 let tag = self.parse_tag(raw)?;
229 self.pos += KB_TAG_LEN;
230 let tlen = self.parse_tag_len(raw)?;
231 self.pos += KB_SZ_LEN;
232
233 if (self.pos + tlen as usize) <= KB_EXCLUDED_LEN + self.get_len() {
235 let bytes = &raw[self.pos..(self.pos + tlen as usize)];
236 self.pos += tlen as usize;
237
238 Ok(Keybagv5Item {
241 tag: tag.to_owned(),
242 len: tlen,
243 data: bytes.to_vec(),
244 })
245 } else {
246 Err("Number of bytes requested larger than Keybag size".into())
247 }
248 }
249
250 fn parse_key_class(&mut self, raw: &[u8]) -> Result<Keybagv5ClassKey, BoxedError> {
251 Ok(Keybagv5ClassKey {
252 uuid: self.parse_item(raw)?,
253 class: self.parse_item(raw)?,
254 key_type: self.parse_item(raw)?,
255 wrap: self.parse_item(raw)?,
256 wrapped_key: self.parse_item(raw)?,
257 pbky: if "PBKY" == self.peek_tag(raw)? {
259 Some(self.parse_item(raw)?)
260 } else {
261 None
262 },
263 })
264 }
265}
266
267impl std::fmt::Debug for Keybagv5 {
268 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
269 f.debug_struct("Keybag")
270 .field("pos", &self.pos)
271 .field("len", &self.len)
272 .field("KB Version", &self.kb_vers)
273 .field("KB Type", &self.kb_type)
274 .field("KB Metadata", &self.metadata)
275 .field("KB Class Keys", &self.class_keys)
276 .field("KB signature", &self.sig)
277 .finish()
278 }
279}
280
281#[cfg(test)]
282mod tests {
283
284 #[test]
285 fn not_keybag_file() {
286 let test_file_data = [
287 0x2F, 0xCD, 0xCE, 0xDB, 0xE9, 0x99, 0x4B, 0xA4, 0xAD, 0x38, 0x9C, 0x59, 0x31, 0x25,
288 0x43, 0x5F,
289 ];
290
291 assert_eq!(true, super::Keybagv5::parse(&test_file_data).is_err());
292 }
293
294 #[test]
295 fn bounds_check_parse_tag() {
296 let mut bad_kb = super::Keybagv5::default();
297 bad_kb.pos = 36;
300 bad_kb.len = 3;
301
302 let test_file_data = [0x2F, 0xCD, 0xCE];
303
304 assert_eq!(true, bad_kb.parse_tag(&test_file_data).is_err());
305 }
306
307 #[test]
308 fn bounds_check_parse_tag_len() {
309 let mut bad_kb = super::Keybagv5::default();
310 bad_kb.pos = 36;
313 bad_kb.len = 3;
314
315 let test_file_data = [0x2F, 0xCD, 0xCE];
316
317 assert_eq!(true, bad_kb.parse_tag_len(&test_file_data).is_err());
318 }
319
320 #[test]
321 fn bounds_check_parse_item_bad_length() {
322 let mut bad_kb = super::Keybagv5::default();
323 bad_kb.pos = 36;
326 bad_kb.len = 12;
327
328 let test_file_data = [
333 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x41, 0x54, 0x41, 0xFF, 0x00,
336 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
337 ];
338
339 assert_eq!(true, bad_kb.parse_item(&test_file_data).is_err());
340 }
341}