1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright 2018-2021 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_utils_mem.h>
12 #include <caam_utils_status.h>
13 #include <drvcrypt.h>
14 #include <drvcrypt_acipher.h>
15 #include <mm/core_memprot.h>
16 #include <tee/cache.h>
17 #include <string.h>
18
19 #ifdef CFG_CAAM_64BIT
20 #define MAX_DESC_KEY_GEN 14
21 #define MAX_DESC_SHARED 14
22 #else
23 #define MAX_DESC_KEY_GEN 9
24 #define MAX_DESC_SHARED 9
25 #endif
26
27 /*
28 * Definition of the local DH Keypair
29 */
30 struct caam_dh_keypair {
31 struct caambuf g; /* Generator */
32 struct caambuf p; /* Prime Number Modulus */
33 struct caambuf x; /* Private key */
34 struct caambuf y; /* Public key */
35 };
36
37 /*
38 * Free local DH keypair
39 *
40 * @key DH keypair
41 */
do_keypair_free(struct caam_dh_keypair * key)42 static void do_keypair_free(struct caam_dh_keypair *key)
43 {
44 caam_free_buf(&key->g);
45 caam_free_buf(&key->p);
46 caam_free_buf(&key->x);
47 caam_free_buf(&key->y);
48 }
49
50 /*
51 * Convert Crypto DH Key p and g bignumbers to local buffers
52 * (via keypair object).
53 *
54 * @outkey [out] Output keypair in local format
55 * @inkey Input key in TEE Crypto format
56 */
do_keypair_conv_p_g(struct caam_dh_keypair * outkey,const struct dh_keypair * inkey)57 static enum caam_status do_keypair_conv_p_g(struct caam_dh_keypair *outkey,
58 const struct dh_keypair *inkey)
59 {
60 enum caam_status retstatus = CAAM_OUT_MEMORY;
61 size_t p_size = 0;
62 size_t field_size = 0;
63
64 p_size = crypto_bignum_num_bytes(inkey->p);
65
66 DH_TRACE("DH Convert Key Parameters (p,g) size %zu bytes", p_size);
67
68 /* Prime Number Modulus */
69 retstatus = caam_calloc_buf(&outkey->p, p_size);
70 if (retstatus != CAAM_NO_ERROR)
71 return retstatus;
72
73 crypto_bignum_bn2bin(inkey->p, outkey->p.data);
74 cache_operation(TEE_CACHECLEAN, outkey->p.data, outkey->p.length);
75
76 /* Generator */
77 retstatus = caam_calloc_buf(&outkey->g, p_size);
78 if (retstatus != CAAM_NO_ERROR)
79 return retstatus;
80
81 /* Get the number of bytes of g to pad with 0's */
82 field_size = crypto_bignum_num_bytes(inkey->g);
83 crypto_bignum_bn2bin(inkey->g, outkey->g.data + p_size - field_size);
84 cache_operation(TEE_CACHECLEAN, outkey->g.data, outkey->g.length);
85
86 return CAAM_NO_ERROR;
87 }
88
89 /*
90 * Convert Crypto DH Private Key to a local Private Key (via keypair object)
91 *
92 * @outkey [out] Output local keypair
93 * @inkey Input Private key in TEE Crypto format
94 */
do_keypriv_conv(struct caam_dh_keypair * outkey,const struct dh_keypair * inkey)95 static enum caam_status do_keypriv_conv(struct caam_dh_keypair *outkey,
96 const struct dh_keypair *inkey)
97 {
98 enum caam_status retstatus = CAAM_OUT_MEMORY;
99 size_t key_size = inkey->xbits / 8;
100 size_t p_size = 0;
101
102 if (!key_size)
103 key_size = crypto_bignum_num_bytes(inkey->x);
104
105 DH_TRACE("DH Convert Private Key size %zu bytes", key_size);
106
107 /* Prime */
108 p_size = crypto_bignum_num_bytes(inkey->p);
109 retstatus = caam_calloc_buf(&outkey->p, p_size);
110 if (retstatus != CAAM_NO_ERROR)
111 return retstatus;
112
113 crypto_bignum_bn2bin(inkey->p, outkey->p.data);
114 cache_operation(TEE_CACHECLEAN, outkey->p.data, outkey->p.length);
115
116 /* Private Key X */
117 retstatus = caam_calloc_buf(&outkey->x, key_size);
118 if (retstatus != CAAM_NO_ERROR)
119 return retstatus;
120
121 crypto_bignum_bn2bin(inkey->x, outkey->x.data);
122 cache_operation(TEE_CACHECLEAN, outkey->x.data, outkey->x.length);
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(size_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
230 l_bytes = crypto_bignum_num_bytes(key->p);
231 if (!l_bytes)
232 return TEE_ERROR_BAD_PARAMETERS;
233
234 /*
235 * If @key_size not specified, private key size is
236 * same as the public key size (same as prime size)
237 */
238 if (!n_bytes)
239 n_bytes = l_bytes;
240
241 /*
242 * CAAM private key support is limited to the descriptor PDB
243 * N maximum value (PDB_DL_KEY_N_MASK)
244 */
245 if (n_bytes > PDB_DL_KEY_N_MASK)
246 n_bytes = PDB_DL_KEY_N_MASK;
247
248 DH_TRACE("Request %zu bits key -> so do %zu bytes key", key_size,
249 n_bytes);
250
251 /* Allocate the job used to prepare the operation */
252 desc = caam_calloc_desc(MAX_DESC_KEY_GEN);
253 if (!desc) {
254 ret = TEE_ERROR_OUT_OF_MEMORY;
255 goto out;
256 }
257
258 /* Allocate Private Key to be generated */
259 retstatus = caam_calloc_align_buf(&caam_dh_key.x, n_bytes);
260 if (retstatus != CAAM_NO_ERROR) {
261 ret = caam_status_to_tee_result(retstatus);
262 goto out;
263 }
264 cache_operation(TEE_CACHEFLUSH, caam_dh_key.x.data,
265 caam_dh_key.x.length);
266
267 /* Allocate Public Key to be generated */
268 retstatus = caam_calloc_align_buf(&caam_dh_key.y, l_bytes);
269 if (retstatus != CAAM_NO_ERROR) {
270 ret = caam_status_to_tee_result(retstatus);
271 goto out;
272 }
273 cache_operation(TEE_CACHEFLUSH, caam_dh_key.y.data,
274 caam_dh_key.y.length);
275
276 /* Allocate Private Key modulus (r) and fill it with one's */
277 retstatus = caam_calloc_buf(&dh_r, n_bytes);
278 if (retstatus != CAAM_NO_ERROR) {
279 ret = caam_status_to_tee_result(retstatus);
280 goto out;
281 }
282
283 memset(dh_r.data, UINT8_MAX, dh_r.length);
284 cache_operation(TEE_CACHECLEAN, dh_r.data, dh_r.length);
285
286 /* Generator and Prime */
287 retstatus = do_keypair_conv_p_g(&caam_dh_key, key);
288 if (retstatus != CAAM_NO_ERROR) {
289 ret = caam_status_to_tee_result(retstatus);
290 goto out;
291 }
292
293 /*
294 * Build the descriptor using the PDB Public Key generation
295 * block (PD=0)
296 */
297 caam_desc_init(desc);
298 caam_desc_add_word(desc, DESC_HEADER(0));
299 caam_desc_add_word(desc, PDB_DL_KEY_L_SIZE(l_bytes) |
300 PDB_DL_KEY_N_SIZE(n_bytes));
301 caam_desc_add_ptr(desc, caam_dh_key.p.paddr);
302 caam_desc_add_ptr(desc, dh_r.paddr);
303 caam_desc_add_ptr(desc, caam_dh_key.g.paddr);
304 caam_desc_add_ptr(desc, caam_dh_key.x.paddr);
305 caam_desc_add_ptr(desc, caam_dh_key.y.paddr);
306 caam_desc_add_word(desc, PK_KEYPAIR_GEN(DL));
307
308 desclen = caam_desc_get_len(desc);
309 caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
310
311 DH_DUMPDESC(desc);
312
313 /*
314 * If the Secure Key X doesn't have the correct size
315 * retry a new generation.
316 * Retry 10 times before returing an error to not lock the system.
317 */
318 for (counter = 0; counter < 10; counter++) {
319 memset(&jobctx, 0, sizeof(jobctx));
320 jobctx.desc = desc;
321 retstatus = caam_jr_enqueue(&jobctx, NULL);
322
323 if (retstatus == CAAM_NO_ERROR) {
324 cache_operation(TEE_CACHEINVALIDATE, caam_dh_key.x.data,
325 caam_dh_key.x.length);
326 cache_operation(TEE_CACHEINVALIDATE, caam_dh_key.y.data,
327 caam_dh_key.y.length);
328
329 /* Copy Private and Public keypair */
330 ret = crypto_bignum_bin2bn(caam_dh_key.x.data,
331 caam_dh_key.x.length,
332 key->x);
333 if (ret != TEE_SUCCESS)
334 goto out;
335
336 if (crypto_bignum_num_bytes(key->x) != n_bytes) {
337 DH_TRACE("Error X size=%zu expected %zu",
338 crypto_bignum_num_bytes(key->x),
339 n_bytes);
340 DH_DUMPBUF("X", caam_dh_key.x.data,
341 caam_dh_key.x.length);
342 DH_DUMPBUF("Y", caam_dh_key.y.data,
343 caam_dh_key.y.length);
344 continue;
345 }
346
347 ret = crypto_bignum_bin2bn(caam_dh_key.y.data,
348 caam_dh_key.y.length,
349 key->y);
350 if (ret != TEE_SUCCESS)
351 goto out;
352
353 /* Set the Private Key size in bits */
354 key->xbits = n_bytes * 8;
355
356 ret = TEE_SUCCESS;
357 goto out;
358 } else {
359 DH_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
360 ret = job_status_to_tee_result(jobctx.status);
361 goto out;
362 }
363 }
364
365 out:
366 caam_free_desc(&desc);
367 caam_free_buf(&dh_r);
368 do_keypair_free(&caam_dh_key);
369
370 return ret;
371 }
372
373 /*
374 * Compute the shared secret data from DH Private key and Public Key
375 *
376 * @sdata [in/out] DH Shared Secret data
377 */
do_shared_secret(struct drvcrypt_secret_data * sdata)378 static TEE_Result do_shared_secret(struct drvcrypt_secret_data *sdata)
379 {
380 TEE_Result ret = TEE_ERROR_GENERIC;
381 enum caam_status retstatus = CAAM_FAILURE;
382 struct dh_keypair *inkeypair = sdata->key_priv;
383 struct caam_dh_keypair caam_dh_key = { };
384 struct caamdmaobj secret = { };
385 struct caam_jobctx jobctx = { };
386 uint32_t *desc = NULL;
387 uint32_t desclen = 0;
388 uint32_t pdb_sgt_flags = 0;
389
390 DH_TRACE("DH Shared Secret");
391
392 /* Allocate the job descriptor */
393 desc = caam_calloc_desc(MAX_DESC_SHARED);
394 if (!desc) {
395 ret = TEE_ERROR_OUT_OF_MEMORY;
396 goto out;
397 }
398
399 /*
400 * ReAllocate the secret result buffer with a maximum size
401 * of the secret size if not cache aligned
402 */
403 ret = caam_dmaobj_output_sgtbuf(&secret, sdata->secret.data,
404 sdata->secret.length,
405 sdata->secret.length);
406 if (ret)
407 goto out;
408
409 if (secret.sgtbuf.sgt_type)
410 pdb_sgt_flags |= PDB_SGT_PKDH_SECRET;
411
412 caam_dmaobj_cache_push(&secret);
413
414 /* Convert the Private key to local key */
415 retstatus = do_keypriv_conv(&caam_dh_key, inkeypair);
416 if (retstatus != CAAM_NO_ERROR) {
417 ret = TEE_ERROR_OUT_OF_MEMORY;
418 goto out;
419 }
420
421 /* Convert the Public key to local key */
422 retstatus = do_keypub_conv(&caam_dh_key, sdata->key_pub);
423 if (retstatus != CAAM_NO_ERROR) {
424 ret = caam_status_to_tee_result(retstatus);
425 goto out;
426 }
427
428 /*
429 * Build the descriptor using PDB Shared Secret
430 */
431 caam_desc_init(desc);
432 caam_desc_add_word(desc, DESC_HEADER(0));
433 caam_desc_add_word(desc, pdb_sgt_flags |
434 PDB_DL_KEY_L_SIZE(caam_dh_key.y.length) |
435 PDB_DL_KEY_N_SIZE(caam_dh_key.x.length));
436 /* Prime */
437 caam_desc_add_ptr(desc, caam_dh_key.p.paddr);
438 /* Modulus - Not used */
439 caam_desc_add_ptr(desc, 0);
440 /* Public key */
441 caam_desc_add_ptr(desc, caam_dh_key.y.paddr);
442 /* Private key */
443 caam_desc_add_ptr(desc, caam_dh_key.x.paddr);
444 /* Output secret */
445 caam_desc_add_ptr(desc, secret.sgtbuf.paddr);
446
447 caam_desc_add_word(desc, SHARED_SECRET(DL));
448 desclen = caam_desc_get_len(desc);
449 caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
450
451 DH_DUMPDESC(desc);
452 jobctx.desc = desc;
453
454 retstatus = caam_jr_enqueue(&jobctx, NULL);
455
456 if (retstatus == CAAM_NO_ERROR) {
457 sdata->secret.length = caam_dmaobj_copy_to_orig(&secret);
458
459 DH_DUMPBUF("Secret", sdata->secret.data, sdata->secret.length);
460 ret = caam_status_to_tee_result(retstatus);
461 } else {
462 DH_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
463 ret = job_status_to_tee_result(jobctx.status);
464 }
465
466 out:
467 caam_free_desc(&desc);
468 do_keypair_free(&caam_dh_key);
469 caam_dmaobj_free(&secret);
470
471 return ret;
472 }
473
474 /*
475 * Registration of the ECC Driver
476 */
477 static struct drvcrypt_dh driver_dh = {
478 .alloc_keypair = do_allocate_keypair,
479 .gen_keypair = do_gen_keypair,
480 .shared_secret = do_shared_secret,
481 };
482
caam_dh_init(struct caam_jrcfg * caam_jrcfg)483 enum caam_status caam_dh_init(struct caam_jrcfg *caam_jrcfg)
484 {
485 enum caam_status retstatus = CAAM_FAILURE;
486 vaddr_t jr_base = caam_jrcfg->base + caam_jrcfg->offset;
487
488 if (caam_hal_ctrl_pknum(jr_base) &&
489 drvcrypt_register_dh(&driver_dh) == TEE_SUCCESS)
490 retstatus = CAAM_NO_ERROR;
491
492 return retstatus;
493 }
494