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