1use aes_gcm_siv::aead::Aead;
6use aes_gcm_siv::{Aes256GcmSiv, KeyInit as _, Nonce};
7use anyhow::{Error, bail};
8use async_trait::async_trait;
9use fidl_fuchsia_fxfs::{
10 self as ffxfs, CryptRequest, CryptRequestStream, FscryptKeyIdentifier,
11 FscryptKeyIdentifierAndNonce, ObjectType, WrappedKey,
12};
13use fidl_fuchsia_hardware_inlineencryption::DeviceSynchronousProxy;
14use fscrypt::hkdf::{HKDF_CONTEXT_INODE_HASH_KEY, HKDF_CONTEXT_KEY_IDENTIFIER, fscrypt_hkdf};
15use fuchsia_sync::Mutex;
16use futures::stream::StreamExt;
17use fxfs_crypto::{Crypt, EncryptionKey, FxfsKey, KeyPurpose, UnwrappedKey, WrappingKeyId};
18use hkdf::Hkdf;
19use linux_uapi::FSCRYPT_KEY_IDENTIFIER_SIZE;
20use starnix_uapi::errors::{Errno, errno, error, from_status_like_fdio};
21use std::collections::hash_map::{Entry, HashMap};
22use std::sync::OnceLock;
23
24const FXFS_FSCRYPT_WRAPPING_KEY_INFO: &str = "fscrypt1";
29const FSCRYPT_HKDF_NONCE_PREFIX: &[u8] = b"fscrypt\0";
30
31const DATA_UNIT_SIZE: u32 = 4096;
32const AES256_KEY_SIZE: usize = 32;
33
34pub type EncryptionKeyId = [u8; FSCRYPT_KEY_IDENTIFIER_SIZE as usize];
36
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
39pub enum FscryptMode {
40 #[default]
43 Standard,
44 InoLblk64,
47 InoLblk32,
50}
51
52impl FscryptMode {
53 pub fn from_flags(flags: fidl_fuchsia_io::FscryptPolicyFlags) -> Self {
54 if flags.contains(fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_64) {
55 Self::InoLblk64
56 } else if flags.contains(fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32) {
57 Self::InoLblk32
58 } else {
59 Self::Standard
60 }
61 }
62}
63
64struct KeyInfo {
65 users: Vec<u32>,
66 key: Box<[u8]>,
67 key_token: Option<zx::EventPair>,
68}
69
70struct CryptServiceInner {
71 keys: HashMap<EncryptionKeyId, KeyInfo>,
72 metadata_key: Option<EncryptionKeyId>,
73 data_key: Option<EncryptionKeyId>,
74}
75
76pub struct CryptService {
77 inner: Mutex<CryptServiceInner>,
78 inline_crypto_proxy: Option<DeviceSynchronousProxy>,
79 uuid: OnceLock<[u8; 16]>,
80}
81
82impl CryptService {
83 pub fn new(
85 raw_metadata_key: &[u8],
86 raw_data_key: &[u8],
87 inline_crypto_proxy: Option<DeviceSynchronousProxy>,
88 ) -> Self {
89 let metadata_wrapping_key_id = derive_lblk32_wrapping_key_id(raw_metadata_key);
90 let data_wrapping_key_id = derive_lblk32_wrapping_key_id(raw_data_key);
91
92 fn to_key_info(key: &[u8]) -> KeyInfo {
93 KeyInfo { users: Vec::new(), key: key.into(), key_token: None }
94 }
95
96 Self {
97 inner: Mutex::new(CryptServiceInner {
98 keys: HashMap::from_iter([
99 (metadata_wrapping_key_id, to_key_info(raw_metadata_key)),
100 (data_wrapping_key_id, to_key_info(raw_data_key)),
101 ]),
102 metadata_key: Some(metadata_wrapping_key_id),
103 data_key: Some(data_wrapping_key_id),
104 }),
105 inline_crypto_proxy,
106 uuid: OnceLock::new(),
107 }
108 }
109
110 pub fn contains_key(&self, key: EncryptionKeyId) -> bool {
112 let inner = self.inner.lock();
113 inner.keys.contains_key(&key)
114 }
115
116 pub fn get_users_for_key(&self, key: EncryptionKeyId) -> Option<Vec<u32>> {
118 let inner = self.inner.lock();
119 inner.keys.get(&key).map(|x| x.users.clone())
120 }
121
122 pub fn add_wrapping_key(&self, raw_key: &[u8], uid: u32) -> Result<EncryptionKeyId, Errno> {
124 let (key, key_token) = if let Some(proxy) = self.inline_crypto_proxy.as_ref() {
125 let key = Box::from(
126 proxy
127 .derive_raw_secret(raw_key, zx::MonotonicInstant::INFINITE)
128 .map_err(|error| {
129 log::error!(error:?; "derive_raw_secret FIDL error");
130 errno!(EPIPE)
131 })?
132 .map_err(|status| {
133 let status = zx::Status::err_from_raw(status);
134 log::error!(status:?; "derive_raw_secret failed");
135 from_status_like_fdio!(status)
136 })?,
137 );
138 let key_token = proxy
139 .program_key(raw_key, DATA_UNIT_SIZE, zx::MonotonicInstant::INFINITE)
140 .map_err(|error| {
141 log::error!(error:?; "program_key FIDL error");
142 errno!(EPIPE)
143 })?
144 .map_err(|status| {
145 let status = zx::Status::err_from_raw(status);
146 log::error!(status:?; "program_key failed");
147 from_status_like_fdio!(status)
148 })?;
149 (key, Some(key_token))
150 } else {
151 (Box::from(raw_key), None)
152 };
153 let key_identifier = derive_lblk32_wrapping_key_id(&key);
154 let mut inner = self.inner.lock();
155 match inner.keys.entry(key_identifier) {
156 Entry::Occupied(mut e) => {
157 let users = &mut e.get_mut().users;
158 if !users.contains(&uid) {
159 users.push(uid);
160 }
161 Ok(key_identifier)
162 }
163 Entry::Vacant(vacant) => {
164 vacant.insert(KeyInfo { users: vec![uid], key, key_token });
165 Ok(key_identifier)
166 }
167 }
168 }
169
170 pub async fn handle_connection(&self, mut stream: CryptRequestStream) -> Result<(), Error> {
172 while let Some(request) = stream.next().await {
173 match request {
174 Ok(CryptRequest::CreateKey { owner, purpose, responder }) => {
175 match self.create_key(owner, purpose) {
176 Ok((key, unwrapped_key)) => responder.send(Ok((
177 &key.wrapping_key_id,
178 &key.key[..],
179 &unwrapped_key[..],
180 ))),
181 Err(e) => responder.send(Err(e.into_raw())),
182 }
183 .unwrap_or_else(
184 |error| log::error!(error:?; "Failed to send CreateKey response"),
185 );
186 }
187 Ok(CryptRequest::CreateKeyWithId {
188 owner,
189 wrapping_key_id,
190 object_type,
191 flags,
192 responder,
193 }) => {
194 match self.create_key_with_id(owner, wrapping_key_id, object_type, flags) {
195 Ok((key, unwrapped_key, key_token)) => {
196 responder.send(Ok((&key.into(), &unwrapped_key[..], key_token)))
197 }
198 Err(e) => responder.send(Err(e.into_raw())),
199 }
200 .unwrap_or_else(
201 |error| log::error!(error:?; "Failed to send CreateKeyWithId response"),
202 );
203 }
204 Ok(CryptRequest::UnwrapKey { owner, wrapped_key, responder }) => {
205 match self.unwrap_key(&wrapped_key, owner) {
206 Ok((unwrapped_key, key_token)) => {
207 responder.send(Ok((&unwrapped_key[..], key_token)))
208 }
209 Err(e) => responder.send(Err(e.into_raw())),
210 }
211 .unwrap_or_else(
212 |error| log::error!(error:?; "Failed to send UnwrapKey response"),
213 );
214 }
215 Err(error) => {
216 log::error!(error:?; "Error in CryptRequestStream");
217 bail!(error);
218 }
219 }
220 }
221 Ok(())
222 }
223
224 pub fn forget_wrapping_key(
226 &self,
227 wrapping_key_id: EncryptionKeyId,
228 uid: u32,
229 ) -> Result<(), Errno> {
230 let mut inner = self.inner.lock();
231 match inner.keys.entry(EncryptionKeyId::from(wrapping_key_id)) {
232 Entry::Occupied(mut e) => {
233 let user_ids = &mut e.get_mut().users;
234 if !user_ids.contains(&uid) {
235 return error!(ENOKEY);
236 } else {
237 let index = user_ids.iter().position(|x: &u32| *x == uid).unwrap();
238 user_ids.remove(index);
239 if user_ids.is_empty() {
240 e.remove();
241 }
242 }
243 }
244 Entry::Vacant(_) => {
245 return error!(ENOKEY);
246 }
247 }
248 Ok(())
249 }
250
251 pub fn set_uuid(&self, uuid: [u8; 16]) {
252 self.uuid.set(uuid).unwrap();
253 }
254
255 fn derive_directory_key(
256 &self,
257 key: &[u8],
258 nonce: &[u8],
259 mode: FscryptMode,
260 ) -> Result<Vec<u8>, zx::Status> {
261 let uuid = self.uuid.get().ok_or(zx::Status::BAD_STATE)?;
262 Ok(match mode {
263 FscryptMode::InoLblk64 => {
264 fscrypt::to_directory_keys_lblk64(key, uuid, nonce).to_unwrapped_key()
265 }
266 FscryptMode::InoLblk32 | FscryptMode::Standard => {
267 fscrypt::to_directory_keys(key, uuid, nonce).to_unwrapped_key()
268 }
269 })
270 }
271
272 fn create_key(
273 &self,
274 owner: u64,
275 purpose: ffxfs::KeyPurpose,
276 ) -> Result<(FxfsKey, UnwrappedKey), zx::Status> {
277 let inner = self.inner.lock();
278 let wrapping_key_id = match purpose {
279 ffxfs::KeyPurpose::Data => inner.data_key.as_ref().ok_or_else(|| {
280 log::error!(
281 "tried to create key with KeyPurpose::Data but no active data wrapping key"
282 );
283 zx::Status::BAD_STATE
284 })?,
285 ffxfs::KeyPurpose::Metadata => inner.metadata_key.as_ref().ok_or_else(|| {
286 log::error!(
287 "tried to create key with KeyPurpose::Metadata but no active data wrapping key"
288 );
289 zx::Status::BAD_STATE
290 })?,
291 _ => return Err(zx::Status::INVALID_ARGS),
292 };
293 let key = &inner.keys.get(wrapping_key_id).ok_or(zx::Status::BAD_STATE)?.key;
294 let cipher = get_fxfs_cipher(key);
295 let nonce = zero_extended_nonce(owner);
296
297 let mut key = [0u8; 32];
298 rand::fill(&mut key[..]);
299
300 let wrapped = cipher.encrypt(&nonce, &key[..]).map_err(|error| {
301 log::error!(error:?; "Failed to wrap key");
302 zx::Status::INTERNAL
303 })?;
304 debug_assert!(
305 wrapped.len() == fxfs_crypto::FXFS_WRAPPED_KEY_SIZE,
306 "wrapped key is the wrong length"
307 );
308
309 Ok((
310 FxfsKey {
311 wrapping_key_id: *wrapping_key_id,
312 key: wrapped.try_into().unwrap(),
314 },
315 UnwrappedKey::new(key.into()),
316 ))
317 }
318
319 fn create_key_with_id(
320 &self,
321 owner: u64,
322 wrapping_key_id: WrappingKeyId,
323 object_type: ObjectType,
324 flags: fidl_fuchsia_io::FscryptPolicyFlags,
325 ) -> Result<(EncryptionKey, UnwrappedKey, Option<zx::EventPair>), zx::Status> {
326 let mut inner = self.inner.lock();
327 let key_info = inner.keys.get_mut(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
328 let mode = FscryptMode::from_flags(flags);
329 match object_type {
330 ObjectType::Directory | ObjectType::Symlink => {
331 let mut nonce = [0; 16];
332 zx::cprng_draw(&mut nonce);
333 let unwrapped_key = self.derive_directory_key(&key_info.key, &nonce, mode)?;
334 let encryption_key = match mode {
335 FscryptMode::InoLblk64 => EncryptionKey::FscryptInoLblk64Dir {
336 key_identifier: wrapping_key_id,
337 nonce,
338 },
339 FscryptMode::InoLblk32 | FscryptMode::Standard => {
340 EncryptionKey::FscryptInoLblk32Dir {
341 key_identifier: wrapping_key_id,
342 nonce,
343 }
344 }
345 };
346 Ok((encryption_key, UnwrappedKey::new(unwrapped_key), None))
347 }
348 ObjectType::File => {
349 if let (Some(key_token), FscryptMode::InoLblk32 | FscryptMode::InoLblk64) =
354 (&key_info.key_token, mode)
355 {
356 let dup_token = key_token.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
357 let unwrapped_key = derive_file_key(&key_info.key, mode);
358 let encryption_key = match mode {
359 FscryptMode::InoLblk64 => {
360 EncryptionKey::FscryptInoLblk64File { key_identifier: wrapping_key_id }
361 }
362 FscryptMode::InoLblk32 => {
363 EncryptionKey::FscryptInoLblk32File { key_identifier: wrapping_key_id }
364 }
365 FscryptMode::Standard => unreachable!(),
366 };
367 Ok((encryption_key, UnwrappedKey::new(unwrapped_key), Some(dup_token)))
368 } else {
369 let cipher = get_fxfs_cipher(&key_info.key);
371 let nonce = zero_extended_nonce(owner);
372
373 let mut key = [0u8; 32];
374 rand::fill(&mut key[..]);
375
376 let wrapped = cipher.encrypt(&nonce, &key[..]).map_err(|error| {
377 log::error!(error:?; "Failed to wrap key");
378 zx::Status::INTERNAL
379 })?;
380 debug_assert!(
381 wrapped.len() == fxfs_crypto::FXFS_WRAPPED_KEY_SIZE,
382 "wrapped key is the wrong length"
383 );
384
385 Ok((
386 EncryptionKey::Fxfs(FxfsKey {
387 wrapping_key_id,
388 key: wrapped.try_into().unwrap(),
390 }),
391 UnwrappedKey::new(key.into()),
392 None,
393 ))
394 }
395 }
396 _ => Err(zx::Status::NOT_SUPPORTED),
397 }
398 }
399
400 fn unwrap_key(
401 &self,
402 wrapped_key: &WrappedKey,
403 owner: u64,
404 ) -> Result<(UnwrappedKey, Option<zx::EventPair>), zx::Status> {
405 let mut inner = self.inner.lock();
406 match wrapped_key {
407 WrappedKey::Fxfs(ffxfs::FxfsKey { wrapping_key_id, wrapped_key }) => {
408 let key_info = inner.keys.get(wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
409 let cipher = get_fxfs_cipher(&key_info.key);
410 let nonce = zero_extended_nonce(owner);
411
412 Ok((
413 UnwrappedKey::new(
414 cipher
415 .decrypt(&nonce, &wrapped_key[..])
416 .map_err(|_| zx::Status::IO_DATA_INTEGRITY)?,
417 ),
418 None,
419 ))
420 }
421 WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { key_identifier }) => {
422 let key_info = inner.keys.get_mut(key_identifier).ok_or(zx::Status::UNAVAILABLE)?;
423 let Some(key_token) = &key_info.key_token else {
424 return Err(zx::Status::UNAVAILABLE);
427 };
428 let dup_token = key_token.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
429 Ok((
430 UnwrappedKey::new(derive_file_key(&key_info.key, FscryptMode::InoLblk32)),
431 Some(dup_token),
432 ))
433 }
434 WrappedKey::FscryptInoLblk64File(FscryptKeyIdentifier { key_identifier }) => {
435 let key_info = inner.keys.get_mut(key_identifier).ok_or(zx::Status::UNAVAILABLE)?;
436 let Some(key_token) = &key_info.key_token else {
437 return Err(zx::Status::UNAVAILABLE);
438 };
439 let dup_token = key_token.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
440 Ok((
441 UnwrappedKey::new(derive_file_key(&key_info.key, FscryptMode::InoLblk64)),
442 Some(dup_token),
443 ))
444 }
445 WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
446 key_identifier,
447 nonce,
448 }) => {
449 let key_info = inner.keys.get(key_identifier).ok_or(zx::Status::UNAVAILABLE)?;
450
451 Ok((
452 UnwrappedKey::new(self.derive_directory_key(
453 &key_info.key,
454 nonce,
455 FscryptMode::InoLblk32,
456 )?),
457 None,
458 ))
459 }
460 WrappedKey::FscryptInoLblk64Dir(FscryptKeyIdentifierAndNonce {
461 key_identifier,
462 nonce,
463 }) => {
464 let key_info = inner.keys.get(key_identifier).ok_or(zx::Status::UNAVAILABLE)?;
465
466 Ok((
467 UnwrappedKey::new(self.derive_directory_key(
468 &key_info.key,
469 nonce,
470 FscryptMode::InoLblk64,
471 )?),
472 None,
473 ))
474 }
475 _ => Err(zx::Status::NOT_SUPPORTED),
476 }
477 }
478}
479
480#[async_trait]
481impl Crypt for CryptService {
482 async fn create_key(
483 &self,
484 owner: u64,
485 purpose: KeyPurpose,
486 ) -> Result<(FxfsKey, UnwrappedKey), zx::Status> {
487 self.create_key(owner, purpose.into())
488 }
489
490 async fn create_key_with_id(
491 &self,
492 owner: u64,
493 wrapping_key_id: WrappingKeyId,
494 object_type: ObjectType,
495 flags: fidl_fuchsia_io::FscryptPolicyFlags,
496 ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status> {
497 let (key, unwrapped_key, _key_token) =
498 self.create_key_with_id(owner, wrapping_key_id, object_type, flags)?;
499 Ok((key, unwrapped_key))
500 }
501
502 async fn unwrap_key(
503 &self,
504 wrapped_key: &WrappedKey,
505 owner: u64,
506 ) -> Result<UnwrappedKey, zx::Status> {
507 let (unwrapped_key, _key_token) = self.unwrap_key(wrapped_key, owner)?;
508 Ok(unwrapped_key)
509 }
510}
511
512fn zero_extended_nonce(val: u64) -> Nonce {
513 let mut nonce = Nonce::default();
514 nonce.as_mut_slice()[..8].copy_from_slice(&val.to_le_bytes());
515 nonce
516}
517
518fn derive_file_key(key: &[u8], mode: FscryptMode) -> Vec<u8> {
519 match mode {
520 FscryptMode::InoLblk32 | FscryptMode::Standard => {
521 let ino_hash_key: [u8; 16] = fscrypt_hkdf(key, &[], HKDF_CONTEXT_INODE_HASH_KEY);
522 ino_hash_key.to_vec()
523 }
524 FscryptMode::InoLblk64 => Vec::new(),
525 }
526}
527
528fn get_fxfs_cipher(raw_key: &[u8]) -> Aes256GcmSiv {
529 let hk = Hkdf::<sha2::Sha256>::new(None, raw_key);
530 let mut wrapping_key = [0u8; AES256_KEY_SIZE];
531 hk.expand(FXFS_FSCRYPT_WRAPPING_KEY_INFO.as_bytes(), &mut wrapping_key).unwrap();
532 Aes256GcmSiv::new((&wrapping_key).into())
533}
534
535fn derive_lblk32_wrapping_key_id(raw_key: &[u8]) -> [u8; FSCRYPT_KEY_IDENTIFIER_SIZE as usize] {
536 let hk = Hkdf::<sha2::Sha512>::new(None, raw_key);
537 let mut key_identifier = [0u8; FSCRYPT_KEY_IDENTIFIER_SIZE as usize];
538 let mut hkdf_info = FSCRYPT_HKDF_NONCE_PREFIX.to_vec();
539 hkdf_info.push(HKDF_CONTEXT_KEY_IDENTIFIER);
540 hk.expand(&hkdf_info, &mut key_identifier).unwrap();
541 key_identifier
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547 use assert_matches::assert_matches;
548 use block_client::RemoteBlockClient;
549 use fidl_fuchsia_fxfs::WrappedKey;
550 use fidl_fuchsia_hardware_inlineencryption::{DeviceMarker, DeviceRequest};
551 use fidl_fuchsia_storage_block::BlockProxy;
552 use fxfs_crypto::FindKeyResult;
553
554 use fuchsia_async::LocalExecutor;
555 use starnix_uapi::errno;
556 use std::sync::Arc;
557 use storage_device::block_device::BlockDevice;
558 use storage_device::{Device, InlineCryptoOptions, ReadOptions, WriteOptions};
559 use test_vmo_backed_block_server::VmoBackedServer;
560
561 const BLOCK_SIZE: u32 = 4096;
562 const BLOCK_COUNT: u64 = 393216;
563 const TEST_UUID: [u8; 16] =
564 [75, 146, 230, 48, 132, 165, 68, 97, 141, 247, 22, 242, 153, 171, 153, 38];
565
566 #[test]
567 fn add_and_forget_wrapping_keys() {
568 let service = CryptService::new(&[0; 32], &[1; 32], None);
569
570 let wrapping_key_id =
572 service.add_wrapping_key(&u128::to_le_bytes(1), 0).expect("add wrapping key failed");
573
574 let wrapping_key_id2 =
575 service.add_wrapping_key(&u128::to_le_bytes(1), 1).expect("add wrapping key failed");
576
577 assert_eq!(wrapping_key_id2, wrapping_key_id);
578
579 let wrapping_key_id3 =
581 service.add_wrapping_key(&u128::to_le_bytes(1), 1).expect("add wrapping key failed");
582
583 assert_eq!(wrapping_key_id3, wrapping_key_id);
584
585 {
586 let inner = service.inner.lock();
587 assert_eq!(inner.keys.get(&wrapping_key_id).unwrap().users, [0, 1]);
588 }
589
590 service.forget_wrapping_key(wrapping_key_id, 1).expect("forget wrapping key failed");
593 service
594 .create_key_with_id(
595 0,
596 wrapping_key_id,
597 ObjectType::File,
598 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
599 )
600 .expect("create key with id failed");
601
602 assert_eq!(
604 service.forget_wrapping_key(wrapping_key_id, 1).expect_err(
605 "forget wrapping key should fail if the key was already removed by this user"
606 ),
607 errno!(ENOKEY)
608 );
609 service.forget_wrapping_key(wrapping_key_id, 0).expect("forget wrapping key failed");
611 assert_eq!(
612 service
613 .create_key_with_id(
614 0,
615 EncryptionKeyId::from(u128::to_le_bytes(1)),
616 ObjectType::File,
617 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
618 )
619 .expect_err(
620 "create_key_with_id should fail if the key hasn't been added by the caller"
621 ),
622 zx::Status::UNAVAILABLE
623 );
624 service.add_wrapping_key(&u128::to_le_bytes(1), 0).expect("add wrapping key failed");
625 }
626
627 #[fuchsia::test]
628 async fn test_derive_wrapping_key_id_and_lblk32_derived_keys() {
629 const EXPECTED_WRAPPING_KEY_ID: [u8; 16] =
630 [40, 205, 90, 253, 77, 129, 133, 220, 222, 25, 208, 200, 136, 101, 239, 101];
631 const EXPECTED_CTS_KEY: [u8; 32] = [
632 223, 72, 191, 189, 133, 62, 81, 175, 91, 93, 132, 0, 9, 246, 22, 32, 76, 91, 28, 2, 96,
633 27, 182, 66, 131, 84, 218, 118, 230, 226, 142, 115,
634 ];
635 const EXPECTED_INO_HASH_KEY: [u8; 16] =
636 [241, 22, 180, 110, 76, 135, 84, 48, 206, 33, 210, 253, 11, 10, 230, 122];
637
638 let block_server = Arc::new(
639 VmoBackedServer::new(BLOCK_COUNT, BLOCK_SIZE, &[])
640 .expect("Failed to create VmoBackedServer"),
641 );
642
643 let (insecure_inilne_crypto_proxy, server) =
644 fidl::endpoints::create_sync_proxy::<DeviceMarker>();
645 std::thread::spawn(|| {
646 LocalExecutor::default().run_singlethreaded(async move {
647 block_server.connect_insecure_inline_encryption_server(server, TEST_UUID).await;
648 })
649 });
650
651 let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
652 service.set_uuid(TEST_UUID);
653 let wrapping_key_id = service.add_wrapping_key(&[0xdc; 32], 0).unwrap();
654 assert_eq!(wrapping_key_id, EXPECTED_WRAPPING_KEY_ID);
655
656 let (_, unwrapped_key, key_token) = service
657 .create_key_with_id(
658 0,
659 wrapping_key_id,
660 ObjectType::Directory,
661 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
662 )
663 .unwrap();
664 assert!(key_token.is_none());
665 let (cts_key, remainder) = unwrapped_key.split_at(EXPECTED_CTS_KEY.len());
666 let (ino_hash_key, _dir_hash_key) = remainder.split_at(EXPECTED_INO_HASH_KEY.len());
667
668 assert_eq!(cts_key, &EXPECTED_CTS_KEY);
669 assert_eq!(ino_hash_key, &EXPECTED_INO_HASH_KEY);
670 }
671
672 #[fuchsia::test]
673 async fn test_create_key_with_id_with_lblk32_key() {
674 let block_server = Arc::new(
675 VmoBackedServer::new(BLOCK_COUNT, BLOCK_SIZE, &[])
676 .expect("Failed to create VmoBackedServer"),
677 );
678
679 let block_server_clone = block_server.clone();
680 let (insecure_inilne_crypto_proxy, server) =
681 fidl::endpoints::create_sync_proxy::<DeviceMarker>();
682 std::thread::spawn(|| {
683 LocalExecutor::default().run_singlethreaded(async move {
684 block_server_clone
685 .connect_insecure_inline_encryption_server(server, TEST_UUID)
686 .await;
687 })
688 });
689
690 let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
691 let wrapping_key_id = service.add_wrapping_key(&[0xcd; 32], 0).unwrap();
692
693 let (wrapped_key, unwrapped_key, key_token) = service
694 .create_key_with_id(
695 0,
696 wrapping_key_id,
697 ObjectType::File,
698 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
699 )
700 .expect("create_key failed");
701 assert_matches!(wrapped_key, EncryptionKey::FscryptInoLblk32File { .. });
702 let key_token = key_token.expect("expected key_token");
703
704 let mut key = [0xcd; 32];
705 for b in &mut key {
706 *b = *b >> 4 | *b << 4;
707 }
708 let expected_ino_hash_key: [u8; 16] = fscrypt_hkdf(&key, &[], HKDF_CONTEXT_INODE_HASH_KEY);
709 assert_eq!(unwrapped_key[..16], expected_ino_hash_key);
710 let device = BlockDevice::new(
712 RemoteBlockClient::new(block_server.clone().connect::<BlockProxy>())
713 .await
714 .expect("Unable to create block client"),
715 false,
716 )
717 .await
718 .unwrap();
719
720 let expected_slot = device.register_key(key_token).await.expect("register_key failed");
721
722 let plaintext: &[u8] = b"This is aligned sensitive data!!";
723 let mut buf = device.allocate_buffer(4096).await;
724 buf.subslice_mut(..plaintext.len()).copy_from_slice(plaintext);
725 device
726 .write_with_opts(
727 0,
728 buf.as_ref(),
729 WriteOptions {
730 inline_crypto: InlineCryptoOptions::enabled(expected_slot, 0),
731 ..Default::default()
732 },
733 )
734 .await
735 .expect("failed to write data");
736
737 let mut read_buf = device.allocate_buffer(4096).await;
738
739 device
741 .read_with_opts(0, read_buf.as_mut(), ReadOptions::default())
742 .await
743 .expect("Read failed");
744 assert_ne!(&read_buf.to_vec()[..plaintext.len()], plaintext);
745
746 let wrapping_key_id_2 = service.add_wrapping_key(&[0xab; 32], 0).unwrap();
748 let (_, _, key_token_2) = service
749 .create_key_with_id(
750 0,
751 wrapping_key_id_2,
752 ObjectType::File,
753 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
754 )
755 .unwrap();
756 device
758 .read_with_opts(
759 0,
760 read_buf.as_mut(),
761 ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot + 1, 0) },
762 )
763 .await
764 .expect_err("Read passed unexpectedly with unregistered key slot");
765
766 let slot_2 =
767 device.register_key(key_token_2.unwrap()).await.expect("register_key 2 failed");
768 device
769 .read_with_opts(
770 0,
771 read_buf.as_mut(),
772 ReadOptions { inline_crypto: InlineCryptoOptions::enabled(slot_2, 0) },
773 )
774 .await
775 .expect("Read failed");
776 assert_ne!(&read_buf.to_vec()[..plaintext.len()], plaintext);
777
778 device
780 .read_with_opts(
781 0,
782 read_buf.as_mut(),
783 ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot + 2, 0) },
784 )
785 .await
786 .expect_err("Read passed unexpectedly with unused key slot");
787
788 device
790 .read_with_opts(
791 0,
792 read_buf.as_mut(),
793 ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot, 0) },
794 )
795 .await
796 .expect("Read failed");
797 assert_eq!(&read_buf.to_vec()[..plaintext.len()], plaintext);
798 }
799
800 #[fuchsia::test]
801 fn unwrap_fxfs_wrapped_key_with_lblk32_key() {
802 let block_server = Arc::new(
803 VmoBackedServer::new(BLOCK_COUNT, BLOCK_SIZE, &[])
804 .expect("Failed to create VmoBackedServer"),
805 );
806 let (insecure_inilne_crypto_proxy, server) =
807 fidl::endpoints::create_sync_proxy::<DeviceMarker>();
808 std::thread::spawn(|| {
809 LocalExecutor::default().run_singlethreaded(async move {
810 block_server.connect_insecure_inline_encryption_server(server, TEST_UUID).await;
811 })
812 });
813
814 let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
815 service.set_uuid(TEST_UUID);
816 let wrapping_key_id =
817 service.add_wrapping_key(&[0xcd; 32], 0).expect("add wrapping key failed");
818 let (wrapped_key, expected_unwrapped_key, _) = service
819 .create_key_with_id(
820 0,
821 wrapping_key_id,
822 ObjectType::Directory,
823 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
824 )
825 .expect("create_key failed");
826 assert_matches!(
827 wrapped_key,
828 EncryptionKey::FscryptInoLblk32Dir {
829 key_identifier,
830 ..
831 } if key_identifier == wrapping_key_id
832 );
833 let (unwrapped_key, _) =
834 service.unwrap_key(&wrapped_key.into(), 0).expect("create_key failed");
835 assert_eq!(*unwrapped_key, *expected_unwrapped_key);
836 }
837
838 #[test]
839 fn wrap_unwrap_key() {
840 let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
841
842 let (fxfs_key, unwrapped_key) =
843 service.create_key(0, ffxfs::KeyPurpose::Data).expect("create_key failed");
844 let (unwrap_result, _) = service
845 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.clone().into()), 0)
846 .expect("unwrap_key failed");
847 assert_eq!(*unwrap_result, *unwrapped_key);
848
849 let (fxfs_key, unwrapped_key) =
851 service.create_key(1, ffxfs::KeyPurpose::Data).expect("create_key failed");
852 let (unwrap_result, _) =
853 service.unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 1).expect("unwrap_key failed");
854 assert_eq!(*unwrap_result, *unwrapped_key);
855 }
856
857 #[test]
858 fn wrap_unwrap_key_with_arbitrary_wrapping_key() {
859 let service = CryptService::new(&[0; 32], &[1; 32], None);
860
861 let wrapping_key_id =
862 service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
863
864 let (wrapped_key, unwrapped_key, _) = service
865 .create_key_with_id(
866 0,
867 wrapping_key_id,
868 ObjectType::File,
869 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
870 )
871 .expect("create_key_with_id failed");
872 match wrapped_key {
874 EncryptionKey::Fxfs(fxfs_key) => {
875 assert_eq!(fxfs_key.wrapping_key_id, wrapping_key_id);
876 let (unwrap_result, _) = service
877 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 0)
878 .expect("unwrap_key failed");
879 assert_eq!(*unwrap_result, *unwrapped_key);
880 }
881 _ => panic!("Found a non-FxfsKey wrapped key"),
882 }
883
884 let (wrapped_key, unwrapped_key, _) = service
886 .create_key_with_id(
887 1,
888 wrapping_key_id,
889 ObjectType::File,
890 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
891 )
892 .expect("create_key_with_id failed");
893 match wrapped_key {
895 EncryptionKey::Fxfs(fxfs_key) => {
896 assert_eq!(fxfs_key.wrapping_key_id, wrapping_key_id);
897 let (unwrap_result, _) = service
898 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 1)
899 .expect("unwrap_key failed");
900 assert_eq!(*unwrap_result, *unwrapped_key);
901 }
902 _ => panic!("Found a non-FxfsKey wrapped key"),
903 }
904 }
905
906 #[test]
907 fn create_key_with_wrapping_key_that_does_not_exist() {
908 let service = CryptService::new(&[0; 32], &[1; 32], None);
909
910 let wrapping_key_id =
911 service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
912
913 let (wrapped_key, unwrapped_key, _) = service
914 .create_key_with_id(
915 0,
916 wrapping_key_id,
917 ObjectType::File,
918 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
919 )
920 .expect("create_key_with_id failed");
921
922 match wrapped_key {
924 EncryptionKey::Fxfs(fxfs_key) => {
925 assert_eq!(fxfs_key.wrapping_key_id, wrapping_key_id);
926 let (unwrap_result, _) = service
927 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 0)
928 .expect("unwrap_key failed");
929 assert_eq!(*unwrap_result, *unwrapped_key);
930 }
931 _ => panic!("Found a non-FxfsKey wrapped key"),
932 }
933
934 service.forget_wrapping_key(wrapping_key_id, 0).unwrap();
935
936 service
937 .create_key_with_id(
938 0,
939 wrapping_key_id,
940 ObjectType::File,
941 fidl_fuchsia_io::FscryptPolicyFlags::empty(),
942 )
943 .expect_err("create_key_with_id should fail if the wrapping key does not exist");
944 }
945
946 #[test]
947 fn unwrap_key_wrong_key() {
948 let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
949 let (mut fxfs_key, _) =
950 service.create_key(0, ffxfs::KeyPurpose::Data).expect("create_key failed");
951 for byte in &mut fxfs_key.key.0 {
952 *byte ^= 0xff;
953 }
954 service
955 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 0)
956 .expect_err("unwrap_key should fail");
957 }
958
959 #[test]
960 fn unwrap_key_wrong_owner() {
961 let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
962
963 let (fxfs_key, _) =
964 service.create_key(0, ffxfs::KeyPurpose::Data).expect("create_key failed");
965 service
966 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 1)
967 .expect_err("unwrap_key should fail");
968 }
969
970 #[fuchsia::test]
971 async fn test_add_wrapping_key_uses_raw_key() {
972 let (client, server) = fidl::endpoints::create_sync_proxy::<DeviceMarker>();
973 let raw_key_bytes = [0xAB; 32];
974 let expected_key = raw_key_bytes.clone();
975
976 std::thread::spawn(move || {
977 LocalExecutor::default().run_singlethreaded(async move {
978 let mut stream = server.into_stream();
979 while let Some(Ok(request)) = stream.next().await {
980 match request {
981 DeviceRequest::DeriveRawSecret { wrapped_key, responder } => {
982 let mut derived = wrapped_key.clone();
983 derived[0] ^= 0xFF;
984 responder.send(Ok(&derived)).unwrap();
985 }
986 DeviceRequest::ProgramKey { wrapped_key, responder, .. } => {
987 if wrapped_key == expected_key {
988 let (_server_ep, client_ep) = zx::EventPair::create();
989 responder.send(Ok(client_ep)).unwrap();
990 } else {
991 responder.send(Err(zx::Status::INVALID_ARGS.into_raw())).unwrap();
992 }
993 }
994 }
995 }
996 })
997 });
998
999 let service = CryptService::new(&[0; 32], &[1; 32], Some(client));
1000 service.set_uuid([0x55; 16]);
1001 let key_id = service.add_wrapping_key(&raw_key_bytes, 0).expect("add_wrapping_key failed");
1002
1003 let (wrapped_lblk32, unwrapped_lblk32, token_lblk32) = service
1006 .create_key_with_id(
1007 0,
1008 key_id,
1009 ObjectType::File,
1010 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
1011 )
1012 .expect("create_key failed");
1013 assert_eq!(unwrapped_lblk32.len(), 16);
1014 assert!(token_lblk32.is_some());
1015 let (_, unwrapped_dir32, token_dir32) = service
1016 .create_key_with_id(
1017 0,
1018 key_id,
1019 ObjectType::Directory,
1020 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
1021 )
1022 .expect("create_key dir failed");
1023 assert_eq!(unwrapped_dir32.len(), 64);
1024 assert!(token_dir32.is_none());
1025
1026 assert!(service.contains_key(key_id));
1029 let (wrapped_lblk64, unwrapped_lblk64, token_lblk64) = service
1030 .create_key_with_id(
1031 0,
1032 key_id,
1033 ObjectType::File,
1034 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_64,
1035 )
1036 .expect("create_key failed");
1037 assert!(unwrapped_lblk64.is_empty());
1038 assert!(token_lblk64.is_some());
1039 let (wrapped_dir64, unwrapped_dir64, token_dir64) = service
1040 .create_key_with_id(
1041 0,
1042 key_id,
1043 ObjectType::Directory,
1044 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_64,
1045 )
1046 .expect("create_key dir failed");
1047 assert_eq!(unwrapped_dir64.len(), 48);
1048 assert!(token_dir64.is_none());
1049
1050 assert!(service.contains_key(key_id));
1052 let (wrapped_std, unwrapped_std, token_std) = service
1053 .create_key_with_id(
1054 42,
1055 key_id,
1056 ObjectType::File,
1057 fidl_fuchsia_io::FscryptPolicyFlags::PAD_16,
1058 )
1059 .expect("create_key failed");
1060 assert!(matches!(wrapped_std, EncryptionKey::Fxfs(_)));
1061 assert_eq!(unwrapped_std.len(), 32);
1062 assert!(token_std.is_none());
1063
1064 let (unwrapped, token) =
1066 service.unwrap_key(&wrapped_lblk32.into(), 0).expect("unwrap failed");
1067 assert_eq!(*unwrapped, *unwrapped_lblk32);
1068 assert!(token.is_some());
1069 let (unwrapped, token) =
1070 service.unwrap_key(&wrapped_lblk64.into(), 0).expect("unwrap failed");
1071 assert!(unwrapped.is_empty());
1072 assert!(token.is_some());
1073 let (unwrapped, token) =
1074 service.unwrap_key(&wrapped_dir64.into(), 0).expect("unwrap failed");
1075 assert_eq!(*unwrapped, *unwrapped_dir64);
1076 assert!(token.is_none());
1077 let (unwrapped, token) =
1078 service.unwrap_key(&wrapped_std.into(), 42).expect("unwrap failed");
1079 assert_eq!(*unwrapped, *unwrapped_std);
1080 assert!(token.is_none());
1081 }
1082
1083 #[fuchsia::test]
1084 async fn test_crypt_trait_create_and_unwrap_key() {
1085 let service = CryptService::new(&[0; 32], &[1; 32], None);
1086 service.set_uuid(TEST_UUID);
1087 let wrapping_key_id =
1088 service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
1089
1090 let crypt: &dyn Crypt = &service;
1091
1092 for purpose in [KeyPurpose::Data, KeyPurpose::Metadata] {
1094 let (fxfs_key, unwrapped_key) =
1095 crypt.create_key(0, purpose).await.expect("create_key failed");
1096 let unwrapped = crypt
1097 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.clone().into()), 0)
1098 .await
1099 .expect("unwrap_key failed");
1100 assert_eq!(*unwrapped, *unwrapped_key);
1101
1102 assert_eq!(
1104 crypt
1105 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 1)
1106 .await
1107 .expect_err("unwrap with wrong owner should fail"),
1108 zx::Status::IO_DATA_INTEGRITY
1109 );
1110 }
1111
1112 let (dir_key, unwrapped_dir) = crypt
1114 .create_key_with_id(
1115 0,
1116 wrapping_key_id,
1117 ObjectType::Directory,
1118 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
1119 )
1120 .await
1121 .expect("create_key_with_id dir failed");
1122 let unwrapped =
1123 crypt.unwrap_key(&WrappedKey::from(dir_key), 0).await.expect("unwrap_key dir failed");
1124 assert_eq!(*unwrapped, *unwrapped_dir);
1125
1126 let (file_key, unwrapped_file) = crypt
1128 .create_key_with_id(
1129 0,
1130 wrapping_key_id,
1131 ObjectType::File,
1132 fidl_fuchsia_io::FscryptPolicyFlags::PAD_16,
1133 )
1134 .await
1135 .expect("create_key_with_id file failed");
1136 let unwrapped =
1137 crypt.unwrap_key(&WrappedKey::from(file_key), 0).await.expect("unwrap_key file failed");
1138 assert_eq!(*unwrapped, *unwrapped_file);
1139 }
1140
1141 #[fuchsia::test]
1142 async fn test_crypt_trait_batch_unwrap_keys() {
1143 let service = CryptService::new(&[0; 32], &[1; 32], None);
1144 service.set_uuid(TEST_UUID);
1145 let wrapping_key_id =
1146 service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
1147
1148 let crypt: &dyn Crypt = &service;
1149
1150 let (fxfs_data_key, _) =
1151 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key data failed");
1152 let (fxfs_meta_key, _) =
1153 crypt.create_key(0, KeyPurpose::Metadata).await.expect("create_key metadata failed");
1154 let (dir_key, _) = crypt
1155 .create_key_with_id(
1156 0,
1157 wrapping_key_id,
1158 ObjectType::Directory,
1159 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
1160 )
1161 .await
1162 .expect("create_key_with_id failed");
1163
1164 let missing_key =
1166 EncryptionKey::FscryptInoLblk32Dir { key_identifier: [0xff; 16], nonce: [0; 16] };
1167
1168 let keys = vec![
1169 (1, EncryptionKey::Fxfs(fxfs_data_key)),
1170 (2, EncryptionKey::Fxfs(fxfs_meta_key)),
1171 (3, dir_key),
1172 (4, missing_key),
1173 ];
1174
1175 let cipher_set = Arc::new(crypt.unwrap_keys(&keys, 0).await.expect("unwrap_keys failed"));
1176
1177 assert!(matches!(cipher_set.find_key(1), FindKeyResult::Key(_)));
1179 assert!(matches!(cipher_set.find_key(2), FindKeyResult::Key(_)));
1180 let dir_cipher = match cipher_set.find_key(3) {
1181 FindKeyResult::Key(c) => c,
1182 _ => panic!("Expected key 3 to be found"),
1183 };
1184 assert!(matches!(cipher_set.find_key(4), FindKeyResult::Unavailable));
1186 assert!(matches!(cipher_set.find_key(5), FindKeyResult::NotFound));
1188
1189 let mut filename = b"test_filename".to_vec();
1191 dir_cipher.encrypt_filename(100, &mut filename).expect("encrypt filename failed");
1192 assert_ne!(&filename, b"test_filename");
1193 dir_cipher.decrypt_filename(100, &mut filename).expect("decrypt filename failed");
1194 assert_eq!(&filename, b"test_filename");
1195 }
1196
1197 #[fuchsia::test]
1198 async fn test_crypt_trait_forgotten_key_lifecycle() {
1199 let service = CryptService::new(&[0; 32], &[1; 32], None);
1200 service.set_uuid(TEST_UUID);
1201 let wrapping_key_id =
1202 service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
1203
1204 let crypt: &dyn Crypt = &service;
1205
1206 let (dir_key, unwrapped_dir) = crypt
1207 .create_key_with_id(
1208 0,
1209 wrapping_key_id,
1210 ObjectType::Directory,
1211 fidl_fuchsia_io::FscryptPolicyFlags::IV_INO_LBLK_32,
1212 )
1213 .await
1214 .expect("create_key_with_id failed");
1215
1216 let unwrapped = crypt
1217 .unwrap_key(&WrappedKey::from(dir_key.clone()), 0)
1218 .await
1219 .expect("unwrap_key before forget should succeed");
1220 assert_eq!(*unwrapped, *unwrapped_dir);
1221
1222 service.forget_wrapping_key(wrapping_key_id, 0).expect("forget wrapping key failed");
1225
1226 assert_eq!(
1227 crypt
1228 .unwrap_key(&WrappedKey::from(dir_key), 0)
1229 .await
1230 .expect_err("unwrap_key after forget should fail"),
1231 zx::Status::UNAVAILABLE
1232 );
1233 }
1234}