1use aes_gcm_siv::aead::Aead;
6use aes_gcm_siv::{Aes128GcmSiv, Aes256GcmSiv, Key, 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 aes_gcm_siv::Nonce::from_slice(&self.0)
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(Key::<Aes128GcmSiv>::from_slice(bytes));
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(Key::<Aes256GcmSiv>::from_slice(bytes));
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(Key::<Aes128GcmSiv>::from_slice(wrap_bytes));
384 cipher.decrypt(nonce.as_crypto_nonce(), &bytes[..])
385 }
386 WrappingKey::Aes256(wrap_bytes) => {
387 let cipher = Aes256GcmSiv::new(Key::<Aes256GcmSiv>::from_slice(wrap_bytes));
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::{Aes256Key, Error, KeyBagManager, UnwrapError, WrappingKey};
419 use assert_matches::assert_matches;
420 use std::os::fd::{FromRawFd as _, IntoRawFd as _, OwnedFd};
421 use tempfile::NamedTempFile;
422
423 fn open_dir(path: impl openat::AsPath) -> OwnedFd {
424 let dir = openat::Dir::open(path).unwrap();
425 unsafe { OwnedFd::from_raw_fd(dir.into_raw_fd()) }
426 }
427
428 #[test]
429 fn nonexistent_keybag() {
430 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
431 let path: &std::path::Path = owned_path.as_ref();
432 std::fs::remove_file(path).expect("unlink failed");
433 let dir = open_dir(path.parent().unwrap());
434 let keybag = KeyBagManager::create(dir, path).expect("Open nonexistent keybag failed");
435 assert!(keybag.key_bag.keys.is_empty());
436 }
437
438 #[test]
439 fn empty_keybag() {
440 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
441 let path: &std::path::Path = owned_path.as_ref();
442 let dir = open_dir(path.parent().unwrap());
443 let keybag = KeyBagManager::create(dir, path).expect("Open empty keybag failed");
444 assert!(keybag.key_bag.keys.is_empty());
445 }
446
447 #[test]
448 fn add_remove_key() {
449 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
450 let path: &std::path::Path = owned_path.as_ref();
451 {
452 let dir = open_dir(path.parent().unwrap());
453 let mut keybag = KeyBagManager::create(dir, path).expect("Open empty keybag failed");
454 let key = WrappingKey::Aes256([0u8; 32]);
455 keybag.new_key(0, &key).expect("new key failed");
456 assert_eq!(
457 Error::SlotAlreadyUsed,
458 keybag.new_key(0, &key).expect_err("new key on used slot failed")
459 );
460 }
461 {
462 let dir = open_dir(path.parent().unwrap());
463 let mut keybag = KeyBagManager::open(dir, path)
464 .expect("Open keybag failed")
465 .expect("keybag not found");
466 keybag.remove_key(0).expect("remove_key failed");
467 assert_eq!(
468 Error::SlotNotFound,
469 keybag.remove_key(1).expect_err("remove_key with invalid key specified failed")
470 );
471 }
472 let dir = open_dir(path.parent().unwrap());
473 let keybag =
474 KeyBagManager::open(dir, path).expect("Open keybag failed").expect("keybag not found");
475 assert!(keybag.key_bag.keys.is_empty());
476 }
477
478 #[test]
479 fn unwrap_key() {
480 let owned_path = NamedTempFile::new().unwrap().into_temp_path();
481 let path: &std::path::Path = owned_path.as_ref();
482 let dir = open_dir(path.parent().unwrap());
483 let mut keybag = KeyBagManager::create(dir, path).expect("Open empty keybag failed");
484
485 let key = WrappingKey::Aes256([3u8; 32]);
486 let key2 = WrappingKey::Aes128([0xffu8; 16]);
487
488 keybag.new_key(0, &key).expect("new_key failed");
489 keybag.new_key(1, &key2).expect("new_key failed");
490 keybag.new_key(2, &key).expect("new_key failed");
491
492 assert_matches!(keybag.unwrap_key(0, &key), Ok(_));
493 assert_eq!(keybag.unwrap_key(1, &key), Err(UnwrapError::AccessDenied));
494 assert_matches!(keybag.unwrap_key(2, &key), Ok(_));
495 assert_eq!(keybag.unwrap_key(3, &key), Err(UnwrapError::SlotNotFound));
496
497 assert_eq!(keybag.unwrap_key(0, &key2), Err(UnwrapError::AccessDenied));
498 assert_matches!(keybag.unwrap_key(1, &key2), Ok(_));
499 assert_eq!(keybag.unwrap_key(2, &key2), Err(UnwrapError::AccessDenied));
500 assert_eq!(keybag.unwrap_key(3, &key2), Err(UnwrapError::SlotNotFound));
501 }
502
503 #[test]
504 fn from_testdata() {
505 let path = std::path::Path::new("/pkg/data/key_bag.json");
509 let dir = open_dir(path.parent().unwrap());
510 let keybag =
511 KeyBagManager::open(dir, path).expect("Open keybag failed").expect("keybag not found");
512
513 let mut expected = Aes256Key::default();
514 expected.0[..6].copy_from_slice(b"secret");
515
516 let key = WrappingKey::Aes256([0u8; 32]);
517 assert_eq!(keybag.unwrap_key(0, &key).as_ref().map(|s| &s.0), Ok(&expected.0));
518 assert_eq!(keybag.unwrap_key(1, &key), Err(UnwrapError::AccessDenied));
519 assert_eq!(keybag.unwrap_key(2, &key), Ok(expected));
520 assert_eq!(keybag.unwrap_key(3, &key), Err(UnwrapError::SlotNotFound));
521 }
522}