1 /*
2 * Copyright (c) 2019,2020 Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdio.h>
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/logging/log_ctrl.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/data/json.h>
13
14 #include "mbedtls/pk.h"
15 #include "mbedtls/x509.h"
16 #include "mbedtls/x509_csr.h"
17
18 #include "psa_crypto.h"
19 #include "util_app_log.h"
20 #include "util_sformat.h"
21
22 /** Declare a reference to the application logging interface. */
23 LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL);
24
25 /* Formatting details for displaying hex dumps. */
26 struct sf_hex_tbl_fmt crp_fmt = {
27 .ascii = true,
28 .addr_label = true,
29 .addr = 0
30 };
31
32 struct csr_json_struct {
33 const char *CSR;
34 };
35
36 static const struct json_obj_descr csr_json_descr[] = {
37 JSON_OBJ_DESCR_PRIM(struct csr_json_struct, CSR, JSON_TOK_STRING)
38 };
39
40 /**
41 * @brief Extracts the public key from the specified persistent key id.
42 *
43 * @param key_id The permanent identifier for the generated key.
44 * @param key Pointer to the buffer where the public key data
45 * will be written.
46 * @param key_buf_size Size of key buffer in bytes.
47 * @param key_len Number of bytes written into key by this function.
48 */
crp_get_pub_key(psa_key_id_t key_id,uint8_t * key,size_t key_buf_size,size_t * key_len)49 static psa_status_t crp_get_pub_key(psa_key_id_t key_id,
50 uint8_t *key, size_t key_buf_size,
51 size_t *key_len)
52 {
53 psa_status_t status;
54 psa_key_handle_t key_handle;
55
56 LOG_INF("Retrieving public key for key #%d", key_id);
57 al_dump_log();
58
59 /* Now try to re-open the persisted key based on the key ID. */
60 status = al_psa_status(
61 psa_open_key(key_id, &key_handle),
62 __func__);
63 if (status != PSA_SUCCESS) {
64 LOG_ERR("Failed to open persistent key #%d", key_id);
65 goto err;
66 }
67
68 /* Export the persistent key's public key part. */
69 status = al_psa_status(
70 psa_export_public_key(key_handle, key, key_buf_size, key_len),
71 __func__);
72 if (status != PSA_SUCCESS) {
73 LOG_ERR("Failed to export public key.");
74 goto err;
75 }
76
77 /* Display the binary key data for debug purposes. */
78 sf_hex_tabulate_16(&crp_fmt, key, *key_len);
79
80 /* Close the key to free up the volatile slot. */
81 status = al_psa_status(
82 psa_close_key(key_handle),
83 __func__);
84 if (status != PSA_SUCCESS) {
85 LOG_ERR("Failed to close persistent key.");
86 goto err;
87 }
88
89 return status;
90 err:
91 al_dump_log();
92 return status;
93 }
94
95 #if CONFIG_PSA_IMPORT_KEY
96 /**
97 * @brief Stores a new persistent secp256r1 key (usage: ecdsa-with-SHA256)
98 * in ITS, associating it with the specified unique key identifier.
99 *
100 * This function will store a new persistent secp256r1 key in internal trusted
101 * storage. Cryptographic operations can then be performed using the key
102 * identifier (key_id) associated with this persistent key. Only the 32-byte
103 * private key needs to be supplied, the public key can be derived using
104 * the supplied private key value.
105 *
106 * @param key_id The permament identifier for the generated key.
107 * @param key_usage The usage policy for the key.
108 * @param key_data Pointer to the 32-byte private key data.
109 */
crp_imp_key_secp256r1(psa_key_id_t key_id,psa_key_usage_t key_usage,uint8_t * key_data)110 static psa_status_t crp_imp_key_secp256r1(psa_key_id_t key_id,
111 psa_key_usage_t key_usage,
112 uint8_t *key_data)
113 {
114 psa_status_t status = PSA_SUCCESS;
115 psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
116 psa_key_type_t key_type =
117 PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
118 psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
119 psa_key_handle_t key_handle;
120 size_t key_len = 32;
121 size_t data_len;
122 uint8_t data_out[65] = { 0 }; /* ECDSA public key = 65 bytes. */
123 int comp_result;
124
125 LOG_INF("Persisting SECP256R1 key as #%d", (uint32_t)key_id);
126 al_dump_log();
127
128 /* Setup the key's attributes before the creation request. */
129 psa_set_key_id(&key_attributes, key_id);
130 psa_set_key_usage_flags(&key_attributes, key_usage);
131 psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
132 psa_set_key_algorithm(&key_attributes, alg);
133 psa_set_key_type(&key_attributes, key_type);
134
135 /* Import the private key, creating the persistent key on success */
136 status = al_psa_status(
137 psa_import_key(&key_attributes, key_data, key_len, &key_handle),
138 __func__);
139 if (status != PSA_SUCCESS) {
140 LOG_ERR("Failed to import key.");
141 goto err;
142 }
143
144 /* Close the key to free up the volatile slot. */
145 status = al_psa_status(
146 psa_close_key(key_handle),
147 __func__);
148 if (status != PSA_SUCCESS) {
149 LOG_ERR("Failed to close persistent key.");
150 goto err;
151 }
152
153 /* Try to retrieve the public key. */
154 status = crp_get_pub_key(key_id, data_out, sizeof(data_out), &data_len);
155
156 /* Export the private key if usage includes PSA_KEY_USAGE_EXPORT. */
157 if (key_usage & PSA_KEY_USAGE_EXPORT) {
158 /* Re-open the persisted key based on the key ID. */
159 status = al_psa_status(
160 psa_open_key(key_id, &key_handle),
161 __func__);
162 if (status != PSA_SUCCESS) {
163 LOG_ERR("Failed to open persistent key #%d", key_id);
164 goto err;
165 }
166
167 /* Read the original (private) key data back. */
168 status = al_psa_status(
169 psa_export_key(key_handle, data_out,
170 sizeof(data_out), &data_len),
171 __func__);
172 if (status != PSA_SUCCESS) {
173 LOG_ERR("Failed to export key.");
174 goto err;
175 }
176
177 /* Check key len. */
178 if (data_len != key_len) {
179 LOG_ERR("Unexpected number of bytes in exported key.");
180 goto err;
181 }
182
183 /* Verify that the exported private key matches input data. */
184 comp_result = memcmp(data_out, key_data, key_len);
185 if (comp_result != 0) {
186 LOG_ERR("Imported/exported private key mismatch.");
187 goto err;
188 }
189
190 /* Display the private key. */
191 LOG_INF("Private key data:");
192 al_dump_log();
193 sf_hex_tabulate_16(&crp_fmt, data_out, data_len);
194
195 /* Close the key to free up the volatile slot. */
196 status = al_psa_status(
197 psa_close_key(key_handle),
198 __func__);
199 if (status != PSA_SUCCESS) {
200 LOG_ERR("Failed to close persistent key.");
201 goto err;
202 }
203 }
204
205 return status;
206 err:
207 al_dump_log();
208 return status;
209 }
210
211 #else /* !CONFIG_PSA_IMPORT_KEY */
212 /**
213 * @brief Generates a new permanent, persistent prime256v1 (ecdsa-with-SHA256)
214 * key in ITS, associating it with the specified unique key identifier.
215 *
216 * This function will generate a new permanent prime256v1 key in internal trusted
217 * storage. Cryptographic operations can then be performed using the key
218 * identifier (key_id) associated with this persistent key.
219 *
220 * @param key_id The permanent identifier for the generated key.
221 * @param key_usage The usage policy for the key.
222 */
crp_gen_key_secp256r1(psa_key_id_t key_id,psa_key_usage_t key_usage)223 static psa_status_t crp_gen_key_secp256r1(psa_key_id_t key_id,
224 psa_key_usage_t key_usage)
225 {
226 psa_status_t status = PSA_SUCCESS;
227 psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
228 psa_key_type_t key_type =
229 PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
230 psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
231 psa_key_handle_t key_handle;
232 size_t key_len = 32;
233 size_t data_len;
234 uint8_t data_out[65] = { 0 }; /* ECDSA public key = 65 bytes. */
235
236 LOG_INF("Persisting SECP256R1 key as #%d", (uint32_t)key_id);
237 al_dump_log();
238
239 /* Setup the key's attributes before the creation request. */
240 psa_set_key_id(&key_attributes, key_id);
241 psa_set_key_usage_flags(&key_attributes, key_usage);
242 psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
243 psa_set_key_algorithm(&key_attributes, alg);
244 psa_set_key_type(&key_attributes, key_type);
245 psa_set_key_bits(&key_attributes, 256);
246
247 /* Generate the private key, creating the persistent key on success */
248 status = al_psa_status(
249 psa_generate_key(&key_attributes, &key_handle),
250 __func__);
251 if (status != PSA_SUCCESS) {
252 LOG_ERR("Failed to generate key.");
253 goto err;
254 }
255
256 /* Close the key to free up the volatile slot. */
257 status = al_psa_status(
258 psa_close_key(key_handle),
259 __func__);
260 if (status != PSA_SUCCESS) {
261 LOG_ERR("Failed to close persistent key.");
262 goto err;
263 }
264
265 /* Try to retrieve the public key. */
266 status = crp_get_pub_key(key_id, data_out, sizeof(data_out), &data_len);
267
268 /* Export the private key if usage includes PSA_KEY_USAGE_EXPORT. */
269 if (key_usage & PSA_KEY_USAGE_EXPORT) {
270 /* Re-open the persisted key based on the key ID. */
271 status = al_psa_status(
272 psa_open_key(key_id, &key_handle),
273 __func__);
274 if (status != PSA_SUCCESS) {
275 LOG_ERR("Failed to open persistent key #%d", key_id);
276 goto err;
277 }
278
279 /* Read the original (private) key data back. */
280 status = al_psa_status(
281 psa_export_key(key_handle, data_out,
282 sizeof(data_out), &data_len),
283 __func__);
284 if (status != PSA_SUCCESS) {
285 LOG_ERR("Failed to export key.");
286 goto err;
287 }
288
289 /* Check key len. */
290 if (data_len != key_len) {
291 LOG_ERR("Unexpected number of bytes in exported key.");
292 goto err;
293 }
294
295 /* Display the private key. */
296 LOG_INF("Private key data:");
297 al_dump_log();
298
299 sf_hex_tabulate_16(&crp_fmt, data_out, data_len);
300
301 /* Close the key to free up the volatile slot. */
302 status = al_psa_status(
303 psa_close_key(key_handle),
304 __func__);
305 if (status != PSA_SUCCESS) {
306 LOG_ERR("Failed to close persistent key.");
307 goto err;
308 }
309 }
310
311 return status;
312 err:
313 al_dump_log();
314 return status;
315 }
316 #endif /* CONFIG_PSA_IMPORT_KEY */
317
318 /**
319 * @brief PSA Random number generator wrapper for Mbed TLS
320 */
psa_rng_for_mbedtls(void * p_rng,unsigned char * output,size_t output_len)321 static int psa_rng_for_mbedtls(void *p_rng,
322 unsigned char *output, size_t output_len)
323 {
324 (void)p_rng;
325
326 return psa_generate_random(output, output_len);
327 }
328
329 /**
330 * @brief Generates device certificate signing request (CSR) using Mbed TLS
331 * X.509 and TF-M crypto service.
332 */
crp_generate_csr(void)333 void crp_generate_csr(void)
334 {
335 psa_status_t status;
336 psa_key_id_t key_slot = 1;
337 psa_key_handle_t key_handle;
338
339 unsigned char output_buf[1024];
340 unsigned char json_encoded_buf[1024];
341
342 mbedtls_pk_context pk_key_container;
343 mbedtls_x509write_csr req;
344
345 struct csr_json_struct csr_json = {
346 .CSR = output_buf
347 };
348
349 /* Initialize Mbed TLS structures. */
350 mbedtls_x509write_csr_init(&req);
351 mbedtls_pk_init(&pk_key_container);
352 memset(output_buf, 0, sizeof(output_buf));
353
354 /* Initialize crypto API. */
355 LOG_INF("Initialising PSA crypto");
356 al_dump_log();
357
358 status = al_psa_status(psa_crypto_init(), __func__);
359 if (status != PSA_SUCCESS) {
360 LOG_ERR("Crypto init failed.");
361 goto err;
362 }
363
364 LOG_INF("PSA crypto init completed");
365 al_dump_log();
366
367 /* prime256v1 (ecdsa-with-SHA256) private key. */
368 #if CONFIG_PSA_IMPORT_KEY
369 #if CONFIG_PRIVATE_KEY_STATIC
370 /* This value is based on the private key in user.pem,
371 * which can be viewed viw the following command:
372 *
373 * $ openssl ec -in user.pem -text -noout
374 */
375 uint8_t priv_key_data[32] = {
376 0x14, 0xbc, 0xb9, 0x53, 0xa4, 0xee, 0xed, 0x50,
377 0x09, 0x36, 0x92, 0x07, 0x1d, 0xdb, 0x24, 0x2c,
378 0xef, 0xf9, 0x57, 0x92, 0x40, 0x4f, 0x49, 0xaa,
379 0xd0, 0x7c, 0x5b, 0x3f, 0x26, 0xa7, 0x80, 0x48
380 };
381 #else /* !CONFIG_PRIVATE_KEY_STATIC */
382 /* Randomly generate the private key. */
383 uint8_t priv_key_data[32] = { 0 };
384
385 LOG_INF("Generate rnadom data for private key");
386 al_dump_log();
387
388 psa_generate_random(priv_key_data, sizeof(priv_key_data));
389 LOG_INF("Random data generation for private key completed");
390 al_dump_log();
391
392 #endif /* CONFIG_PRIVATE_KEY_STATIC */
393
394 /* Generate persistent prime256v1 (ecdsa-with-SHA256) key w/ID #1. */
395 /* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */
396 status = al_psa_status(
397 crp_imp_key_secp256r1(key_slot,
398 PSA_KEY_USAGE_SIGN_HASH |
399 PSA_KEY_USAGE_VERIFY_HASH,
400 priv_key_data),
401 __func__);
402 if (status != PSA_SUCCESS) {
403 LOG_ERR("Failed to create persistent key #%d", key_slot);
404 goto err;
405 }
406 #else /* !CONFIG_PSA_IMPORT_KEY */
407
408 /* NOTE: The certificate signing request (CSR) can be generated using
409 * openssl by using following commands:
410 *
411 * Generate a new key:
412 *
413 * $ openssl ecparam -name secp256k1 -genkey -out USER.key
414 *
415 * Generate a certificate signing request, containing the user public key
416 * and required details to be inserted into the user certificate.
417 * openssl req -new -key USER.key -out USER.csr \
418 * -subj "/O=Linaro/CN=$(uuidgen | tr '[:upper:]' '[:lower:]')"
419 *
420 */
421
422 /* Generate persistent prime256v1 (ecdsa-with-SHA256) key w/ID #1. */
423 /* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */
424 status = al_psa_status(
425 crp_gen_key_secp256r1(key_slot,
426 PSA_KEY_USAGE_SIGN_HASH |
427 PSA_KEY_USAGE_VERIFY_HASH),
428 __func__);
429 if (status != PSA_SUCCESS) {
430 LOG_ERR("Failed to create persistent key #%d", key_slot);
431 goto err;
432 }
433 #endif /* CONFIG_PSA_IMPORT_KEY */
434
435 status = al_psa_status(
436 psa_open_key(key_slot, &key_handle),
437 __func__);
438 if (status != PSA_SUCCESS) {
439 LOG_ERR("Failed to open persistent key #%d", key_slot);
440 goto err;
441 }
442
443 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
444
445 psa_get_key_attributes(key_handle, &attributes);
446 mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256);
447
448 LOG_INF("Adding subject name to CSR");
449 al_dump_log();
450
451 status = mbedtls_x509write_csr_set_subject_name(&req, "O=Linaro,CN=Device Certificate");
452 if (status != 0) {
453 LOG_ERR("failed! mbedtls_x509write_csr_set_subject_name returned %d", status);
454 goto err;
455 }
456
457 LOG_INF("Adding subject name to CSR completed");
458 al_dump_log();
459
460 LOG_INF("Adding EC key to PK container");
461 al_dump_log();
462
463 status = mbedtls_pk_setup_opaque(&pk_key_container, key_handle);
464 if (status != 0) {
465 LOG_ERR("failed! mbedtls_pk_setup_opaque returned -0x%04x", (unsigned int) -status);
466 goto err;
467 }
468
469 LOG_INF("Adding EC key to PK container completed");
470 al_dump_log();
471
472 mbedtls_x509write_csr_set_key(&req, &pk_key_container);
473
474 LOG_INF("Create device Certificate Signing Request");
475 al_dump_log();
476
477 status = mbedtls_x509write_csr_pem(&req, output_buf, sizeof(output_buf),
478 psa_rng_for_mbedtls, NULL);
479 if (status < 0) {
480 LOG_ERR("failed! mbedtls_x509write_csr_pem returned -0x%04x",
481 (unsigned int) -status);
482 goto err;
483 }
484
485 LOG_INF("Create device Certificate Signing Request completed");
486 al_dump_log();
487
488 LOG_INF("Certificate Signing Request:\n");
489 al_dump_log();
490
491 printf("%s\n", output_buf);
492
493 /*
494 * 1.3. Encoding CSR as JSON
495 */
496 LOG_INF("Encoding CSR as json");
497 al_dump_log();
498
499 status = json_obj_encode_buf(csr_json_descr, ARRAY_SIZE(csr_json_descr),
500 &csr_json, json_encoded_buf, sizeof(json_encoded_buf));
501
502 if (status != 0) {
503 LOG_ERR("failed! json_obj_encode_buf returned 0x%04x", status);
504 goto err;
505 }
506
507 LOG_INF("Encoding CSR as json completed");
508 al_dump_log();
509
510 LOG_INF("Certificate Signing Request in JSON:\n");
511 al_dump_log();
512
513 printf("%s\n\n", json_encoded_buf);
514
515 /* Close the key to free up the volatile slot. */
516 status = al_psa_status(
517 psa_close_key(key_handle),
518 __func__);
519 if (status != PSA_SUCCESS) {
520 LOG_ERR("Failed to close persistent key.");
521 goto err;
522 }
523
524 err:
525 al_dump_log();
526 mbedtls_x509write_csr_free(&req);
527 mbedtls_pk_free(&pk_key_container);
528 }
529
530 /**
531 * @brief Calculates the SHA256 hash for the supplied message.
532 *
533 * @param msg Pointer to the buffer to read when generating the hash.
534 * @param msg_len Number of bytes in msg.
535 * @param hash Pointer to the buffer where the hash should be written.
536 * @param hash_buf_size Size of hash in bytes.
537 * @param hash_len Placeholder for the number of hash bytes written.
538 */
crp_hash_payload(uint8_t * msg,size_t msg_len,uint8_t * hash,size_t hash_buf_size,size_t * hash_len)539 static psa_status_t crp_hash_payload(uint8_t *msg, size_t msg_len,
540 uint8_t *hash, size_t hash_buf_size,
541 size_t *hash_len)
542 {
543 psa_status_t status;
544 psa_hash_operation_t hash_handle = psa_hash_operation_init();
545 psa_algorithm_t alg = PSA_ALG_SHA_256;
546
547 LOG_INF("Calculating SHA-256 hash of value");
548 al_dump_log();
549
550 /* Display the input message */
551 sf_hex_tabulate_16(&crp_fmt, msg, msg_len);
552
553 /* Setup the hash object. */
554 status = al_psa_status(psa_hash_setup(&hash_handle, alg),
555 __func__);
556 if (status != PSA_SUCCESS) {
557 LOG_ERR("Failed to setup hash op.");
558 goto err;
559 }
560
561 /* Update object with all the message chunks. */
562 /* For the moment, the message is passed in a single operation, */
563 /* but this can be broken up in chunks for larger messages. */
564 status = al_psa_status(psa_hash_update(&hash_handle, msg, msg_len),
565 __func__);
566 if (status != PSA_SUCCESS) {
567 LOG_ERR("Failed to update hash.");
568 goto err;
569 }
570
571 /* Finalize the hash calculation. */
572 status = al_psa_status(psa_hash_finish(&hash_handle,
573 hash, hash_buf_size, hash_len),
574 __func__);
575 if (status != PSA_SUCCESS) {
576 LOG_ERR("Failed to finalize hash op.");
577 goto err;
578 }
579
580 /* Display the SHA-256 hash for debug purposes */
581 sf_hex_tabulate_16(&crp_fmt, hash, (size_t)(PSA_HASH_MAX_SIZE));
582
583 return status;
584 err:
585 psa_hash_abort(&hash_handle);
586 al_dump_log();
587 return status;
588 }
589
590 /**
591 * @brief Signs the supplied hash using the specified persistent key.
592 *
593 * @param key_id The identifier of the key to use when signing.
594 * @param hash Pointer to the buffer where the hash should be written.
595 * @param hash_buf_size Size of hash in bytes.
596 * @param sig Pointer to the buffer to read when generating the sig.
597 * @param sig_buf_size Size of sig buffer in bytes.
598 * @param sig_len Number of bytes written to sig.
599 */
crp_sign_hash(psa_key_id_t key_id,uint8_t * hash,size_t hash_buf_size,uint8_t * sig,size_t sig_buf_size,size_t * sig_len)600 static psa_status_t crp_sign_hash(psa_key_id_t key_id,
601 uint8_t *hash, size_t hash_buf_size,
602 uint8_t *sig, size_t sig_buf_size,
603 size_t *sig_len)
604 {
605 psa_status_t status;
606 psa_key_handle_t key_handle;
607
608 LOG_INF("Signing SHA-256 hash");
609 al_dump_log();
610
611 /* Try to open the persisted key based on the key ID. */
612 status = al_psa_status(
613 psa_open_key(key_id, &key_handle),
614 __func__);
615 if (status != PSA_SUCCESS) {
616 LOG_ERR("Failed to open persistent key #%d", key_id);
617 goto err;
618 }
619
620 /* Sign using psa_sign_hash. */
621 status = al_psa_status(
622 psa_sign_hash(key_handle,
623 PSA_ALG_ECDSA(PSA_ALG_SHA_256),
624 hash, hash_buf_size,
625 sig, sig_buf_size, sig_len),
626 __func__);
627 if (status != PSA_SUCCESS) {
628 LOG_ERR("Failed to sign hash w/persistent key #%d", key_id);
629 goto err;
630 }
631
632 /* Display the ECDSA signature for debug purposes */
633 sf_hex_tabulate_16(&crp_fmt, sig, *sig_len);
634
635 /* You can test this same operation with openssl as follows:
636 *
637 * $ openssl dgst -sha256 -sign
638 */
639
640 /* Close the key to free up the volatile slot. */
641 status = al_psa_status(
642 psa_close_key(key_handle),
643 __func__);
644 if (status != PSA_SUCCESS) {
645 LOG_ERR("Failed to close persistent key.");
646 goto err;
647 }
648
649 return status;
650 err:
651 al_dump_log();
652 return status;
653 }
654
655 /**
656 * @brief Verifies the hash signature using the public key associated
657 * with key_id.
658 *
659 * @param key_id The identifier for the persistent key.
660 * @param hash Pointer to the hash data to verify.
661 * @param hash_len Size of the hash buffer in bytes.
662 * @param sig Pointer to the signature buffer.
663 * @param sig_len Size of the signature buffer in bytes.
664 */
crp_verify_sign(psa_key_id_t key_id,uint8_t * hash,size_t hash_len,uint8_t * sig,size_t sig_len)665 static psa_status_t crp_verify_sign(psa_key_id_t key_id,
666 uint8_t *hash, size_t hash_len,
667 uint8_t *sig, size_t sig_len)
668 {
669 psa_status_t status;
670 psa_key_handle_t key_handle;
671
672 LOG_INF("Verifying signature for SHA-256 hash");
673 al_dump_log();
674
675 /* Try to open the persisted key based on the key ID. */
676 status = al_psa_status(
677 psa_open_key(key_id, &key_handle),
678 __func__);
679 if (status != PSA_SUCCESS) {
680 LOG_ERR("Failed to open persistent key #%d", key_id);
681 goto err;
682 }
683
684 /* Verify the hash signature. */
685 status = al_psa_status(
686 psa_verify_hash(key_handle,
687 PSA_ALG_ECDSA(PSA_ALG_SHA_256),
688 hash, hash_len,
689 sig, sig_len),
690 __func__);
691 if (status != PSA_SUCCESS) {
692 LOG_ERR("Signature verification failed!");
693 goto err;
694 }
695
696 LOG_INF("Signature verified.");
697 al_dump_log();
698
699 /* Close the key to free up the volatile slot. */
700 status = al_psa_status(
701 psa_close_key(key_handle),
702 __func__);
703 if (status != PSA_SUCCESS) {
704 LOG_ERR("Failed to close persistent key.");
705 goto err;
706 }
707
708 return status;
709 err:
710 al_dump_log();
711 return status;
712 }
713
714 /**
715 * @brief Destroys the specified persistent key.
716 *
717 * @param key_id The identifier for the persistent key.
718 */
crp_dest_key(psa_key_id_t key_id)719 static psa_status_t crp_dest_key(psa_key_id_t key_id)
720 {
721 psa_status_t status;
722 psa_key_handle_t key_handle;
723
724 /* Try to open the persisted key based on the key ID. */
725 status = al_psa_status(
726 psa_open_key(key_id, &key_handle),
727 __func__);
728 if (status != PSA_SUCCESS) {
729 LOG_ERR("Failed to open persistent key #%d", key_id);
730 goto err;
731 }
732
733 /* Destroy the persistent key */
734 status = al_psa_status(
735 psa_destroy_key(key_handle),
736 __func__);
737 if (status != PSA_SUCCESS) {
738 LOG_ERR("Failed to destroy a persistent key");
739 goto err;
740 }
741
742 LOG_INF("Destroyed persistent key #%d", (uint32_t)key_id);
743 al_dump_log();
744
745 return status;
746 err:
747 al_dump_log();
748 return status;
749 }
750
crp_test(void)751 void crp_test(void)
752 {
753 psa_status_t status;
754 uint8_t msg[] = "Please hash and sign this message.";
755 uint8_t hash[PSA_HASH_MAX_SIZE] = { 0 };
756 size_t hash_len;
757 uint8_t sig[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE] = { 0 };
758 size_t sig_len;
759
760 /* secp256r1 private key. */
761 #if CONFIG_PSA_IMPORT_KEY
762 #if CONFIG_PRIVATE_KEY_STATIC
763 /* This value is based on the private key in user.pem,
764 * which can be viewed viw the following command:
765 *
766 * $ openssl ec -in user.pem -text -noout
767 */
768 uint8_t priv_key_data[32] = {
769 0x14, 0xbc, 0xb9, 0x53, 0xa4, 0xee, 0xed, 0x50,
770 0x09, 0x36, 0x92, 0x07, 0x1d, 0xdb, 0x24, 0x2c,
771 0xef, 0xf9, 0x57, 0x92, 0x40, 0x4f, 0x49, 0xaa,
772 0xd0, 0x7c, 0x5b, 0x3f, 0x26, 0xa7, 0x80, 0x48
773 };
774 #else /* !CONFIG_PRIVATE_KEY_STATIC */
775 /* Randomly generate the private key. */
776 uint8_t priv_key_data[32] = { 0 };
777
778 psa_generate_random(priv_key_data, sizeof(priv_key_data));
779 #endif /* CONFIG_PRIVATE_KEY_STATIC */
780 #endif /* CONFIG_PSA_IMPORT_KEY */
781
782 /* Initialize crypto API. */
783 status = al_psa_status(psa_crypto_init(), __func__);
784 if (status != PSA_SUCCESS) {
785 LOG_ERR("Crypto init failed.");
786 return;
787 }
788
789 /* NOTE: The same key generation, SHA256 hash, sign, and verify
790 * operations performed in this file can be also performed with
791 * openssl using the commands described below.
792 *
793 * Generate a new key:
794 *
795 * The curve `prime256v1` is same as `secp256r1` in OpenSSL
796 * (https://github.com/openssl/openssl/blob/master/apps/ecparam.c#L216)
797 * $ openssl ecparam -name prime256v1 -genkey -out user.pem
798 *
799 * Display the public and private keys in hexadecimal format:
800 *
801 * $ openssl ec -in user.pem -text -noout
802 *
803 * Update the private key value in priv_key_data with the hexadecimal
804 * values from "priv:" to be able to compare the PSA API and openssl
805 * output.
806 *
807 * Generate a PEM file with the public key (which will be used to
808 * verify any data signed with the private key):
809 *
810 * $ openssl ec -in user.pem -pubout -out user_pub.pem
811 *
812 * Hash the message with SHA256, and sign it with the private key:
813 *
814 * $ echo "Please hash and sign this message." > message.txt
815 * $ openssl dgst -sha256 -sign user.pem message.txt > signature.der
816 *
817 * Verify the signature using the public key and message file:
818 *
819 * $ openssl dgst -sha256 -verify user_pub.pem \
820 * -signature signature.der message.txt
821 *
822 * If everything ws OK you should see "Verified OK".
823 */
824
825 /* Generate persistent secp256r1 key w/ID #1. */
826 /* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */
827 #if CONFIG_PSA_IMPORT_KEY
828 status = crp_imp_key_secp256r1(1,
829 PSA_KEY_USAGE_SIGN_HASH |
830 PSA_KEY_USAGE_VERIFY_HASH,
831 priv_key_data);
832 #else /* !CONFIG_PSA_IMPORT_KEY */
833 status = crp_gen_key_secp256r1(1,
834 PSA_KEY_USAGE_SIGN_HASH |
835 PSA_KEY_USAGE_VERIFY_HASH);
836 #endif
837
838 /* Hash some data with the key using SHA256. */
839 status = crp_hash_payload(msg, strlen(msg),
840 hash, sizeof(hash), &hash_len);
841
842 /* Sign the hash using key #1. */
843 status = crp_sign_hash(1,
844 hash, hash_len,
845 sig, sizeof(sig), &sig_len);
846
847 /* Verify the hash signature using the public key. */
848 status = crp_verify_sign(1, hash, hash_len, sig, sig_len);
849
850 /* Destroy the key. */
851 status = crp_dest_key(1);
852 }
853
854 /**
855 * @brief Generates random values using the TF-M crypto service.
856 */
crp_test_rng(void)857 void crp_test_rng(void)
858 {
859 psa_status_t status;
860 uint8_t outbuf[256] = { 0 };
861 struct sf_hex_tbl_fmt fmt = {
862 .ascii = true,
863 .addr_label = true,
864 .addr = 0
865 };
866
867 status = al_psa_status(psa_generate_random(outbuf, 256), __func__);
868 LOG_INF("Generating 256 bytes of random data.");
869 al_dump_log();
870 sf_hex_tabulate_16(&fmt, outbuf, 256);
871 }
872