fxfs_platform_testing/fuchsia/
remote_crypt.rs1use 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 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}