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 test_vmo_backed_block_server::VmoBackedServer;
405
406    const BLOCK_SIZE: u32 = 4096;
407    const BLOCK_COUNT: u64 = 393216;
408    const TEST_UUID: [u8; 16] =
409        [75, 146, 230, 48, 132, 165, 68, 97, 141, 247, 22, 242, 153, 171, 153, 38];
410
411    #[test]
412    fn add_and_forget_wrapping_keys() {
413        let service = CryptService::new(&[0; 32], &[1; 32], None);
414
415        // Add the wrapping key for users 0 and 1
416        let wrapping_key_id =
417            service.add_wrapping_key(&u128::to_le_bytes(1), 0).expect("add wrapping key failed");
418
419        let wrapping_key_id2 =
420            service.add_wrapping_key(&u128::to_le_bytes(1), 1).expect("add wrapping key failed");
421
422        assert_eq!(wrapping_key_id2, wrapping_key_id);
423
424        // A user should be able to add the same key multiple times.
425        let wrapping_key_id3 =
426            service.add_wrapping_key(&u128::to_le_bytes(1), 1).expect("add wrapping key failed");
427
428        assert_eq!(wrapping_key_id3, wrapping_key_id);
429
430        {
431            let inner = service.inner.lock();
432            assert_eq!(inner.keys.get(&wrapping_key_id).unwrap().users, [0, 1]);
433        }
434
435        // User 1 forgets the wrapping key. Since user 0 still has the key added,
436        // create_key_with_id should still succeed.
437        service.forget_wrapping_key(wrapping_key_id, 1).expect("forget wrapping key failed");
438        service
439            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
440            .expect("create key with id failed");
441
442        // User 1 cannot forget the same key a second time.
443        assert_eq!(
444            service.forget_wrapping_key(wrapping_key_id, 1).expect_err(
445                "forget wrapping key should fail if the key was already removed by this user"
446            ),
447            errno!(ENOKEY)
448        );
449        // Once both users remove the key, create_key_with_id should fail.
450        service.forget_wrapping_key(wrapping_key_id, 0).expect("forget wrapping key failed");
451        assert_eq!(
452            service
453                .create_key_with_id(
454                    0,
455                    EncryptionKeyId::from(u128::to_le_bytes(1)),
456                    ObjectType::File,
457                )
458                .expect_err(
459                    "create_key_with_id should fail if the key hasn't been added by the caller"
460                ),
461            zx::Status::UNAVAILABLE
462        );
463        service.add_wrapping_key(&u128::to_le_bytes(1), 0).expect("add wrapping key failed");
464    }
465
466    #[fuchsia::test]
467    async fn test_derive_wrapping_key_id_and_lblk32_derived_keys() {
468        const EXPECTED_WRAPPING_KEY_ID: [u8; 16] =
469            [40, 205, 90, 253, 77, 129, 133, 220, 222, 25, 208, 200, 136, 101, 239, 101];
470        const EXPECTED_CTS_KEY: [u8; 32] = [
471            223, 72, 191, 189, 133, 62, 81, 175, 91, 93, 132, 0, 9, 246, 22, 32, 76, 91, 28, 2, 96,
472            27, 182, 66, 131, 84, 218, 118, 230, 226, 142, 115,
473        ];
474        const EXPECTED_INO_HASH_KEY: [u8; 16] =
475            [241, 22, 180, 110, 76, 135, 84, 48, 206, 33, 210, 253, 11, 10, 230, 122];
476
477        let block_server = Arc::new(
478            VmoBackedServer::new(BLOCK_COUNT, BLOCK_SIZE, &[])
479                .expect("Failed to create VmoBackedServer"),
480        );
481
482        let (insecure_inilne_crypto_proxy, server) =
483            fidl::endpoints::create_sync_proxy::<DeviceMarker>();
484        std::thread::spawn(|| {
485            LocalExecutor::default().run_singlethreaded(async move {
486                block_server.connect_insecure_inline_encryption_server(server, TEST_UUID).await;
487            })
488        });
489
490        let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
491        service.set_uuid(TEST_UUID);
492        let wrapping_key_id = service.add_wrapping_key(&[0xdc; 32], 0).unwrap();
493        assert_eq!(wrapping_key_id, EXPECTED_WRAPPING_KEY_ID);
494
495        let (_, unwrapped_key) =
496            service.create_key_with_id(0, wrapping_key_id, ObjectType::Directory).unwrap();
497        let (cts_key, remainder) = unwrapped_key.split_at(EXPECTED_CTS_KEY.len());
498        let (ino_hash_key, _dir_hash_key) = remainder.split_at(EXPECTED_INO_HASH_KEY.len());
499
500        assert_eq!(cts_key, &EXPECTED_CTS_KEY);
501        assert_eq!(ino_hash_key, &EXPECTED_INO_HASH_KEY);
502    }
503
504    #[fuchsia::test]
505    async fn test_create_key_with_id_with_lblk32_key() {
506        let block_server = Arc::new(
507            VmoBackedServer::new(BLOCK_COUNT, BLOCK_SIZE, &[])
508                .expect("Failed to create VmoBackedServer"),
509        );
510
511        let block_server_clone = block_server.clone();
512        let (insecure_inilne_crypto_proxy, server) =
513            fidl::endpoints::create_sync_proxy::<DeviceMarker>();
514        std::thread::spawn(|| {
515            LocalExecutor::default().run_singlethreaded(async move {
516                block_server_clone
517                    .connect_insecure_inline_encryption_server(server, TEST_UUID)
518                    .await;
519            })
520        });
521
522        let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
523        let wrapping_key_id = service.add_wrapping_key(&[0xcd; 32], 0).unwrap();
524
525        let (wrapped_key, unwrapped_key) = service
526            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
527            .expect("create_key failed");
528        assert_matches!(wrapped_key, WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { .. }));
529        let expected_slot = 0;
530        assert_eq!(unwrapped_key[0], expected_slot);
531
532        let mut key = [0xcd; 32];
533        for b in &mut key {
534            *b = *b >> 4 | *b << 4;
535        }
536        let expected_ino_hash_key: [u8; 16] = fscrypt_hkdf(&key, &[], HKDF_CONTEXT_INODE_HASH_KEY);
537        assert_eq!(unwrapped_key[1..17], expected_ino_hash_key);
538        // Validate encrypted reads/writes with the key we just programmed.
539        let device = BlockDevice::new(
540            RemoteBlockClient::new(block_server.clone().connect::<BlockProxy>())
541                .await
542                .expect("Unable to create block client"),
543            false,
544        )
545        .await
546        .unwrap();
547
548        let plaintext: &[u8] = b"This is aligned sensitive data!!";
549        let mut buf = device.allocate_buffer(4096).await;
550        buf.as_mut_slice()[..plaintext.len()].copy_from_slice(plaintext);
551        device
552            .write_with_opts(
553                0,
554                buf.as_ref(),
555                WriteOptions {
556                    inline_crypto: InlineCryptoOptions::enabled(expected_slot, 0),
557                    ..Default::default()
558                },
559            )
560            .await
561            .expect("failed to write data");
562
563        let mut read_buf = device.allocate_buffer(4096).await;
564
565        // Reading without inline crypto should return garbage.
566        device
567            .read_with_opts(0, read_buf.as_mut(), ReadOptions::default())
568            .await
569            .expect("Read failed");
570        assert_ne!(&read_buf.as_slice()[..plaintext.len()], plaintext);
571
572        // Reading using a different key than the one used for writing should also return garbage.
573        // Cheat: we know the insecure inline encryption provider adds this to the next available
574        // slot which is `expected_slot + 1`
575        let _wrapping_key_id = service.add_wrapping_key(&[0xab; 32], 0).unwrap();
576        device
577            .read_with_opts(
578                0,
579                read_buf.as_mut(),
580                ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot + 1, 0) },
581            )
582            .await
583            .expect("Read failed");
584        assert_ne!(&read_buf.as_slice()[..plaintext.len()], plaintext);
585
586        // Should not be able to read from an unused key slot.
587        device
588            .read_with_opts(
589                0,
590                read_buf.as_mut(),
591                ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot + 2, 0) },
592            )
593            .await
594            .expect_err("Read passed unexpectedly with unused key slot");
595
596        // Reading with the correct key should work.
597        device
598            .read_with_opts(
599                0,
600                read_buf.as_mut(),
601                ReadOptions { inline_crypto: InlineCryptoOptions::enabled(expected_slot, 0) },
602            )
603            .await
604            .expect("Read failed");
605        assert_eq!(&read_buf.as_slice()[..plaintext.len()], plaintext);
606    }
607
608    #[fuchsia::test]
609    fn unwrap_fxfs_wrapped_key_with_lblk32_key() {
610        let block_server = Arc::new(
611            VmoBackedServer::new(BLOCK_COUNT, BLOCK_SIZE, &[])
612                .expect("Failed to create VmoBackedServer"),
613        );
614        let (insecure_inilne_crypto_proxy, server) =
615            fidl::endpoints::create_sync_proxy::<DeviceMarker>();
616        std::thread::spawn(|| {
617            LocalExecutor::default().run_singlethreaded(async move {
618                block_server.connect_insecure_inline_encryption_server(server, TEST_UUID).await;
619            })
620        });
621
622        let service = CryptService::new(&[0; 32], &[1; 32], Some(insecure_inilne_crypto_proxy));
623        service.set_uuid(TEST_UUID);
624        let wrapping_key_id =
625            service.add_wrapping_key(&[0xcd; 32], 0).expect("add wrapping key failed");
626        let (wrapped_key, expected_unwrapped_key) = service
627            .create_key_with_id(0, wrapping_key_id, ObjectType::Directory)
628            .expect("create_key failed");
629        assert_matches!(
630            wrapped_key,
631            WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
632                key_identifier,
633                ..
634            }) if key_identifier == wrapping_key_id
635        );
636        let unwrapped_key = service.unwrap_key(0, wrapped_key).expect("create_key failed");
637        assert_eq!(unwrapped_key, expected_unwrapped_key);
638    }
639
640    #[test]
641    fn wrap_unwrap_key() {
642        let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
643
644        let (wrapping_key_id, wrapped_key, unwrapped_key) =
645            service.create_key(0, KeyPurpose::Data).expect("create_key failed");
646        let unwrap_result = service
647            .unwrap_key(
648                0,
649                WrappedKey::Fxfs(FxfsKey {
650                    wrapping_key_id,
651                    wrapped_key: wrapped_key.try_into().unwrap(),
652                }),
653            )
654            .expect("unwrap_key failed");
655        assert_eq!(unwrap_result, unwrapped_key);
656
657        // Do it twice to make sure the service can use the same key repeatedly.
658        let (wrapping_key_id, wrapped_key, unwrapped_key) =
659            service.create_key(1, KeyPurpose::Data).expect("create_key failed");
660        let unwrap_result = service
661            .unwrap_key(
662                1,
663                WrappedKey::Fxfs(FxfsKey {
664                    wrapping_key_id,
665                    wrapped_key: wrapped_key.try_into().unwrap(),
666                }),
667            )
668            .expect("unwrap_key failed");
669        assert_eq!(unwrap_result, unwrapped_key);
670    }
671
672    #[test]
673    fn wrap_unwrap_key_with_arbitrary_wrapping_key() {
674        let service = CryptService::new(&[0; 32], &[1; 32], None);
675
676        let wrapping_key_id =
677            service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
678
679        let (wrapped_key, unwrapped_key) = service
680            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
681            .expect("create_key_with_id failed");
682        // TODO(https://fxbug.dev/436902004): Switch to lkb32 wrapped key type.
683        match wrapped_key {
684            WrappedKey::Fxfs(fxfs_key) => {
685                let unwrap_result = service
686                    .unwrap_key(
687                        0,
688                        WrappedKey::Fxfs(FxfsKey {
689                            wrapping_key_id,
690                            wrapped_key: fxfs_key.wrapped_key.try_into().unwrap(),
691                        }),
692                    )
693                    .expect("unwrap_key failed");
694                assert_eq!(unwrap_result, unwrapped_key);
695            }
696            _ => panic!("Found a non-FxfsKey wrapped key"),
697        }
698
699        // Do it twice to make sure the service can use the same key repeatedly.
700        let (wrapped_key, unwrapped_key) = service
701            .create_key_with_id(1, wrapping_key_id, ObjectType::File)
702            .expect("create_key_with_id failed");
703        // TODO(https://fxbug.dev/436902004): Switch to lkb32 wrapped key type.
704        match wrapped_key {
705            WrappedKey::Fxfs(fxfs_key) => {
706                let unwrap_result = service
707                    .unwrap_key(
708                        1,
709                        WrappedKey::Fxfs(FxfsKey {
710                            wrapping_key_id,
711                            wrapped_key: fxfs_key.wrapped_key.try_into().unwrap(),
712                        }),
713                    )
714                    .expect("unwrap_key failed");
715                assert_eq!(unwrap_result, unwrapped_key);
716            }
717            _ => panic!("Found a non-FxfsKey wrapped key"),
718        }
719    }
720
721    #[test]
722    fn create_key_with_wrapping_key_that_does_not_exist() {
723        let service = CryptService::new(&[0; 32], &[1; 32], None);
724
725        let wrapping_key_id =
726            service.add_wrapping_key(&[2; 32], 0).expect("add wrapping key failed");
727
728        let (wrapped_key, unwrapped_key) = service
729            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
730            .expect("create_key_with_id failed");
731
732        // TODO(https://fxbug.dev/436902004): Switch to lkb32 wrapped key type.
733        match wrapped_key {
734            WrappedKey::Fxfs(fxfs_key) => {
735                let unwrap_result = service
736                    .unwrap_key(
737                        0,
738                        WrappedKey::Fxfs(FxfsKey {
739                            wrapping_key_id,
740                            wrapped_key: fxfs_key.wrapped_key.try_into().unwrap(),
741                        }),
742                    )
743                    .expect("unwrap_key failed");
744                assert_eq!(unwrap_result, unwrapped_key);
745            }
746            _ => panic!("Found a non-FxfsKey wrapped key"),
747        }
748
749        service.forget_wrapping_key(wrapping_key_id, 0).unwrap();
750
751        service
752            .create_key_with_id(0, wrapping_key_id, ObjectType::File)
753            .expect_err("create_key_with_id should fail if the wrapping key does not exist");
754    }
755
756    #[test]
757    fn unwrap_key_wrong_key() {
758        let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
759        let (wrapping_key_id, mut wrapped_key, _) =
760            service.create_key(0, KeyPurpose::Data).expect("create_key failed");
761        for byte in &mut wrapped_key {
762            *byte ^= 0xff;
763        }
764        service
765            .unwrap_key(
766                0,
767                WrappedKey::Fxfs(FxfsKey {
768                    wrapping_key_id,
769                    wrapped_key: wrapped_key.try_into().unwrap(),
770                }),
771            )
772            .expect_err("unwrap_key should fail");
773    }
774
775    #[test]
776    fn unwrap_key_wrong_owner() {
777        let service = CryptService::new(&[0; 32], &[0xcd; 32], None);
778
779        let (wrapping_key_id, wrapped_key, _) =
780            service.create_key(0, KeyPurpose::Data).expect("create_key failed");
781        service
782            .unwrap_key(
783                1,
784                WrappedKey::Fxfs(FxfsKey {
785                    wrapping_key_id,
786                    wrapped_key: wrapped_key.try_into().unwrap(),
787                }),
788            )
789            .expect_err("unwrap_key should fail");
790    }
791
792    #[fuchsia::test]
793    async fn test_add_wrapping_key_uses_raw_key() {
794        let (client, server) = fidl::endpoints::create_sync_proxy::<DeviceMarker>();
795        let raw_key_bytes = [0xAB; 32];
796        let expected_key = raw_key_bytes.clone();
797
798        std::thread::spawn(move || {
799            LocalExecutor::default().run_singlethreaded(async move {
800                let mut stream = server.into_stream();
801                while let Some(Ok(request)) = stream.next().await {
802                    match request {
803                        DeviceRequest::DeriveRawSecret { wrapped_key, responder } => {
804                            let mut derived = wrapped_key.clone();
805                            derived[0] ^= 0xFF;
806                            responder.send(Ok(&derived)).unwrap();
807                        }
808                        DeviceRequest::ProgramKey { wrapped_key, responder, .. } => {
809                            if wrapped_key == expected_key {
810                                responder.send(Ok(0)).unwrap();
811                            } else {
812                                responder.send(Err(zx::Status::INVALID_ARGS.into_raw())).unwrap();
813                            }
814                        }
815                    }
816                }
817            })
818        });
819
820        let service = CryptService::new(&[0; 32], &[1; 32], Some(client));
821        service.add_wrapping_key(&raw_key_bytes, 0).expect("add_wrapping_key failed");
822    }
823}