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