kernel/hil/symmetric_encryption.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// Copyright OxidOS Automotive 2026.
5
6//! Interface for symmetric-cipher encryption
7//!
8//! see boards/imix/src/aes_test.rs for example usage
9
10use crate::ErrorCode;
11
12/// Implement this trait and use `set_client()` in order to receive callbacks from an `AES`
13/// instance.
14pub trait Client<'a> {
15 fn crypt_done(&'a self, source: Option<&'static mut [u8]>, dest: &'static mut [u8]);
16}
17
18/// The number of bytes used for AES block operations. Keys and IVs must have this length,
19/// and encryption/decryption inputs must be have a multiple of this length.
20pub const AES_BLOCK_SIZE: usize = 16;
21pub const AES128_KEY_SIZE: usize = 16;
22pub const AES192_KEY_SIZE: usize = 24;
23pub const AES256_KEY_SIZE: usize = 32;
24
25pub trait AESKeySize {
26 const LENGTH: usize;
27}
28
29pub struct AES128;
30impl AESKeySize for AES128 {
31 const LENGTH: usize = 16;
32}
33pub struct AES192;
34impl AESKeySize for AES192 {
35 const LENGTH: usize = 24;
36}
37
38pub struct AES256;
39impl AESKeySize for AES256 {
40 const LENGTH: usize = 32;
41}
42
43pub trait AES<'a, K: AESKeySize> {
44 /// Enable the AES hardware.
45 /// Must be called before any other methods
46 fn enable(&self);
47
48 /// Disable the AES hardware
49 fn disable(&self);
50
51 /// Set the client instance which will receive `crypt_done()` callbacks
52 fn set_client(&'a self, client: &'a dyn Client<'a>);
53
54 /// Set the encryption key.
55 /// Returns `INVAL` if length is not `AESKeySize::LENGTH`
56 fn set_key(&self, key: &[u8]) -> Result<(), ErrorCode>;
57
58 /// Set the IV (or initial counter).
59 /// Returns `INVAL` if length is not `AES_BLOCK_SIZE`
60 fn set_iv(&self, iv: &[u8]) -> Result<(), ErrorCode>;
61
62 /// Begin a new message (with the configured IV) when `crypt()` is
63 /// next called. Multiple calls to `crypt()` may be made between
64 /// calls to `start_message()`, allowing the encryption context to
65 /// extend over non-contiguous extents of data.
66 ///
67 /// If an encryption operation is in progress, this method instead
68 /// has no effect.
69 fn start_message(&self);
70
71 /// Request an encryption/decryption
72 ///
73 /// If the source buffer is not `None`, the encryption input
74 /// will be that entire buffer. Otherwise the destination buffer
75 /// at indices between `start_index` and `stop_index` will
76 /// provide the input, which will be overwritten.
77 ///
78 /// If `None` is returned, the client's `crypt_done` method will eventually
79 /// be called, and the portion of the data buffer between `start_index`
80 /// and `stop_index` will hold the result of the encryption/decryption.
81 ///
82 /// If `Some(result, source, dest)` is returned, `result` is the
83 /// error condition and `source` and `dest` are the buffers that
84 /// were passed to `crypt`.
85 ///
86 /// The indices `start_index` and `stop_index` must be valid
87 /// offsets in the destination buffer, and the length
88 /// `stop_index - start_index` must be a multiple of
89 /// `AES_BLOCK_SIZE`. Otherwise, `Some(INVAL, ...)` will be
90 /// returned.
91 ///
92 /// If the source buffer is not `None`, its length must be
93 /// `stop_index - start_index`. Otherwise, `Some(INVAL, ...)`
94 /// will be returned.
95 ///
96 /// If an encryption operation is already in progress,
97 /// `Some(BUSY, ...)` will be returned.
98 ///
99 /// For correct operation, the methods `set_key` and `set_iv` must have
100 /// previously been called to set the buffers containing the
101 /// key and the IV (or initial counter value), and a method `set_mode_*()`
102 /// must have been called to set the desired mode. These settings persist
103 /// across calls to `crypt()`.
104 ///
105 fn crypt(
106 &self,
107 source: Option<&'static mut [u8]>,
108 dest: &'static mut [u8],
109 start_index: usize,
110 stop_index: usize,
111 ) -> Option<(
112 Result<(), ErrorCode>,
113 Option<&'static mut [u8]>,
114 &'static mut [u8],
115 )>;
116}
117
118pub trait AESCtr {
119 /// Call before `AES::crypt()` to perform AESCtr
120 fn set_mode_aesctr(&self, encrypting: bool) -> Result<(), ErrorCode>;
121}
122
123pub trait AESCBC {
124 /// Call before `AES::crypt()` to perform AESCBC
125 fn set_mode_aescbc(&self, encrypting: bool) -> Result<(), ErrorCode>;
126}
127
128pub trait AESECB {
129 /// Call before `AES::crypt()` to perform AESECB
130 fn set_mode_aesecb(&self, encrypting: bool) -> Result<(), ErrorCode>;
131}
132
133pub trait CCMClient {
134 /// `res` is Ok(()) if the encryption/decryption process succeeded. This
135 /// does not mean that the message has been verified in the case of
136 /// decryption.
137 /// If we are encrypting: `tag_is_valid` is `true` iff `res` is Ok(()).
138 /// If we are decrypting: `tag_is_valid` is `true` iff `res` is Ok(()) and the
139 /// message authentication tag is valid.
140 fn crypt_done(&self, buf: &'static mut [u8], res: Result<(), ErrorCode>, tag_is_valid: bool);
141}
142
143pub const CCM_NONCE_LENGTH: usize = 13;
144
145pub trait AESCCM<'a, K: AESKeySize> {
146 /// Set the client instance which will receive `crypt_done()` callbacks
147 fn set_client(&'a self, client: &'a dyn CCMClient);
148
149 /// Set the key to be used for CCM encryption
150 fn set_key(&self, key: &[u8]) -> Result<(), ErrorCode>;
151
152 /// Set the nonce (length NONCE_LENGTH) to be used for CCM encryption
153 fn set_nonce(&self, nonce: &[u8]) -> Result<(), ErrorCode>;
154
155 /// Try to begin the encryption/decryption process
156 fn crypt(
157 &self,
158 buf: &'static mut [u8],
159 a_off: usize,
160 m_off: usize,
161 m_len: usize,
162 mic_len: usize,
163 confidential: bool,
164 encrypting: bool,
165 ) -> Result<(), (ErrorCode, &'static mut [u8])>;
166}
167
168pub trait GCMClient {
169 /// `res` is Ok(()) if the encryption/decryption process succeeded. This
170 /// does not mean that the message has been verified in the case of
171 /// decryption.
172 /// If we are encrypting: `tag_is_valid` is `true` iff `res` is Ok(()).
173 /// If we are decrypting: `tag_is_valid` is `true` iff `res` is Ok(()) and the
174 /// message authentication tag is valid.
175 fn crypt_done(&self, buf: &'static mut [u8], res: Result<(), ErrorCode>, tag_is_valid: bool);
176}
177
178pub trait AESGCM<'a, K: AESKeySize> {
179 /// Set the client instance which will receive `crypt_done()` callbacks
180 fn set_client(&'a self, client: &'a dyn GCMClient);
181
182 /// Set the key to be used for GCM encryption
183 /// Returns `INVAL` if length is not `AESKeySize::LENGTH`
184 fn set_key(&self, key: &[u8]) -> Result<(), ErrorCode>;
185
186 /// Set the IV to be used for GCM encryption. The IV should be less
187 /// or equal to 12 bytes (96 bits) as recommened in NIST-800-38D.
188 /// Returns `INVAL` if length is greater then 12 bytes
189 fn set_iv(&self, nonce: &[u8]) -> Result<(), ErrorCode>;
190
191 /// Try to begin the encryption/decryption process
192 /// The possible ErrorCodes are:
193 /// - `BUSY`: An operation is already in progress
194 /// - `SIZE`: The offset and lengths don't fit inside the buffer
195 fn crypt(
196 &self,
197 buf: &'static mut [u8],
198 aad_offset: usize,
199 message_offset: usize,
200 message_len: usize,
201 tag_len: usize,
202 encrypting: bool,
203 ) -> Result<(), (ErrorCode, &'static mut [u8])>;
204}