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_mapper(self: &Arc<Self>) -> fblock::MapperProxy {
90 let (proxy, server) = fidl::endpoints::create_proxy::<fblock::MapperMarker>();
91 let this = self.clone();
92 fuchsia_async::Task::spawn(async move {
93 let _ = this.server.handle_mapper_requests(server.into_stream()).await;
94 })
95 .detach();
96 proxy
97 }
98
99 pub fn connect_insecure_inline_encryption_server(
100 self: &Arc<Self>,
101 server: ServerEnd<DeviceMarker>,
102 uuid: [u8; 16],
103 ) -> impl Future<Output = ()> + Send {
104 let this = self.clone();
105 this.serve_insecure_inline_encryption(server.into_stream(), uuid)
106 }
107
108 pub fn evict_key_slot(&self, slot: u8) -> Result<(), zx::Status> {
110 self.server.session_manager().interface().fscrypt_keys().evict_key(slot)
111 }
112
113 fn program_key(&self, xts_key: &[u8; 64]) -> Result<u8, zx::Status> {
116 self.server.session_manager().interface().fscrypt_keys().program_key(xts_key)
117 }
118
119 pub async fn serve_insecure_inline_encryption(
120 self: Arc<Self>,
121 mut requests: DeviceRequestStream,
122 uuid: [u8; 16],
123 ) {
124 while let Some(Ok(request)) = requests.next().await {
125 match request {
126 DeviceRequest::ProgramKey { wrapped_key, data_unit_size: _, responder } => {
127 responder
128 .send(
129 self.program_key(&fscrypt::to_xts_key(&wrapped_key, uuid))
130 .map_err(zx::Status::into_raw),
131 )
132 .unwrap_or_else(|e| {
133 log::error!("failed to send ProgramKey response. error: {:?}", e);
134 });
135 }
136 DeviceRequest::DeriveRawSecret { mut wrapped_key, responder } => {
137 for b in &mut wrapped_key {
139 *b = *b >> 4 | *b << 4;
140 }
141 responder.send(Ok(&wrapped_key)).unwrap();
142 }
143 }
144 }
145 }
146}
147
148pub enum InitialContents<'a> {
150 FromCapacity(u64),
152 FromCapacityAndBuffer(u64, &'a [u8]),
155 FromBuffer(&'a [u8]),
158 FromVmo(zx::Vmo),
160}
161
162pub struct VmoBackedServerOptions<'a> {
163 pub info: DeviceInfo,
165 pub block_size: u32,
166 pub initial_contents: InitialContents<'a>,
167 #[cfg(feature = "for-testing")]
168 pub observer: Option<Box<dyn Observer>>,
169 #[cfg(feature = "for-testing")]
172 pub write_tracking: bool,
173 #[cfg(feature = "for-testing")]
176 pub max_jitter_usec: Option<u64>,
177}
178
179impl Default for VmoBackedServerOptions<'_> {
180 fn default() -> Self {
181 VmoBackedServerOptions {
182 info: DeviceInfo::Block(BlockInfo {
183 device_flags: fblock::DeviceFlag::empty(),
184 block_count: 0,
185 max_transfer_blocks: None,
186 }),
187 block_size: 512,
188 initial_contents: InitialContents::FromCapacity(0),
189 #[cfg(feature = "for-testing")]
190 observer: None,
191 #[cfg(feature = "for-testing")]
192 write_tracking: false,
193 #[cfg(feature = "for-testing")]
194 max_jitter_usec: None,
195 }
196 }
197}
198
199impl VmoBackedServerOptions<'_> {
200 pub fn build(self) -> Result<VmoBackedServer, Error> {
201 let (data, block_count) = match self.initial_contents {
202 InitialContents::FromCapacity(block_count) => {
203 (zx::Vmo::create(block_count * self.block_size as u64)?, block_count)
204 }
205 InitialContents::FromCapacityAndBuffer(block_count, buf) => {
206 let needed =
207 buf.len()
208 .checked_next_multiple_of(self.block_size as usize)
209 .ok_or_else(|| anyhow!("Invalid buffer size"))? as u64
210 / self.block_size as u64;
211 if needed > block_count {
212 return Err(anyhow!("Not enough capacity: {needed} vs {block_count}"));
213 }
214 let vmo = zx::Vmo::create(block_count * self.block_size as u64)?;
215 if !buf.is_empty() {
216 vmo.write(buf, 0)?;
217 }
218 (vmo, block_count)
219 }
220 InitialContents::FromBuffer(buf) => {
221 let block_count =
222 buf.len()
223 .checked_next_multiple_of(self.block_size as usize)
224 .ok_or_else(|| anyhow!("Invalid buffer size"))? as u64
225 / self.block_size as u64;
226 let vmo = zx::Vmo::create(block_count * self.block_size as u64)?;
227 if !buf.is_empty() {
228 vmo.write(buf, 0)?;
229 }
230 (vmo, block_count)
231 }
232 InitialContents::FromVmo(vmo) => {
233 let size = vmo.get_size()?;
234 let block_count = size / self.block_size as u64;
235 (vmo, block_count)
236 }
237 };
238
239 let info = match self.info {
240 DeviceInfo::Block(mut info) => {
241 info.block_count = block_count;
242 DeviceInfo::Block(info)
243 }
244 DeviceInfo::Partition(mut info) => {
245 info.block_count = block_count;
246 DeviceInfo::Partition(info)
247 }
248 DeviceInfo::Volume(info) => DeviceInfo::Volume(info),
249 };
250 Ok(VmoBackedServer {
251 server: BlockServer::new(
252 self.block_size,
253 Arc::new(Data {
254 info,
255 block_size: self.block_size,
256 data,
257 #[cfg(feature = "for-testing")]
258 observer: self.observer,
259 #[cfg(feature = "for-testing")]
260 write_cache: self
261 .write_tracking
262 .then(|| fuchsia_sync::Mutex::new(WriteCache::new(self.block_size as u64))),
263 #[cfg(feature = "for-testing")]
264 max_jitter_usec: self.max_jitter_usec,
265 #[cfg(feature = "for-testing")]
266 fscrypt_keys: fuchsia_sync::Mutex::new(FscryptKeys::new()),
267 }),
268 ),
269 })
270 }
271}
272
273pub struct VmoBackedServerConnector {
275 scope: fuchsia_async::ScopeHandle,
276 server: Arc<VmoBackedServer>,
277}
278
279impl VmoBackedServerConnector {
280 pub fn new(server: Arc<VmoBackedServer>) -> Self {
282 Self { scope: fuchsia_async::Scope::current(), server }
283 }
284
285 pub fn new_with_scope(server: Arc<VmoBackedServer>, scope: fuchsia_async::ScopeHandle) -> Self {
287 Self { scope, server }
288 }
289}
290
291impl BlockConnector for VmoBackedServerConnector {
292 fn connect_channel_to_block(
293 &self,
294 server_end: ServerEnd<fblock::BlockMarker>,
295 ) -> Result<(), Error> {
296 let server = self.server.clone();
297 let _ = self.scope.spawn(async move {
298 let _ = server.serve(server_end.into_stream()).await;
299 });
300 Ok(())
301 }
302}
303
304pub trait BlockClient: FromClient {}
305
306impl BlockClient for fblock::BlockProxy {}
307impl BlockClient for fblock::BlockSynchronousProxy {}
308impl BlockClient for ClientEnd<fblock::BlockMarker> {}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use block_server::async_interface::Interface;
314 use block_server::{InlineCryptoOptions, ReadOptions, WriteOptions};
315
316 #[fuchsia::test]
317 async fn test_program_and_evict_key_slot() {
318 let block_size = 4096;
319 let server =
320 VmoBackedServer::new(100, block_size, &[]).expect("Failed to create VmoBackedServer");
321
322 let key = [0xaa; 64];
323 let slot = server.program_key(&key).expect("program_key failed");
324 assert_eq!(slot, 0);
325
326 let block_interface = server.server.session_manager().interface();
328 let vmo = Arc::new(zx::Vmo::create(block_size as u64).expect("Vmo::create failed"));
330 let original_data = vec![0xbb; block_size as usize];
331 vmo.write(&original_data, 0).expect("Vmo::write failed");
332 let write_opts = WriteOptions {
333 inline_crypto: InlineCryptoOptions::enabled(slot, 0),
334 ..Default::default()
335 };
336 block_interface.write(0, 1, &vmo, 0, write_opts, None).await.expect("write failed");
337
338 let vmo_read = Arc::new(zx::Vmo::create(block_size as u64).expect("Vmo::create failed"));
340 let read_opts = ReadOptions {
341 inline_crypto: InlineCryptoOptions::enabled(slot, 0),
342 ..Default::default()
343 };
344 block_interface.read(0, 1, &vmo_read, 0, read_opts, None).await.expect("read failed");
345 let mut read_data = vec![0u8; block_size as usize];
346 vmo_read.read(&mut read_data, 0).expect("Vmo::read failed");
347 assert_eq!(read_data, original_data);
348
349 server.evict_key_slot(slot).expect("evict_key_slot failed");
350 assert_eq!(server.evict_key_slot(slot), Err(zx::Status::INVALID_ARGS));
351
352 assert_eq!(
354 block_interface.read(0, 1, &vmo_read, 0, read_opts, None).await,
355 Err(zx::Status::IO)
356 );
357
358 assert_eq!(
359 block_interface.write(0, 1, &vmo, 0, write_opts, None).await,
360 Err(zx::Status::IO)
361 );
362 }
363
364 #[fuchsia::test]
365 async fn test_program_key_out_of_slots() {
366 let server = VmoBackedServer::new(100, 512, &[]).expect("Failed to create VmoBackedServer");
367
368 let key = [0xaa; 64];
369 for expected_slot in 0..=u8::MAX {
370 let slot = server.program_key(&key).expect("program_key failed");
371 assert_eq!(slot, expected_slot);
372 }
373 assert_eq!(server.program_key(&key), Err(zx::Status::NO_RESOURCES));
374 }
375}