nrf5x/aes.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//! AES128 driver, nRF5X-family
6//!
7//! Provides a simple driver to encrypt and decrypt messages using aes128-ctr
8//! mode on top of aes128-ecb, as well as encrypt with aes128-ecb and
9//! aes128-cbc.
10//!
11//! Roughly, the module uses three buffers with the following content:
12//!
13//! * Key
14//! * Initial counter
15//! * Payload, to be encrypted or decrypted
16//!
17//! ### Key
18//! The key is used for getting a key and configure it in the AES chip
19//!
20//! ### Initial Counter
21//! Counter to be used for aes-ctr and it is entered into AES to generate the
22//! the keystream. After each encryption the initial counter is incremented
23//!
24//! ### Payload
25//! Data to be encrypted or decrypted it is XOR:ed with the generated keystream
26//!
27//! ### Things to highlight that can be improved:
28//!
29//! * ECB_DATA must be a static mut \[u8\] and can't be located in the struct
30//! * PAYLOAD size is restricted to 128 bytes
31//!
32//! Authors
33//! --------
34//! * Niklas Adolfsson <niklasadolfsson1@gmail.com>
35//! * Fredrik Nilsson <frednils@student.chalmers.se>
36//! * Date: April 21, 2017
37
38use core::cell::Cell;
39use core::ptr::addr_of;
40use kernel::hil::symmetric_encryption;
41use kernel::utilities::cells::OptionalCell;
42use kernel::utilities::cells::TakeCell;
43use kernel::utilities::registers::interfaces::{Readable, Writeable};
44use kernel::utilities::registers::{register_bitfields, ReadWrite, WriteOnly};
45use kernel::utilities::StaticRef;
46use kernel::ErrorCode;
47
48// DMA buffer that the aes chip will mutate during encryption
49// Byte 0-15 - Key
50// Byte 16-32 - Payload
51// Byte 33-47 - Ciphertext
52static mut ECB_DATA: [u8; 48] = [0; 48];
53
54#[allow(dead_code)]
55const KEY_START: usize = 0;
56#[allow(dead_code)]
57const KEY_END: usize = 15;
58const PLAINTEXT_START: usize = 16;
59const PLAINTEXT_END: usize = 32;
60#[allow(dead_code)]
61const CIPHERTEXT_START: usize = 33;
62#[allow(dead_code)]
63const CIPHERTEXT_END: usize = 47;
64
65const AESECB_BASE: StaticRef<AesEcbRegisters> =
66 unsafe { StaticRef::new(0x4000E000 as *const AesEcbRegisters) };
67
68#[repr(C)]
69struct AesEcbRegisters {
70 /// Start ECB block encrypt
71 /// - Address 0x000 - 0x004
72 task_startecb: WriteOnly<u32, Task::Register>,
73 /// Abort a possible executing ECB operation
74 /// - Address: 0x004 - 0x008
75 task_stopecb: WriteOnly<u32, Task::Register>,
76 /// Reserved
77 _reserved1: [u32; 62],
78 /// ECB block encrypt complete
79 /// - Address: 0x100 - 0x104
80 event_endecb: ReadWrite<u32, Event::Register>,
81 /// ECB block encrypt aborted because of a STOPECB task or due to an error
82 /// - Address: 0x104 - 0x108
83 event_errorecb: ReadWrite<u32, Event::Register>,
84 /// Reserved
85 _reserved2: [u32; 127],
86 /// Enable interrupt
87 /// - Address: 0x304 - 0x308
88 intenset: ReadWrite<u32, Intenset::Register>,
89 /// Disable interrupt
90 /// - Address: 0x308 - 0x30c
91 intenclr: ReadWrite<u32, Intenclr::Register>,
92 /// Reserved
93 _reserved3: [u32; 126],
94 /// ECB block encrypt memory pointers
95 /// - Address: 0x504 - 0x508
96 ecbdataptr: ReadWrite<u32, EcbDataPointer::Register>,
97}
98
99register_bitfields! [u32,
100 /// Start task
101 Task [
102 ENABLE OFFSET(0) NUMBITS(1)
103 ],
104
105 /// Read event
106 Event [
107 READY OFFSET(0) NUMBITS(1)
108 ],
109
110 /// Enabled interrupt
111 Intenset [
112 ENDECB OFFSET(0) NUMBITS(1),
113 ERRORECB OFFSET(1) NUMBITS(1)
114 ],
115
116 /// Disable interrupt
117 Intenclr [
118 ENDECB OFFSET(0) NUMBITS(1),
119 ERRORECB OFFSET(1) NUMBITS(1)
120 ],
121
122 /// ECB block encrypt memory pointers
123 EcbDataPointer [
124 POINTER OFFSET(0) NUMBITS(32)
125 ]
126];
127
128#[derive(Copy, Clone, Debug)]
129enum AESMode {
130 ECB,
131 CTR,
132 CBC,
133}
134
135pub struct AesECB<'a> {
136 registers: StaticRef<AesEcbRegisters>,
137 mode: Cell<AESMode>,
138 client: OptionalCell<&'a dyn kernel::hil::symmetric_encryption::Client<'a>>,
139 /// Input either plaintext or ciphertext to be encrypted or decrypted.
140 input: TakeCell<'static, [u8]>,
141 output: TakeCell<'static, [u8]>,
142 current_idx: Cell<usize>,
143 start_idx: Cell<usize>,
144 end_idx: Cell<usize>,
145}
146
147impl<'a> AesECB<'a> {
148 pub const fn new() -> AesECB<'a> {
149 AesECB {
150 registers: AESECB_BASE,
151 mode: Cell::new(AESMode::CTR),
152 client: OptionalCell::empty(),
153 input: TakeCell::empty(),
154 output: TakeCell::empty(),
155 current_idx: Cell::new(0),
156 start_idx: Cell::new(0),
157 end_idx: Cell::new(0),
158 }
159 }
160
161 fn set_dma(&self) {
162 self.registers.ecbdataptr.set(addr_of!(ECB_DATA) as u32);
163 }
164
165 /// Verify that the provided start and stop indices work with the given
166 /// buffers.
167 fn try_set_indices(&self, start_index: usize, stop_index: usize) -> bool {
168 stop_index.checked_sub(start_index).is_some_and(|sublen| {
169 sublen % symmetric_encryption::AES128_BLOCK_SIZE == 0 && {
170 self.input.map_or_else(
171 || {
172 // The destination buffer is also the input
173 if self.output.map_or(false, |dest| stop_index <= dest.len()) {
174 self.current_idx.set(0);
175 self.start_idx.set(start_index);
176 self.end_idx.set(stop_index);
177 true
178 } else {
179 false
180 }
181 },
182 |source| {
183 if sublen == source.len()
184 && self.output.map_or(false, |dest| stop_index <= dest.len())
185 {
186 // We will start writing to the AES from the
187 // beginning of `source`, and end at its end
188 self.current_idx.set(0);
189
190 // We will start reading from the AES into `dest` at
191 // `start_index`, and continue until `stop_index`
192 self.start_idx.set(start_index);
193 self.end_idx.set(stop_index);
194 true
195 } else {
196 false
197 }
198 },
199 )
200 }
201 })
202 }
203
204 // FIXME: should this be performed in constant time i.e. skip the break part
205 // and always loop 16 times?
206 fn update_ctr(&self) {
207 for i in (PLAINTEXT_START..PLAINTEXT_END).rev() {
208 unsafe {
209 ECB_DATA[i] += 1;
210 if ECB_DATA[i] != 0 {
211 break;
212 }
213 }
214 }
215 }
216
217 /// Get the relevant positions of our input data whether we are using a
218 /// source buffer or overwriting the destination buffer.
219 fn get_start_end_take(&self) -> (usize, usize, usize) {
220 let current_idx = self.current_idx.get();
221
222 // Location in the appropriate source buffer we are currently working
223 // on.
224 let start = current_idx + self.input.map_or(self.start_idx.get(), |_| 0);
225 // Last index in the appropriate source buffer we need to work on.
226 let end = self.end_idx.get() - self.input.map_or(0, |_| self.start_idx.get());
227
228 // Get the number of bytes that were used in the keystream/block.
229 let take = match end.checked_sub(start) {
230 Some(v) if v > symmetric_encryption::AES128_BLOCK_SIZE => {
231 symmetric_encryption::AES128_BLOCK_SIZE
232 }
233 Some(v) => v,
234 None => 0,
235 };
236
237 (start, end, take)
238 }
239
240 fn copy_plaintext(&self) {
241 let (start, _end, take) = self.get_start_end_take();
242
243 // Copy the plaintext either from the source if it exists or from the
244 // destination buffer.
245 if take > 0 {
246 match self.mode.get() {
247 AESMode::ECB => {
248 self.input.map_or_else(
249 || {
250 self.output.map(|output| {
251 for i in 0..take {
252 // Copy into static mut DMA buffer
253 unsafe {
254 ECB_DATA[i + PLAINTEXT_START] = output[i + start];
255 }
256 }
257 });
258 },
259 |input| {
260 for i in 0..take {
261 // Copy into static mut DMA buffer
262 unsafe {
263 ECB_DATA[i + PLAINTEXT_START] = input[i + start];
264 }
265 }
266 },
267 );
268 }
269
270 AESMode::CBC => {
271 self.input.map_or_else(
272 || {
273 self.output.map(|output| {
274 for i in 0..take {
275 let ecb_idx = i + PLAINTEXT_START;
276
277 // Copy into static mut DMA buffer
278 unsafe {
279 ECB_DATA[ecb_idx] ^= output[i + start];
280 }
281 }
282 });
283 },
284 |input| {
285 for i in 0..take {
286 let ecb_idx = i + PLAINTEXT_START;
287 // Copy into static mut DMA buffer
288 unsafe {
289 ECB_DATA[ecb_idx] ^= input[i + start];
290 }
291 }
292 },
293 );
294 }
295
296 AESMode::CTR => {
297 // no copying plaintext in ctr mode
298 }
299 }
300 }
301 }
302
303 fn crypt(&self) {
304 match self.mode.get() {
305 AESMode::CTR => {}
306 AESMode::ECB => {
307 // Need to copy the plaintext to the ECB buffer.
308 self.copy_plaintext();
309 }
310 AESMode::CBC => {
311 self.copy_plaintext();
312 }
313 }
314
315 self.registers.event_endecb.write(Event::READY::CLEAR);
316 self.registers.task_startecb.set(1);
317
318 self.enable_interrupts();
319 }
320
321 /// AesEcb Interrupt handler
322 pub fn handle_interrupt(&self) {
323 // disable interrupts
324 self.disable_interrupts();
325
326 if self.registers.event_endecb.get() == 1 {
327 let (start, end, take) = self.get_start_end_take();
328 let start_idx = self.start_idx.get();
329 let current_idx = self.current_idx.get();
330
331 match self.mode.get() {
332 AESMode::CTR => {
333 // Fill in the ciphertext in the output buffer.
334 if take > 0 {
335 self.input.map_or_else(
336 || {
337 // No input buffer, so source data comes from
338 // output buffer.
339 self.output.map(|output| {
340 for i in 0..take {
341 let in_byte = output[start + i];
342 let keystream_byte = unsafe { ECB_DATA[i + PLAINTEXT_END] };
343
344 output[start + i] = keystream_byte ^ in_byte;
345 }
346 });
347 },
348 |input| {
349 self.output.map(|output| {
350 let start_idx = self.start_idx.get();
351
352 for i in 0..take {
353 let in_byte = input[start + i];
354 let keystream_byte = unsafe { ECB_DATA[i + PLAINTEXT_END] };
355
356 output[start_idx + current_idx + i] =
357 keystream_byte ^ in_byte;
358 }
359 });
360 },
361 );
362
363 self.update_ctr();
364 }
365 }
366
367 AESMode::ECB => {
368 // Copy ciphertext to output.
369 if take > 0 {
370 self.output.map(|output| {
371 for i in 0..take {
372 // We write to the buffer starting at the
373 // originally provided start index, plus our
374 // offset at current_idx.
375 let dest_idx = start_idx + current_idx + i;
376 // Copy out of static mut DMA buffer
377 unsafe {
378 output[dest_idx] = ECB_DATA[i + PLAINTEXT_END];
379 }
380 }
381 });
382 }
383 }
384 AESMode::CBC => {
385 // Copy ciphertext to both output AND the ECB payload to use
386 // on the next iteration.
387 if take > 0 {
388 self.output.map(|output| {
389 for i in 0..take {
390 // We write to the buffer starting at the
391 // originally provided start index, plus our
392 // offset at current_idx.
393 let dest_idx = start_idx + current_idx + i;
394 // Copy out of static mut DMA buffer
395 unsafe {
396 output[dest_idx] = ECB_DATA[i + PLAINTEXT_END];
397 ECB_DATA[i + PLAINTEXT_START] = ECB_DATA[i + PLAINTEXT_END];
398 }
399 }
400 });
401 }
402 }
403 }
404
405 // Advance through the buffer.
406 self.current_idx.set(current_idx + take);
407
408 // Check if we are done or if we need to crypt another block.
409 if start + take < end {
410 // More to do.
411 self.crypt();
412 } else {
413 self.output.take().map(|output| {
414 self.client
415 .map(move |client| client.crypt_done(self.input.take(), output));
416 });
417 }
418 }
419 }
420
421 fn enable_interrupts(&self) {
422 self.registers
423 .intenset
424 .write(Intenset::ENDECB::SET + Intenset::ERRORECB::SET);
425 }
426
427 fn disable_interrupts(&self) {
428 self.registers
429 .intenclr
430 .write(Intenclr::ENDECB::SET + Intenclr::ERRORECB::SET);
431 }
432}
433
434impl<'a> kernel::hil::symmetric_encryption::AES128<'a> for AesECB<'a> {
435 fn enable(&self) {
436 self.set_dma();
437 }
438
439 fn disable(&self) {
440 self.registers.task_stopecb.write(Task::ENABLE::CLEAR);
441 self.disable_interrupts();
442 }
443
444 fn set_client(&'a self, client: &'a dyn symmetric_encryption::Client<'a>) {
445 self.client.set(client);
446 }
447
448 fn set_key(&self, key: &[u8]) -> Result<(), ErrorCode> {
449 if key.len() != symmetric_encryption::AES128_KEY_SIZE {
450 Err(ErrorCode::INVAL)
451 } else {
452 for (i, c) in key.iter().enumerate() {
453 unsafe {
454 ECB_DATA[i] = *c;
455 }
456 }
457 Ok(())
458 }
459 }
460
461 fn set_iv(&self, iv: &[u8]) -> Result<(), ErrorCode> {
462 if iv.len() != symmetric_encryption::AES128_BLOCK_SIZE {
463 Err(ErrorCode::INVAL)
464 } else {
465 for (i, c) in iv.iter().enumerate() {
466 unsafe {
467 ECB_DATA[i + PLAINTEXT_START] = *c;
468 }
469 }
470 Ok(())
471 }
472 }
473
474 // not needed by NRF5x
475 fn start_message(&self) {}
476
477 fn crypt(
478 &self,
479 source: Option<&'static mut [u8]>,
480 dest: &'static mut [u8],
481 start_index: usize,
482 stop_index: usize,
483 ) -> Option<(
484 Result<(), ErrorCode>,
485 Option<&'static mut [u8]>,
486 &'static mut [u8],
487 )> {
488 self.input.put(source);
489 self.output.replace(dest);
490 if self.try_set_indices(start_index, stop_index) {
491 self.crypt();
492 None
493 } else {
494 Some((
495 Err(ErrorCode::INVAL),
496 self.input.take(),
497 self.output.take().unwrap(),
498 ))
499 }
500 }
501}
502
503impl kernel::hil::symmetric_encryption::AES128ECB for AesECB<'_> {
504 // not needed by NRF5x (the configuration is the same for encryption and decryption)
505 fn set_mode_aes128ecb(&self, encrypting: bool) -> Result<(), ErrorCode> {
506 if encrypting {
507 self.mode.set(AESMode::ECB);
508 Ok(())
509 } else {
510 Err(ErrorCode::INVAL)
511 }
512 }
513}
514
515impl kernel::hil::symmetric_encryption::AES128Ctr for AesECB<'_> {
516 // not needed by NRF5x (the configuration is the same for encryption and decryption)
517 fn set_mode_aes128ctr(&self, _encrypting: bool) -> Result<(), ErrorCode> {
518 self.mode.set(AESMode::CTR);
519 Ok(())
520 }
521}
522
523impl kernel::hil::symmetric_encryption::AES128CBC for AesECB<'_> {
524 fn set_mode_aes128cbc(&self, encrypting: bool) -> Result<(), ErrorCode> {
525 if encrypting {
526 self.mode.set(AESMode::CBC);
527 Ok(())
528 } else {
529 Err(ErrorCode::INVAL)
530 }
531 }
532}
533
534//TODO: replace this placeholder with a proper implementation of the AES system
535impl<'a> kernel::hil::symmetric_encryption::AES128CCM<'a> for AesECB<'a> {
536 /// Set the client instance which will receive `crypt_done()` callbacks
537 fn set_client(&'a self, _client: &'a dyn kernel::hil::symmetric_encryption::CCMClient) {}
538
539 /// Set the key to be used for CCM encryption
540 fn set_key(&self, _key: &[u8]) -> Result<(), ErrorCode> {
541 Ok(())
542 }
543
544 /// Set the nonce (length NONCE_LENGTH) to be used for CCM encryption
545 fn set_nonce(&self, _nonce: &[u8]) -> Result<(), ErrorCode> {
546 Ok(())
547 }
548
549 /// Try to begin the encryption/decryption process
550 fn crypt(
551 &self,
552 _buf: &'static mut [u8],
553 _a_off: usize,
554 _m_off: usize,
555 _m_len: usize,
556 _mic_len: usize,
557 _confidential: bool,
558 _encrypting: bool,
559 ) -> Result<(), (ErrorCode, &'static mut [u8])> {
560 Ok(())
561 }
562}