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