Skip to main content

fxfs_platform/fuchsia/
remote_crypt.rs

1// Copyright 2021 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 super::errors::map_to_status;
6use async_trait::async_trait;
7use fidl::endpoints::ClientEnd;
8use fidl_fuchsia_fxfs::{CryptMarker, CryptProxy, KeyPurpose as FidlKeyPurpose};
9use fxfs_crypto::{
10    Crypt, EncryptionKey, FxfsKey, KeyPurpose, ObjectType, UnwrappedKey, WrappedKey,
11    WrappedKeyBytes, WrappingKeyId,
12};
13
14use fuchsia_sync::Mutex;
15use std::collections::HashMap;
16use std::sync::Arc;
17use storage_device::Device;
18
19pub struct RemoteCrypt {
20    client: CryptProxy,
21    device: Option<Arc<dyn Device>>,
22    registered_keys: Mutex<HashMap<zx::Koid, u8>>,
23}
24
25impl RemoteCrypt {
26    pub fn new(client: ClientEnd<CryptMarker>) -> Self {
27        Self { client: client.into_proxy(), device: None, registered_keys: Mutex::default() }
28    }
29
30    /// Creates a `RemoteCrypt` that registers any returned inline encryption `key_token` handles
31    /// with `device`.
32    pub fn new_with_device(client: ClientEnd<CryptMarker>, device: Arc<dyn Device>) -> Self {
33        Self {
34            client: client.into_proxy(),
35            device: Some(device),
36            registered_keys: Mutex::default(),
37        }
38    }
39
40    async fn register_key(&self, key_token: zx::EventPair) -> Result<u8, zx::Status> {
41        let koid = key_token.koid()?;
42        if let Some(&slot) = self.registered_keys.lock().get(&koid) {
43            return Ok(slot);
44        }
45        let slot =
46            self.device.as_ref().ok_or(zx::Status::NOT_SUPPORTED)?.register_key(key_token).await?;
47        self.registered_keys.lock().insert(koid, slot);
48        Ok(slot)
49    }
50}
51
52trait IntoFidlKeyPurpose {
53    fn into_fidl(self) -> FidlKeyPurpose;
54}
55
56impl IntoFidlKeyPurpose for KeyPurpose {
57    fn into_fidl(self) -> FidlKeyPurpose {
58        match self {
59            KeyPurpose::Data => FidlKeyPurpose::Data,
60            KeyPurpose::Metadata => FidlKeyPurpose::Metadata,
61        }
62    }
63}
64
65#[async_trait]
66impl Crypt for RemoteCrypt {
67    async fn create_key(
68        &self,
69        owner: u64,
70        purpose: KeyPurpose,
71    ) -> Result<(FxfsKey, UnwrappedKey), zx::Status> {
72        let (wrapping_key_id, key, unwrapped_key) = self
73            .client
74            .create_key(owner, purpose.into_fidl())
75            .await
76            .map_err(|e| map_to_status(e.into()))?
77            .map_err(zx::Status::err_from_raw)?;
78        Ok((
79            FxfsKey {
80                wrapping_key_id,
81                key: WrappedKeyBytes::try_from(key).map_err(map_to_status)?,
82            },
83            UnwrappedKey::new(unwrapped_key),
84        ))
85    }
86
87    async fn create_key_with_id(
88        &self,
89        owner: u64,
90        wrapping_key_id: WrappingKeyId,
91        object_type: ObjectType,
92        flags: fidl_fuchsia_io::FscryptPolicyFlags,
93    ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status> {
94        let (key, unwrapped_key, key_token) = self
95            .client
96            .create_key_with_id(owner, &wrapping_key_id, object_type, flags)
97            .await
98            .map_err(|e| map_to_status(e.into()))?
99            .map_err(zx::Status::err_from_raw)?;
100        let slot = if let Some(key_token) = key_token {
101            Some(self.register_key(key_token).await?)
102        } else {
103            None
104        };
105        Ok((key.try_into()?, UnwrappedKey::new_with_slot(unwrapped_key, slot)))
106    }
107
108    async fn unwrap_key(
109        &self,
110        wrapped_key: &WrappedKey,
111        owner: u64,
112    ) -> Result<UnwrappedKey, zx::Status> {
113        let (unwrapped, key_token) = self
114            .client
115            .unwrap_key(owner, &wrapped_key)
116            .await
117            .map_err(|e| map_to_status(e.into()))?
118            .map_err(zx::Status::err_from_raw)?;
119        let slot = if let Some(key_token) = key_token {
120            Some(self.register_key(key_token).await?)
121        } else {
122            None
123        };
124        Ok(UnwrappedKey::new_with_slot(unwrapped, slot))
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use anyhow::Error;
132    use fidl::endpoints::create_endpoints;
133    use fidl_fuchsia_fxfs::{CryptRequest, FscryptKeyIdentifier};
134    use fuchsia_async as fasync;
135    use futures::TryStreamExt as _;
136    use std::ops::Range;
137    use std::sync::atomic::{AtomicUsize, Ordering};
138    use storage_device::buffer::{BufferFuture, BufferRef, MutableBufferRef};
139    use storage_device::fake_device::FakeDevice;
140    use storage_device::{ReadOptions, WriteOptions};
141
142    struct MockDevice {
143        inner: FakeDevice,
144        expected_server_koid: zx::Koid,
145        slot: u8,
146        register_key_calls: AtomicUsize,
147    }
148
149    #[async_trait]
150    impl Device for MockDevice {
151        fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
152            self.inner.allocate_buffer(size)
153        }
154        fn block_size(&self) -> u32 {
155            self.inner.block_size()
156        }
157        fn block_count(&self) -> u64 {
158            self.inner.block_count()
159        }
160        async fn read_with_opts(
161            &self,
162            offset: u64,
163            buffer: MutableBufferRef<'_>,
164            read_opts: ReadOptions,
165        ) -> Result<(), Error> {
166            self.inner.read_with_opts(offset, buffer, read_opts).await
167        }
168        async fn write_with_opts(
169            &self,
170            offset: u64,
171            buffer: BufferRef<'_>,
172            write_opts: WriteOptions,
173        ) -> Result<(), Error> {
174            self.inner.write_with_opts(offset, buffer, write_opts).await
175        }
176        async fn trim(&self, range: Range<u64>) -> Result<(), Error> {
177            self.inner.trim(range).await
178        }
179        async fn close(&self) -> Result<(), Error> {
180            self.inner.close().await
181        }
182        async fn flush(&self) -> Result<(), Error> {
183            self.inner.flush().await
184        }
185        fn is_read_only(&self) -> bool {
186            self.inner.is_read_only()
187        }
188        fn supports_trim(&self) -> bool {
189            self.inner.supports_trim()
190        }
191        async fn register_key(&self, key_token: zx::EventPair) -> Result<u8, zx::Status> {
192            self.register_key_calls.fetch_add(1, Ordering::Relaxed);
193            let info = key_token.basic_info()?;
194            if info.related_koid == self.expected_server_koid {
195                Ok(self.slot)
196            } else {
197                Err(zx::Status::ACCESS_DENIED)
198            }
199        }
200    }
201
202    #[fuchsia::test]
203    async fn test_remote_crypt_registers_key_token() {
204        let (server_ep, client_ep) = zx::EventPair::create();
205        let server_koid = server_ep.koid().unwrap();
206
207        let (crypt_client, crypt_server) = create_endpoints::<CryptMarker>();
208        let scope = fasync::Scope::new();
209        scope.spawn(async move {
210            let mut stream = crypt_server.into_stream();
211            while let Some(request) = stream.try_next().await.unwrap() {
212                match request {
213                    CryptRequest::CreateKeyWithId { wrapping_key_id, responder, .. } => {
214                        let wrapped = WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier {
215                            key_identifier: wrapping_key_id,
216                        });
217                        let token = client_ep.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
218                        responder.send(Ok((&wrapped, &[0x11; 16], Some(token)))).unwrap();
219                    }
220                    CryptRequest::UnwrapKey { responder, .. } => {
221                        let token = client_ep.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
222                        responder.send(Ok((&[0x22; 16], Some(token)))).unwrap();
223                    }
224                    _ => unreachable!(),
225                }
226            }
227        });
228
229        let mock_device = Arc::new(MockDevice {
230            inner: FakeDevice::new(64, 4096),
231            expected_server_koid: server_koid,
232            slot: 9,
233            register_key_calls: AtomicUsize::new(0),
234        });
235        let device: Arc<dyn Device> = mock_device.clone();
236        let remote_crypt = RemoteCrypt::new_with_device(crypt_client, device);
237
238        let (_wrapped, unwrapped) = remote_crypt
239            .create_key_with_id(
240                1,
241                [0xaa; 16],
242                ObjectType::File,
243                fidl_fuchsia_io::FscryptPolicyFlags::empty(),
244            )
245            .await
246            .unwrap();
247        assert_eq!(unwrapped.slot(), Some(9));
248        assert_eq!(&*unwrapped, &[0x11; 16]);
249        assert_eq!(mock_device.register_key_calls.load(Ordering::Relaxed), 1);
250
251        let wrapped =
252            WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { key_identifier: [0xaa; 16] });
253        let unwrapped = remote_crypt.unwrap_key(&wrapped, 1).await.unwrap();
254        assert_eq!(unwrapped.slot(), Some(9));
255        assert_eq!(&*unwrapped, &[0x22; 16]);
256        assert_eq!(mock_device.register_key_calls.load(Ordering::Relaxed), 1);
257    }
258}