capsules_extra/tutorials/encryption_oracle_chkpt4.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;
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 unimplemented!()
306 }
307}