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