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