capsules_extra/sha256_driver.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! SHA Userspace Driver
6//!
7//! Currently only supports SHA256.
8
9use capsules_core::driver;
10use kernel::errorcode::into_statuscode;
11use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
12use kernel::hil::digest;
13use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
14use kernel::syscall::{CommandReturn, SyscallDriver};
15use kernel::utilities::cells::{OptionalCell, TakeCell};
16use kernel::utilities::leasable_buffer::SubSlice;
17use kernel::utilities::leasable_buffer::SubSliceMut;
18use kernel::{ErrorCode, ProcessId};
19
20/// Syscall driver number.
21pub const DRIVER_NUM: usize = driver::NUM::Sha as usize;
22
23/// Upcalls for SHA operations completing.
24mod upcall {
25 pub const HASH: usize = 0;
26 pub const COUNT: u8 = 1;
27}
28
29/// Ids for read-only allow buffers
30mod ro_allow {
31 pub const DATA: usize = 0;
32 /// The number of allow buffers the kernel stores for this grant
33 pub const COUNT: u8 = 1;
34}
35
36/// Ids for read-write allow buffers
37mod rw_allow {
38 pub const DEST: usize = 0;
39 /// The number of allow buffers the kernel stores for this grant
40 pub const COUNT: u8 = 1;
41}
42
43#[derive(Copy, Clone, PartialEq)]
44enum AppOp {
45 Hash,
46 // Verify,
47}
48
49#[derive(Default)]
50pub struct App {
51 sha_algorithm: ShaAlgorithm,
52 operation: OptionalCell<AppOp>,
53 data_offset: usize,
54}
55
56#[derive(Default)]
57enum ShaAlgorithm {
58 #[default]
59 Sha256,
60 // Sha384,
61 // Sha512,
62}
63
64pub struct ShaDriver<'a, H: digest::DigestDataHash<'a, DIGEST_LEN>, const DIGEST_LEN: usize> {
65 /// Underlying hasher to use for the SHA operations.
66 sha: &'a H,
67
68 /// Virtualized capsule that supports a single operation per app.
69 apps: Grant<
70 App,
71 UpcallCount<{ upcall::COUNT }>,
72 AllowRoCount<{ ro_allow::COUNT }>,
73 AllowRwCount<{ rw_allow::COUNT }>,
74 >,
75
76 /// The process currently using the SHA hasher.
77 processid: OptionalCell<ProcessId>,
78
79 /// Buffer to hold the data we are copying to the SHA hasher.
80 data_buffer: TakeCell<'static, [u8]>,
81
82 /// Buffer to hold the output of the SHA hasher.
83 dest_buffer: TakeCell<'static, [u8; DIGEST_LEN]>,
84}
85
86impl<'a, H: digest::DigestDataHash<'a, DIGEST_LEN> + digest::Sha256, const DIGEST_LEN: usize>
87 ShaDriver<'a, H, DIGEST_LEN>
88{
89 pub fn new(
90 sha: &'a H,
91 data_buffer: &'static mut [u8],
92 dest_buffer: &'static mut [u8; DIGEST_LEN],
93 grant: Grant<
94 App,
95 UpcallCount<{ upcall::COUNT }>,
96 AllowRoCount<{ ro_allow::COUNT }>,
97 AllowRwCount<{ rw_allow::COUNT }>,
98 >,
99 ) -> ShaDriver<'a, H, DIGEST_LEN> {
100 ShaDriver {
101 sha,
102 apps: grant,
103 processid: OptionalCell::empty(),
104 data_buffer: TakeCell::new(data_buffer),
105 dest_buffer: TakeCell::new(dest_buffer),
106 }
107 }
108
109 fn run(&self, processid: ProcessId) -> Result<(), ErrorCode> {
110 // Save this process as the active process.
111 self.processid.set(processid);
112
113 self.apps
114 .enter(processid, |app, kernel_data| {
115 // First, set the operation of the underlying hasher.
116 match app.sha_algorithm {
117 ShaAlgorithm::Sha256 => self.sha.set_mode_sha256()?,
118 // ShaAlgorithm::Sha384 => self.sha.set_mode_sha384()?,
119 // ShaAlgorithm::Sha512 => self.sha.set_mode_sha512()?,
120 }
121
122 // Now, start copying data from the allowed buffer into our `data_buffer`
123 // and then share that data with the underlying hasher.
124 kernel_data
125 .get_readonly_processbuffer(ro_allow::DATA)
126 .and_then(|data| {
127 data.enter(|data| {
128 self.data_buffer.take().map_or(Err(ErrorCode::FAIL), |buf| {
129 // Copy as much data as we have or as much as we can fit in our
130 // kernel buffer.
131 let copy_len = core::cmp::min(data.len(), buf.len());
132 let _ =
133 data[0..copy_len].copy_to_slice_or_err(&mut buf[0..copy_len]);
134
135 // Save how far into the buffer we are.
136 app.data_offset = copy_len;
137
138 // Add data to the hasher.
139 let mut lease_buf = SubSliceMut::new(buf);
140 lease_buf.slice(0..copy_len);
141 if let Err((e, buf)) = self.sha.add_mut_data(lease_buf) {
142 self.data_buffer.replace(buf.take());
143 Err(e)
144 } else {
145 Ok(())
146 }
147 })
148 })
149 })
150 .unwrap_or(Err(ErrorCode::RESERVE))
151 })
152 .unwrap_or_else(|err| Err(err.into()))
153 }
154
155 fn check_queue(&self) -> Result<(), ErrorCode> {
156 // Check if there is already something using the SHA hasher.
157 if self.processid.is_some() {
158 // Something is using the hasher. That is fine, we have nothing to do,
159 // pending operations will run later.
160 Ok(())
161 } else {
162 let ready_app = self.apps.iter().find_map(|appiter| {
163 let possible_process = appiter.processid();
164 let ready = appiter.enter(|app, _| app.operation.is_some());
165 if ready { Some(possible_process) } else { None }
166 });
167
168 if let Some(ready_app) = ready_app {
169 self.run(ready_app)
170 } else {
171 // Nothing to do
172 Ok(())
173 }
174 }
175 }
176
177 // Check queue, but instead of returning an error, trigger an upcall.
178 fn check_queue_async(&self) {
179 if let Err(e) = self.check_queue() {
180 self.processid.take().map(|processid| {
181 let _ = self.apps.enter(processid, |app, kernel_data| {
182 let upcall_num = match app.operation.get() {
183 Some(AppOp::Hash) | None => upcall::HASH,
184 };
185 app.operation.clear();
186
187 let _ =
188 kernel_data.schedule_upcall(upcall_num, (into_statuscode(e.into()), 0, 0));
189 });
190 });
191 }
192 }
193}
194
195impl<'a, H: digest::DigestDataHash<'a, DIGEST_LEN> + digest::Sha256, const DIGEST_LEN: usize>
196 digest::ClientData<DIGEST_LEN> for ShaDriver<'a, H, DIGEST_LEN>
197{
198 // Because data needs to be copied from a userspace buffer into a kernel (RAM) one,
199 // we always pass mut data; this callback should never be invoked.
200 fn add_data_done(&self, _result: Result<(), ErrorCode>, _data: SubSlice<'static, u8>) {}
201
202 fn add_mut_data_done(&self, _result: Result<(), ErrorCode>, data: SubSliceMut<'static, u8>) {
203 // Unconditionally return our kernel buffer.
204 self.data_buffer.replace(data.take());
205
206 // Continue with the active process. If there is more data to add, do that.
207 // If all data has been added, then do the requested operation.
208 self.processid.map(|processid| {
209 self.apps
210 .enter(processid, |app, kernel_data| {
211 // Check if we have more data to copy.
212 let res = kernel_data
213 .get_readonly_processbuffer(ro_allow::DATA)
214 .and_then(|data| {
215 data.enter(|data| {
216 let remaining = data.len() - app.data_offset;
217
218 if remaining > 0 {
219 // More data to add.
220 self.data_buffer.take().map_or(Err(ErrorCode::FAIL), |buf| {
221 let copy_len = core::cmp::min(remaining, buf.len());
222 let src_start = app.data_offset;
223 let src_end = src_start + copy_len;
224
225 let _ = data[src_start..src_end]
226 .copy_to_slice_or_err(&mut buf[0..copy_len]);
227
228 // Save how far into the buffer we are.
229 app.data_offset = src_end;
230
231 // Add data to the hasher.
232 let mut lease_buf = SubSliceMut::new(buf);
233 lease_buf.slice(0..copy_len);
234 self.sha.add_mut_data(lease_buf).and(Ok(true)).map_err(
235 |(e, buf)| {
236 self.sha.clear_data();
237 self.processid.clear();
238 self.data_buffer.replace(buf.take());
239 e
240 },
241 )
242 })
243 } else {
244 Ok(false)
245 }
246 })
247 })
248 .unwrap_or_else(|err| err.into());
249
250 // If we did have more data to copy, we will get `Ok(true)` and we
251 // have nothing more to do. If we did not have more data to copy, we
252 // will get `Ok(false)` and can move to the hash operation. If we
253 // got an error, we do an upcall to the app.
254 let _ = match res {
255 Ok(false) => {
256 match app.operation.get() {
257 Some(AppOp::Hash) => {
258 // No more data to copy. Run the hash.
259 self.dest_buffer.take().map_or(Err(ErrorCode::FAIL), |buf| {
260 self.sha.run(buf).map_err(|(e, buf)| {
261 // Error, clear the processid and data
262 self.sha.clear_data();
263 self.processid.clear();
264 self.dest_buffer.replace(buf);
265 e
266 })
267 })
268 }
269
270 _ => Ok(()),
271 }
272 }
273 Ok(true) => Ok(()),
274 Err(e) => Err(e),
275 };
276 if let Err(e) = res {
277 // Notify the process.
278 let upcall_num = match app.operation.get() {
279 Some(AppOp::Hash) | None => upcall::HASH,
280 };
281 let _ = kernel_data
282 .schedule_upcall(upcall_num, (into_statuscode(e.into()), 0, 0));
283 }
284 })
285 .map_err(|err| {
286 if err == kernel::process::Error::NoSuchApp
287 || err == kernel::process::Error::InactiveApp
288 {
289 self.sha.clear_data();
290 self.processid.clear();
291 }
292 })
293 });
294
295 // Check for more work to do.
296 self.check_queue_async();
297 }
298}
299
300impl<'a, H: digest::DigestDataHash<'a, DIGEST_LEN> + digest::Sha256, const DIGEST_LEN: usize>
301 digest::ClientHash<DIGEST_LEN> for ShaDriver<'a, H, DIGEST_LEN>
302{
303 fn hash_done(&self, result: Result<(), ErrorCode>, digest: &'static mut [u8; DIGEST_LEN]) {
304 // Clear the underlying hasher.
305 self.sha.clear_data();
306
307 // Do our best to copy the digest to the app.
308 //
309 // If the app is gone, or didn't give us a `DIGEST_LEN` buffer, we won't
310 // be able to copy the buffer. If the app still exists it will get an
311 // upcall either way.
312 self.processid.map(|processid| {
313 let _ = self.apps.enter(processid, |app, kernel_data| {
314 // Mark app operation as completed.
315 app.operation.clear();
316
317 let res = result.and_then(|()| {
318 // Do our best to copy to the app's buffer. The app MUST have given
319 // us a `DIGEST_LEN` length buffer to copy to. If not, the app won't
320 // get the digest.
321 kernel_data
322 .get_readwrite_processbuffer(rw_allow::DEST)
323 .and_then(|dest| {
324 dest.mut_enter(|dest| {
325 if dest.len() == DIGEST_LEN {
326 let _ = dest.copy_from_slice_or_err(digest);
327 Ok(())
328 } else {
329 Err(ErrorCode::NOMEM)
330 }
331 })
332 })
333 .unwrap_or_else(|err| err.into())
334 });
335
336 // Notify the app the operation has finished.
337 let _ = kernel_data.schedule_upcall(upcall::HASH, (into_statuscode(res), 0, 0));
338 });
339 });
340
341 // Unconditionally clear the current app. Either, the app still exists
342 // and we did the upcall, or the app is gone and we need to reset.
343 self.processid.clear();
344
345 // Be sure to replace our buffer.
346 self.dest_buffer.replace(digest);
347
348 // Check for more work to do.
349 self.check_queue_async();
350 }
351}
352
353impl<'a, H: digest::DigestDataHash<'a, DIGEST_LEN> + digest::Sha256, const DIGEST_LEN: usize>
354 SyscallDriver for ShaDriver<'a, H, DIGEST_LEN>
355{
356 /// Setup and run a SHA hash.
357 ///
358 /// We expect userspace to setup buffers for the data, and either the
359 /// generated hash or a hash to compare with. These buffers must be
360 /// allocated and specified to the kernel with allow calls.
361 ///
362 /// We expect userspace not to change the value while running. If userspace
363 /// changes the value we have no guarantee of what is passed to the
364 /// hardware. This isn't a security issue, it will just provide the requesting
365 /// app with invalid data.
366 ///
367 /// The driver will take care of clearing data from the underlying
368 /// implementation by calling the `clear_data()` function when the
369 /// `hash_complete()` callback is called or if an error is encountered.
370 ///
371 /// ### `command_num`
372 ///
373 /// - `0`: driver check
374 /// - `1`: set_algorithm
375 /// - `2`: hash
376 fn command(
377 &self,
378 command_num: usize,
379 data1: usize,
380 _data2: usize,
381 processid: ProcessId,
382 ) -> CommandReturn {
383 match command_num {
384 // check if present
385 0 => CommandReturn::success(),
386
387 // set_algorithm
388 1 => {
389 self.apps
390 .enter(processid, |app, _kernel_data| {
391 match data1 {
392 // SHA256
393 0 => {
394 app.sha_algorithm = ShaAlgorithm::Sha256;
395 CommandReturn::success()
396 }
397 // // SHA384
398 // 1 => {
399 // app.sha_algorithm = ShaAlgorithm::Sha384;
400 // CommandReturn::success()
401 // }
402 // // SHA512
403 // 2 => {
404 // app.sha_algorithm = ShaAlgorithm::Sha512;
405 // CommandReturn::success()
406 // }
407 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
408 }
409 })
410 .unwrap_or_else(|err| err.into())
411 }
412
413 // hash
414 2 => {
415 let res = self.apps.enter(processid, |app, _kernel_data| {
416 if app.operation.is_some() {
417 // No more room in the queue, nowhere to store this request.
418 Err(ErrorCode::NOMEM)
419 } else {
420 app.operation.set(AppOp::Hash);
421 Ok(())
422 }
423 });
424 match res {
425 Ok(_) => {
426 // If we were able to enqueue the operation, check if we can
427 // actually run it. If there was an error starting it return the
428 // error, otherwise return ok if the operation started successfully
429 // or was queued for later. This also ensures we are not already in
430 // the grant.
431 self.check_queue()
432 .inspect_err(|_| {
433 let _ = self.apps.enter(processid, |app, _kernel_data| {
434 app.operation.clear();
435 });
436 })
437 .into()
438 }
439 Err(e) => e.into(),
440 }
441 }
442
443 // default
444 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
445 }
446 }
447
448 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
449 self.apps.enter(processid, |_, _| {})
450 }
451}