1 /*
2  * Copyright (c) 2016 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file Shim layer for TinyCrypt, making it complaint to crypto API.
9  */
10 
11 #include <tinycrypt/cbc_mode.h>
12 #include <tinycrypt/ctr_mode.h>
13 #include <tinycrypt/ccm_mode.h>
14 #include <tinycrypt/constants.h>
15 #include <tinycrypt/utils.h>
16 #include <string.h>
17 #include <zephyr/crypto/crypto.h>
18 #include "crypto_tc_shim_priv.h"
19 
20 #define LOG_LEVEL CONFIG_CRYPTO_LOG_LEVEL
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(tinycrypt);
23 
24 #define CRYPTO_MAX_SESSION CONFIG_CRYPTO_TINYCRYPT_SHIM_MAX_SESSION
25 
26 static struct tc_shim_drv_state tc_driver_state[CRYPTO_MAX_SESSION];
27 
do_cbc_encrypt(struct cipher_ctx * ctx,struct cipher_pkt * op,uint8_t * iv)28 static int do_cbc_encrypt(struct cipher_ctx *ctx, struct cipher_pkt *op,
29 			  uint8_t *iv)
30 {
31 	struct tc_shim_drv_state *data =  ctx->drv_sessn_state;
32 
33 	if (tc_cbc_mode_encrypt(op->out_buf,
34 				op->out_buf_max,
35 				op->in_buf, op->in_len,
36 				iv,
37 				&data->session_key) == TC_CRYPTO_FAIL) {
38 		LOG_ERR("TC internal error during CBC encryption");
39 		return -EIO;
40 	}
41 
42 	/* out_len is the same as in_len in CBC mode */
43 	op->out_len = op->in_len;
44 
45 	return 0;
46 }
47 
do_cbc_decrypt(struct cipher_ctx * ctx,struct cipher_pkt * op,uint8_t * iv)48 static int do_cbc_decrypt(struct cipher_ctx *ctx, struct cipher_pkt *op,
49 			  uint8_t *iv)
50 {
51 	struct tc_shim_drv_state *data =  ctx->drv_sessn_state;
52 
53 	/* TinyCrypt expects the IV and cipher text to be in a contiguous
54 	 * buffer for efficiency
55 	 */
56 	if (iv != op->in_buf) {
57 		LOG_ERR("TC needs contiguous iv and ciphertext");
58 		return -EIO;
59 	}
60 
61 	if (tc_cbc_mode_decrypt(op->out_buf,
62 			op->out_buf_max,
63 			op->in_buf + TC_AES_BLOCK_SIZE,
64 			op->in_len - TC_AES_BLOCK_SIZE,
65 			op->in_buf, &data->session_key) == TC_CRYPTO_FAIL) {
66 		LOG_ERR("Func TC internal error during CBC decryption");
67 		return -EIO;
68 	}
69 
70 	/* out_len is the same as in_len in CBC mode */
71 	op->out_len = op->in_len;
72 
73 	return 0;
74 }
75 
do_ctr_op(struct cipher_ctx * ctx,struct cipher_pkt * op,uint8_t * iv)76 static int do_ctr_op(struct cipher_ctx *ctx, struct cipher_pkt *op,
77 		     uint8_t *iv)
78 {
79 	struct tc_shim_drv_state *data =  ctx->drv_sessn_state;
80 	uint8_t ctr[16] = {0};	/* CTR mode Counter =  iv:ctr */
81 	int ivlen = ctx->keylen - (ctx->mode_params.ctr_info.ctr_len >> 3);
82 
83 	/* Tinycrypt takes the last 4 bytes of the counter parameter as the
84 	 * true counter start. IV forms the first 12 bytes of the split counter.
85 	 */
86 	memcpy(ctr, iv, ivlen);
87 
88 	if (tc_ctr_mode(op->out_buf, op->out_buf_max, op->in_buf,
89 			op->in_len, ctr,
90 			&data->session_key) == TC_CRYPTO_FAIL) {
91 		LOG_ERR("TC internal error during CTR OP");
92 		return -EIO;
93 	}
94 
95 	/* out_len is the same as in_len in CTR mode */
96 	op->out_len = op->in_len;
97 
98 	return 0;
99 }
100 
do_ccm_encrypt_mac(struct cipher_ctx * ctx,struct cipher_aead_pkt * aead_op,uint8_t * nonce)101 static int do_ccm_encrypt_mac(struct cipher_ctx *ctx,
102 			     struct cipher_aead_pkt *aead_op, uint8_t *nonce)
103 {
104 	struct tc_ccm_mode_struct ccm;
105 	struct tc_shim_drv_state *data =  ctx->drv_sessn_state;
106 	struct ccm_params *ccm_param = &ctx->mode_params.ccm_info;
107 	struct cipher_pkt *op = aead_op->pkt;
108 
109 	if (tc_ccm_config(&ccm, &data->session_key, nonce,
110 			ccm_param->nonce_len,
111 			ccm_param->tag_len) == TC_CRYPTO_FAIL) {
112 		LOG_ERR("TC internal error during CCM encryption config");
113 		return -EIO;
114 	}
115 
116 	if (tc_ccm_generation_encryption(op->out_buf, op->out_buf_max,
117 					 aead_op->ad, aead_op->ad_len, op->in_buf,
118 					 op->in_len, &ccm) == TC_CRYPTO_FAIL) {
119 		LOG_ERR("TC internal error during CCM Encryption OP");
120 		return -EIO;
121 	}
122 
123 	/* Looks like TinyCrypt appends the MAC to the end of out_buf as it
124 	 * does not give a separate hash parameter. The user needs to be aware
125 	 * of this and provide sufficient buffer space in output buffer to hold
126 	 * both encrypted output and hash
127 	 */
128 	if (aead_op->tag) {
129 		memcpy(aead_op->tag, op->out_buf + op->in_len, ccm.mlen);
130 	}
131 
132 	/* Before returning TC_CRYPTO_SUCCESS, tc_ccm_generation_encryption()
133 	 * will advance the output buffer pointer by op->in_len bytes,
134 	 * and then increment it ccm.mlen times (while writing to it).
135 	 */
136 	op->out_len = op->in_len + ccm.mlen;
137 
138 	return 0;
139 }
140 
do_ccm_decrypt_auth(struct cipher_ctx * ctx,struct cipher_aead_pkt * aead_op,uint8_t * nonce)141 static int do_ccm_decrypt_auth(struct cipher_ctx *ctx,
142 			       struct cipher_aead_pkt *aead_op, uint8_t *nonce)
143 {
144 	struct tc_ccm_mode_struct ccm;
145 	struct tc_shim_drv_state *data =  ctx->drv_sessn_state;
146 	struct ccm_params *ccm_param = &ctx->mode_params.ccm_info;
147 	struct cipher_pkt *op = aead_op->pkt;
148 
149 	if (tc_ccm_config(&ccm, &data->session_key, nonce,
150 			  ccm_param->nonce_len,
151 			  ccm_param->tag_len) == TC_CRYPTO_FAIL) {
152 		LOG_ERR("TC internal error during CCM decryption config");
153 		return -EIO;
154 	}
155 
156 	/* TinyCrypt expects the hash/MAC to be present at the end of in_buf
157 	 * as it doesnt take a separate hash parameter. Ideally this should
158 	 * be moved to a ctx.flag check during session_setup, later.
159 	 */
160 	if (aead_op->tag != op->in_buf + op->in_len) {
161 		LOG_ERR("TC needs contiguous hash  at the end of inbuf");
162 		return -EIO;
163 	}
164 
165 	if (tc_ccm_decryption_verification(op->out_buf, op->out_buf_max,
166 					   aead_op->ad, aead_op->ad_len,
167 					   op->in_buf,
168 					   op->in_len + ccm_param->tag_len,
169 					   &ccm) == TC_CRYPTO_FAIL) {
170 		LOG_ERR("TC internal error during CCM decryption OP");
171 		return -EIO;
172 	}
173 
174 	op->out_len = op->in_len + ccm_param->tag_len;
175 
176 	return 0;
177 }
178 
get_unused_session(void)179 static int get_unused_session(void)
180 {
181 	int i;
182 
183 	for (i = 0; i < CRYPTO_MAX_SESSION; i++) {
184 		if (tc_driver_state[i].in_use == 0) {
185 			tc_driver_state[i].in_use = 1;
186 			break;
187 		}
188 	}
189 
190 	return i;
191 }
192 
tc_session_setup(const struct device * dev,struct cipher_ctx * ctx,enum cipher_algo algo,enum cipher_mode mode,enum cipher_op op_type)193 static int tc_session_setup(const struct device *dev, struct cipher_ctx *ctx,
194 			    enum cipher_algo algo, enum cipher_mode mode,
195 			    enum cipher_op op_type)
196 {
197 	struct tc_shim_drv_state *data;
198 	int idx;
199 
200 	ARG_UNUSED(dev);
201 
202 	/* The shim currently supports only CBC or CTR mode for AES */
203 	if (algo != CRYPTO_CIPHER_ALGO_AES) {
204 		LOG_ERR("TC Shim Unsupported algo");
205 		return -EINVAL;
206 	}
207 
208 	/* TinyCrypt being a software library, only synchronous operations
209 	 * make sense.
210 	 */
211 	if (!(ctx->flags & CAP_SYNC_OPS)) {
212 		LOG_ERR("Async not supported by this driver");
213 		return -EINVAL;
214 	}
215 
216 	if (ctx->keylen != TC_AES_KEY_SIZE) {
217 		/* TinyCrypt supports only 128 bits */
218 		LOG_ERR("TC Shim Unsupported key size");
219 		return -EINVAL;
220 	}
221 
222 	if (op_type == CRYPTO_CIPHER_OP_ENCRYPT) {
223 		switch (mode) {
224 		case CRYPTO_CIPHER_MODE_CBC:
225 			ctx->ops.cbc_crypt_hndlr = do_cbc_encrypt;
226 			break;
227 		case CRYPTO_CIPHER_MODE_CTR:
228 			if (ctx->mode_params.ctr_info.ctr_len != 32U) {
229 				LOG_ERR("Tinycrypt supports only 32 bit "
230 					    "counter");
231 				return -EINVAL;
232 			}
233 			ctx->ops.ctr_crypt_hndlr = do_ctr_op;
234 			break;
235 		case CRYPTO_CIPHER_MODE_CCM:
236 			ctx->ops.ccm_crypt_hndlr = do_ccm_encrypt_mac;
237 			break;
238 		default:
239 			LOG_ERR("TC Shim Unsupported mode");
240 			return -EINVAL;
241 		}
242 	} else {
243 		switch (mode) {
244 		case CRYPTO_CIPHER_MODE_CBC:
245 			ctx->ops.cbc_crypt_hndlr = do_cbc_decrypt;
246 			break;
247 		case CRYPTO_CIPHER_MODE_CTR:
248 			/* Maybe validate CTR length */
249 			if (ctx->mode_params.ctr_info.ctr_len != 32U) {
250 				LOG_ERR("Tinycrypt supports only 32 bit "
251 					    "counter");
252 				return -EINVAL;
253 			}
254 			ctx->ops.ctr_crypt_hndlr = do_ctr_op;
255 			break;
256 		case CRYPTO_CIPHER_MODE_CCM:
257 			ctx->ops.ccm_crypt_hndlr = do_ccm_decrypt_auth;
258 			break;
259 		default:
260 			LOG_ERR("TC Shim Unsupported mode");
261 			return -EINVAL;
262 		}
263 
264 	}
265 
266 	ctx->ops.cipher_mode = mode;
267 
268 	idx = get_unused_session();
269 	if (idx == CRYPTO_MAX_SESSION) {
270 		LOG_ERR("Max sessions in progress");
271 		return -ENOSPC;
272 	}
273 
274 	data = &tc_driver_state[idx];
275 
276 	if (tc_aes128_set_encrypt_key(&data->session_key, ctx->key.bit_stream)
277 			 == TC_CRYPTO_FAIL) {
278 		LOG_ERR("TC internal error in setting key");
279 		tc_driver_state[idx].in_use = 0;
280 
281 		return -EIO;
282 	}
283 
284 	ctx->drv_sessn_state = data;
285 
286 	return 0;
287 }
288 
tc_query_caps(const struct device * dev)289 static int tc_query_caps(const struct device *dev)
290 {
291 	return (CAP_RAW_KEY | CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS);
292 }
293 
tc_session_free(const struct device * dev,struct cipher_ctx * sessn)294 static int tc_session_free(const struct device *dev, struct cipher_ctx *sessn)
295 {
296 	struct tc_shim_drv_state *data =  sessn->drv_sessn_state;
297 
298 	ARG_UNUSED(dev);
299 	(void)memset(data, 0, sizeof(struct tc_shim_drv_state));
300 	data->in_use = 0;
301 
302 	return 0;
303 }
304 
tc_shim_init(const struct device * dev)305 static int tc_shim_init(const struct device *dev)
306 {
307 	int i;
308 
309 	ARG_UNUSED(dev);
310 	for (i = 0; i < CRYPTO_MAX_SESSION; i++) {
311 		tc_driver_state[i].in_use = 0;
312 	}
313 
314 	return 0;
315 }
316 
317 static DEVICE_API(crypto, crypto_enc_funcs) = {
318 	.cipher_begin_session = tc_session_setup,
319 	.cipher_free_session = tc_session_free,
320 	.cipher_async_callback_set = NULL,
321 	.query_hw_caps = tc_query_caps,
322 };
323 
324 DEVICE_DEFINE(crypto_tinycrypt, CONFIG_CRYPTO_TINYCRYPT_SHIM_DRV_NAME,
325 		    &tc_shim_init, NULL, NULL, NULL,
326 		    POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY,
327 		    (void *)&crypto_enc_funcs);
328