1use aes_gcm_siv::aead::Aead;
6use aes_gcm_siv::{Aes128GcmSiv, Aes256GcmSiv, KeyInit};
7
8use itertools::Itertools;
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use std::collections::HashMap;
11use std::collections::hash_map::Entry;
12use std::fmt::Debug;
13use std::ops::{Deref, DerefMut};
14use std::os::fd::{FromRawFd as _, IntoRawFd as _};
15use thiserror::Error;
16
17#[derive(Debug, Deserialize, Serialize)]
20pub struct KeyBag {
21 version: u16,
22 keys: HashMap<KeySlot, WrappedKey>,
23}
24
25#[derive(Error, Debug)]
26pub enum OpenError {
27 #[error("Path to keybag was invalid")]
28 InvalidPath,
29 #[error("Failed to open the keybag: {0:?}")]
30 FailedToOpen(#[from] std::io::Error),
31 #[error("Keybag failed to parse due to {0}")]
32 KeyBagInvalid(String),
33 #[error("Keybag was of wrong version (have {0} want {1})")]
34 KeyBagVersionMismatch(u16, u16),
35 #[error("Failed to persist keybag")]
36 FailedToPersist,
37}
38
39impl From<OpenError> for zx::Status {
40 fn from(error: OpenError) -> zx::Status {
41 match error {
42 OpenError::InvalidPath => zx::Status::INVALID_ARGS,
43 OpenError::FailedToOpen(..) => zx::Status::IO,
44 OpenError::KeyBagInvalid(..) => zx::Status::IO_DATA_INTEGRITY,
45 OpenError::KeyBagVersionMismatch(..) => zx::Status::NOT_SUPPORTED,
46 OpenError::FailedToPersist => zx::Status::IO,
47 }
48 }
49}
50
51#[derive(Error, Debug, PartialEq)]
52pub enum Error {
53 #[error("Failed to persist keybag")]
54 FailedToPersist,
55 #[error("Key at given slot not found")]
56 SlotNotFound,
57 #[error("Key slot is already in use")]
58 SlotAlreadyUsed,
59 #[error("Internal")]
60 Internal,
61}
62
63impl From<Error> for zx::Status {
64 fn from(error: Error) -> zx::Status {
65 match error {
66 Error::FailedToPersist => zx::Status::IO,
67 Error::SlotNotFound => zx::Status::NOT_FOUND,
68 Error::SlotAlreadyUsed => zx::Status::ALREADY_EXISTS,
69 Error::Internal => zx::Status::INTERNAL,
70 }
71 }
72}
73
74#[derive(Error, Debug, PartialEq)]
75pub enum UnwrapError {
76 #[error("Key at given slot not found")]
77 SlotNotFound,
78 #[error("Failed to unwrap the key, most likely due to the wrong wrapping key")]
79 AccessDenied,
80}
81
82impl From<UnwrapError> for zx::Status {
83 fn from(error: UnwrapError) -> zx::Status {
84 match error {
85 UnwrapError::SlotNotFound => zx::Status::NOT_FOUND,
86 UnwrapError::AccessDenied => zx::Status::ACCESS_DENIED,
87 }
88 }
89}
90
91impl Default for KeyBag {
92 fn default() -> Self {
93 Self { version: CURRENT_VERSION, keys: Default::default() }
94 }
95}
96
97pub struct KeyBagManager {
101 key_bag: KeyBag,
102 dir: openat::Dir,
103 path: String,
104}
105
106pub const AES128_KEY_SIZE: usize = 16;
107pub const AES256_KEY_SIZE: usize = 32;
108
109const CURRENT_VERSION: u16 = 1;
110const AES256_GCM_SIV_NONCE_SIZE: usize = 12;
111const WRAPPED_AES256_KEY_SIZE: usize = AES256_KEY_SIZE + 16;
112
113pub type KeySlot = u16;
114
115#[derive(Deserialize, Serialize, Debug)]
118pub enum WrappedKey {
119 Aes128GcmSivWrapped(Nonce, KeyBytes),
120 Aes256GcmSivWrapped(Nonce, KeyBytes),
121}
122
123macro_rules! impl_serde {
126 ($T:ty) => {
127 impl Serialize for $T {
128 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129 where
130 S: Serializer,
131 {
132 serializer.serialize_str(
133 &self
134 .0
135 .iter()
136 .format_with("", |item, f| f(&format_args!("{:02x}", item)))
137 .to_string(),
138 )
139 }
140 }
141
142 impl<'de> Deserialize<'de> for $T {
143 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144 where
145 D: Deserializer<'de>,
146 {
147 <String>::deserialize(deserializer).and_then(|s| {
148 let mut this = Self::default();
149 let decoded = hex::decode(s).map_err(|_| {
150 serde::de::Error::custom("failed to parse byte string".to_owned())
151 })?;
152 if decoded.len() != this.0.len() {
153 return Err(serde::de::Error::custom(format!(
154 "Invalid length (have {} want {})",
155 decoded.len(),
156 this.0.len()
157 )));
158 }
159 this.0.copy_from_slice(&decoded[..]);
160 Ok(this)
161 })
162 }
163 }
164 };
165}
166
167#[derive(Debug, Default)]
168pub struct Nonce([u8; AES256_GCM_SIV_NONCE_SIZE]);
169
170impl Nonce {
171 fn as_crypto_nonce(&self) -> &aes_gcm_siv::Nonce {
172 (&self.0[..]).try_into().unwrap()
173 }
174}
175
176impl_serde!(Nonce);
177
178#[derive(Debug)]
180pub struct KeyBytes([u8; WRAPPED_AES256_KEY_SIZE]);
181
182impl Deref for KeyBytes {
183 type Target = [u8; WRAPPED_AES256_KEY_SIZE];
184 fn deref(&self) -> &Self::Target {
185 &self.0
186 }
187}
188
189impl DerefMut for KeyBytes {
190 fn deref_mut(&mut self) -> &mut Self::Target {
191 &mut self.0
192 }
193}
194
195impl TryFrom<Vec<u8>> for KeyBytes {
196 type Error = Vec<u8>;
197 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
198 if value.len() == WRAPPED_AES256_KEY_SIZE {
199 let mut key = KeyBytes::default();
200 key.0.copy_from_slice(&value[..]);
201 Ok(key)
202 } else {
203 Err(value)
204 }
205 }
206}
207
208impl Default for KeyBytes {
209 fn default() -> Self {
210 Self([0u8; WRAPPED_AES256_KEY_SIZE])
211 }
212}
213
214impl_serde!(KeyBytes);
215
216#[repr(C)]
217#[derive(Default, PartialEq)]
218pub struct Aes256Key([u8; AES256_KEY_SIZE]);
219
220impl TryFrom<Vec<u8>> for Aes256Key {
221 type Error = ();
222 fn try_from(mut value: Vec<u8>) -> Result<Self, Self::Error> {
223 if value.len() != AES256_KEY_SIZE {
224 return Err(());
225 }
226 let mut this = Self([0u8; AES256_KEY_SIZE]);
227 this.0.copy_from_slice(&value[..]);
228 value.fill(0);
229 Ok(this)
230 }
231}
232
233impl Aes256Key {
234 pub const fn create(data: [u8; AES256_KEY_SIZE]) -> Self {
235 Self(data)
236 }
237}
238
239impl Deref for Aes256Key {
240 type Target = [u8; AES256_KEY_SIZE];
241 fn deref(&self) -> &Self::Target {
242 &self.0
243 }
244}
245
246impl DerefMut for Aes256Key {
247 fn deref_mut(&mut self) -> &mut Self::Target {
248 &mut self.0
249 }
250}
251
252impl Debug for Aes256Key {
253 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 formatter.write_str("Aes256Key")
255 }
256}
257
258#[repr(C)]
259#[derive(PartialEq)]
260pub enum WrappingKey {
261 Aes128([u8; AES128_KEY_SIZE]),
262 Aes256([u8; AES256_KEY_SIZE]),
263}
264
265fn generate_key() -> Aes256Key {
266 let mut key = Aes256Key::default();
267 zx::cprng_draw(&mut key.0);
268 key
269}
270
271fn generate_nonce() -> Nonce {
272 let mut nonce = Nonce::default();
273 zx::cprng_draw(&mut nonce.0);
274 nonce
275}
276
277impl KeyBagManager {
278 pub fn open(
280 directory: std::os::fd::OwnedFd,
281 path: &std::path::Path,
282 ) -> Result<Option<Self>, OpenError> {
283 let dir = unsafe { openat::Dir::from_raw_fd(directory.into_raw_fd()) };
286 let path_str = path.to_str().map(str::to_string).ok_or(OpenError::InvalidPath)?;
287 match dir.metadata(path) {
288 Err(e) => {
289 if e.kind() == std::io::ErrorKind::NotFound {
290 return Ok(None);
291 } else {
292 return Err(e.into());
293 }
294 }
295 Ok(m) if m.len() == 0 => return Ok(None),
296 _ => (),
297 };
298 let reader = std::io::BufReader::new(dir.open_file(path)?);
299 let key_bag: KeyBag =
300 serde_json::from_reader(reader).map_err(|e| OpenError::KeyBagInvalid(e.to_string()))?;
301 if key_bag.version != CURRENT_VERSION {
302 return Err(OpenError::KeyBagVersionMismatch(key_bag.version, CURRENT_VERSION));
303 }
304 Ok(Some(Self { key_bag, dir, path: path_str }))
305 }
306
307 pub fn create(
309 directory: std::os::fd::OwnedFd,
310 path: &std::path::Path,
311 ) -> Result<Self, OpenError> {
312 let dir = unsafe { openat::Dir::from_raw_fd(directory.into_raw_fd()) };
315 let path_str = path.to_str().map(str::to_string).ok_or(OpenError::InvalidPath)?;
316 let mut this = Self { key_bag: KeyBag::default(), dir, path: path_str };
317 this.commit().map_err(|_| OpenError::FailedToPersist)?;
318 return Ok(this);
319 }
320
321 pub fn new_key(
324 &mut self,
325 slot: KeySlot,
326 wrapping_key: &WrappingKey,
327 ) -> Result<Aes256Key, Error> {
328 let key = match self.key_bag.keys.entry(slot) {
329 Entry::Occupied(_) => return Err(Error::SlotAlreadyUsed),
330 Entry::Vacant(v) => {
331 let key = generate_key();
332 let nonce = generate_nonce();
333
334 let entry = match wrapping_key {
335 WrappingKey::Aes128(bytes) => {
336 let cipher = Aes128GcmSiv::new_from_slice(bytes).unwrap();
337 let wrapped = cipher
338 .encrypt(nonce.as_crypto_nonce(), &key.0[..])
339 .map_err(|_| Error::Internal)
340 .and_then(|k| k.try_into().map_err(|_| Error::Internal))?;
341 WrappedKey::Aes128GcmSivWrapped(nonce, wrapped)
342 }
343 WrappingKey::Aes256(bytes) => {
344 let cipher = Aes256GcmSiv::new_from_slice(bytes).unwrap();
345 let wrapped = cipher
346 .encrypt(nonce.as_crypto_nonce(), &key.0[..])
347 .map_err(|_| Error::Internal)
348 .and_then(|k| k.try_into().map_err(|_| Error::Internal))?;
349 WrappedKey::Aes256GcmSivWrapped(nonce, wrapped)
350 }
351 };
352 v.insert(entry);
353 key
354 }
355 };
356
357 self.commit().map(|_| key)
358 }
359
360 pub fn remove_key(&mut self, slot: KeySlot) -> Result<(), Error> {
362 if let None = self.key_bag.keys.remove(&slot) {
363 return Err(Error::SlotNotFound);
364 }
365 self.commit()
366 }
367
368 pub fn unwrap_key(
370 &self,
371 slot: KeySlot,
372 wrapping_key: &WrappingKey,
373 ) -> Result<Aes256Key, UnwrapError> {
374 let key = self.key_bag.keys.get(&slot).ok_or(UnwrapError::SlotNotFound)?;
375 let (nonce, bytes) = match key {
376 WrappedKey::Aes128GcmSivWrapped(nonce, bytes) => (nonce, bytes),
377 WrappedKey::Aes256GcmSivWrapped(nonce, bytes) => (nonce, bytes),
378 };
379 let decrypt_res = match wrapping_key {
382 WrappingKey::Aes128(wrap_bytes) => {
383 let cipher = Aes128GcmSiv::new_from_slice(wrap_bytes).unwrap();
384 cipher.decrypt(nonce.as_crypto_nonce(), &bytes[..])
385 }
386 WrappingKey::Aes256(wrap_bytes) => {
387 let cipher = Aes256GcmSiv::new_from_slice(wrap_bytes).unwrap();
388 cipher.decrypt(nonce.as_crypto_nonce(), &bytes[..])
389 }
390 };
391 match decrypt_res {
392 Ok(unwrapped) => {
393 let mut key = Aes256Key([0u8; 32]);
394 key.0.copy_from_slice(&unwrapped[..]);
395 Ok(key)
396 }
397 Err(_) => Err(UnwrapError::AccessDenied),
398 }
399 }
400
401 fn commit(&mut self) -> Result<(), Error> {
402 let path = std::path::Path::new(&self.path);
403 let tmp_path = path.with_extension("tmp");
404 let _ = self.dir.remove_file(&tmp_path);
405 {
406 let tmpfile = std::io::BufWriter::new(
407 self.dir.write_file(&tmp_path, 0).map_err(|_| Error::FailedToPersist)?,
408 );
409 serde_json::to_writer(tmpfile, &self.key_bag).map_err(|_| Error::FailedToPersist)?;
410 }
411 self.dir.local_rename(&tmp_path, path).map_err(|_| Error::FailedToPersist)?;
412 Ok(())
413 }
414}
415
416#[cfg(test)]
417mod tests {
418 use super::{
419 AES256_KEY_SIZE, Aes256Key, Error, KeyBagManager, KeyBytes, Nonce, OpenError, UnwrapError,
420 WRAPPED_AES256_KEY_SIZE, WrappingKey,
421 };
422 use assert_matches::assert_matches;
423 use std::os::fd::{FromRawFd as _, IntoRawFd as _, OwnedFd};
424 use tempfile::NamedTempFile;
425
426 fn open_dir(path: impl openat::AsPath) -> OwnedFd {
427 let dir = openat::Dir::open(path).unwrap();
428 unsafe { OwnedFd::from_raw_fd(dir.into_raw_fd()) }
429 }
430
431 #[test]
432 fn nonexistent_keybag() {
433 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
434 let path: &std::path::Path = owned_path.as_ref();
435 std::fs::remove_file(path).expect("unlink failed");
436 let dir = open_dir(path.parent().unwrap());
437 let keybag = KeyBagManager::create(dir, path).expect("Open nonexistent keybag failed");
438 assert!(keybag.key_bag.keys.is_empty());
439 }
440
441 #[test]
442 fn empty_keybag() {
443 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
444 let path: &std::path::Path = owned_path.as_ref();
445 let dir = open_dir(path.parent().unwrap());
446 let keybag = KeyBagManager::create(dir, path).expect("Open empty keybag failed");
447 assert!(keybag.key_bag.keys.is_empty());
448 }
449
450 #[test]
451 fn add_remove_key() {
452 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
453 let path: &std::path::Path = owned_path.as_ref();
454 {
455 let dir = open_dir(path.parent().unwrap());
456 let mut keybag = KeyBagManager::create(dir, path).expect("Open empty keybag failed");
457 let key = WrappingKey::Aes256([0u8; 32]);
458 keybag.new_key(0, &key).expect("new key failed");
459 assert_eq!(
460 Error::SlotAlreadyUsed,
461 keybag.new_key(0, &key).expect_err("new key on used slot failed")
462 );
463 }
464 {
465 let dir = open_dir(path.parent().unwrap());
466 let mut keybag = KeyBagManager::open(dir, path)
467 .expect("Open keybag failed")
468 .expect("keybag not found");
469 keybag.remove_key(0).expect("remove_key failed");
470 assert_eq!(
471 Error::SlotNotFound,
472 keybag.remove_key(1).expect_err("remove_key with invalid key specified failed")
473 );
474 }
475 let dir = open_dir(path.parent().unwrap());
476 let keybag =
477 KeyBagManager::open(dir, path).expect("Open keybag failed").expect("keybag not found");
478 assert!(keybag.key_bag.keys.is_empty());
479 }
480
481 #[test]
482 fn unwrap_key() {
483 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
484 let path: &std::path::Path = owned_path.as_ref();
485 let dir = open_dir(path.parent().unwrap());
486 let mut keybag = KeyBagManager::create(dir, path).expect("Open empty keybag failed");
487
488 let key = WrappingKey::Aes256([3u8; 32]);
489 let key2 = WrappingKey::Aes128([0xffu8; 16]);
490
491 keybag.new_key(0, &key).expect("new_key failed");
492 keybag.new_key(1, &key2).expect("new_key failed");
493 keybag.new_key(2, &key).expect("new_key failed");
494
495 assert_matches!(keybag.unwrap_key(0, &key), Ok(_));
496 assert_eq!(keybag.unwrap_key(1, &key), Err(UnwrapError::AccessDenied));
497 assert_matches!(keybag.unwrap_key(2, &key), Ok(_));
498 assert_eq!(keybag.unwrap_key(3, &key), Err(UnwrapError::SlotNotFound));
499
500 assert_eq!(keybag.unwrap_key(0, &key2), Err(UnwrapError::AccessDenied));
501 assert_matches!(keybag.unwrap_key(1, &key2), Ok(_));
502 assert_eq!(keybag.unwrap_key(2, &key2), Err(UnwrapError::AccessDenied));
503 assert_eq!(keybag.unwrap_key(3, &key2), Err(UnwrapError::SlotNotFound));
504 }
505
506 #[test]
507 fn from_testdata() {
508 let path = std::path::Path::new("/pkg/data/key_bag.json");
512 let dir = open_dir(path.parent().unwrap());
513 let keybag =
514 KeyBagManager::open(dir, path).expect("Open keybag failed").expect("keybag not found");
515
516 let mut expected = Aes256Key::default();
517 expected.0[..6].copy_from_slice(b"secret");
518
519 let key = WrappingKey::Aes256([0u8; 32]);
520 assert_eq!(keybag.unwrap_key(0, &key).as_ref().map(|s| &s.0), Ok(&expected.0));
521 assert_eq!(keybag.unwrap_key(1, &key), Err(UnwrapError::AccessDenied));
522 assert_eq!(keybag.unwrap_key(2, &key), Ok(expected));
523 assert_eq!(keybag.unwrap_key(3, &key), Err(UnwrapError::SlotNotFound));
524 }
525
526 #[test]
527 fn error_to_zx_status_conversions() {
528 assert_eq!(zx::Status::from(OpenError::InvalidPath), zx::Status::INVALID_ARGS);
529 assert_eq!(
530 zx::Status::from(OpenError::FailedToOpen(std::io::Error::other("io"))),
531 zx::Status::IO
532 );
533 assert_eq!(
534 zx::Status::from(OpenError::KeyBagInvalid("bad".to_string())),
535 zx::Status::IO_DATA_INTEGRITY
536 );
537 assert_eq!(
538 zx::Status::from(OpenError::KeyBagVersionMismatch(2, 1)),
539 zx::Status::NOT_SUPPORTED
540 );
541 assert_eq!(zx::Status::from(OpenError::FailedToPersist), zx::Status::IO);
542
543 assert_eq!(zx::Status::from(Error::FailedToPersist), zx::Status::IO);
544 assert_eq!(zx::Status::from(Error::SlotNotFound), zx::Status::NOT_FOUND);
545 assert_eq!(zx::Status::from(Error::SlotAlreadyUsed), zx::Status::ALREADY_EXISTS);
546 assert_eq!(zx::Status::from(Error::Internal), zx::Status::INTERNAL);
547
548 assert_eq!(zx::Status::from(UnwrapError::SlotNotFound), zx::Status::NOT_FOUND);
549 assert_eq!(zx::Status::from(UnwrapError::AccessDenied), zx::Status::ACCESS_DENIED);
550 }
551
552 #[test]
553 fn key_bytes_and_aes256_key_methods() {
554 let mut kb = KeyBytes::try_from(vec![0x11u8; WRAPPED_AES256_KEY_SIZE]).unwrap();
555 assert_eq!(kb[0], 0x11);
556 kb[0] = 0x22;
557 assert_eq!(kb[0], 0x22);
558 assert!(KeyBytes::try_from(vec![0u8; 5]).is_err());
559
560 let mut key = Aes256Key::create([0x33u8; AES256_KEY_SIZE]);
561 assert_eq!(key[0], 0x33);
562 key[0] = 0x44;
563 assert_eq!(key[0], 0x44);
564 assert_eq!(format!("{:?}", key), "Aes256Key");
565
566 let from_vec = Aes256Key::try_from(vec![0x55u8; AES256_KEY_SIZE]).unwrap();
567 assert_eq!(from_vec[0], 0x55);
568 assert_eq!(Aes256Key::try_from(vec![0u8; 4]), Err(()));
569 }
570
571 #[test]
572 fn serde_hex_errors_and_open_edge_cases() {
573 assert!(serde_json::from_str::<Nonce>("\"not_valid_hex!\"").is_err());
574 assert!(serde_json::from_str::<Nonce>("\"0011\"").is_err());
575 assert!(serde_json::from_str::<KeyBytes>("\"not_valid_hex!\"").is_err());
576 assert!(serde_json::from_str::<KeyBytes>("\"0011\"").is_err());
577
578 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
580 let path: &std::path::Path = owned_path.as_ref();
581 std::fs::remove_file(path).unwrap();
582 let dir = open_dir(path.parent().unwrap());
583 assert!(KeyBagManager::open(dir, path).unwrap().is_none());
584
585 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
587 let path: &std::path::Path = owned_path.as_ref();
588 let dir = open_dir(path.parent().unwrap());
589 assert!(KeyBagManager::open(dir, path).unwrap().is_none());
590
591 std::fs::write(path, b"{invalid json").unwrap();
593 let dir = open_dir(path.parent().unwrap());
594 assert_matches!(KeyBagManager::open(dir, path).err(), Some(OpenError::KeyBagInvalid(_)));
595
596 std::fs::write(path, br#"{"version": 99, "keys": {}}"#).unwrap();
598 let dir = open_dir(path.parent().unwrap());
599 assert_matches!(
600 KeyBagManager::open(dir, path).err(),
601 Some(OpenError::KeyBagVersionMismatch(99, 1))
602 );
603 }
604}