1use aes_gcm_siv::aead::Aead;
6use aes_gcm_siv::{Aes256GcmSiv, KeyInit as _, Nonce};
7use async_trait::async_trait;
8use fidl_fuchsia_fxfs_common as fidl_fuchsia_fxfs;
9use fidl_fuchsia_io_common as fidl_fuchsia_io;
10use fuchsia_sync::Mutex;
11use fxfs_crypto::{
12 Crypt, EncryptionKey, FscryptKeyIdentifierAndNonce, KeyPurpose, ObjectType, UnwrappedKey,
13 WrappedKey, WrappingKeyId,
14};
15use rand::RngExt as _;
16use rand::rngs::StdRng;
17use std::collections::hash_map::{Entry, HashMap};
18use std::sync::atomic::{AtomicBool, Ordering};
19use zx_status as zx;
20
21fn zero_extended_nonce(val: u64) -> Nonce {
22 let mut nonce = Nonce::default();
23 nonce.as_mut_slice()[..8].copy_from_slice(&val.to_le_bytes());
24 nonce
25}
26
27struct Cipher {
28 aes_gcm_siv: Aes256GcmSiv,
30 wrapping_key: [u8; 32],
32}
33
34impl Cipher {
35 fn new(wrapping_key: [u8; 32]) -> Self {
36 Self { aes_gcm_siv: Aes256GcmSiv::new_from_slice(&wrapping_key).unwrap(), wrapping_key }
37 }
38
39 fn encrypt(&self, nonce: &Nonce, plaintext: &[u8]) -> Result<Vec<u8>, zx::Status> {
40 self.aes_gcm_siv.encrypt(nonce, plaintext).map_err(|_e| zx::Status::INTERNAL)
41 }
42
43 fn decrypt(&self, nonce: &Nonce, ciphertext: &[u8]) -> Result<Vec<u8>, zx::Status> {
44 self.aes_gcm_siv.decrypt(nonce, ciphertext).map_err(|_e| zx::Status::INTERNAL)
45 }
46}
47
48struct CryptBaseInner {
49 ciphers: HashMap<WrappingKeyId, Cipher>,
50 active_data_key: Option<WrappingKeyId>,
51 active_metadata_key: Option<WrappingKeyId>,
52}
53
54pub struct CryptBase {
56 inner: Mutex<CryptBaseInner>,
57 shutdown: AtomicBool,
58 filesystem_uuid: [u8; 16],
61}
62
63impl CryptBase {
64 pub fn new() -> Self {
65 Self {
66 inner: Mutex::new(CryptBaseInner {
67 ciphers: HashMap::new(),
68 active_data_key: None,
69 active_metadata_key: None,
70 }),
71 shutdown: AtomicBool::new(false),
72 filesystem_uuid: [0; 16],
73 }
74 }
75
76 pub fn add_wrapping_key(&self, id: WrappingKeyId, key: [u8; 32]) -> Result<(), zx::Status> {
77 let mut inner = self.inner.lock();
78 match inner.ciphers.entry(id) {
79 Entry::Occupied(_) => Err(zx::Status::ALREADY_EXISTS),
80 Entry::Vacant(v) => {
81 v.insert(Cipher::new(key));
82 Ok(())
83 }
84 }
85 }
86
87 pub fn set_active_key(&self, purpose: KeyPurpose, id: WrappingKeyId) -> Result<(), zx::Status> {
88 let mut inner = self.inner.lock();
89 if !inner.ciphers.contains_key(&id) {
90 return Err(zx::Status::NOT_FOUND);
91 }
92 match purpose {
93 KeyPurpose::Data => inner.active_data_key = Some(id),
94 KeyPurpose::Metadata => inner.active_metadata_key = Some(id),
95 }
96 Ok(())
97 }
98
99 pub fn forget_wrapping_key(&self, id: &WrappingKeyId) -> Result<(), zx::Status> {
100 let mut inner = self.inner.lock();
101 if let Some(active_id) = inner.active_data_key {
102 if active_id == *id {
103 return Err(zx::Status::INVALID_ARGS);
104 }
105 }
106 if let Some(active_id) = inner.active_metadata_key {
107 if active_id == *id {
108 return Err(zx::Status::INVALID_ARGS);
109 }
110 }
111 inner.ciphers.remove(id);
112 Ok(())
113 }
114
115 pub fn shutdown(&self) {
116 self.shutdown.store(true, Ordering::Relaxed);
117 }
118
119 pub fn set_filesystem_uuid(&mut self, uuid: &[u8; 16]) {
123 self.filesystem_uuid = *uuid;
124 }
125
126 pub async fn create_key_with_id(
127 &self,
128 owner: u64,
129 wrapping_key_id: WrappingKeyId,
130 object_type: ObjectType,
131 ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status> {
132 <Self as Crypt>::create_key_with_id(
133 self,
134 owner,
135 wrapping_key_id,
136 object_type,
137 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
138 )
139 .await
140 }
141}
142
143#[async_trait]
144impl Crypt for CryptBase {
145 async fn create_key(
146 &self,
147 owner: u64,
148 purpose: KeyPurpose,
149 ) -> Result<(fxfs_crypto::FxfsKey, UnwrappedKey), zx::Status> {
150 if self.shutdown.load(Ordering::Relaxed) {
151 return Err(zx::Status::INTERNAL);
152 }
153 let inner = self.inner.lock();
154 let wrapping_key_id = match purpose {
155 KeyPurpose::Data => inner.active_data_key,
156 KeyPurpose::Metadata => inner.active_metadata_key,
157 }
158 .ok_or(zx::Status::INVALID_ARGS)?;
159
160 let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
161
162 let nonce = zero_extended_nonce(owner);
163
164 let mut uwnrapped_key = [0u8; 32];
165 rand::make_rng::<StdRng>().fill(&mut uwnrapped_key);
166
167 let wrapped_key = cipher.encrypt(&nonce, &uwnrapped_key[..])?;
168 Ok((
169 fxfs_crypto::FxfsKey {
170 wrapping_key_id,
171 key: wrapped_key.try_into().map_err(|_| zx::Status::INTERNAL)?,
172 },
173 UnwrappedKey::new(uwnrapped_key.to_vec()),
174 ))
175 }
176
177 async fn create_key_with_id(
178 &self,
179 owner: u64,
180 wrapping_key_id: WrappingKeyId,
181 object_type: ObjectType,
182 _flags: fidl_fuchsia_io::FscryptPolicyFlags,
183 ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status> {
184 if self.shutdown.load(Ordering::Relaxed) {
185 return Err(zx::Status::INTERNAL);
186 }
187
188 match object_type {
189 ObjectType::Directory | ObjectType::Symlink => {
190 let mut nonce = [0; 16];
191 rand::make_rng::<StdRng>().fill(&mut nonce);
192 let inner = self.inner.lock();
193 let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
194 let mut unwrapped_key = [0u8; 96];
195 fscrypt::hkdf::hkdf(&cipher.wrapping_key, &nonce, &mut unwrapped_key);
196 Ok((
197 EncryptionKey::FscryptInoLblk32Dir {
198 key_identifier: wrapping_key_id,
199 nonce: nonce.try_into().map_err(|_| zx::Status::INTERNAL)?,
200 },
201 UnwrappedKey::new(unwrapped_key.to_vec()),
202 ))
203 }
204 _ => {
205 let inner = self.inner.lock();
206 let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
207 let nonce = zero_extended_nonce(owner);
208 let mut unwrapped_key = [0u8; 32];
209 rand::make_rng::<StdRng>().fill(&mut unwrapped_key);
210 let wrapped = cipher.encrypt(&nonce, &unwrapped_key[..])?;
211 Ok((
212 EncryptionKey::Fxfs(fxfs_crypto::FxfsKey {
213 wrapping_key_id,
214 key: wrapped.try_into().map_err(|_| zx::Status::INTERNAL)?,
215 }),
216 UnwrappedKey::new(unwrapped_key.to_vec()),
217 ))
218 }
219 }
220 }
221
222 async fn unwrap_key(
223 &self,
224 wrapped_key: &WrappedKey,
225 owner: u64,
226 ) -> Result<UnwrappedKey, zx::Status> {
227 if self.shutdown.load(Ordering::Relaxed) {
228 return Err(zx::Status::INTERNAL);
229 }
230
231 match wrapped_key {
232 WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
233 key_identifier,
234 nonce,
235 }) => {
236 let inner = self.inner.lock();
237 let cipher = inner.ciphers.get(key_identifier).ok_or(zx::Status::UNAVAILABLE)?;
238 let mut unwrapped_key = [0u8; 96];
239 fscrypt::hkdf::hkdf(&cipher.wrapping_key, nonce, &mut unwrapped_key);
240 Ok(UnwrappedKey::new(unwrapped_key.to_vec()))
241 }
242 WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey { wrapping_key_id, wrapped_key }) => {
243 let inner = self.inner.lock();
244 let cipher = inner.ciphers.get(wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
245 let mut nonce = Nonce::default();
246 nonce.as_mut_slice()[..8].copy_from_slice(&owner.to_le_bytes());
247 Ok(UnwrappedKey::new(cipher.decrypt(&nonce, wrapped_key)?))
248 }
249 _ => Err(zx::Status::NOT_SUPPORTED),
250 }
251 }
252}
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[fuchsia::test]
258 async fn test_wrap_unwrap() {
259 let crypt = CryptBase::new();
260 let key = [0xABu8; 32];
261 let id = [1u8; 16];
262 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
263 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
264
265 let (fxfs_key, unwrapped_key) =
266 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key failed");
267 assert_eq!(fxfs_key.wrapping_key_id, id);
268 assert_eq!(unwrapped_key.len(), 32);
269
270 let unwrapped_back = crypt
271 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 0)
272 .await
273 .expect("unwrap_key failed");
274 assert_eq!(*unwrapped_key, *unwrapped_back);
275 }
276
277 #[fuchsia::test]
278 async fn test_forget_wrapping_key() {
279 let crypt = CryptBase::new();
280 let key = [0xABu8; 32];
281 let id = [1u8; 16];
282 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
283 assert_eq!(crypt.add_wrapping_key(id, key), Err(zx::Status::ALREADY_EXISTS));
284 crypt.forget_wrapping_key(&id).unwrap();
285 assert_eq!(
286 crypt
287 .unwrap_key(
288 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
289 wrapping_key_id: id,
290 wrapped_key: [0u8; 48]
291 }),
292 0
293 )
294 .await
295 .expect_err("unwrap_key should fail when wrapping key is forgotten"),
296 zx::Status::UNAVAILABLE
297 );
298 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
299 }
300
301 #[fuchsia::test]
302 async fn test_active_key_management() {
303 let crypt = CryptBase::new();
304 let key = [0xABu8; 32];
305 let id1 = [0u8; 16];
306 let id2 = [1u8; 16];
307 crypt.add_wrapping_key(id1, key).expect("add_wrapping_key failed");
308 crypt.add_wrapping_key(id2, key).expect("add_wrapping_key failed");
309
310 crypt.set_active_key(KeyPurpose::Data, id1).expect("set_active_key failed");
311 crypt.set_active_key(KeyPurpose::Metadata, id2).expect("set_active_key failed");
312
313 assert_eq!(crypt.forget_wrapping_key(&id1), Err(zx::Status::INVALID_ARGS));
314 assert_eq!(crypt.forget_wrapping_key(&id2), Err(zx::Status::INVALID_ARGS));
315 }
316
317 #[fuchsia::test]
318 async fn test_shutdown() {
319 let crypt = CryptBase::new();
320 let key = [0xABu8; 32];
321 let id = [1u8; 16];
322 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
323 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
324
325 crypt.shutdown();
326
327 assert_eq!(
328 crypt
329 .create_key(0, KeyPurpose::Data)
330 .await
331 .expect_err("create_key should fail when crypt has shut down"),
332 zx::Status::INTERNAL
333 );
334 assert_eq!(
335 crypt
336 .create_key_with_id(0, id, ObjectType::File)
337 .await
338 .expect_err("create_key_with_id should fail when crypt has shut down"),
339 zx::Status::INTERNAL
340 );
341 assert_eq!(
342 crypt
343 .unwrap_key(
344 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
345 wrapping_key_id: id,
346 wrapped_key: [0u8; 48]
347 }),
348 0,
349 )
350 .await
351 .expect_err("unwrap_key should fail when crypt has shut down"),
352 zx::Status::INTERNAL
353 );
354 }
355
356 #[fuchsia::test]
357 async fn test_create_key_no_active_key() {
358 let crypt = CryptBase::new();
359 assert_eq!(
360 crypt
361 .create_key(0, KeyPurpose::Data)
362 .await
363 .expect_err("create_key should fail when no active key is set"),
364 zx::Status::INVALID_ARGS
365 );
366 }
367
368 #[fuchsia::test]
369 async fn test_create_key_with_id_not_found() {
370 let crypt = CryptBase::new();
371 let id = [1u8; 16];
372 assert_eq!(
373 crypt.create_key_with_id(0, id, ObjectType::File).await.expect_err(
374 "create_key_with_id should fail when no active key is set at wrapping key id"
375 ),
376 zx::Status::UNAVAILABLE
377 );
378 }
379
380 #[fuchsia::test]
381 async fn test_unwrap_key_not_found() {
382 let crypt = CryptBase::new();
383 let id = [1u8; 16];
384 assert_eq!(
385 crypt
386 .unwrap_key(
387 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
388 wrapping_key_id: id,
389 wrapped_key: [0u8; 48]
390 }),
391 0,
392 )
393 .await
394 .expect_err("unwrap_key should fail when no active key is set at wrapping key id"),
395 zx::Status::UNAVAILABLE
396 );
397 }
398
399 #[fuchsia::test]
400 async fn test_unwrap_key_wrong_owner() {
401 let crypt = CryptBase::new();
402 let key = [0xABu8; 32];
403 let id = [0u8; 16];
404 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
405 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
406
407 let (fxfs_key, _unwrapped_key) =
408 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key failed");
409 assert_eq!(
411 crypt
412 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 1)
413 .await
414 .expect_err("unwrap_key should fail when owner does not match"),
415 zx::Status::INTERNAL
416 );
417 }
418
419 #[fuchsia::test]
420 async fn test_wrap_unwrap_key_with_arbitrary_wrapping_key_id() {
421 let crypt = CryptBase::new();
422 let key = [0xABu8; 32];
423 let id = [2u8; 16];
424 crypt.add_wrapping_key(id, key).expect("add_key failed");
425
426 let (wrapped_key, unwrapped_key) = crypt
427 .create_key_with_id(0, id, ObjectType::File)
428 .await
429 .expect("create_key_with_id failed");
430 let unwrap_result =
431 crypt.unwrap_key(&WrappedKey::from(wrapped_key), 0).await.expect("unwrap_key failed");
432 assert_eq!(*unwrap_result, *unwrapped_key);
433
434 let (wrapped_key, unwrapped_key) = crypt
436 .create_key_with_id(1, id, ObjectType::File)
437 .await
438 .expect("create_key_with_id failed");
439 let unwrap_result =
440 crypt.unwrap_key(&WrappedKey::from(wrapped_key), 1).await.expect("unwrap_key failed");
441 assert_eq!(*unwrap_result, *unwrapped_key);
442 }
443
444 #[fuchsia::test]
445 async fn test_unwrap_key_wrong_key() {
446 let crypt = CryptBase::new();
447 let key = [0xABu8; 32];
448 let id = [0u8; 16];
449 crypt.add_wrapping_key(id, key).expect("add_key failed");
450 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
451
452 let (fxfs_key, _unwrapped_key) =
453 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key failed");
454 let mut modified_wrapped_key = fxfs_key.key.to_vec();
455 for byte in &mut modified_wrapped_key {
456 *byte ^= 0xff;
457 }
458 assert_eq!(
459 crypt
460 .unwrap_key(
461 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
462 wrapping_key_id: fxfs_key.wrapping_key_id,
463 wrapped_key: modified_wrapped_key.clone().try_into().unwrap(),
464 }),
465 0,
466 )
467 .await
468 .is_err(),
469 true
470 );
471 }
472}