Skip to main content

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}