1 /*
2 * Copyright (c) 2023 Basalte bv
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_mcux_dcp
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(mcux_dcp, CONFIG_CRYPTO_LOG_LEVEL);
11
12 #include <errno.h>
13 #include <string.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/cache.h>
16 #include <zephyr/crypto/crypto.h>
17 #include <zephyr/sys/util.h>
18
19 #include <fsl_dcp.h>
20
21 #define CRYPTO_DCP_CIPHER_CAPS (CAP_RAW_KEY | CAP_SEPARATE_IO_BUFS |\
22 CAP_SYNC_OPS | CAP_NO_IV_PREFIX)
23 #define CRYPTO_DCP_HASH_CAPS (CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS)
24
25 struct crypto_dcp_session {
26 dcp_handle_t handle;
27 dcp_hash_ctx_t hash_ctx;
28 bool in_use;
29 };
30
31 struct crypto_dcp_config {
32 DCP_Type *base;
33 };
34
35 struct crypto_dcp_data {
36 struct crypto_dcp_session sessions[CONFIG_CRYPTO_MCUX_DCP_MAX_SESSION];
37 };
38
39 /* Helper function to convert common FSL error status codes to errno codes */
fsl_to_errno(status_t status)40 static inline int fsl_to_errno(status_t status)
41 {
42 switch (status) {
43 case kStatus_Success:
44 return 0;
45 case kStatus_InvalidArgument:
46 return -EINVAL;
47 case kStatus_Timeout:
48 return -EAGAIN;
49 }
50
51 return -1;
52 }
53
get_session(const struct device * dev)54 static struct crypto_dcp_session *get_session(const struct device *dev)
55 {
56 struct crypto_dcp_data *data = dev->data;
57
58 for (size_t i = 0; i < CONFIG_CRYPTO_MCUX_DCP_MAX_SESSION; ++i) {
59 if (!data->sessions[i].in_use) {
60 data->sessions[i].in_use = true;
61
62 return &data->sessions[i];
63 }
64 }
65
66 return NULL;
67 }
68
free_session(struct crypto_dcp_session * session)69 static inline void free_session(struct crypto_dcp_session *session)
70 {
71 session->in_use = false;
72 }
73
crypto_dcp_query_hw_caps(const struct device * dev)74 static int crypto_dcp_query_hw_caps(const struct device *dev)
75 {
76 ARG_UNUSED(dev);
77
78 return CRYPTO_DCP_CIPHER_CAPS | CRYPTO_DCP_HASH_CAPS;
79 }
80
crypto_dcp_aes_cbc_encrypt(struct cipher_ctx * ctx,struct cipher_pkt * pkt,uint8_t * iv)81 static int crypto_dcp_aes_cbc_encrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt, uint8_t *iv)
82 {
83 const struct crypto_dcp_config *cfg = ctx->device->config;
84 struct crypto_dcp_session *session = ctx->drv_sessn_state;
85 status_t status;
86 size_t iv_bytes;
87 uint8_t *p_iv, iv_loc[16];
88
89 if ((ctx->flags & CAP_NO_IV_PREFIX) == 0U) {
90 /* Prefix IV to ciphertext, which is default behavior of Zephyr
91 * crypto API, unless CAP_NO_IV_PREFIX is requested.
92 */
93 iv_bytes = 16U;
94 memcpy(pkt->out_buf, iv, 16U);
95 p_iv = iv;
96 } else {
97 iv_bytes = 0U;
98 memcpy(iv_loc, iv, 16U);
99 p_iv = iv_loc;
100 }
101
102 sys_cache_data_disable();
103 status = DCP_AES_EncryptCbc(cfg->base, &session->handle, pkt->in_buf,
104 pkt->out_buf + iv_bytes, pkt->in_len, p_iv);
105 sys_cache_data_enable();
106
107 if (status != kStatus_Success) {
108 return fsl_to_errno(status);
109 }
110
111 pkt->out_len = pkt->in_len + iv_bytes;
112
113 return 0;
114 }
115
crypto_dcp_aes_cbc_decrypt(struct cipher_ctx * ctx,struct cipher_pkt * pkt,uint8_t * iv)116 static int crypto_dcp_aes_cbc_decrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt, uint8_t *iv)
117 {
118 const struct crypto_dcp_config *cfg = ctx->device->config;
119 struct crypto_dcp_session *session = ctx->drv_sessn_state;
120 status_t status;
121 size_t iv_bytes;
122 uint8_t *p_iv, iv_loc[16];
123
124 if ((ctx->flags & CAP_NO_IV_PREFIX) == 0U) {
125 iv_bytes = 16U;
126 p_iv = iv;
127 } else {
128 iv_bytes = 0U;
129 memcpy(iv_loc, iv, 16U);
130 p_iv = iv_loc;
131 }
132
133 sys_cache_data_disable();
134 status = DCP_AES_DecryptCbc(cfg->base, &session->handle, pkt->in_buf + iv_bytes,
135 pkt->out_buf, pkt->in_len, p_iv);
136 sys_cache_data_enable();
137
138 if (status != kStatus_Success) {
139 return fsl_to_errno(status);
140 }
141
142 pkt->out_len = pkt->in_len - iv_bytes;
143
144 return 0;
145 }
146
crypto_dcp_aes_ecb_encrypt(struct cipher_ctx * ctx,struct cipher_pkt * pkt)147 static int crypto_dcp_aes_ecb_encrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt)
148 {
149 const struct crypto_dcp_config *cfg = ctx->device->config;
150 struct crypto_dcp_session *session = ctx->drv_sessn_state;
151 status_t status;
152
153 sys_cache_data_disable();
154 status = DCP_AES_EncryptEcb(cfg->base, &session->handle, pkt->in_buf, pkt->out_buf,
155 pkt->in_len);
156 sys_cache_data_enable();
157
158 if (status != kStatus_Success) {
159 return fsl_to_errno(status);
160 }
161
162 pkt->out_len = pkt->in_len;
163
164 return 0;
165 }
166
crypto_dcp_aes_ecb_decrypt(struct cipher_ctx * ctx,struct cipher_pkt * pkt)167 static int crypto_dcp_aes_ecb_decrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt)
168 {
169 const struct crypto_dcp_config *cfg = ctx->device->config;
170 struct crypto_dcp_session *session = ctx->drv_sessn_state;
171 status_t status;
172
173 sys_cache_data_disable();
174 status = DCP_AES_DecryptEcb(cfg->base, &session->handle, pkt->in_buf, pkt->out_buf,
175 pkt->in_len);
176 sys_cache_data_enable();
177
178 if (status != kStatus_Success) {
179 return fsl_to_errno(status);
180 }
181
182 pkt->out_len = pkt->in_len;
183
184 return 0;
185 }
186
crypto_dcp_cipher_begin_session(const struct device * dev,struct cipher_ctx * ctx,enum cipher_algo algo,enum cipher_mode mode,enum cipher_op op_type)187 static int crypto_dcp_cipher_begin_session(const struct device *dev, struct cipher_ctx *ctx,
188 enum cipher_algo algo, enum cipher_mode mode,
189 enum cipher_op op_type)
190 {
191 const struct crypto_dcp_config *cfg = dev->config;
192 struct crypto_dcp_session *session;
193 status_t status;
194
195 if (algo != CRYPTO_CIPHER_ALGO_AES ||
196 (mode != CRYPTO_CIPHER_MODE_CBC && mode != CRYPTO_CIPHER_MODE_ECB)) {
197 return -ENOTSUP;
198 }
199
200 if (ctx->flags & ~(CRYPTO_DCP_CIPHER_CAPS)) {
201 return -ENOTSUP;
202 }
203
204 session = get_session(dev);
205 if (session == NULL) {
206 return -ENOSPC;
207 }
208
209 if (mode == CRYPTO_CIPHER_MODE_CBC) {
210 if (op_type == CRYPTO_CIPHER_OP_DECRYPT) {
211 ctx->ops.cbc_crypt_hndlr = crypto_dcp_aes_cbc_decrypt;
212 } else {
213 ctx->ops.cbc_crypt_hndlr = crypto_dcp_aes_cbc_encrypt;
214 }
215 } else {
216 if (op_type == CRYPTO_CIPHER_OP_DECRYPT) {
217 ctx->ops.block_crypt_hndlr = crypto_dcp_aes_ecb_decrypt;
218 } else {
219 ctx->ops.block_crypt_hndlr = crypto_dcp_aes_ecb_encrypt;
220 }
221 }
222
223 ctx->drv_sessn_state = session;
224
225 status = DCP_AES_SetKey(cfg->base, &session->handle, ctx->key.bit_stream, ctx->keylen);
226 if (status != kStatus_Success) {
227 free_session(session);
228 return fsl_to_errno(status);
229 }
230
231 return 0;
232 }
233
crypto_dcp_cipher_free_session(const struct device * dev,struct cipher_ctx * ctx)234 static int crypto_dcp_cipher_free_session(const struct device *dev, struct cipher_ctx *ctx)
235 {
236 struct crypto_dcp_session *session;
237
238 ARG_UNUSED(dev);
239
240 session = ctx->drv_sessn_state;
241 free_session(session);
242
243 return 0;
244 }
245
crypto_dcp_sha256(struct hash_ctx * ctx,struct hash_pkt * pkt,bool finish)246 static int crypto_dcp_sha256(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish)
247 {
248 const struct crypto_dcp_config *cfg = ctx->device->config;
249 struct crypto_dcp_session *session = ctx->drv_sessn_state;
250 status_t status;
251
252 sys_cache_data_disable();
253 status = DCP_HASH_Update(cfg->base, &session->hash_ctx, pkt->in_buf, pkt->in_len);
254 sys_cache_data_enable();
255
256 if (status != kStatus_Success) {
257 return fsl_to_errno(status);
258 }
259
260 if (finish) {
261 sys_cache_data_disable();
262 status = DCP_HASH_Finish(cfg->base, &session->hash_ctx, pkt->out_buf, NULL);
263 sys_cache_data_enable();
264 }
265
266 return fsl_to_errno(status);
267 }
268
crypto_dcp_hash_begin_session(const struct device * dev,struct hash_ctx * ctx,enum hash_algo algo)269 static int crypto_dcp_hash_begin_session(const struct device *dev, struct hash_ctx *ctx,
270 enum hash_algo algo)
271 {
272 const struct crypto_dcp_config *cfg = dev->config;
273 struct crypto_dcp_session *session;
274 status_t status;
275
276 if (algo != CRYPTO_HASH_ALGO_SHA256) {
277 return -ENOTSUP;
278 }
279
280 if (ctx->flags & ~(CRYPTO_DCP_HASH_CAPS)) {
281 return -ENOTSUP;
282 }
283
284 session = get_session(dev);
285 if (session == NULL) {
286 return -ENOSPC;
287 }
288
289 status = DCP_HASH_Init(cfg->base, &session->handle, &session->hash_ctx, kDCP_Sha256);
290 if (status != kStatus_Success) {
291 free_session(session);
292 return fsl_to_errno(status);
293 }
294
295 ctx->drv_sessn_state = session;
296 ctx->hash_hndlr = crypto_dcp_sha256;
297
298 return 0;
299 }
300
crypto_dcp_hash_free_session(const struct device * dev,struct hash_ctx * ctx)301 static int crypto_dcp_hash_free_session(const struct device *dev, struct hash_ctx *ctx)
302 {
303 struct crypto_dcp_session *session;
304
305 ARG_UNUSED(dev);
306
307 session = ctx->drv_sessn_state;
308 free_session(session);
309
310 return 0;
311 }
312
crypto_dcp_init(const struct device * dev)313 static int crypto_dcp_init(const struct device *dev)
314 {
315 const struct crypto_dcp_config *cfg = dev->config;
316 struct crypto_dcp_data *data = dev->data;
317 dcp_config_t hal_cfg;
318
319 DCP_GetDefaultConfig(&hal_cfg);
320
321 DCP_Init(cfg->base, &hal_cfg);
322
323 /* Assign unique channels/key slots to each session */
324 for (size_t i = 0; i < CONFIG_CRYPTO_MCUX_DCP_MAX_SESSION; ++i) {
325 data->sessions[i].in_use = false;
326 data->sessions[i].handle.channel = kDCP_Channel0 << i;
327 data->sessions[i].handle.keySlot = kDCP_KeySlot0 + i;
328 data->sessions[i].handle.swapConfig = kDCP_NoSwap;
329 }
330
331 return 0;
332 }
333
334 static const struct crypto_driver_api crypto_dcp_api = {
335 .query_hw_caps = crypto_dcp_query_hw_caps,
336 .cipher_begin_session = crypto_dcp_cipher_begin_session,
337 .cipher_free_session = crypto_dcp_cipher_free_session,
338 .hash_begin_session = crypto_dcp_hash_begin_session,
339 .hash_free_session = crypto_dcp_hash_free_session,
340 };
341
342 #define CRYPTO_DCP_DEFINE(inst) \
343 static const struct crypto_dcp_config crypto_dcp_config_##inst = { \
344 .base = (DCP_Type *)DT_INST_REG_ADDR(inst), \
345 }; \
346 static struct crypto_dcp_data crypto_dcp_data_##inst; \
347 DEVICE_DT_INST_DEFINE(inst, crypto_dcp_init, NULL, \
348 &crypto_dcp_data_##inst, &crypto_dcp_config_##inst, \
349 POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY, &crypto_dcp_api);
350
351 DT_INST_FOREACH_STATUS_OKAY(CRYPTO_DCP_DEFINE)
352