1use anyhow::{Error, anyhow};
6use block_server::async_interface::SessionManager;
7use block_server::{BlockInfo, BlockServer, DeviceInfo};
8#[cfg(feature = "for-testing")]
9use fidl::endpoints::RequestStream;
10use fidl::endpoints::{ClientEnd, FromClient, ServerEnd};
11#[cfg(feature = "for-testing")]
12use fidl_fuchsia_hardware_inlineencryption::{DeviceMarker, DeviceRequest, DeviceRequestStream};
13use fidl_fuchsia_storage_block as fblock;
14use fs_management::filesystem::BlockConnector;
15#[cfg(feature = "for-testing")]
16use futures::StreamExt;
17use std::sync::Arc;
18
19#[cfg(not(feature = "for-testing"))]
20mod data;
21#[cfg(not(feature = "for-testing"))]
22use data::Data;
23
24#[cfg(feature = "for-testing")]
25mod data_for_testing;
26#[cfg(feature = "for-testing")]
27use data_for_testing::{Data, FscryptKeys};
28#[cfg(feature = "for-testing")]
29pub use data_for_testing::{Observer, WriteAction, WriteCache};
30
31pub struct VmoBackedServer {
33 server: BlockServer<SessionManager<Data>>,
34}
35
36impl VmoBackedServer {
37 pub async fn serve(&self, requests: fblock::BlockRequestStream) -> Result<(), Error> {
39 let res = self.server.handle_requests(requests).await;
40
41 #[cfg(feature = "for-testing")]
42 self.server.session_manager().interface().client_closed()?;
43
44 res
45 }
46
47 pub fn new(block_count: u64, block_size: u32, initial_content: &[u8]) -> Result<Self, Error> {
48 VmoBackedServerOptions {
49 block_size,
50 initial_contents: InitialContents::FromCapacityAndBuffer(block_count, initial_content),
51 ..Default::default()
52 }
53 .build()
54 }
55
56 pub fn from_vmo(block_size: u32, vmo: zx::Vmo) -> Result<Self, Error> {
57 VmoBackedServerOptions {
58 block_size,
59 initial_contents: InitialContents::FromVmo(vmo),
60 ..Default::default()
61 }
62 .build()
63 }
64}
65
66#[cfg(feature = "for-testing")]
67impl VmoBackedServer {
68 pub fn from_file(block_size: u32, path: &str) -> Self {
69 let contents = std::fs::read(path).expect("Failed to read file");
70 VmoBackedServerOptions {
71 block_size,
72 initial_contents: InitialContents::FromBuffer(&contents),
73 ..Default::default()
74 }
75 .build()
76 .expect("Failed to create VmoBackedServer.")
77 }
78
79 pub fn connect<R: BlockClient>(self: &Arc<Self>) -> R {
80 let (client, server) = fidl::endpoints::create_endpoints::<R::Protocol>();
81 let this = self.clone();
82 fuchsia_async::Task::spawn(async move {
83 let _ = this.serve(server.into_stream().cast_stream()).await;
84 })
85 .detach();
86 R::from_client(client)
87 }
88
89 pub fn connect_insecure_inline_encryption_server(
90 self: &Arc<Self>,
91 server: ServerEnd<DeviceMarker>,
92 uuid: [u8; 16],
93 ) -> impl Future<Output = ()> + Send {
94 let this = self.clone();
95 this.serve_insecure_inline_encryption(server.into_stream(), uuid)
96 }
97
98 pub fn evict_key_slot(&self, slot: u8) -> Result<(), zx::Status> {
100 self.server.session_manager().interface().fscrypt_keys().evict_key(slot)
101 }
102
103 fn program_key(&self, xts_key: &[u8; 64]) -> Result<u8, zx::Status> {
106 self.server.session_manager().interface().fscrypt_keys().program_key(xts_key)
107 }
108
109 pub async fn serve_insecure_inline_encryption(
110 self: Arc<Self>,
111 mut requests: DeviceRequestStream,
112 uuid: [u8; 16],
113 ) {
114 while let Some(Ok(request)) = requests.next().await {
115 match request {
116 DeviceRequest::ProgramKey { wrapped_key, data_unit_size: _, responder } => {
117 responder
118 .send(
119 self.program_key(&fscrypt::to_xts_key(&wrapped_key, uuid))
120 .map_err(zx::Status::into_raw),
121 )
122 .unwrap_or_else(|e| {
123 log::error!("failed to send ProgramKey response. error: {:?}", e);
124 });
125 }
126 DeviceRequest::DeriveRawSecret { mut wrapped_key, responder } => {
127 for b in &mut wrapped_key {
129 *b = *b >> 4 | *b << 4;
130 }
131 responder.send(Ok(&wrapped_key)).unwrap();
132 }
133 }
134 }
135 }
136}
137
138pub enum InitialContents<'a> {
140 FromCapacity(u64),
142 FromCapacityAndBuffer(u64, &'a [u8]),
145 FromBuffer(&'a [u8]),
148 FromVmo(zx::Vmo),
150}
151
152pub struct VmoBackedServerOptions<'a> {
153 pub info: DeviceInfo,
155 pub block_size: u32,
156 pub initial_contents: InitialContents<'a>,
157 #[cfg(feature = "for-testing")]
158 pub observer: Option<Box<dyn Observer>>,
159 #[cfg(feature = "for-testing")]
162 pub write_tracking: bool,
163 #[cfg(feature = "for-testing")]
166 pub max_jitter_usec: Option<u64>,
167}
168
169impl Default for VmoBackedServerOptions<'_> {
170 fn default() -> Self {
171 VmoBackedServerOptions {
172 info: DeviceInfo::Block(BlockInfo {
173 device_flags: fblock::DeviceFlag::empty(),
174 block_count: 0,
175 max_transfer_blocks: None,
176 }),
177 block_size: 512,
178 initial_contents: InitialContents::FromCapacity(0),
179 #[cfg(feature = "for-testing")]
180 observer: None,
181 #[cfg(feature = "for-testing")]
182 write_tracking: false,
183 #[cfg(feature = "for-testing")]
184 max_jitter_usec: None,
185 }
186 }
187}
188
189impl VmoBackedServerOptions<'_> {
190 pub fn build(self) -> Result<VmoBackedServer, Error> {
191 let (data, block_count) = match self.initial_contents {
192 InitialContents::FromCapacity(block_count) => {
193 (zx::Vmo::create(block_count * self.block_size as u64)?, block_count)
194 }
195 InitialContents::FromCapacityAndBuffer(block_count, buf) => {
196 let needed =
197 buf.len()
198 .checked_next_multiple_of(self.block_size as usize)
199 .ok_or_else(|| anyhow!("Invalid buffer size"))? as u64
200 / self.block_size as u64;
201 if needed > block_count {
202 return Err(anyhow!("Not enough capacity: {needed} vs {block_count}"));
203 }
204 let vmo = zx::Vmo::create(block_count * self.block_size as u64)?;
205 if !buf.is_empty() {
206 vmo.write(buf, 0)?;
207 }
208 (vmo, block_count)
209 }
210 InitialContents::FromBuffer(buf) => {
211 let block_count =
212 buf.len()
213 .checked_next_multiple_of(self.block_size as usize)
214 .ok_or_else(|| anyhow!("Invalid buffer size"))? as u64
215 / self.block_size as u64;
216 let vmo = zx::Vmo::create(block_count * self.block_size as u64)?;
217 if !buf.is_empty() {
218 vmo.write(buf, 0)?;
219 }
220 (vmo, block_count)
221 }
222 InitialContents::FromVmo(vmo) => {
223 let size = vmo.get_size()?;
224 let block_count = size / self.block_size as u64;
225 (vmo, block_count)
226 }
227 };
228
229 let info = match self.info {
230 DeviceInfo::Block(mut info) => {
231 info.block_count = block_count;
232 DeviceInfo::Block(info)
233 }
234 DeviceInfo::Partition(mut info) => {
235 info.block_count = block_count;
236 DeviceInfo::Partition(info)
237 }
238 DeviceInfo::Volume(info) => DeviceInfo::Volume(info),
239 };
240 Ok(VmoBackedServer {
241 server: BlockServer::new(
242 self.block_size,
243 Arc::new(Data {
244 info,
245 block_size: self.block_size,
246 data,
247 #[cfg(feature = "for-testing")]
248 observer: self.observer,
249 #[cfg(feature = "for-testing")]
250 write_cache: self
251 .write_tracking
252 .then(|| fuchsia_sync::Mutex::new(WriteCache::new(self.block_size as u64))),
253 #[cfg(feature = "for-testing")]
254 max_jitter_usec: self.max_jitter_usec,
255 #[cfg(feature = "for-testing")]
256 fscrypt_keys: fuchsia_sync::Mutex::new(FscryptKeys::new()),
257 }),
258 ),
259 })
260 }
261}
262
263pub struct VmoBackedServerConnector {
265 scope: fuchsia_async::ScopeHandle,
266 server: Arc<VmoBackedServer>,
267}
268
269impl VmoBackedServerConnector {
270 pub fn new(server: Arc<VmoBackedServer>) -> Self {
272 Self { scope: fuchsia_async::Scope::current(), server }
273 }
274
275 pub fn new_with_scope(server: Arc<VmoBackedServer>, scope: fuchsia_async::ScopeHandle) -> Self {
277 Self { scope, server }
278 }
279}
280
281impl BlockConnector for VmoBackedServerConnector {
282 fn connect_channel_to_block(
283 &self,
284 server_end: ServerEnd<fblock::BlockMarker>,
285 ) -> Result<(), Error> {
286 let server = self.server.clone();
287 let _ = self.scope.spawn(async move {
288 let _ = server.serve(server_end.into_stream()).await;
289 });
290 Ok(())
291 }
292}
293
294pub trait BlockClient: FromClient {}
295
296impl BlockClient for fblock::BlockProxy {}
297impl BlockClient for fblock::BlockSynchronousProxy {}
298impl BlockClient for ClientEnd<fblock::BlockMarker> {}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303 use block_server::async_interface::Interface;
304 use block_server::{InlineCryptoOptions, ReadOptions, WriteOptions};
305
306 #[fuchsia::test]
307 async fn test_program_and_evict_key_slot() {
308 let block_size = 4096;
309 let server =
310 VmoBackedServer::new(100, block_size, &[]).expect("Failed to create VmoBackedServer");
311
312 let key = [0xaa; 64];
313 let slot = server.program_key(&key).expect("program_key failed");
314 assert_eq!(slot, 0);
315
316 let block_interface = server.server.session_manager().interface();
318 let vmo = Arc::new(zx::Vmo::create(block_size as u64).expect("Vmo::create failed"));
320 let original_data = vec![0xbb; block_size as usize];
321 vmo.write(&original_data, 0).expect("Vmo::write failed");
322 let write_opts = WriteOptions {
323 inline_crypto: InlineCryptoOptions::enabled(slot, 0),
324 ..Default::default()
325 };
326 block_interface.write(0, 1, &vmo, 0, write_opts, None).await.expect("write failed");
327
328 let vmo_read = Arc::new(zx::Vmo::create(block_size as u64).expect("Vmo::create failed"));
330 let read_opts = ReadOptions {
331 inline_crypto: InlineCryptoOptions::enabled(slot, 0),
332 ..Default::default()
333 };
334 block_interface.read(0, 1, &vmo_read, 0, read_opts, None).await.expect("read failed");
335 let mut read_data = vec![0u8; block_size as usize];
336 vmo_read.read(&mut read_data, 0).expect("Vmo::read failed");
337 assert_eq!(read_data, original_data);
338
339 server.evict_key_slot(slot).expect("evict_key_slot failed");
340 assert_eq!(server.evict_key_slot(slot), Err(zx::Status::INVALID_ARGS));
341
342 assert_eq!(
344 block_interface.read(0, 1, &vmo_read, 0, read_opts, None).await,
345 Err(zx::Status::IO)
346 );
347
348 assert_eq!(
349 block_interface.write(0, 1, &vmo, 0, write_opts, None).await,
350 Err(zx::Status::IO)
351 );
352 }
353
354 #[fuchsia::test]
355 async fn test_program_key_out_of_slots() {
356 let server = VmoBackedServer::new(100, 512, &[]).expect("Failed to create VmoBackedServer");
357
358 let key = [0xaa; 64];
359 for expected_slot in 0..=u8::MAX {
360 let slot = server.program_key(&key).expect("program_key failed");
361 assert_eq!(slot, expected_slot);
362 }
363 assert_eq!(server.program_key(&key), Err(zx::Status::NO_RESOURCES));
364 }
365}