Skip to main content

starnix_crypt/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use aes_gcm_siv::aead::Aead;
6use aes_gcm_siv::{Aes256GcmSiv, KeyInit as _, Nonce};
7use anyhow::{Error, bail};
8use fidl_fuchsia_fxfs::{
9    CryptCreateKeyResult, CryptRequest, CryptRequestStream, FscryptKeyIdentifier,
10    FscryptKeyIdentifierAndNonce, FxfsKey, KeyPurpose, ObjectType, WrappedKey,
11};
12use fidl_fuchsia_hardware_inlineencryption::DeviceSynchronousProxy;
13use fscrypt::hkdf::{HKDF_CONTEXT_INODE_HASH_KEY, HKDF_CONTEXT_KEY_IDENTIFIER, fscrypt_hkdf};
14use fuchsia_sync::Mutex;
15use futures::stream::StreamExt;
16use hkdf::Hkdf;
17use linux_uapi::FSCRYPT_KEY_IDENTIFIER_SIZE;
18use starnix_uapi::errors::{Errno, errno, error, from_status_like_fdio};
19use std::collections::hash_map::{Entry, HashMap};
20use std::sync::OnceLock;
21
22// In this implementation of fscrypt, we use a HKDF (Hmac Key Derivation Function) to derive a
23// a wrapping key and wrapping key id from the raw key bytes passed in by a user on
24// FS_IOC_ADD_ENCRYPTION_KEY. HKDFs requires an input "info" string. We define constants for the
25// respective "info" strings here.
26const FXFS_FSCRYPT_WRAPPING_KEY_INFO: &str = "fscrypt1";
27const FSCRYPT_HKDF_NONCE_PREFIX: &[u8] = b"fscrypt\0";
28
29const DATA_UNIT_SIZE: u32 = 4096;
30const AES256_KEY_SIZE: usize = 32;
31
32/// An fscrypt wrapping key id.
33pub type EncryptionKeyId = [u8; FSCRYPT_KEY_IDENTIFIER_SIZE as usize];
34
35struct KeyInfo {
36    users: Vec<u32>,
37    key: Box<[u8]>,
38    slot: Option<u8>,
39}
40
41struct CryptServiceInner {
42    keys: HashMap<EncryptionKeyId, KeyInfo>,
43    metadata_key: Option<EncryptionKeyId>,
44    data_key: Option<EncryptionKeyId>,
45}
46
47pub struct CryptService {
48    inner: Mutex<CryptServiceInner>,
49    inline_crypto_proxy: Option<DeviceSynchronousProxy>,
50    uuid: OnceLock<[u8; 16]>,
51}
52
53impl CryptService {
54    /// Creates a new crypt service that supports Starnix user volumes.
55    pub fn new(
56        raw_metadata_key: &[u8],
57        raw_data_key: &[u8],
58        inline_crypto_proxy: Option<DeviceSynchronousProxy>,
59    ) -> Self {
60        let metadata_wrapping_key_id = derive_lblk32_wrapping_key_id(raw_metadata_key);
61        let data_wrapping_key_id = derive_lblk32_wrapping_key_id(raw_data_key);
62
63        fn to_key_info(key: &[u8]) -> KeyInfo {
64            KeyInfo { users: Vec::new(), key: key.into(), slot: None }
65        }
66
67        Self {
68            inner: Mutex::new(CryptServiceInner {
69                keys: HashMap::from_iter([
70                    (metadata_wrapping_key_id, to_key_info(raw_metadata_key)),
71                    (data_wrapping_key_id, to_key_info(raw_data_key)),
72                ]),
73                metadata_key: Some(metadata_wrapping_key_id),
74                data_key: Some(data_wrapping_key_id),
75            }),
76            inline_crypto_proxy,
77            uuid: OnceLock::new(),
78        }
79    }
80
81    /// Returns true if `key` is registered with the service.
82    pub fn contains_key(&self, key: EncryptionKeyId) -> bool {
83        let inner = self.inner.lock();
84        inner.keys.contains_key(&key)
85    }
86
87    /// Returns the users registered for `key`.
88    pub fn get_users_for_key(&self, key: EncryptionKeyId) -> Option<Vec<u32>> {
89        let inner = self.inner.lock();
90        inner.keys.get(&key).map(|x| x.users.clone())
91    }
92
93    /// Adds the specified wrapping key for user `uid`.
94    pub fn add_wrapping_key(&self, raw_key: &[u8], uid: u32) -> Result<EncryptionKeyId, Errno> {
95        let (key, slot) = if let Some(proxy) = self.inline_crypto_proxy.as_ref() {
96            let key = Box::from(
97                proxy
98                    .derive_raw_secret(raw_key, zx::MonotonicInstant::INFINITE)
99                    .map_err(|error| {
100                        log::error!(error:?; "derive_raw_secret FIDL error");
101                        errno!(EPIPE)
102                    })?
103                    .map_err(|status| {
104                        let status = zx::Status::from_raw(status);
105                        log::error!(status:?; "derive_raw_secret failed");
106                        from_status_like_fdio!(status)
107                    })?,
108            );
109            let slot = proxy
110                .program_key(raw_key, DATA_UNIT_SIZE, zx::MonotonicInstant::INFINITE)
111                .map_err(|error| {
112                    log::error!(error:?; "program_key FIDL error");
113                    errno!(EPIPE)
114                })?
115                .map_err(|status| {
116                    let status = zx::Status::from_raw(status);
117                    log::error!(status:?; "program_key failed");
118                    from_status_like_fdio!(status)
119                })?;
120            (key, Some(slot))
121        } else {
122            (Box::from(raw_key), None)
123        };
124        let key_identifier = derive_lblk32_wrapping_key_id(&key);
125        let mut inner = self.inner.lock();
126        match inner.keys.entry(key_identifier) {
127            Entry::Occupied(mut e) => {
128                let users = &mut e.get_mut().users;
129                if !users.contains(&uid) {
130                    users.push(uid);
131                }
132                Ok(key_identifier)
133            }
134            Entry::Vacant(vacant) => {
135                vacant.insert(KeyInfo { users: vec![uid], key, slot });
136                Ok(key_identifier)
137            }
138        }
139    }
140
141    /// Serves crypt requests.
142    pub async fn handle_connection(&self, mut stream: CryptRequestStream) -> Result<(), Error> {
143        while let Some(request) = stream.next().await {
144            match request {
145                Ok(CryptRequest::CreateKey { owner, purpose, responder }) => {
146                    responder
147                        .send(match &self.create_key(owner, purpose) {
148                            Ok((id, wrapped, key)) => Ok((id, wrapped, key)),
149                            Err(e) => Err(*e),
150                        })
151                        .unwrap_or_else(
152                            |error| log::error!(error:?; "Failed to send CreateKey response"),
153                        );
154                }
155                Ok(CryptRequest::CreateKeyWithId {
156                    owner,
157                    wrapping_key_id,
158                    object_type,
159                    responder,
160                    ..
161                }) => {
162                    responder
163                        .send(
164                            match self.create_key_with_id(
165                                owner,
166                                EncryptionKeyId::from(wrapping_key_id),
167                                object_type,
168                            ) {
169                                Ok((ref wrapped, ref key)) => Ok((wrapped, key)),
170                                Err(e) => Err(e.into_raw()),
171                            },
172                        )
173                        .unwrap_or_else(
174                            |error| log::error!(error:?; "Failed to send CreateKeyWithId response"),
175                        );
176                }
177                Ok(CryptRequest::UnwrapKey { owner, wrapped_key, responder }) => {
178                    responder
179                        .send(match self.unwrap_key(owner, wrapped_key) {
180                            Ok(ref unwrapped) => Ok(unwrapped),
181                            Err(e) => Err(e.into_raw()),
182                        })
183                        .unwrap_or_else(
184                            |error| log::error!(error:?; "Failed to send UnwrapKey response"),
185                        );
186                }
187                Err(error) => {
188                    log::error!(error:?; "Error in CryptRequestStream");
189                    bail!(error);
190                }
191            }
192        }
193        Ok(())
194    }
195
196    /// Removes `wrapping_key_id` for user `uid`.
197    pub fn forget_wrapping_key(
198        &self,
199        wrapping_key_id: EncryptionKeyId,
200        uid: u32,
201    ) -> Result<(), Errno> {
202        let mut inner = self.inner.lock();
203        match inner.keys.entry(EncryptionKeyId::from(wrapping_key_id)) {
204            Entry::Occupied(mut e) => {
205                let user_ids = &mut e.get_mut().users;
206                if !user_ids.contains(&uid) {
207                    return error!(ENOKEY);
208                } else {
209                    let index = user_ids.iter().position(|x: &u32| *x == uid).unwrap();
210                    user_ids.remove(index);
211                    if user_ids.is_empty() {
212                        e.remove();
213                    }
214                }
215            }
216            Entry::Vacant(_) => {
217                return error!(ENOKEY);
218            }
219        }
220        Ok(())
221    }
222
223    pub fn set_uuid(&self, uuid: [u8; 16]) {
224        self.uuid.set(uuid).unwrap();
225    }
226
227    fn derive_directory_key(&self, key: &[u8], nonce: &[u8]) -> Result<Vec<u8>, zx::Status> {
228        Ok(fscrypt::to_directory_keys(key, self.uuid.get().ok_or(zx::Status::BAD_STATE)?, nonce)
229            .to_unwrapped_key())
230    }
231
232    fn create_key(&self, owner: u64, purpose: KeyPurpose) -> CryptCreateKeyResult {
233        let inner = self.inner.lock();
234        let wrapping_key_id = match purpose {
235            KeyPurpose::Data => inner.data_key.as_ref().ok_or_else(|| {
236                log::error!(
237                    "tried to create key with KeyPurpose::Data but no active data wrapping key"
238                );
239                zx::Status::BAD_STATE.into_raw()
240            })?,
241            KeyPurpose::Metadata => inner.metadata_key.as_ref().ok_or_else(|| {
242                log::error!(
243                    "tried to create key with KeyPurpose::Metadata but no active data wrapping key"
244                );
245                zx::Status::BAD_STATE.into_raw()
246            })?,
247            _ => return Err(zx::Status::INVALID_ARGS.into_raw()),
248        };
249        let key =
250            &inner.keys.get(wrapping_key_id).ok_or_else(|| zx::Status::BAD_STATE.into_raw())?.key;
251        let cipher = get_fxfs_cipher(key);
252        let nonce = zero_extended_nonce(owner);
253
254        let mut key = [0u8; 32];
255        rand::fill(&mut key[..]);
256
257        let wrapped = cipher.encrypt(&nonce, &key[..]).map_err(|error| {
258            log::error!(error:?; "Failed to wrap key");
259            zx::Status::INTERNAL.into_raw()
260        })?;
261
262        Ok((*wrapping_key_id, wrapped.into(), key.into()))
263    }
264
265    fn create_key_with_id(
266        &self,
267        owner: u64,
268        wrapping_key_id: EncryptionKeyId,
269        object_type: ObjectType,
270    ) -> Result<(WrappedKey, Vec<u8>), zx::Status> {
271        let mut inner = self.inner.lock();
272        let key_info = inner.keys.get_mut(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
273        match object_type {
274            ObjectType::Directory | ObjectType::Symlink => {
275                let mut nonce = [0; 16];
276                zx::cprng_draw(&mut nonce);
277                let unwrapped_key = self.derive_directory_key(&key_info.key, &nonce)?;
278                Ok((
279                    WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
280                        key_identifier: wrapping_key_id,
281                        nonce,
282                    }),
283                    unwrapped_key,
284                ))
285            }
286            ObjectType::File => {
287                if let Some(slot) = key_info.slot {
288                    let unwrapped_key = derive_file_key(slot, &key_info.key);
289                    Ok((
290                        WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier {
291                            key_identifier: wrapping_key_id,
292                        }),
293                        unwrapped_key,
294                    ))
295                } else {
296                    // Use a software backed key.
297                    let cipher = get_fxfs_cipher(&key_info.key);
298                    let nonce = zero_extended_nonce(owner);
299
300                    let mut key = [0u8; 32];
301                    rand::fill(&mut key[..]);
302
303                    let wrapped = cipher.encrypt(&nonce, &key[..]).map_err(|error| {
304                        log::error!(error:?; "Failed to wrap key");
305                        zx::Status::INTERNAL
306                    })?;
307
308                    Ok((
309                        WrappedKey::Fxfs(FxfsKey {
310                            wrapping_key_id,
311                            wrapped_key: wrapped.try_into().expect("wrapped key wrong size"),
312                        }),
313                        key.into(),
314                    ))
315                }
316            }
317            _ => Err(zx::Status::NOT_SUPPORTED),
318        }
319    }
320
321    fn unwrap_key(&self, owner: u64, key: WrappedKey) -> Result<Vec<u8>, zx::Status> {
322        let mut inner = self.inner.lock();
323        match key {
324            WrappedKey::Fxfs(FxfsKey { wrapping_key_id, wrapped_key }) => {
325                let wrapping_key_id = EncryptionKeyId::from(wrapping_key_id);
326                let key_info = inner.keys.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
327
328                let cipher = get_fxfs_cipher(&key_info.key);
329                let nonce = zero_extended_nonce(owner);
330
331                Ok(cipher
332                    .decrypt(&nonce, &wrapped_key[..])
333                    .map_err(|_| zx::Status::IO_DATA_INTEGRITY)?)
334            }
335            WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { key_identifier }) => {
336                let wrapping_key_id = EncryptionKeyId::from(key_identifier);
337                let key_info =
338                    inner.keys.get_mut(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
339                let Some(slot) = key_info.slot else {
340                    // The caller is trying to use a hardware wrapped key, but we
341                    // don't have access to the wrapping hardware.
342                    return Err(zx::Status::UNAVAILABLE);
343                };
344                Ok(derive_file_key(slot, &key_info.key))
345            }
346            WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
347                key_identifier,
348                nonce,
349            }) => {
350                let wrapping_key_id = EncryptionKeyId::from(key_identifier);
351                let key_info = inner.keys.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
352
353                self.derive_directory_key(&key_info.key, &nonce)
354            }
355            _ => Err(zx::Status::NOT_SUPPORTED),
356        }
357    }
358}
359
360fn zero_extended_nonce(val: u64) -> Nonce {
361    let mut nonce = Nonce::default();
362    nonce.as_mut_slice()[..8].copy_from_slice(&val.to_le_bytes());
363    nonce
364}
365
366fn derive_file_key(slot: u8, key: &[u8]) -> Vec<u8> {
367    let mut unwrapped_key = Vec::with_capacity(17);
368    unwrapped_key.push(slot);
369    let ino_hash_key: [u8; 16] = fscrypt_hkdf(key, &[], HKDF_CONTEXT_INODE_HASH_KEY);
370    unwrapped_key.extend_from_slice(&ino_hash_key);
371    unwrapped_key
372}
373
374fn get_fxfs_cipher(raw_key: &[u8]) -> Aes256GcmSiv {
375    let hk = Hkdf::<sha2::Sha256>::new(None, raw_key);
376    let mut wrapping_key = [0u8; AES256_KEY_SIZE];
377    hk.expand(FXFS_FSCRYPT_WRAPPING_KEY_INFO.as_bytes(), &mut wrapping_key).unwrap();
378    Aes256GcmSiv::new((&wrapping_key).into())
379}
380
381fn derive_lblk32_wrapping_key_id(raw_key: &[u8]) -> [u8; FSCRYPT_KEY_IDENTIFIER_SIZE as usize] {
382    let hk = Hkdf::<sha2::Sha512>::new(None, raw_key);
383    let mut key_identifier = [0u8; FSCRYPT_KEY_IDENTIFIER_SIZE as usize];
384    let mut hkdf_info = FSCRYPT_HKDF_NONCE_PREFIX.to_vec();
385    hkdf_info.push(HKDF_CONTEXT_KEY_IDENTIFIER);
386    hk.expand(&hkdf_info, &mut key_identifier).unwrap();
387    key_identifier
388}
389
390#[cfg(test)]
391mod tests {
392    use super::*;
393    use assert_matches::assert_matches;
394    use block_client::RemoteBlockClient;
395    use fidl_fuchsia_fxfs::{FxfsKey, KeyPurpose, WrappedKey};
396    use fidl_fuchsia_hardware_inlineencryption::{DeviceMarker, DeviceRequest};
397    use fidl_fuchsia_storage_block::BlockProxy;
398
399    use fuchsia_async::LocalExecutor;
400    use starnix_uapi::errno;
401    use std::sync::Arc;
402    use storage_device::block_device::BlockDevice;
403    use storage_device::{Device, InlineCryptoOptions, ReadOptions, WriteOptions};
404    use vmo_backed_block_server::{
405        InitialContents, VmoBackedServerOptions, VmoBackedServerTestingExt,
406    };
407
408    const BLOCK_SIZE: u32 = 4096;
409    const TEST_UUID: [u8; 16] =
410        [75, 146, 230, 48, 132, 165, 68, 97, 141, 247, 22, 242, 153, 171, 153, 38];
411
412    #[test]
413    fn add_and_forget_wrapping_keys() {
414        let service = CryptService::new(&[0; 32], &[1; 32], None);
415
416        // Add the wrapping key for users 0 and 1
417        let wrapping_key_id =
418            service.add_wrapping_key(&u128::to_le_bytes(1), 0).expect("add wrapping key failed");
419
420        let wrapping_key_id2 =
421            service.add_wrapping_key(&u128::to_le_bytes(1), 1).expect("add wrapping key failed");
422
423        assert_eq!(wrapping_key_id2, wrapping_key_id);
424
425        // A user should be able to add the same key multiple times.
426        let wrapping_key_id3 =
427            service.add_wrapping_key(&u128::to_le_bytes(1), 1).expect("add wrapping key failed");
428
429        assert_eq!(wrapping_key_id3, wrapping_key_id);
430
431        {
432            let inner = service.inner.lock();
433            assert_eq!(inner.keys.get(&wrapping_key_id).unwrap().users, [0, 1]);
434        }
435
436        // User 1 forgets the wrapping key. Since user 0 still has the key added,
437        // create_key_with_id should still succeed.
438        service.forget_wrapping_key(wrapping_key_id, 1).expect("forget wrapping key failed");
439        service
440            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
441            .expect("create key with id failed");
442
443        // User 1 cannot forget the same key a second time.
444        assert_eq!(
445            service.forget_wrapping_key(wrapping_key_id, 1).expect_err(
446                "forget wrapping key should fail if the key was already removed by this user"
447            ),
448            errno!(ENOKEY)
449        );
450        // Once both users remove the key, create_key_with_id should fail.
451        service.forget_wrapping_key(wrapping_key_id, 0).expect("forget wrapping key failed");
452        assert_eq!(
453            service
454                .create_key_with_id(
455                    0,
456                    EncryptionKeyId::from(u128::to_le_bytes(1)),
457                    ObjectType::File,
458                )
459                .expect_err(
460                    "create_key_with_id should fail if the key hasn't been added by the caller"
461                ),
462            zx::Status::UNAVAILABLE
463        );
464        service.add_wrapping_key(&u128::to_le_bytes(1), 0).expect("add wrapping key failed");
465    }
466
467    #[fuchsia::test]
468    async fn test_derive_wrapping_key_id_and_lblk32_derived_keys() {
469        const EXPECTED_WRAPPING_KEY_ID: [u8; 16] =
470            [40, 205, 90, 253, 77, 129, 133, 220, 222, 25, 208, 200, 136, 101, 239, 101];
471        const EXPECTED_CTS_KEY: [u8; 32] = [
472            223, 72, 191, 189, 133, 62, 81, 175, 91, 93, 132, 0, 9, 246, 22, 32, 76, 91, 28, 2, 96,
473            27, 182, 66, 131, 84, 218, 118, 230, 226, 142, 115,
474        ];
475        const EXPECTED_INO_HASH_KEY: [u8; 16] =
476            [241, 22, 180, 110, 76, 135, 84, 48, 206, 33, 210, 253, 11, 10, 230, 122];
477
478        let block_server = Arc::new(
479            VmoBackedServerOptions {
480                block_size: BLOCK_SIZE,
481                initial_contents: InitialContents::FromCapacity(393216),
482                ..Default::default()
483            }
484            .build()
485            .expect("build failed"),
486        );
487
488        let block_server_clone = block_server.clone();
489        let (insecure_inilne_crypto_proxy, server) =
490            fidl::endpoints::create_sync_proxy::<DeviceMarker>();
491        std::thread::spawn(|| {
492            LocalExecutor::default().run_singlethreaded(async move {
493                block_server_clone
494                    .connect_insecure_inline_encryption_server(server, TEST_UUID)
495                    .await;
496            })
497        });
498
499        let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
500        service.set_uuid(TEST_UUID);
501        let wrapping_key_id = service.add_wrapping_key(&[0xdc; 32], 0).unwrap();
502        assert_eq!(wrapping_key_id, EXPECTED_WRAPPING_KEY_ID);
503
504        let (_, unwrapped_key) =
505            service.create_key_with_id(0, wrapping_key_id, ObjectType::Directory).unwrap();
506        let (cts_key, remainder) = unwrapped_key.split_at(EXPECTED_CTS_KEY.len());
507        let (ino_hash_key, _dir_hash_key) = remainder.split_at(EXPECTED_INO_HASH_KEY.len());
508
509        assert_eq!(cts_key, &EXPECTED_CTS_KEY);
510        assert_eq!(ino_hash_key, &EXPECTED_INO_HASH_KEY);
511    }
512
513    #[fuchsia::test]
514    async fn test_create_key_with_id_with_lblk32_key() {
515        let block_server = Arc::new(
516            VmoBackedServerOptions {
517                block_size: BLOCK_SIZE,
518                initial_contents: InitialContents::FromCapacity(393216),
519                ..Default::default()
520            }
521            .build()
522            .expect("build failed"),
523        );
524
525        let block_server_clone = block_server.clone();
526        let (insecure_inilne_crypto_proxy, server) =
527            fidl::endpoints::create_sync_proxy::<DeviceMarker>();
528        std::thread::spawn(|| {
529            LocalExecutor::default().run_singlethreaded(async move {
530                block_server_clone
531                    .connect_insecure_inline_encryption_server(server, TEST_UUID)
532                    .await;
533            })
534        });
535
536        let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
537        let wrapping_key_id = service.add_wrapping_key(&[0xcd; 32], 0).unwrap();
538
539        let (wrapped_key, unwrapped_key) = service
540            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
541            .expect("create_key failed");
542        assert_matches!(wrapped_key, WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { .. }));
543        let expected_slot = 0;
544        assert_eq!(unwrapped_key[0], expected_slot);
545
546        let mut key = [0xcd; 32];
547        for b in &mut key {
548            *b = *b >> 4 | *b << 4;
549        }
550        let expected_ino_hash_key: [u8; 16] = fscrypt_hkdf(&key, &[], HKDF_CONTEXT_INODE_HASH_KEY);
551        assert_eq!(unwrapped_key[1..17], expected_ino_hash_key);
552        // Validate encrypted reads/writes with the key we just programmed.
553        let device = BlockDevice::new(
554            RemoteBlockClient::new(block_server.clone().connect::<BlockProxy>())
555                .await
556                .expect("Unable to create block client"),
557            false,
558        )
559        .await
560        .unwrap();
561
562        let plaintext: &[u8] = b"This is aligned sensitive data!!";
563        let mut buf = device.allocate_buffer(4096).await;
564        buf.as_mut_slice()[..plaintext.len()].copy_from_slice(plaintext);
565        device
566            .write_with_opts(
567                0,
568                buf.as_ref(),
569                WriteOptions {
570                    inline_crypto: InlineCryptoOptions::enabled(expected_slot, 0),
571                    ..Default::default()
572                },
573            )
574            .await
575            .expect("failed to write data");
576
577        let mut read_buf = device.allocate_buffer(4096).await;
578
579        // Reading without inline crypto should return garbage.
580        device
581            .read_with_opts(0, read_buf.as_mut(), ReadOptions::default())
582            .await
583            .expect("Read failed");
584        assert_ne!(&read_buf.as_slice()[..plaintext.len()], plaintext);
585
586        // Reading using a different key than the one used for writing should also return garbage.
587        // Cheat: we know the insecure inline encryption provider adds this to the next available
588        // slot which is `expected_slot + 1`
589        let _wrapping_key_id = service.add_wrapping_key(&[0xab; 32], 0).unwrap();
590        device
591            .read_with_opts(
592                0,
593                read_buf.as_mut(),
594                ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot + 1, 0) },
595            )
596            .await
597            .expect("Read failed");
598        assert_ne!(&read_buf.as_slice()[..plaintext.len()], plaintext);
599
600        // Should not be able to read from an unused key slot.
601        device
602            .read_with_opts(
603                0,
604                read_buf.as_mut(),
605                ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot + 2, 0) },
606            )
607            .await
608            .expect_err("Read passed unexpectedly with unused key slot");
609
610        // Reading with the correct key should work.
611        device
612            .read_with_opts(
613                0,
614                read_buf.as_mut(),
615                ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot, 0) },
616            )
617            .await
618            .expect("Read failed");
619        assert_eq!(&read_buf.as_slice()[..plaintext.len()], plaintext);
620    }
621
622    #[fuchsia::test]
623    fn unwrap_fxfs_wrapped_key_with_lblk32_key() {
624        let block_server = Arc::new(
625            VmoBackedServerOptions {
626                block_size: BLOCK_SIZE,
627                initial_contents: InitialContents::FromCapacity(393216),
628                ..Default::default()
629            }
630            .build()
631            .expect("build failed"),
632        );
633        let block_server_clone = block_server.clone();
634        let (insecure_inilne_crypto_proxy, server) =
635            fidl::endpoints::create_sync_proxy::<DeviceMarker>();
636        std::thread::spawn(|| {
637            LocalExecutor::default().run_singlethreaded(async move {
638                block_server_clone
639                    .connect_insecure_inline_encryption_server(server, TEST_UUID)
640                    .await;
641            })
642        });
643
644        let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
645        service.set_uuid(TEST_UUID);
646        let wrapping_key_id =
647            service.add_wrapping_key(&[0xcd; 32], 0).expect("add wrapping key failed");
648        let (wrapped_key, expected_unwrapped_key) = service
649            .create_key_with_id(0, wrapping_key_id, ObjectType::Directory)
650            .expect("create_key failed");
651        assert_matches!(
652            wrapped_key,
653            WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
654                key_identifier,
655                ..
656            }) if key_identifier == wrapping_key_id
657        );
658        let unwrapped_key = service.unwrap_key(0, wrapped_key).expect("create_key failed");
659        assert_eq!(unwrapped_key, expected_unwrapped_key);
660    }
661
662    #[test]
663    fn wrap_unwrap_key() {
664        let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
665
666        let (wrapping_key_id, wrapped_key, unwrapped_key) =
667            service.create_key(0, KeyPurpose::Data).expect("create_key failed");
668        let unwrap_result = service
669            .unwrap_key(
670                0,
671                WrappedKey::Fxfs(FxfsKey {
672                    wrapping_key_id,
673                    wrapped_key: wrapped_key.try_into().unwrap(),
674                }),
675            )
676            .expect("unwrap_key failed");
677        assert_eq!(unwrap_result, unwrapped_key);
678
679        // Do it twice to make sure the service can use the same key repeatedly.
680        let (wrapping_key_id, wrapped_key, unwrapped_key) =
681            service.create_key(1, KeyPurpose::Data).expect("create_key failed");
682        let unwrap_result = service
683            .unwrap_key(
684                1,
685                WrappedKey::Fxfs(FxfsKey {
686                    wrapping_key_id,
687                    wrapped_key: wrapped_key.try_into().unwrap(),
688                }),
689            )
690            .expect("unwrap_key failed");
691        assert_eq!(unwrap_result, unwrapped_key);
692    }
693
694    #[test]
695    fn wrap_unwrap_key_with_arbitrary_wrapping_key() {
696        let service = CryptService::new(&[0; 32], &[1; 32], None);
697
698        let wrapping_key_id =
699            service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
700
701        let (wrapped_key, unwrapped_key) = service
702            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
703            .expect("create_key_with_id failed");
704        // TODO(https://fxbug.dev/436902004): Switch to lkb32 wrapped key type.
705        match wrapped_key {
706            WrappedKey::Fxfs(fxfs_key) => {
707                let unwrap_result = service
708                    .unwrap_key(
709                        0,
710                        WrappedKey::Fxfs(FxfsKey {
711                            wrapping_key_id,
712                            wrapped_key: fxfs_key.wrapped_key.try_into().unwrap(),
713                        }),
714                    )
715                    .expect("unwrap_key failed");
716                assert_eq!(unwrap_result, unwrapped_key);
717            }
718            _ => panic!("Found a non-FxfsKey wrapped key"),
719        }
720
721        // Do it twice to make sure the service can use the same key repeatedly.
722        let (wrapped_key, unwrapped_key) = service
723            .create_key_with_id(1, wrapping_key_id, ObjectType::File)
724            .expect("create_key_with_id failed");
725        // TODO(https://fxbug.dev/436902004): Switch to lkb32 wrapped key type.
726        match wrapped_key {
727            WrappedKey::Fxfs(fxfs_key) => {
728                let unwrap_result = service
729                    .unwrap_key(
730                        1,
731                        WrappedKey::Fxfs(FxfsKey {
732                            wrapping_key_id,
733                            wrapped_key: fxfs_key.wrapped_key.try_into().unwrap(),
734                        }),
735                    )
736                    .expect("unwrap_key failed");
737                assert_eq!(unwrap_result, unwrapped_key);
738            }
739            _ => panic!("Found a non-FxfsKey wrapped key"),
740        }
741    }
742
743    #[test]
744    fn create_key_with_wrapping_key_that_does_not_exist() {
745        let service = CryptService::new(&[0; 32], &[1; 32], None);
746
747        let wrapping_key_id =
748            service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
749
750        let (wrapped_key, unwrapped_key) = service
751            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
752            .expect("create_key_with_id failed");
753
754        // TODO(https://fxbug.dev/436902004): Switch to lkb32 wrapped key type.
755        match wrapped_key {
756            WrappedKey::Fxfs(fxfs_key) => {
757                let unwrap_result = service
758                    .unwrap_key(
759                        0,
760                        WrappedKey::Fxfs(FxfsKey {
761                            wrapping_key_id,
762                            wrapped_key: fxfs_key.wrapped_key.try_into().unwrap(),
763                        }),
764                    )
765                    .expect("unwrap_key failed");
766                assert_eq!(unwrap_result, unwrapped_key);
767            }
768            _ => panic!("Found a non-FxfsKey wrapped key"),
769        }
770
771        service.forget_wrapping_key(wrapping_key_id, 0).unwrap();
772
773        service
774            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
775            .expect_err("create_key_with_id should fail if the wrapping key does not exist");
776    }
777
778    #[test]
779    fn unwrap_key_wrong_key() {
780        let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
781        let (wrapping_key_id, mut wrapped_key, _) =
782            service.create_key(0, KeyPurpose::Data).expect("create_key failed");
783        for byte in &mut wrapped_key {
784            *byte ^= 0xff;
785        }
786        service
787            .unwrap_key(
788                0,
789                WrappedKey::Fxfs(FxfsKey {
790                    wrapping_key_id,
791                    wrapped_key: wrapped_key.try_into().unwrap(),
792                }),
793            )
794            .expect_err("unwrap_key should fail");
795    }
796
797    #[test]
798    fn unwrap_key_wrong_owner() {
799        let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
800
801        let (wrapping_key_id, wrapped_key, _) =
802            service.create_key(0, KeyPurpose::Data).expect("create_key failed");
803        service
804            .unwrap_key(
805                1,
806                WrappedKey::Fxfs(FxfsKey {
807                    wrapping_key_id,
808                    wrapped_key: wrapped_key.try_into().unwrap(),
809                }),
810            )
811            .expect_err("unwrap_key should fail");
812    }
813
814    #[fuchsia::test]
815    async fn test_add_wrapping_key_uses_raw_key() {
816        let (client, server) = fidl::endpoints::create_sync_proxy::<DeviceMarker>();
817        let raw_key_bytes = [0xAB; 32];
818        let expected_key = raw_key_bytes.clone();
819
820        std::thread::spawn(move || {
821            LocalExecutor::default().run_singlethreaded(async move {
822                let mut stream = server.into_stream();
823                while let Some(Ok(request)) = stream.next().await {
824                    match request {
825                        DeviceRequest::DeriveRawSecret { wrapped_key, responder } => {
826                            let mut derived = wrapped_key.clone();
827                            derived[0] ^= 0xFF;
828                            responder.send(Ok(&derived)).unwrap();
829                        }
830                        DeviceRequest::ProgramKey { wrapped_key, responder, .. } => {
831                            if wrapped_key == expected_key {
832                                responder.send(Ok(0)).unwrap();
833                            } else {
834                                responder.send(Err(zx::Status::INVALID_ARGS.into_raw())).unwrap();
835                            }
836                        }
837                    }
838                }
839            })
840        });
841
842        let service = CryptService::new(&[0; 32], &[1; 32], Some(client));
843        service.add_wrapping_key(&raw_key_bytes, 0).expect("add_wrapping_key failed");
844    }
845}