capsules_extra/tutorials/
encryption_oracle_chkpt5.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
5use core::cell::Cell;
6
7use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
8use kernel::hil::symmetric_encryption::{AES128Ctr, Client, AES128, AES128_BLOCK_SIZE};
9use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
10use kernel::syscall::{CommandReturn, SyscallDriver};
11use kernel::utilities::cells::{OptionalCell, TakeCell};
12use kernel::ErrorCode;
13use kernel::ProcessId;
14
15pub const DRIVER_NUM: usize = 0x99999;
16
17pub static KEY: &[u8; kernel::hil::symmetric_encryption::AES128_KEY_SIZE] = b"InsecureAESKey12";
18
19#[derive(Default)]
20pub struct ProcessState {
21    request_pending: bool,
22    offset: usize,
23}
24
25/// Ids for subscribe upcalls
26mod upcall {
27    pub const DONE: usize = 0;
28    /// The number of subscribe upcalls the kernel stores for this grant
29    pub const COUNT: u8 = 1;
30}
31
32/// Ids for read-only allow buffers
33mod ro_allow {
34    pub const IV: usize = 0;
35    pub const SOURCE: usize = 1;
36    /// The number of allow buffers the kernel stores for this grant
37    pub const COUNT: u8 = 2;
38}
39
40/// Ids for read-write allow buffers
41mod rw_allow {
42    pub const DEST: usize = 0;
43    /// The number of allow buffers the kernel stores for this grant
44    pub const COUNT: u8 = 1;
45}
46
47pub struct EncryptionOracleDriver<'a, A: AES128<'a> + AES128Ctr> {
48    aes: &'a A,
49    process_grants: Grant<
50        ProcessState,
51        UpcallCount<{ upcall::COUNT }>,
52        AllowRoCount<{ ro_allow::COUNT }>,
53        AllowRwCount<{ rw_allow::COUNT }>,
54    >,
55
56    current_process: OptionalCell<ProcessId>,
57    source_buffer: TakeCell<'static, [u8]>,
58    dest_buffer: TakeCell<'static, [u8]>,
59    crypt_len: Cell<usize>,
60}
61
62impl<'a, A: AES128<'a> + AES128Ctr> EncryptionOracleDriver<'a, A> {
63    /// Create a new instance of our encryption oracle userspace driver:
64    pub fn new(
65        aes: &'a A,
66        source_buffer: &'static mut [u8],
67        dest_buffer: &'static mut [u8],
68        process_grants: Grant<
69            ProcessState,
70            UpcallCount<{ upcall::COUNT }>,
71            AllowRoCount<{ ro_allow::COUNT }>,
72            AllowRwCount<{ rw_allow::COUNT }>,
73        >,
74    ) -> Self {
75        EncryptionOracleDriver {
76            process_grants,
77            aes,
78            current_process: OptionalCell::empty(),
79            source_buffer: TakeCell::new(source_buffer),
80            dest_buffer: TakeCell::new(dest_buffer),
81            crypt_len: Cell::new(0),
82        }
83    }
84
85    /// Return a `ProcessId` which has `request_pending` set, if there is some:
86    fn next_pending(&self) -> Option<ProcessId> {
87        for process_grant in self.process_grants.iter() {
88            let processid = process_grant.processid();
89            if process_grant.enter(|grant, _| grant.request_pending) {
90                // The process to which `process_grant` belongs
91                // has a request pending, return its id:
92                return Some(processid);
93            }
94        }
95
96        // No process with `request_pending` found:
97        None
98    }
99
100    /// The run method initiates a new decryption operation or continues an
101    /// existing asynchronous decryption in the context of a process.
102    ///
103    /// If the process-state `offset` is larger or equal to the process-provided
104    /// source or destination buffer size, this indicates that we have completed
105    /// the requested description operation and return an error of
106    /// `ErrorCode::NOMEM`. A caller can use this as a method to check whether
107    /// the descryption operation has finished.
108    ///
109    /// If the process-state `offset` is `0`, we will initialize the AES engine
110    /// with an initialization vector (IV) provided by the application, and
111    /// configure it to perform an AES128-CTR operation.
112    fn run(&self, processid: ProcessId) -> Result<(), ErrorCode> {
113        // If the kernel's source buffer is not present in its TakeCell,
114        // this implies that we're currently running a decryption
115        // operation:
116        if self.source_buffer.is_none() {
117            return Err(ErrorCode::BUSY);
118        }
119
120        self.process_grants
121            .enter(processid, |grant, kernel_data| {
122                // Get a reference to both the application-provided source
123                // and destination buffers:
124                let (source_processbuffer, dest_processbuffer) = kernel_data
125                    .get_readonly_processbuffer(ro_allow::SOURCE)
126                    .and_then(|source| {
127                        kernel_data
128                            .get_readwrite_processbuffer(rw_allow::DEST)
129                            .map(|dest| (source, dest))
130                    })?;
131
132                // Calculate the minimum length of the source & destination
133                // buffers:
134                let min_processbuffer_len =
135                    core::cmp::min(source_processbuffer.len(), dest_processbuffer.len());
136
137                // If our operation-offset stored in the process grant exceeds
138                // `min_buffer_len`, then our operation is finished. Return with
139                // the appropriate error code.
140                //
141                // This check also ensures that we're never running a zero-byte
142                // encryption operation.
143                if grant.offset >= min_processbuffer_len {
144                    return Err(ErrorCode::NOMEM);
145                }
146
147                // Perform some special initialization if this is the first
148                // invocation of our AES engine as part of this decryption
149                // operation:
150                if grant.offset == 0 {
151                    // Offset = 0, initialize the AES engine & IV:
152                    self.aes.enable();
153
154                    // Set the AES engine mode to AES128 CTR (counter) mode, and
155                    // make this a decryption operation:
156                    self.aes.set_mode_aes128ctr(true)?;
157
158                    self.aes.set_key(KEY)?;
159
160                    // Set the initialization vector:
161                    kernel_data
162                        .get_readonly_processbuffer(ro_allow::IV)
163                        .and_then(|iv| {
164                            iv.enter(|iv| {
165                                let mut static_buf =
166                                    [0; kernel::hil::symmetric_encryption::AES128_KEY_SIZE];
167                                // Determine the size of the static buffer we have
168                                let copy_len = core::cmp::min(static_buf.len(), iv.len());
169
170                                // Clear any previous iv
171                                for c in static_buf.iter_mut() {
172                                    *c = 0;
173                                }
174                                // Copy the data into the static buffer
175                                iv[..copy_len].copy_to_slice(&mut static_buf[..copy_len]);
176
177                                AES128::set_iv(self.aes, &static_buf[..copy_len])
178                            })
179                        })??;
180                }
181
182                // Our AES engine works with kernel-provided `&'static mut`
183                // buffers. We copy a chunk of application's source buffer
184                // contents into this kernel buffer:
185                source_processbuffer.enter(|source_processbuffer| {
186                    // Attempt to "take" the kernel-internal source &
187                    // destination buffers from their TakeCells. We expect them
188                    // to be placed back into the TakeCell after an encryption
189                    // operation is done, and checked that the `source_buffer`
190                    // is present above -- thus this should never fail:
191                    let source_buffer = self.source_buffer.take().unwrap();
192                    let dest_buffer = self.dest_buffer.take().unwrap();
193
194                    // Determine the amount of data we pass to the AES engine,
195                    // which is the minimum of the user-provided buffer space
196                    // and our kernel-internal buffer capacity:
197                    let data_len = core::cmp::min(source_buffer.len(), min_processbuffer_len);
198
199                    // Now, copy this data into the kernel-internal buffer:
200                    source_processbuffer[..data_len].copy_to_slice(&mut source_buffer[..data_len]);
201
202                    // However, our AES engine requires us to pass it at least
203                    // `AES128_BLOCK_SIZE` data, and have our data length be a
204                    // multiple of the `AES128_BLOCK_SIZE`. We assume that our
205                    // `source_buffer` holds at least a full AES128 block. Then,
206                    // we can round up or down to a multiple of the
207                    // `AES128_BLOCK_SIZE` as required:
208                    let crypt_len = if data_len < AES128_BLOCK_SIZE {
209                        AES128_BLOCK_SIZE
210                    } else {
211                        data_len - (data_len % AES128_BLOCK_SIZE)
212                    };
213
214                    // Save `crypt_len`, so we know how much data to copy back
215                    // to the process buffer after the operation finished:
216                    self.crypt_len.set(crypt_len);
217
218                    // Set this process as active:
219                    self.current_process.set(processid);
220
221                    // Now, run the operation:
222                    if let Some((e, source, dest)) =
223                        AES128::crypt(self.aes, Some(source_buffer), dest_buffer, 0, crypt_len)
224                    {
225                        // An error occurred, clear the currently active process
226                        // and replace the buffers. Reset the current process'
227                        // offset:
228                        grant.offset = 0;
229                        self.aes.disable();
230                        self.current_process.clear();
231                        self.source_buffer.replace(source.unwrap());
232                        self.dest_buffer.replace(dest);
233                        e
234                    } else {
235                        Ok(())
236                    }
237                })?
238            })
239            .unwrap_or(Err(ErrorCode::RESERVE))
240    }
241
242    fn run_next_pending(&self) {
243        if self.current_process.is_some() {
244            return;
245        }
246
247        while let Some(processid) = self.next_pending() {
248            let res = self.run(processid);
249
250            let _ = self.process_grants.enter(processid, |grant, kernel_data| {
251                grant.request_pending = false;
252
253                if let Err(e) = res {
254                    kernel_data
255                        .schedule_upcall(
256                            upcall::DONE,
257                            (kernel::errorcode::into_statuscode(Err(e)), 0, 0),
258                        )
259                        .ok();
260                }
261            });
262        }
263    }
264}
265
266impl<'a, A: AES128<'a> + AES128Ctr> SyscallDriver for EncryptionOracleDriver<'a, A> {
267    fn command(
268        &self,
269        command_num: usize,
270        _data1: usize,
271        _data2: usize,
272        processid: ProcessId,
273    ) -> CommandReturn {
274        match command_num {
275            // Check whether the driver is present:
276            0 => CommandReturn::success(),
277
278            // Request the decryption operation:
279            1 => {
280                let res = self
281                    .process_grants
282                    .enter(processid, |grant, _kernel_data| {
283                        grant.request_pending = true;
284                        CommandReturn::success()
285                    })
286                    .unwrap_or_else(|err| err.into());
287
288                self.run_next_pending();
289
290                res
291            }
292
293            // Unknown command number, return a NOSUPPORT error
294            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
295        }
296    }
297
298    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
299        self.process_grants.enter(processid, |_, _| {})
300    }
301}
302
303impl<'a, A: AES128<'a> + AES128Ctr> Client<'a> for EncryptionOracleDriver<'a, A> {
304    fn crypt_done(&'a self, mut source: Option<&'static mut [u8]>, destination: &'static mut [u8]) {
305        // One segment of encryption/decryption complete, move to next one or
306        // callback to user if done.
307        //
308        // In either case, place our kernel-internal source buffer back:
309        self.source_buffer
310            .replace(source.take().expect("source should never be None"));
311
312        // Attempt to get a reference to the current process. This should never
313        // be none, given that we've just completed an operation:
314        let processid = self.current_process.unwrap_or_panic();
315
316        // Enter the process' grant, to copy the decrypted data back into the
317        // output processbuffer:
318        let _ = self.process_grants.enter(processid, |grant, kernel_data| {
319            if let Ok(dest_processbuffer) = kernel_data.get_readwrite_processbuffer(rw_allow::DEST)
320            {
321                // We have decrypted `self.crypt_len` bytes, starting
322                // `grant.offset` in the source processbuffer. If the
323                // destination processbuffer does not have enough space,
324                // truncate the data. If the buffer is smaller than the current
325                // offset, don't copy anything.
326                if grant.offset < dest_processbuffer.len() {
327                    let copy_len = core::cmp::min(
328                        self.crypt_len.get(),
329                        dest_processbuffer.len() - grant.offset,
330                    );
331                    let _ = dest_processbuffer.mut_enter(|dest_buffer| {
332                        dest_buffer[grant.offset..(grant.offset + copy_len)]
333                            .copy_from_slice(&destination[..copy_len])
334                    });
335                    grant.offset += copy_len;
336                }
337            }
338        });
339
340        // Place back the kernel-internal dest buffer:
341        self.dest_buffer.replace(destination);
342
343        // Try to continue the decryption operation. This will return an error
344        // of `ErrorCode::NOMEM` if there is no more data to decrypt for the
345        // current process.
346        if let Err(ErrorCode::NOMEM) = self.run(processid) {
347            // We've completed the client's decryption request. Remove its
348            // `request_pending` flag, the `currrent_process` indication,
349            // and schedule an upcall accordingly:
350            self.current_process.clear();
351
352            let _ = self.process_grants.enter(processid, |grant, kernel_data| {
353                grant.offset = 0;
354
355                // Pass the encryption/decryption operation length in the 2nd
356                // upcall argument. This is always the minimum of the app's
357                // provided source and destination buffers:
358                let len = core::cmp::min(
359                    kernel_data
360                        .get_readonly_processbuffer(ro_allow::SOURCE)
361                        .map_or(0, |source| source.len()),
362                    kernel_data
363                        .get_readwrite_processbuffer(rw_allow::DEST)
364                        .map_or(0, |dest| dest.len()),
365                );
366
367                kernel_data.schedule_upcall(upcall::DONE, (0, len, 0)).ok();
368            });
369
370            // Attempt to schedule another operation for a new process:
371            self.run_next_pending();
372        }
373    }
374}