capsules_extra/
sha.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
6//!
7//! Usage
8//! -----
9//!
10//! ```rust,ignore
11//! let sha = &earlgrey::sha::HMAC;
12//!
13//! let mux_sha = static_init!(MuxSha<'static, lowrisc::sha::Sha>, MuxSha::new(sha));
14//! digest::DigestMut::set_client(&earlgrey::sha::HMAC, mux_sha);
15//!
16//! let virtual_sha_user = static_init!(
17//!     VirtualMuxSha<'static, lowrisc::sha::Sha>,
18//!     VirtualMuxSha::new(mux_sha)
19//! );
20//! let sha = static_init!(
21//!     capsules::sha::ShaDriver<'static, VirtualMuxSha<'static, lowrisc::sha::Sha>>,
22//!     capsules::sha::ShaDriver::new(
23//!         virtual_sha_user,
24//!         board_kernel.create_grant(&memory_allocation_cap),
25//!     )
26//! );
27//! digest::DigestMut::set_client(virtual_sha_user, sha);
28//! ```
29
30use capsules_core::driver;
31use kernel::errorcode::into_statuscode;
32
33/// Syscall driver number.
34pub const DRIVER_NUM: usize = driver::NUM::Sha as usize;
35
36/// Ids for read-only allow buffers
37mod ro_allow {
38    pub const DATA: usize = 1;
39    pub const COMPARE: usize = 2;
40    /// The number of allow buffers the kernel stores for this grant
41    pub const COUNT: u8 = 3;
42}
43
44/// Ids for read-write allow buffers
45mod rw_allow {
46    pub const DEST: usize = 2;
47    /// The number of allow buffers the kernel stores for this grant
48    pub const COUNT: u8 = 3;
49}
50
51use core::cell::Cell;
52
53use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
54use kernel::hil::digest;
55use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
56use kernel::syscall::{CommandReturn, SyscallDriver};
57use kernel::utilities::cells::{OptionalCell, TakeCell};
58use kernel::utilities::leasable_buffer::SubSlice;
59use kernel::utilities::leasable_buffer::SubSliceMut;
60use kernel::{ErrorCode, ProcessId};
61
62enum ShaOperation {
63    Sha256,
64    Sha384,
65    Sha512,
66}
67
68pub struct ShaDriver<'a, H: digest::Digest<'a, DIGEST_LEN>, const DIGEST_LEN: usize> {
69    sha: &'a H,
70
71    active: Cell<bool>,
72
73    apps: Grant<
74        App,
75        UpcallCount<1>,
76        AllowRoCount<{ ro_allow::COUNT }>,
77        AllowRwCount<{ rw_allow::COUNT }>,
78    >,
79    processid: OptionalCell<ProcessId>,
80
81    data_buffer: TakeCell<'static, [u8]>,
82    data_copied: Cell<usize>,
83    dest_buffer: TakeCell<'static, [u8; DIGEST_LEN]>,
84}
85
86impl<
87        'a,
88        H: digest::Digest<'a, DIGEST_LEN> + digest::Sha256 + digest::Sha384 + digest::Sha512,
89        const DIGEST_LEN: usize,
90    > ShaDriver<'a, H, DIGEST_LEN>
91{
92    pub fn new(
93        sha: &'a H,
94        data_buffer: &'static mut [u8],
95        dest_buffer: &'static mut [u8; DIGEST_LEN],
96        grant: Grant<
97            App,
98            UpcallCount<1>,
99            AllowRoCount<{ ro_allow::COUNT }>,
100            AllowRwCount<{ rw_allow::COUNT }>,
101        >,
102    ) -> ShaDriver<'a, H, DIGEST_LEN> {
103        ShaDriver {
104            sha,
105            active: Cell::new(false),
106            apps: grant,
107            processid: OptionalCell::empty(),
108            data_buffer: TakeCell::new(data_buffer),
109            data_copied: Cell::new(0),
110            dest_buffer: TakeCell::new(dest_buffer),
111        }
112    }
113
114    fn run(&self) -> Result<(), ErrorCode> {
115        self.processid.map_or(Err(ErrorCode::RESERVE), |processid| {
116            self.apps
117                .enter(processid, |app, kernel_data| {
118                    match app.sha_operation {
119                        Some(ShaOperation::Sha256) => self.sha.set_mode_sha256()?,
120                        Some(ShaOperation::Sha384) => self.sha.set_mode_sha384()?,
121                        Some(ShaOperation::Sha512) => self.sha.set_mode_sha512()?,
122                        _ => return Err(ErrorCode::INVAL),
123                    }
124
125                    kernel_data
126                        .get_readonly_processbuffer(ro_allow::DATA)
127                        .and_then(|data| {
128                            data.enter(|data| {
129                                let mut static_buffer_len = 0;
130                                self.data_buffer.map(|buf| {
131                                    // Determine the size of the static buffer we have
132                                    static_buffer_len = buf.len();
133
134                                    if static_buffer_len > data.len() {
135                                        static_buffer_len = data.len()
136                                    }
137
138                                    self.data_copied.set(static_buffer_len);
139
140                                    // Copy the data into the static buffer
141                                    data[..static_buffer_len]
142                                        .copy_to_slice(&mut buf[..static_buffer_len]);
143                                });
144
145                                // Add the data from the static buffer to the HMAC
146                                let mut lease_buf = SubSliceMut::new(
147                                    self.data_buffer.take().ok_or(ErrorCode::RESERVE)?,
148                                );
149                                lease_buf.slice(0..static_buffer_len);
150                                if let Err(e) = self.sha.add_mut_data(lease_buf) {
151                                    self.data_buffer.replace(e.1.take());
152                                    return Err(e.0);
153                                }
154                                Ok(())
155                            })
156                        })
157                        .unwrap_or(Err(ErrorCode::RESERVE))
158                })
159                .unwrap_or_else(|err| Err(err.into()))
160        })
161    }
162
163    fn check_queue(&self) {
164        for appiter in self.apps.iter() {
165            let started_command = appiter.enter(|app, _| {
166                // If an app is already running let it complete
167                if self.processid.is_some() {
168                    return true;
169                }
170
171                // If this app has a pending command let's use it.
172                app.pending_run_app.take().is_some_and(|processid| {
173                    // Mark this driver as being in use.
174                    self.processid.set(processid);
175                    // Actually make the buzz happen.
176                    self.run() == Ok(())
177                })
178            });
179            if started_command {
180                break;
181            }
182        }
183    }
184
185    fn calculate_digest(&self) -> Result<(), ErrorCode> {
186        self.data_copied.set(0);
187
188        if let Err(e) = self
189            .sha
190            .run(self.dest_buffer.take().ok_or(ErrorCode::RESERVE)?)
191        {
192            // Error, clear the processid and data
193            self.sha.clear_data();
194            self.processid.clear();
195            self.dest_buffer.replace(e.1);
196
197            return Err(e.0);
198        }
199
200        Ok(())
201    }
202
203    fn verify_digest(&self) -> Result<(), ErrorCode> {
204        self.data_copied.set(0);
205
206        if let Err(e) = self
207            .sha
208            .verify(self.dest_buffer.take().ok_or(ErrorCode::RESERVE)?)
209        {
210            // Error, clear the processid and data
211            self.sha.clear_data();
212            self.processid.clear();
213            self.dest_buffer.replace(e.1);
214
215            return Err(e.0);
216        }
217
218        Ok(())
219    }
220}
221
222impl<
223        'a,
224        H: digest::Digest<'a, DIGEST_LEN> + digest::Sha256 + digest::Sha384 + digest::Sha512,
225        const DIGEST_LEN: usize,
226    > digest::ClientData<DIGEST_LEN> for ShaDriver<'a, H, DIGEST_LEN>
227{
228    // Because data needs to be copied from a userspace buffer into a kernel (RAM) one,
229    // we always pass mut data; this callback should never be invoked.
230    fn add_data_done(&self, _result: Result<(), ErrorCode>, _data: SubSlice<'static, u8>) {}
231
232    fn add_mut_data_done(&self, _result: Result<(), ErrorCode>, data: SubSliceMut<'static, u8>) {
233        self.processid.map(move |id| {
234            self.apps
235                .enter(id, move |app, kernel_data| {
236                    let mut data_len = 0;
237                    let mut exit = false;
238                    let mut static_buffer_len = 0;
239
240                    self.data_buffer.replace(data.take());
241
242                    self.data_buffer.map(|buf| {
243                        let ret = kernel_data
244                            .get_readonly_processbuffer(ro_allow::DATA)
245                            .and_then(|data| {
246                                data.enter(|data| {
247                                    // Determine the size of the static buffer we have
248                                    static_buffer_len = buf.len();
249
250                                    // Determine how much data we have already copied
251                                    let copied_data = self.data_copied.get();
252
253                                    data_len = data.len();
254
255                                    if data_len > copied_data {
256                                        let remaining_data = &data[copied_data..];
257                                        let remaining_len = data_len - copied_data;
258
259                                        if remaining_len < static_buffer_len {
260                                            remaining_data.copy_to_slice(&mut buf[..remaining_len]);
261                                        } else {
262                                            remaining_data[..static_buffer_len].copy_to_slice(buf);
263                                        }
264                                    }
265                                    Ok(())
266                                })
267                            })
268                            .unwrap_or(Err(ErrorCode::RESERVE));
269
270                        if ret == Err(ErrorCode::RESERVE) {
271                            // No data buffer, clear the processid and data
272                            self.sha.clear_data();
273                            self.processid.clear();
274                            exit = true;
275                        }
276                    });
277
278                    if exit {
279                        return;
280                    }
281
282                    if static_buffer_len > 0 {
283                        let copied_data = self.data_copied.get();
284
285                        if data_len > copied_data {
286                            // Update the amount of data copied
287                            self.data_copied.set(copied_data + static_buffer_len);
288
289                            let mut lease_buf = SubSliceMut::new(self.data_buffer.take().unwrap());
290
291                            // Add the data from the static buffer to the HMAC
292                            if data_len < (copied_data + static_buffer_len) {
293                                lease_buf.slice(..(data_len - copied_data))
294                            }
295
296                            if self.sha.add_mut_data(lease_buf).is_err() {
297                                // Error, clear the processid and data
298                                self.sha.clear_data();
299                                self.processid.clear();
300                                return;
301                            }
302
303                            // Return as we don't want to run the digest yet
304                            return;
305                        }
306                    }
307
308                    // If we get here we are ready to run the digest, reset the copied data
309                    if app.op.get().unwrap() == UserSpaceOp::Run {
310                        if let Err(e) = self.calculate_digest() {
311                            kernel_data
312                                .schedule_upcall(0, (into_statuscode(e.into()), 0, 0))
313                                .ok();
314                        }
315                    } else if app.op.get().unwrap() == UserSpaceOp::Verify {
316                        let _ = kernel_data
317                            .get_readonly_processbuffer(ro_allow::COMPARE)
318                            .and_then(|compare| {
319                                compare.enter(|compare| {
320                                    let mut static_buffer_len = 0;
321                                    self.dest_buffer.map(|buf| {
322                                        // Determine the size of the static buffer we have
323                                        static_buffer_len = buf.len();
324
325                                        if static_buffer_len > compare.len() {
326                                            static_buffer_len = compare.len()
327                                        }
328
329                                        self.data_copied.set(static_buffer_len);
330
331                                        // Copy the data into the static buffer
332                                        compare[..static_buffer_len]
333                                            .copy_to_slice(&mut buf[..static_buffer_len]);
334                                    });
335                                })
336                            });
337
338                        if let Err(e) = self.verify_digest() {
339                            kernel_data
340                                .schedule_upcall(1, (into_statuscode(e.into()), 0, 0))
341                                .ok();
342                        }
343                    } else {
344                        kernel_data.schedule_upcall(0, (0, 0, 0)).ok();
345                    }
346                })
347                .map_err(|err| {
348                    if err == kernel::process::Error::NoSuchApp
349                        || err == kernel::process::Error::InactiveApp
350                    {
351                        self.processid.clear();
352                    }
353                })
354        });
355
356        self.check_queue();
357    }
358}
359
360impl<
361        'a,
362        H: digest::Digest<'a, DIGEST_LEN> + digest::Sha256 + digest::Sha384 + digest::Sha512,
363        const DIGEST_LEN: usize,
364    > digest::ClientHash<DIGEST_LEN> for ShaDriver<'a, H, DIGEST_LEN>
365{
366    fn hash_done(&self, result: Result<(), ErrorCode>, digest: &'static mut [u8; DIGEST_LEN]) {
367        self.processid.map(|id| {
368            self.apps
369                .enter(id, |_, kernel_data| {
370                    self.sha.clear_data();
371
372                    let pointer = digest.as_ref()[0] as *mut u8;
373
374                    let _ = kernel_data
375                        .get_readwrite_processbuffer(rw_allow::DEST)
376                        .and_then(|dest| {
377                            dest.mut_enter(|dest| {
378                                let len = dest.len();
379
380                                if len < DIGEST_LEN {
381                                    dest.copy_from_slice(&digest[0..len]);
382                                } else {
383                                    dest[0..DIGEST_LEN].copy_from_slice(digest);
384                                }
385                            })
386                        });
387
388                    match result {
389                        Ok(()) => kernel_data
390                            .schedule_upcall(0, (0, pointer as usize, 0))
391                            .ok(),
392                        Err(e) => kernel_data
393                            .schedule_upcall(0, (into_statuscode(e.into()), pointer as usize, 0))
394                            .ok(),
395                    };
396
397                    // Clear the current processid as it has finished running
398                    self.processid.clear();
399                })
400                .map_err(|err| {
401                    if err == kernel::process::Error::NoSuchApp
402                        || err == kernel::process::Error::InactiveApp
403                    {
404                        self.processid.clear();
405                    }
406                })
407        });
408
409        self.check_queue();
410        self.dest_buffer.replace(digest);
411    }
412}
413
414impl<
415        'a,
416        H: digest::Digest<'a, DIGEST_LEN> + digest::Sha256 + digest::Sha384 + digest::Sha512,
417        const DIGEST_LEN: usize,
418    > digest::ClientVerify<DIGEST_LEN> for ShaDriver<'a, H, DIGEST_LEN>
419{
420    fn verification_done(
421        &self,
422        result: Result<bool, ErrorCode>,
423        compare: &'static mut [u8; DIGEST_LEN],
424    ) {
425        self.processid.map(|id| {
426            self.apps
427                .enter(id, |_app, kernel_data| {
428                    self.sha.clear_data();
429
430                    match result {
431                        Ok(equal) => kernel_data.schedule_upcall(1, (0, equal as usize, 0)),
432                        Err(e) => kernel_data.schedule_upcall(1, (into_statuscode(e.into()), 0, 0)),
433                    }
434                    .ok();
435
436                    // Clear the current processid as it has finished running
437                    self.processid.clear();
438                })
439                .map_err(|err| {
440                    if err == kernel::process::Error::NoSuchApp
441                        || err == kernel::process::Error::InactiveApp
442                    {
443                        self.processid.clear();
444                    }
445                })
446        });
447
448        self.check_queue();
449        self.dest_buffer.replace(compare);
450    }
451}
452
453impl<
454        'a,
455        H: digest::Digest<'a, DIGEST_LEN> + digest::Sha256 + digest::Sha384 + digest::Sha512,
456        const DIGEST_LEN: usize,
457    > SyscallDriver for ShaDriver<'a, H, DIGEST_LEN>
458{
459    /// Setup and run the HMAC hardware
460    ///
461    /// We expect userspace to setup buffers for the key, data and digest.
462    /// These buffers must be allocated and specified to the kernel from the
463    /// above allow calls.
464    ///
465    /// We expect userspace not to change the value while running. If userspace
466    /// changes the value we have no guarantee of what is passed to the
467    /// hardware. This isn't a security issue, it will just prove the requesting
468    /// app with invalid data.
469    ///
470    /// The driver will take care of clearing data from the underlying implementation
471    /// by calling the `clear_data()` function when the `hash_complete()` callback
472    /// is called or if an error is encountered.
473    ///
474    /// ### `command_num`
475    ///
476    /// - `0`: set_algorithm
477    /// - `1`: run
478    /// - `2`: update
479    /// - `3`: finish
480    fn command(
481        &self,
482        command_num: usize,
483        data1: usize,
484        _data2: usize,
485        processid: ProcessId,
486    ) -> CommandReturn {
487        let match_or_empty_or_nonexistant = self.processid.map_or(true, |owning_app| {
488            // We have recorded that an app has ownership of the HMAC.
489
490            // If the HMAC is still active, then we need to wait for the operation
491            // to finish and the app, whether it exists or not (it may have crashed),
492            // still owns this capsule. If the HMAC is not active, then
493            // we need to verify that that application still exists, and remove
494            // it as owner if not.
495            if self.active.get() {
496                owning_app == processid
497            } else {
498                // Check the app still exists.
499                //
500                // If the `.enter()` succeeds, then the app is still valid, and
501                // we can check if the owning app matches the one that called
502                // the command. If the `.enter()` fails, then the owning app no
503                // longer exists and we return `true` to signify the
504                // "or_nonexistant" case.
505                self.apps
506                    .enter(owning_app, |_, _| owning_app == processid)
507                    .unwrap_or(true)
508            }
509        });
510
511        let app_match = self.processid.map_or(false, |owning_app| {
512            // We have recorded that an app has ownership of the HMAC.
513
514            // If the HMAC is still active, then we need to wait for the operation
515            // to finish and the app, whether it exists or not (it may have crashed),
516            // still owns this capsule. If the HMAC is not active, then
517            // we need to verify that that application still exists, and remove
518            // it as owner if not.
519            if self.active.get() {
520                owning_app == processid
521            } else {
522                // Check the app still exists.
523                //
524                // If the `.enter()` succeeds, then the app is still valid, and
525                // we can check if the owning app matches the one that called
526                // the command. If the `.enter()` fails, then the owning app no
527                // longer exists and we return `true` to signify the
528                // "or_nonexistant" case.
529                self.apps
530                    .enter(owning_app, |_, _| owning_app == processid)
531                    .unwrap_or(true)
532            }
533        });
534
535        // Try the commands where we want to start an operation *not* entered in
536        // an app grant first.
537        if match_or_empty_or_nonexistant
538            && (command_num == 1 || command_num == 2 || command_num == 4)
539        {
540            self.processid.set(processid);
541
542            let _ = self.apps.enter(processid, |app, _| {
543                if command_num == 1 {
544                    // run
545                    // Use key and data to compute hash
546                    // This will trigger a callback once the digest is generated
547                    app.op.set(Some(UserSpaceOp::Run));
548                } else if command_num == 2 {
549                    // update
550                    // Input key and data, don't compute final hash yet
551                    // This will trigger a callback once the data has been added.
552                    app.op.set(Some(UserSpaceOp::Update));
553                } else if command_num == 4 {
554                    // verify
555                    // Use key and data to compute hash and compare it against
556                    // the digest
557                    app.op.set(Some(UserSpaceOp::Verify));
558                }
559            });
560
561            return if let Err(e) = self.run() {
562                self.sha.clear_data();
563                self.processid.clear();
564                self.check_queue();
565                CommandReturn::failure(e)
566            } else {
567                CommandReturn::success()
568            };
569        }
570
571        self.apps
572            .enter(processid, |app, kernel_data| {
573                match command_num {
574                    // set_algorithm
575                    0 => {
576                        match data1 {
577                            // SHA256
578                            0 => {
579                                app.sha_operation = Some(ShaOperation::Sha256);
580                                CommandReturn::success()
581                            }
582                            // SHA384
583                            1 => {
584                                app.sha_operation = Some(ShaOperation::Sha384);
585                                CommandReturn::success()
586                            }
587                            // SHA512
588                            2 => {
589                                app.sha_operation = Some(ShaOperation::Sha512);
590                                CommandReturn::success()
591                            }
592                            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
593                        }
594                    }
595
596                    // run
597                    1 => {
598                        // There is an active app, so queue this request (if possible).
599                        if app.pending_run_app.is_some() {
600                            // No more room in the queue, nowhere to store this
601                            // request.
602                            CommandReturn::failure(ErrorCode::NOMEM)
603                        } else {
604                            // We can store this, so lets do it.
605                            app.pending_run_app = Some(processid);
606                            app.op.set(Some(UserSpaceOp::Run));
607                            CommandReturn::success()
608                        }
609                    }
610
611                    // update
612                    2 => {
613                        // There is an active app, so queue this request (if possible).
614                        if app.pending_run_app.is_some() {
615                            // No more room in the queue, nowhere to store this
616                            // request.
617                            CommandReturn::failure(ErrorCode::NOMEM)
618                        } else {
619                            // We can store this, so lets do it.
620                            app.pending_run_app = Some(processid);
621                            app.op.set(Some(UserSpaceOp::Update));
622                            CommandReturn::success()
623                        }
624                    }
625
626                    // finish
627                    // Compute final hash yet, useful after a update command
628                    3 => {
629                        if app_match {
630                            if let Err(e) = self.calculate_digest() {
631                                kernel_data
632                                    .schedule_upcall(0, (into_statuscode(e.into()), 0, 0))
633                                    .ok();
634                            }
635                            CommandReturn::success()
636                        } else {
637                            // We don't queue this request, the user has to call
638                            // `update` first.
639                            CommandReturn::failure(ErrorCode::OFF)
640                        }
641                    }
642
643                    // verify
644                    4 => {
645                        // There is an active app, so queue this request (if possible).
646                        if app.pending_run_app.is_some() {
647                            // No more room in the queue, nowhere to store this
648                            // request.
649                            CommandReturn::failure(ErrorCode::NOMEM)
650                        } else {
651                            // We can store this, so lets do it.
652                            app.pending_run_app = Some(processid);
653                            app.op.set(Some(UserSpaceOp::Verify));
654                            CommandReturn::success()
655                        }
656                    }
657
658                    // verify_finish
659                    // Use key and data to compute hash and compare it against
660                    // the digest, useful after a update command
661                    5 => {
662                        if app_match {
663                            let _ = kernel_data
664                                .get_readonly_processbuffer(ro_allow::COMPARE)
665                                .and_then(|compare| {
666                                    compare.enter(|compare| {
667                                        let mut static_buffer_len = 0;
668                                        self.dest_buffer.map(|buf| {
669                                            // Determine the size of the static buffer we have
670                                            static_buffer_len = buf.len();
671
672                                            if static_buffer_len > compare.len() {
673                                                static_buffer_len = compare.len()
674                                            }
675
676                                            self.data_copied.set(static_buffer_len);
677
678                                            // Copy the data into the static buffer
679                                            compare[..static_buffer_len]
680                                                .copy_to_slice(&mut buf[..static_buffer_len]);
681                                        });
682                                    })
683                                });
684
685                            if let Err(e) = self.verify_digest() {
686                                kernel_data
687                                    .schedule_upcall(1, (into_statuscode(e.into()), 0, 0))
688                                    .ok();
689                            }
690                            CommandReturn::success()
691                        } else {
692                            // We don't queue this request, the user has to call
693                            // `update` first.
694                            CommandReturn::failure(ErrorCode::OFF)
695                        }
696                    }
697
698                    // default
699                    _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
700                }
701            })
702            .unwrap_or_else(|err| err.into())
703    }
704
705    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
706        self.apps.enter(processid, |_, _| {})
707    }
708}
709
710#[derive(Copy, Clone, PartialEq)]
711enum UserSpaceOp {
712    Run,
713    Update,
714    Verify,
715}
716
717#[derive(Default)]
718pub struct App {
719    pending_run_app: Option<ProcessId>,
720    sha_operation: Option<ShaOperation>,
721    op: Cell<Option<UserSpaceOp>>,
722}