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}