xref: /optee_os/core/drivers/crypto/caam/acipher/caam_dh.c (revision 35a9139e779fa87dadbcf8d4377a458393e67b7c)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright 2018-2021, 2023 NXP
4  *
5  * Implementation of DH
6  */
7 #include <caam_acipher.h>
8 #include <caam_common.h>
9 #include <caam_hal_ctrl.h>
10 #include <caam_jr.h>
11 #include <caam_key.h>
12 #include <caam_utils_mem.h>
13 #include <caam_utils_status.h>
14 #include <drvcrypt.h>
15 #include <drvcrypt_acipher.h>
16 #include <mm/core_memprot.h>
17 #include <tee/cache.h>
18 #include <string.h>
19 
20 #ifdef CFG_CAAM_64BIT
21 #define MAX_DESC_KEY_GEN 14
22 #define MAX_DESC_SHARED  14
23 #else
24 #define MAX_DESC_KEY_GEN 9
25 #define MAX_DESC_SHARED  9
26 #endif
27 
28 /*
29  * Definition of the local DH Keypair
30  */
31 struct caam_dh_keypair {
32 	struct caambuf g; /* Generator */
33 	struct caambuf p; /* Prime Number Modulus */
34 	struct caamkey x; /* Private key */
35 	struct caambuf y; /* Public key */
36 };
37 
38 /*
39  * Free local DH keypair
40  *
41  * @key DH keypair
42  */
do_keypair_free(struct caam_dh_keypair * key)43 static void do_keypair_free(struct caam_dh_keypair *key)
44 {
45 	caam_free_buf(&key->g);
46 	caam_free_buf(&key->p);
47 	caam_key_free(&key->x);
48 	caam_free_buf(&key->y);
49 }
50 
51 /*
52  * Convert Crypto DH Key p and g bignumbers to local buffers
53  * (via keypair object).
54  *
55  * @outkey [out] Output keypair in local format
56  * @inkey  Input key in TEE Crypto format
57  */
do_keypair_conv_p_g(struct caam_dh_keypair * outkey,const struct dh_keypair * inkey)58 static enum caam_status do_keypair_conv_p_g(struct caam_dh_keypair *outkey,
59 					    const struct dh_keypair *inkey)
60 {
61 	enum caam_status retstatus = CAAM_OUT_MEMORY;
62 	size_t p_size = 0;
63 	size_t field_size = 0;
64 
65 	p_size = crypto_bignum_num_bytes(inkey->p);
66 
67 	DH_TRACE("DH Convert Key Parameters (p,g) size %zu bytes", p_size);
68 
69 	/* Prime Number Modulus */
70 	retstatus = caam_calloc_buf(&outkey->p, p_size);
71 	if (retstatus != CAAM_NO_ERROR)
72 		return retstatus;
73 
74 	crypto_bignum_bn2bin(inkey->p, outkey->p.data);
75 	cache_operation(TEE_CACHECLEAN, outkey->p.data, outkey->p.length);
76 
77 	/* Generator */
78 	retstatus = caam_calloc_buf(&outkey->g, p_size);
79 	if (retstatus != CAAM_NO_ERROR)
80 		return retstatus;
81 
82 	/* Get the number of bytes of g to pad with 0's */
83 	field_size = crypto_bignum_num_bytes(inkey->g);
84 	crypto_bignum_bn2bin(inkey->g, outkey->g.data + p_size - field_size);
85 	cache_operation(TEE_CACHECLEAN, outkey->g.data, outkey->g.length);
86 
87 	return CAAM_NO_ERROR;
88 }
89 
90 /*
91  * Convert Crypto DH Private Key to a local Private Key (via keypair object)
92  *
93  * @outkey [out] Output local keypair
94  * @inkey  Input Private key in TEE Crypto format
95  */
do_keypriv_conv(struct caam_dh_keypair * outkey,const struct dh_keypair * inkey)96 static enum caam_status do_keypriv_conv(struct caam_dh_keypair *outkey,
97 					const struct dh_keypair *inkey)
98 {
99 	enum caam_status retstatus = CAAM_OUT_MEMORY;
100 	size_t key_size = inkey->xbits / 8;
101 	size_t p_size = 0;
102 
103 	if (!key_size)
104 		key_size = crypto_bignum_num_bytes(inkey->x);
105 
106 	DH_TRACE("DH Convert Private Key size %zu bytes", key_size);
107 
108 	/* Prime */
109 	p_size = crypto_bignum_num_bytes(inkey->p);
110 	retstatus = caam_calloc_buf(&outkey->p, p_size);
111 	if (retstatus != CAAM_NO_ERROR)
112 		return retstatus;
113 
114 	crypto_bignum_bn2bin(inkey->p, outkey->p.data);
115 	cache_operation(TEE_CACHECLEAN, outkey->p.data, outkey->p.length);
116 
117 	/* Private Key X */
118 	retstatus = caam_key_deserialize_from_bn(inkey->x, &outkey->x, 0);
119 	if (retstatus != CAAM_NO_ERROR)
120 		return retstatus;
121 
122 	caam_key_cache_op(TEE_CACHECLEAN, &outkey->x);
123 
124 	return CAAM_NO_ERROR;
125 }
126 
127 /*
128  * Convert Crypto DH Public Key to local Public Key (via a keypair object)
129  *
130  * @outkey [out] Output local keypair
131  * @inkey  Input Public key in TEE Crypto format
132  */
do_keypub_conv(struct caam_dh_keypair * outkey,const struct bignum * inkey)133 static enum caam_status do_keypub_conv(struct caam_dh_keypair *outkey,
134 				       const struct bignum *inkey)
135 {
136 	enum caam_status retstatus = CAAM_OUT_MEMORY;
137 	size_t key_size = 0;
138 
139 	key_size = crypto_bignum_num_bytes((struct bignum *)inkey);
140 	DH_TRACE("DH Convert Keypair size %zu bytes", key_size);
141 
142 	/* Public Key Y */
143 	retstatus = caam_calloc_buf(&outkey->y, key_size);
144 	if (retstatus != CAAM_NO_ERROR)
145 		return retstatus;
146 
147 	crypto_bignum_bn2bin(inkey, outkey->y.data);
148 	cache_operation(TEE_CACHECLEAN, outkey->y.data, outkey->y.length);
149 
150 	return CAAM_NO_ERROR;
151 }
152 
153 /*
154  * Allocate a TEE DH keypair.
155  * Note: The subprime q is not used but it must be allocated to prevent
156  * system referencing issues when object is destroyed.
157  *
158  * @key       Keypair
159  * @size_bits Key size in bits
160  */
do_allocate_keypair(struct dh_keypair * key,size_t size_bits)161 static TEE_Result do_allocate_keypair(struct dh_keypair *key, size_t size_bits)
162 {
163 	DH_TRACE("Allocate Keypair of %zu bits", size_bits);
164 
165 	/* Initialize the key fields to NULL */
166 	memset(key, 0, sizeof(*key));
167 
168 	/* Allocate Generator Scalar */
169 	key->g = crypto_bignum_allocate(size_bits);
170 	if (!key->g)
171 		goto err;
172 
173 	/* Allocate Prime Number Modulus */
174 	key->p = crypto_bignum_allocate(size_bits);
175 	if (!key->p)
176 		goto err;
177 
178 	/* Allocate Private key X */
179 	key->x = crypto_bignum_allocate(CFG_CORE_BIGNUM_MAX_BITS);
180 	if (!key->x)
181 		goto err;
182 
183 	/* Allocate Public Key Y */
184 	key->y = crypto_bignum_allocate(size_bits);
185 	if (!key->y)
186 		goto err;
187 
188 	/* Allocate Subprime even if not used */
189 	key->q = crypto_bignum_allocate(size_bits);
190 	if (!key->q)
191 		goto err;
192 
193 	return TEE_SUCCESS;
194 
195 err:
196 	DH_TRACE("Allocation error");
197 
198 	crypto_bignum_free(&key->g);
199 	crypto_bignum_free(&key->p);
200 	crypto_bignum_free(&key->x);
201 	crypto_bignum_free(&key->y);
202 
203 	return TEE_ERROR_OUT_OF_MEMORY;
204 }
205 
206 /*
207  * Generates an DH keypair
208  * Keypair @key contains the input prime p and generator g values
209  * The function calculates private x and public y, knowing that the
210  * number of bits of x is either key_size if specified or p size.
211  *
212  * @key      [in/out] Keypair
213  * @q        Sub Prime (not used)
214  * @key_size Key size in bits multiple of 8 bits
215  */
do_gen_keypair(struct dh_keypair * key,struct bignum * q __unused,size_t key_size)216 static TEE_Result do_gen_keypair(struct dh_keypair *key,
217 				 struct bignum *q __unused, size_t key_size)
218 {
219 	TEE_Result ret = TEE_ERROR_GENERIC;
220 	enum caam_status retstatus = CAAM_FAILURE;
221 	struct caam_dh_keypair caam_dh_key = { };
222 	struct caambuf dh_r = { };
223 	size_t n_bytes = key_size / 8;
224 	size_t l_bytes = 0;
225 	struct caam_jobctx jobctx = { };
226 	uint32_t *desc = NULL;
227 	uint32_t desclen = 0;
228 	int counter = 0;
229 	enum caam_key_type key_type = caam_key_default_key_gen_type();
230 
231 	l_bytes = crypto_bignum_num_bytes(key->p);
232 	if (!l_bytes)
233 		return TEE_ERROR_BAD_PARAMETERS;
234 
235 	/*
236 	 * If @key_size not specified, private key size is
237 	 * same as the public key size (same as prime size)
238 	 */
239 	if (!n_bytes)
240 		n_bytes = l_bytes;
241 
242 	/*
243 	 * CAAM private key support is limited to the descriptor PDB
244 	 * N maximum value (PDB_DL_KEY_N_MASK)
245 	 */
246 	if (n_bytes > PDB_DL_KEY_N_MASK)
247 		n_bytes = PDB_DL_KEY_N_MASK;
248 
249 	DH_TRACE("Request %zu bits key -> so do %zu bytes key", key_size,
250 		 n_bytes);
251 
252 	/* Allocate the job used to prepare the operation */
253 	desc = caam_calloc_desc(MAX_DESC_KEY_GEN);
254 	if (!desc) {
255 		ret = TEE_ERROR_OUT_OF_MEMORY;
256 		goto out;
257 	}
258 
259 	/* Allocate Private Key to be generated */
260 	caam_dh_key.x.key_type = key_type;
261 	caam_dh_key.x.sec_size = n_bytes;
262 	caam_dh_key.x.is_blob = false;
263 
264 	retstatus = caam_key_alloc(&caam_dh_key.x);
265 	if (retstatus != CAAM_NO_ERROR) {
266 		ret = caam_status_to_tee_result(retstatus);
267 		goto out;
268 	}
269 
270 	caam_key_cache_op(TEE_CACHEFLUSH, &caam_dh_key.x);
271 
272 	/* Allocate Public Key to be generated */
273 	retstatus = caam_calloc_align_buf(&caam_dh_key.y, l_bytes);
274 	if (retstatus != CAAM_NO_ERROR) {
275 		ret = caam_status_to_tee_result(retstatus);
276 		goto out;
277 	}
278 	cache_operation(TEE_CACHEFLUSH, caam_dh_key.y.data,
279 			caam_dh_key.y.length);
280 
281 	/* Allocate Private Key modulus (r) and fill it with one's */
282 	retstatus = caam_calloc_buf(&dh_r, n_bytes);
283 	if (retstatus != CAAM_NO_ERROR) {
284 		ret = caam_status_to_tee_result(retstatus);
285 		goto out;
286 	}
287 
288 	memset(dh_r.data, UINT8_MAX, dh_r.length);
289 	cache_operation(TEE_CACHECLEAN, dh_r.data, dh_r.length);
290 
291 	/* Generator and Prime */
292 	retstatus = do_keypair_conv_p_g(&caam_dh_key, key);
293 	if (retstatus != CAAM_NO_ERROR) {
294 		ret = caam_status_to_tee_result(retstatus);
295 		goto out;
296 	}
297 
298 	/*
299 	 * Build the descriptor using the PDB Public Key generation
300 	 * block (PD=0)
301 	 */
302 	caam_desc_init(desc);
303 	caam_desc_add_word(desc, DESC_HEADER(0));
304 	caam_desc_add_word(desc, PDB_DL_KEY_L_SIZE(l_bytes) |
305 				 PDB_DL_KEY_N_SIZE(n_bytes));
306 	caam_desc_add_ptr(desc, caam_dh_key.p.paddr);
307 	caam_desc_add_ptr(desc, dh_r.paddr);
308 	caam_desc_add_ptr(desc, caam_dh_key.g.paddr);
309 	caam_desc_add_ptr(desc, caam_dh_key.x.buf.paddr);
310 	caam_desc_add_ptr(desc, caam_dh_key.y.paddr);
311 
312 	switch (key_type) {
313 	case CAAM_KEY_PLAIN_TEXT:
314 		caam_desc_add_word(desc, PK_KEYPAIR_GEN(DL, NONE));
315 		break;
316 	case CAAM_KEY_BLACK_ECB:
317 		caam_desc_add_word(desc, PK_KEYPAIR_GEN(DL, ECB));
318 		break;
319 	case CAAM_KEY_BLACK_CCM:
320 		caam_desc_add_word(desc, PK_KEYPAIR_GEN(DL, CCM));
321 		break;
322 	default:
323 		ret = TEE_ERROR_GENERIC;
324 		goto out;
325 	}
326 
327 	desclen = caam_desc_get_len(desc);
328 	caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
329 
330 	DH_DUMPDESC(desc);
331 
332 	/*
333 	 * If the Secure Key X doesn't have the correct size
334 	 * retry a new generation.
335 	 * Retry 10 times before returing an error to not lock the system.
336 	 */
337 	for (counter = 0; counter < 10; counter++) {
338 		memset(&jobctx, 0, sizeof(jobctx));
339 		jobctx.desc = desc;
340 		retstatus = caam_jr_enqueue(&jobctx, NULL);
341 
342 		if (retstatus == CAAM_NO_ERROR) {
343 			caam_key_cache_op(TEE_CACHEINVALIDATE, &caam_dh_key.x);
344 			cache_operation(TEE_CACHEINVALIDATE, caam_dh_key.y.data,
345 					caam_dh_key.y.length);
346 
347 			/* Copy Private and Public keypair */
348 			ret = caam_key_serialize_to_bn(key->x, &caam_dh_key.x);
349 			if (ret != TEE_SUCCESS)
350 				goto out;
351 
352 			if (crypto_bignum_num_bytes(key->x) != n_bytes) {
353 				DH_TRACE("Error X size=%zu expected %zu",
354 					 crypto_bignum_num_bytes(key->x),
355 					 n_bytes);
356 				DH_DUMPBUF("X", caam_dh_key.x.data,
357 					   caam_dh_key.x.length);
358 				DH_DUMPBUF("Y", caam_dh_key.y.data,
359 					   caam_dh_key.y.length);
360 				continue;
361 			}
362 
363 			ret = crypto_bignum_bin2bn(caam_dh_key.y.data,
364 						   caam_dh_key.y.length,
365 						   key->y);
366 			if (ret != TEE_SUCCESS)
367 				goto out;
368 
369 			/* Set the Private Key size in bits */
370 			key->xbits = n_bytes * 8;
371 
372 			ret = TEE_SUCCESS;
373 			goto out;
374 		} else {
375 			DH_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
376 			ret = job_status_to_tee_result(jobctx.status);
377 			goto out;
378 		}
379 	}
380 
381 out:
382 	caam_free_desc(&desc);
383 	caam_free_buf(&dh_r);
384 	do_keypair_free(&caam_dh_key);
385 
386 	return ret;
387 }
388 
389 /*
390  * Compute the shared secret data from DH Private key and Public Key
391  *
392  * @sdata   [in/out] DH Shared Secret data
393  */
do_shared_secret(struct drvcrypt_secret_data * sdata)394 static TEE_Result do_shared_secret(struct drvcrypt_secret_data *sdata)
395 {
396 	TEE_Result ret = TEE_ERROR_GENERIC;
397 	enum caam_status retstatus = CAAM_FAILURE;
398 	struct dh_keypair *inkeypair = sdata->key_priv;
399 	struct caam_dh_keypair caam_dh_key = { };
400 	struct caamdmaobj secret = { };
401 	struct caam_jobctx jobctx = { };
402 	uint32_t *desc = NULL;
403 	uint32_t desclen = 0;
404 	uint32_t pdb_sgt_flags = 0;
405 
406 	DH_TRACE("DH Shared Secret");
407 
408 	/* Allocate the job descriptor */
409 	desc = caam_calloc_desc(MAX_DESC_SHARED);
410 	if (!desc) {
411 		ret = TEE_ERROR_OUT_OF_MEMORY;
412 		goto out;
413 	}
414 
415 	/*
416 	 * ReAllocate the secret result buffer with a maximum size
417 	 * of the secret size if not cache aligned
418 	 */
419 	ret = caam_dmaobj_output_sgtbuf(&secret, sdata->secret.data,
420 					sdata->secret.length,
421 					sdata->secret.length);
422 	if (ret)
423 		goto out;
424 
425 	if (secret.sgtbuf.sgt_type)
426 		pdb_sgt_flags |= PDB_SGT_PKDH_SECRET;
427 
428 	caam_dmaobj_cache_push(&secret);
429 
430 	/* Convert the Private key to local key */
431 	retstatus = do_keypriv_conv(&caam_dh_key, inkeypair);
432 	if (retstatus != CAAM_NO_ERROR) {
433 		ret = TEE_ERROR_OUT_OF_MEMORY;
434 		goto out;
435 	}
436 
437 	/* Convert the Public key to local key */
438 	retstatus = do_keypub_conv(&caam_dh_key, sdata->key_pub);
439 	if (retstatus != CAAM_NO_ERROR) {
440 		ret = caam_status_to_tee_result(retstatus);
441 		goto out;
442 	}
443 
444 	/*
445 	 * Build the descriptor using PDB Shared Secret
446 	 */
447 	caam_desc_init(desc);
448 	caam_desc_add_word(desc, DESC_HEADER(0));
449 	caam_desc_add_word(desc, pdb_sgt_flags |
450 				 PDB_DL_KEY_L_SIZE(caam_dh_key.y.length) |
451 				 PDB_DL_KEY_N_SIZE(caam_dh_key.x.sec_size));
452 	/* Prime */
453 	caam_desc_add_ptr(desc, caam_dh_key.p.paddr);
454 	/* Modulus - Not used */
455 	caam_desc_add_ptr(desc, 0);
456 	/* Public key */
457 	caam_desc_add_ptr(desc, caam_dh_key.y.paddr);
458 	/* Private key */
459 	caam_desc_add_ptr(desc, caam_dh_key.x.buf.paddr);
460 	/* Output secret */
461 	caam_desc_add_ptr(desc, secret.sgtbuf.paddr);
462 
463 	switch (caam_dh_key.x.key_type) {
464 	case CAAM_KEY_PLAIN_TEXT:
465 		caam_desc_add_word(desc, SHARED_SECRET(DL, NONE));
466 		break;
467 	case CAAM_KEY_BLACK_ECB:
468 		caam_desc_add_word(desc, SHARED_SECRET(DL, ECB));
469 		break;
470 	case CAAM_KEY_BLACK_CCM:
471 		caam_desc_add_word(desc, SHARED_SECRET(DL, CCM));
472 		break;
473 	default:
474 		ret = TEE_ERROR_GENERIC;
475 		goto out;
476 	}
477 
478 	desclen = caam_desc_get_len(desc);
479 	caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
480 
481 	DH_DUMPDESC(desc);
482 	jobctx.desc = desc;
483 
484 	retstatus = caam_jr_enqueue(&jobctx, NULL);
485 
486 	if (retstatus == CAAM_NO_ERROR) {
487 		sdata->secret.length = caam_dmaobj_copy_to_orig(&secret);
488 
489 		DH_DUMPBUF("Secret", sdata->secret.data, sdata->secret.length);
490 		ret = caam_status_to_tee_result(retstatus);
491 	} else {
492 		DH_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
493 		ret = job_status_to_tee_result(jobctx.status);
494 	}
495 
496 out:
497 	caam_free_desc(&desc);
498 	do_keypair_free(&caam_dh_key);
499 	caam_dmaobj_free(&secret);
500 
501 	return ret;
502 }
503 
504 /*
505  * Registration of the ECC Driver
506  */
507 static struct drvcrypt_dh driver_dh = {
508 	.alloc_keypair = do_allocate_keypair,
509 	.gen_keypair = do_gen_keypair,
510 	.shared_secret = do_shared_secret,
511 };
512 
caam_dh_init(struct caam_jrcfg * caam_jrcfg)513 enum caam_status caam_dh_init(struct caam_jrcfg *caam_jrcfg)
514 {
515 	enum caam_status retstatus = CAAM_FAILURE;
516 	vaddr_t jr_base = caam_jrcfg->base + caam_jrcfg->offset;
517 
518 	if (caam_hal_ctrl_pknum(jr_base) &&
519 	    drvcrypt_register_dh(&driver_dh) == TEE_SUCCESS)
520 		retstatus = CAAM_NO_ERROR;
521 
522 	return retstatus;
523 }
524