1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright 2018-2021, 2023 NXP
4 */
5 #include <caam_hal_ctrl.h>
6 #include <caam_jr.h>
7 #include <caam_mp.h>
8 #include <caam_status.h>
9 #include <caam_utils_mem.h>
10 #include <caam_utils_status.h>
11 #include <drivers/caam_extension.h>
12 #include <kernel/pm.h>
13 #include <mm/core_memprot.h>
14 #include <string.h>
15 #include <tee/cache.h>
16 #include <tee_api_types.h>
17 #include <utee_defines.h>
18
19 #define MP_SIGN_MAX_MSG_SIZE (4 * 1024)
20
21 #ifdef CFG_PHYS_64BIT
22 #define MP_PRIV_DESC_ENTRIES 7
23 #define MP_PUB_DESC_ENTRIES 7
24 #define MP_SIGN_DESC_ENTRIES 13
25 #else
26 #define MP_PRIV_DESC_ENTRIES 6
27 #define MP_PUB_DESC_ENTRIES 6
28 #define MP_SIGN_DESC_ENTRIES 9
29 #endif
30
31 /*
32 * MP module private data
33 */
34 static struct mp_privdata {
35 uint8_t curve; /* Protocol Data Block curve selection */
36 uint8_t sec_size; /* Security key size in bytes */
37 vaddr_t ctrl_addr; /* Base address of the controller */
38 enum caam_status mp_status; /* Indicate the MP status */
39 } mp_privdata;
40
41 /*
42 * Generate manufacturing private key.
43 * The ECDSA private key is securely stored in the MPPKR.
44 * This register is locked to prevent reading or writing.
45 *
46 * @passphrase Passphrase
47 * @len Passphrase length
48 */
do_mppriv_gen(const char * passphrase,size_t len)49 static enum caam_status do_mppriv_gen(const char *passphrase, size_t len)
50 {
51 enum caam_status ret = CAAM_FAILURE;
52 struct caam_jobctx jobctx = { };
53 uint32_t *desc = NULL;
54 uint32_t desclen = 0;
55
56 MP_TRACE("MP private key generation");
57
58 assert(passphrase && len);
59
60 desc = caam_calloc_desc(MP_PRIV_DESC_ENTRIES);
61 if (!desc)
62 return CAAM_OUT_MEMORY;
63
64 caam_desc_init(desc);
65 caam_desc_add_word(desc, DESC_HEADER(0));
66 caam_desc_add_word(desc, PROT_MP_CURVE(mp_privdata.curve));
67 caam_desc_add_ptr(desc, virt_to_phys((void *)passphrase));
68 caam_desc_add_word(desc, len);
69 caam_desc_add_word(desc, MPPRIVK);
70
71 desclen = caam_desc_get_len(desc);
72 caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
73
74 MP_DUMPDESC(desc);
75
76 cache_operation(TEE_CACHECLEAN, (void *)passphrase, len);
77
78 jobctx.desc = desc;
79 ret = caam_jr_enqueue(&jobctx, NULL);
80
81 if (ret != CAAM_NO_ERROR) {
82 MP_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
83 ret = CAAM_NOT_SUPPORTED;
84 }
85
86 caam_free_desc(&desc);
87 return ret;
88 }
89
caam_mp_export_mpmr(uint8_t * mpmr,size_t * size)90 TEE_Result caam_mp_export_mpmr(uint8_t *mpmr, size_t *size)
91 {
92 TEE_Result ret = TEE_ERROR_GENERIC;
93 struct caambuf caam_mpmr = {
94 .data = mpmr,
95 .length = *size,
96 };
97
98 MP_TRACE("Get MP message");
99
100 ret = caam_hal_ctrl_read_mpmr(mp_privdata.ctrl_addr, &caam_mpmr);
101 *size = caam_mpmr.length;
102
103 return ret;
104 }
105
caam_mp_export_publickey(uint8_t * pubkey,size_t * size)106 TEE_Result caam_mp_export_publickey(uint8_t *pubkey, size_t *size)
107 {
108 TEE_Result ret = TEE_ERROR_GENERIC;
109 enum caam_status retstatus = CAAM_FAILURE;
110 struct caam_jobctx jobctx = { };
111 struct caamdmaobj reskey = { };
112 uint32_t pdb_sgt_flag = 0;
113 uint32_t desclen = 0;
114 uint32_t *desc = NULL;
115
116 /* Check if MP is operational */
117 if (mp_privdata.mp_status != CAAM_NO_ERROR)
118 return caam_status_to_tee_result(mp_privdata.mp_status);
119
120 if (!pubkey || !size)
121 return TEE_ERROR_BAD_PARAMETERS;
122
123 /* The public key size is twice the private key size */
124 if (*size < 2 * mp_privdata.sec_size) {
125 *size = 2 * mp_privdata.sec_size;
126 return TEE_ERROR_SHORT_BUFFER;
127 }
128
129 ret = caam_dmaobj_output_sgtbuf(&reskey, pubkey, *size,
130 2 * mp_privdata.sec_size);
131 if (ret)
132 return ret;
133
134 if (reskey.sgtbuf.sgt_type)
135 pdb_sgt_flag = PROT_MP_PUBK_SGT;
136
137 desc = caam_calloc_desc(MP_PUB_DESC_ENTRIES);
138 if (!desc) {
139 ret = TEE_ERROR_OUT_OF_MEMORY;
140 goto out;
141 }
142
143 caam_desc_init(desc);
144 caam_desc_add_word(desc, DESC_HEADER(0));
145 caam_desc_add_word(desc,
146 PROT_MP_CURVE(mp_privdata.curve) | pdb_sgt_flag);
147 caam_desc_add_ptr(desc, reskey.sgtbuf.paddr);
148 caam_desc_add_word(desc, reskey.sgtbuf.length);
149 caam_desc_add_word(desc, MPPUBK);
150
151 desclen = caam_desc_get_len(desc);
152 caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
153
154 MP_DUMPDESC(desc);
155
156 caam_dmaobj_cache_push(&reskey);
157
158 jobctx.desc = desc;
159 retstatus = caam_jr_enqueue(&jobctx, NULL);
160
161 if (retstatus == CAAM_NO_ERROR) {
162 MP_TRACE("MP Public Key generated");
163 reskey.orig.length = 2 * mp_privdata.sec_size;
164 *size = caam_dmaobj_copy_to_orig(&reskey);
165
166 MP_DUMPBUF("MP PubKey", pubkey, *size);
167
168 ret = caam_status_to_tee_result(retstatus);
169 } else {
170 MP_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
171 ret = job_status_to_tee_result(jobctx.status);
172 }
173
174 out:
175 caam_dmaobj_free(&reskey);
176 caam_free_desc(&desc);
177
178 return ret;
179 }
180
caam_mp_sign(uint8_t * msg,size_t * msg_size,uint8_t * sig,size_t * sig_size)181 TEE_Result caam_mp_sign(uint8_t *msg, size_t *msg_size, uint8_t *sig,
182 size_t *sig_size)
183 {
184 TEE_Result ret = TEE_ERROR_GENERIC;
185 enum caam_status retstatus = CAAM_FAILURE;
186 struct caam_jobctx jobctx = { };
187 struct caamdmaobj msg_input = { };
188 struct caamdmaobj sign_c = { };
189 struct caamdmaobj sign_d = { };
190 struct caambuf hash = { };
191 uint32_t *desc = NULL;
192 uint32_t desclen = 0;
193 uint32_t pdb_sgt_flags = 0;
194 uint8_t *aligned_msg = NULL;
195 size_t sign_len = 0;
196
197 MP_TRACE("MP sign operation");
198
199 /* Check if MP is operational */
200 if (mp_privdata.mp_status != CAAM_NO_ERROR)
201 return caam_status_to_tee_result(mp_privdata.mp_status);
202
203 if (!msg || !msg_size || !sig || !sig_size)
204 return TEE_ERROR_BAD_PARAMETERS;
205
206 if (*sig_size < 2 * mp_privdata.sec_size) {
207 *sig_size = 2 * mp_privdata.sec_size;
208 return TEE_ERROR_SHORT_BUFFER;
209 }
210
211 if (*msg_size > MP_SIGN_MAX_MSG_SIZE)
212 return TEE_ERROR_EXCESS_DATA;
213
214 /* Re-allocate the message to a cache-aligned buffer */
215 aligned_msg = caam_alloc(*msg_size);
216 if (!aligned_msg) {
217 MP_TRACE("Message reallocation error");
218 ret = TEE_ERROR_OUT_OF_MEMORY;
219 goto exit_mpsign;
220 }
221 memcpy(aligned_msg, msg, *msg_size);
222
223 /*
224 * Allocate the hash buffer of the Message + MPMR payload
225 * Note: Hash is not retrieve, hence no need to do cache
226 * maintenance
227 */
228 retstatus = caam_alloc_align_buf(&hash, TEE_MAX_HASH_SIZE);
229 if (retstatus != CAAM_NO_ERROR) {
230 MP_TRACE("Hash allocation error");
231 ret = caam_status_to_tee_result(retstatus);
232 goto exit_mpsign;
233 }
234
235 /*
236 * Re-allocate the signature result buffer with a maximum size
237 * of the roundup to 16 bytes of the secure size in bytes if
238 * the signature buffer is not aligned or too short.
239 *
240 * - 1st Part: size_sec
241 * - 2nd Part: size_sec roundup to 16 bytes
242 */
243 sign_len = ROUNDUP(mp_privdata.sec_size, 16) + mp_privdata.sec_size;
244
245 ret = caam_dmaobj_output_sgtbuf(&sign_c, sig, *sig_size, sign_len);
246 if (ret)
247 goto exit_mpsign;
248
249 if (sign_c.sgtbuf.sgt_type)
250 pdb_sgt_flags |= PDB_SGT_MP_SIGN_C;
251
252 /* Prepare the 2nd Part of the signature. Derived from sign_c */
253 ret = caam_dmaobj_derive_sgtbuf(&sign_d, &sign_c, mp_privdata.sec_size,
254 ROUNDUP(mp_privdata.sec_size, 16));
255 if (ret)
256 goto exit_mpsign;
257
258 if (sign_d.sgtbuf.sgt_type)
259 pdb_sgt_flags |= PDB_SGT_MP_SIGN_D;
260
261 ret = caam_dmaobj_input_sgtbuf(&msg_input, aligned_msg, *msg_size);
262 if (ret)
263 goto exit_mpsign;
264
265 if (msg_input.sgtbuf.sgt_type)
266 pdb_sgt_flags |= PDB_SGT_MP_SIGN_MSG;
267
268 desc = caam_calloc_desc(MP_SIGN_DESC_ENTRIES);
269 if (!desc) {
270 ret = TEE_ERROR_OUT_OF_MEMORY;
271 goto exit_mpsign;
272 }
273
274 caam_desc_init(desc);
275 caam_desc_add_word(desc, DESC_HEADER(0));
276 caam_desc_add_word(desc,
277 PROT_MP_CURVE(mp_privdata.curve) | pdb_sgt_flags);
278 caam_desc_add_ptr(desc, msg_input.sgtbuf.paddr);
279 caam_desc_add_ptr(desc, hash.paddr);
280 caam_desc_add_ptr(desc, sign_c.sgtbuf.paddr);
281 caam_desc_add_ptr(desc, sign_d.sgtbuf.paddr);
282 caam_desc_add_word(desc, msg_input.sgtbuf.length);
283 caam_desc_add_word(desc, MPSIGN_OP);
284
285 desclen = caam_desc_get_len(desc);
286 caam_desc_update_hdr(desc, DESC_HEADER_IDX(desclen, desclen - 1));
287
288 MP_DUMPDESC(desc);
289
290 caam_dmaobj_cache_push(&msg_input);
291 caam_dmaobj_cache_push(&sign_c);
292
293 jobctx.desc = desc;
294 retstatus = caam_jr_enqueue(&jobctx, NULL);
295
296 if (retstatus == CAAM_NO_ERROR) {
297 sign_c.orig.length = 2 * mp_privdata.sec_size;
298 *sig_size = caam_dmaobj_copy_to_orig(&sign_c);
299
300 MP_DUMPBUF("MP Signature", sdata->signature.data,
301 sdata->signature.length);
302
303 ret = caam_status_to_tee_result(retstatus);
304 } else {
305 MP_TRACE("CAAM Status 0x%08" PRIx32, jobctx.status);
306 ret = job_status_to_tee_result(jobctx.status);
307 }
308
309 exit_mpsign:
310 caam_free(aligned_msg);
311 caam_free_buf(&hash);
312 caam_free_desc(&desc);
313 caam_dmaobj_free(&msg_input);
314 caam_dmaobj_free(&sign_c);
315 caam_dmaobj_free(&sign_d);
316
317 return ret;
318 }
319
caam_mp_init(vaddr_t ctrl_addr)320 enum caam_status caam_mp_init(vaddr_t ctrl_addr)
321 {
322 /*
323 * Manufacturing protection secret values for DSA key pair
324 * generation.
325 */
326 static const char passphrase[] = "manufacturing protection";
327 static const char mpmr_data[] = "value to fill the MPMR content";
328 enum caam_status retstatus = CAAM_FAILURE;
329 uint8_t curve = 0;
330 uint8_t hash_limit = 0;
331
332 struct caambuf msg_mpmr = {
333 .data = (uint8_t *)mpmr_data,
334 .length = strlen(mpmr_data)
335 };
336
337 mp_privdata.ctrl_addr = ctrl_addr;
338 mp_privdata.mp_status = CAAM_NOT_INIT;
339
340 curve = caam_hal_ctrl_get_mpcurve(ctrl_addr);
341
342 if (curve == UINT8_MAX) {
343 mp_privdata.mp_status = CAAM_NOT_SUPPORTED;
344 return mp_privdata.mp_status;
345 }
346
347 if (caam_hal_ctrl_is_mp_set(ctrl_addr)) {
348 mp_privdata.mp_status = CAAM_NO_ERROR;
349 return CAAM_NO_ERROR;
350 }
351
352 if (!curve) {
353 /* Get the device HASH Limit to select the MP Curve */
354 hash_limit = caam_hal_ctrl_hash_limit(ctrl_addr);
355
356 switch (hash_limit) {
357 case TEE_MAIN_ALGO_SHA256:
358 mp_privdata.curve = PDB_MP_CSEL_P256;
359 mp_privdata.sec_size = 32;
360 break;
361 case TEE_MAIN_ALGO_SHA512:
362 mp_privdata.curve = PDB_MP_CSEL_P521;
363 mp_privdata.sec_size = 66;
364 break;
365 default:
366 MP_TRACE("This curve doesn't exist");
367 return CAAM_FAILURE;
368 }
369
370 MP_TRACE("Generating MP Private key");
371 retstatus = do_mppriv_gen(passphrase, strlen(passphrase));
372
373 if (retstatus != CAAM_NO_ERROR) {
374 MP_TRACE("do_mppriv_gen failed!");
375 return retstatus;
376 }
377 } else {
378 /* MP Curve is already programmed. Set the right key size */
379 mp_privdata.curve = curve;
380
381 switch (curve) {
382 case PDB_MP_CSEL_P256:
383 mp_privdata.sec_size = 32;
384 break;
385 case PDB_MP_CSEL_P521:
386 mp_privdata.sec_size = 66;
387 break;
388 default:
389 MP_TRACE("This curve is not supported");
390 return CAAM_FAILURE;
391 }
392 }
393
394 /* Fill the MPMR content then lock it */
395 caam_hal_ctrl_fill_mpmr(ctrl_addr, &msg_mpmr);
396
397 mp_privdata.mp_status = CAAM_NO_ERROR;
398
399 return CAAM_NO_ERROR;
400 }
401
caam_mp_resume(uint32_t pm_hint)402 enum caam_status caam_mp_resume(uint32_t pm_hint)
403 {
404 if (pm_hint == PM_HINT_CONTEXT_STATE)
405 return caam_mp_init(mp_privdata.ctrl_addr);
406
407 return CAAM_NO_ERROR;
408 }
409