samples: tfm: Re-commit psa_crypto sample

Adds a refactored version of the psa_crypto sample back,
which was removed as part of the update to TF-M 1.7.0
due to unresolvable (at the time) issues with use of
MbedTLS instances on the S and NS sides.

This sample takes advantage of changes to MbedTLS and
TF-M that were introduced after the TF-M 1.7.0 and MbedTLS
3.3 release, and cherry-picked in Zephyr, allowing for
improved linking of MbedTLS in secure and non-secure
images. PSA API calls on the non-secure side can now be
correctly routed to the secure partition, while X.509
and TLS calls remain on the non-secure/Zephyr side.

Signed-off-by: Rajkumar Kanagaraj <rajkumar.kanagaraj@linaro.org>
This commit is contained in:
Rajkumar Kanagaraj 2023-03-29 14:39:51 +01:00 committed by Anas Nashif
parent 80a06b2533
commit 682dbae203
21 changed files with 2248 additions and 0 deletions

View file

@ -0,0 +1,29 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(tfm_psa_crypto)
# Source files in this sample
target_sources(app PRIVATE src/main.c)
target_sources(app PRIVATE src/psa_attestation.c)
target_sources(app PRIVATE src/psa_crypto.c)
target_sources(app PRIVATE src/shell.c)
target_sources(app PRIVATE src/util_app_cfg.c)
target_sources(app PRIVATE src/util_app_log.c)
target_sources(app PRIVATE src/util_sformat.c)
target_include_directories(app PRIVATE
$<TARGET_PROPERTY:tfm,TFM_BINARY_DIR>/install/interface/include
)
# In TF-M, default value of CRYPTO_ENGINE_BUF_SIZE is 0x2080. It causes
# insufficient memory failure while verifying signature. Increase it to 0x2400.
set_property(TARGET zephyr_property_target
APPEND PROPERTY TFM_CMAKE_OPTIONS
-DCRYPTO_ENGINE_BUF_SIZE=0x2400
)
zephyr_include_directories(${APPLICATION_SOURCE_DIR}/src/tls_config)

View file

@ -0,0 +1,49 @@
# Private config options for PSA Crypto application
# Copyright (c) 2023 Linaro
# SPDX-License-Identifier: Apache-2.0
mainmenu "PSA Crypto sample application"
menu "Application configuration"
module = PSA
module-str = psa
source "subsys/logging/Kconfig.template.log_config"
endmenu
config PSA_SHELL
bool "The 'psa' shell command"
depends on SHELL
help
Enabling this option will make the 'psa' shell command available.
config PSA_IMPORT_KEY
bool "Support for importing private key data"
help
Enable support for importing a pre-generated or randomly generated
private key using PSA APIs and PRIVATE_KEY_STATIC or
PRIVATE_KEY_RANDOM.
choice
prompt "Private Key"
default PRIVATE_KEY_RANDOM
config PRIVATE_KEY_STATIC
bool "Static"
depends on PSA_IMPORT_KEY
help
A static key value will be used for the elliptic curve 'secp256r1'
private key.
config PRIVATE_KEY_RANDOM
bool "Random"
depends on PSA_IMPORT_KEY
help
A randomly generated value will be used for the elliptic curve
'secp256r1' private key.
endchoice
source "Kconfig.zephyr"

View file

@ -0,0 +1,388 @@
.. _tfm_psa_crypto:
TF-M PSA crypto
################
Overview
********
This TF-M integration example demonstrates how to use the PSA crypto API in
Zephyr for cryptography and device certificate signing request. In addition,
this example also demonstrates certain TF-M features that are covered as part
of the RTOS vendor requirements for a `PSA Certified Level 1`_ product, such
as secure storage for config data, initial attestation for device
verification.
Trusted Firmware (TF-M) Platform Security Architecture (PSA) APIs
are used for the secure processing environment, with Zephyr running in the
non-secure processing environment.
It uses **IPC Mode** for communication, where an IPC mechanism is inserted to
handle secure TF-M API calls and responses.
The sample prints test info to the console either as a single-thread or
multi-thread application.
.. _PSA Certified Level 1:
https://www.psacertified.org/security-certification/psa-certified-level-1/
Key Files
*********
``psa_crypto.c``
================
Demonstrates hash, sign/verify workflow:
- Generate/import a persistent key: secp256r1 (usage: ecdsa-with-SHA256)
- Display the public key based on the private key data above
- Calculate the SHA256 hash of a payload
- Sign the hash with the persistent key
- Verify the signature using the public key
- Destroy the key
Also demonstrates device certificate signing request (CSR) workflow:
- Generate/import a persistent key: secp256r1 (usage: ecdsa-with-SHA256)
- Set subject name in device CSR
- Generate device CSR in PEM format
- Encode device CSR as JSON
Importing/generating the persistent key is based on config option
``PSA_IMPORT_KEY``. When ``PSA_IMPORT_KEY`` is enabled,
the key data can be static if ``PRIVATE_KEY_STATIC`` is set or key data
is generated using ``psa_generate_random`` if ``PRIVATE_KEY_RANDOM``
is set.
``psa_attestation.c``
=====================
Demonstrates how to request an initial attestation token (IAT) from the TF-M
secure processing environment (SPE).
Building and Running
********************
This project outputs startup status and info to the console. It can be built and
executed on an ARM Cortex M33 target board or QEMU.
This sample will only build on a Linux or macOS development system
(not Windows), and has been tested on the following setups:
- macOS Mojave using QEMU 4.2.0 with gcc-arm-none-eabi-7-2018-q2-update
- macOS Mojave with gcc-arm-none-eabi-7-2018-q2-update
- Ubuntu 18.04 using Zephyr SDK 0.11.2
TF-M BL2 logs
=============
Add the following to ``prj.conf`` to see the logs from TF-M BL2:
.. code-block:: bash
CONFIG_TFM_BL2=y
CONFIG_TFM_CMAKE_BUILD_TYPE_DEBUG=y
On MPS2+ AN521:
===============
1. Build Zephyr with a non-secure configuration
(``-DBOARD=mps2_an521_ns``).
Using ``west``
.. code-block:: bash
cd <ZEPHYR_ROOT>
west build -p -b mps2_an521_ns samples/tfm_integration/psa_crypto
Using ``cmake`` and ``ninja``
.. code-block:: bash
cd <ZEPHYR_ROOT>/samples/tfm_integration/psa_crypto/
rm -rf build
mkdir build && cd build
cmake -GNinja -DBOARD=mps2_an521_ns ..
ninja
Using ``cmake`` and ``make``
.. code-block:: bash
cd <ZEPHYR_ROOT>/samples/tfm_integration/psa_crypto/
rm -rf build
mkdir build && cd build
cmake -DBOARD=mps2_an521_ns ..
make
2. Copy application binary files (mcuboot.bin and tfm_sign.bin) to
``<MPS2 device name>/SOFTWARE/``.
3. Edit (e.g., with vim) the ``<MPS2 device name>/MB/HBI0263C/AN521/images.txt``
file, and update it as shown below:
.. code-block:: bash
TITLE: Versatile Express Images Configuration File
[IMAGES]
TOTALIMAGES: 2 ;Number of Images (Max: 32)
IMAGE0ADDRESS: 0x10000000
IMAGE0FILE: \SOFTWARE\mcuboot.bin ; BL2 bootloader
IMAGE1ADDRESS: 0x10080000
IMAGE1FILE: \SOFTWARE\tfm_sign.bin ; TF-M with application binary blob
4. Save the file, exit the editor, and reset the MPS2+ board.
On QEMU:
========
Build Zephyr with a non-secure configuration (``-DBOARD=mps2_an521_ns``)
and run it in qemu via the ``run`` command.
Using ``west``
.. code-block:: bash
cd <ZEPHYR_ROOT>
west build -p -b mps2_an521_ns samples/tfm_integration/psa_crypto -t run
Using ``cmake`` and ``ninja``
.. code-block:: bash
cd <ZEPHYR_ROOT>/samples/tfm_integration/psa_crypto/
rm -rf build
mkdir build && cd build
cmake -GNinja -DBOARD=mps2_an521_ns ..
ninja run
Using ``cmake`` and ``make``
.. code-block:: bash
cd <ZEPHYR_ROOT>/samples/tfm_integration/psa_crypto/
rm -rf build
mkdir build && cd build
cmake -DBOARD=mps2_an521_ns ..
make run
On LPCxpresso55S69:
======================
Build Zephyr with a non-secure configuration:
.. code-block:: bash
$ west build -p -b lpcxpresso55s69_ns samples/tfm_integration/psa_crypto/ --
Make sure your board is set up with :ref:`lpclink2-jlink-onboard-debug-probe`,
since this isn't the debug interface boards ship with from the factory;
Next we need to manually flash the resulting image (``tfm_merged.bin``) with a
J-Link as follows:
.. code-block:: console
JLinkExe -device lpc55s69 -if swd -speed 2000 -autoconnect 1
J-Link>r
J-Link>erase
J-Link>loadfile build/tfm_merged.bin
Resetting the board and erasing it will unlock the board, this is useful in case
it's in an unknown state and can't be flashed.
We need to reset the board manually after flashing the image to run this code.
On nRF5340 and nRF9160:
=======================
Build Zephyr with a non-secure configuration
(``-DBOARD=nrf5340dk_nrf5340_cpuapp_ns`` or ``-DBOARD=nrf9160dk_nrf9160_ns``).
Example, for nRF9160, using ``cmake`` and ``ninja``
.. code-block:: bash
cd <ZEPHYR_ROOT>/samples/tfm_integration/psa_crypto/
rm -rf build
mkdir build && cd build
cmake -GNinja -DBOARD=nrf9160dk_nrf9160_ns ..
If building with BL2 (MCUboot bootloader) enabled, manually flash
the MCUboot bootloader image binary (``bl2.hex``).
Example, using ``nrfjprog`` on nRF9160:
.. code-block:: bash
nrfjprog -f NRF91 --program tfm/bin/bl2.hex --sectorerase
Finally, flash the concatenated TF-M + Zephyr binary.
Example, for nRF9160, using ``cmake`` and ``ninja``
.. code-block:: bash
ninja flash
On BL5340:
==========
Build Zephyr with a non-secure configuration
(``-DBOARD=bl5340_dvk_cpuapp_ns``).
Example using ``cmake`` and ``ninja``
.. code-block:: bash
cd <ZEPHYR_ROOT>/samples/tfm_integration/psa_crypto/
rm -rf build
mkdir build && cd build
cmake -GNinja -DBOARD=bl5340_dvk_cpuapp_ns ..
Flash the concatenated TF-M + Zephyr binary.
Example using ``west``
.. code-block:: bash
west flash --hex-file tfm_merged.hex
Sample Output
=============
.. code-block:: console
[Sec Thread] Secure image initializing!
Booting TFM v1.4.1
[Crypto] Dummy Entropy NV Seed is not suitable for production!
*** Booting Zephyr OS build v2.7.99-1102-gf503ba9f1ab3 ***
[00:00:00.014,000] <inf> app: app_cfg: Creating new config file with UID 0x1055CFDA7A
[00:00:01.215,000] <inf> app: att: System IAT size is: 545 bytes.
[00:00:01.215,000] <inf> app: att: Requesting IAT with 64 byte challenge.
[00:00:01.836,000] <inf> app: att: IAT data received: 545 bytes.
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 D2 84 43 A1 01 26 A0 59 01 D5 AA 3A 00 01 24 FF ..C..&.Y...:..$.
00000010 58 40 00 11 22 33 44 55 66 77 88 99 AA BB CC DD X@.."3DUfw......
00000020 EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD ...."3DUfw......
00000030 EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD ...."3DUfw......
00000040 EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD ...."3DUfw......
00000050 EE FF 3A 00 01 24 FB 58 20 A0 A1 A2 A3 A4 A5 A6 ..:..$.X .......
00000060 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 ................
00000070 B7 B8 B9 BA BB BC BD BE BF 3A 00 01 25 00 58 21 .........:..%.X!
00000080 01 FA 58 75 5F 65 86 27 CE 54 60 F2 9B 75 29 67 ..Xu_e.'.T`..u)g
00000090 13 24 8C AE 7A D9 E2 98 4B 90 28 0E FC BC B5 02 .$..z...K.(.....
000000A0 48 3A 00 01 24 FA 58 20 AA AA AA AA AA AA AA AA H:..$.X ........
000000B0 BB BB BB BB BB BB BB BB CC CC CC CC CC CC CC CC ................
000000C0 DD DD DD DD DD DD DD DD 3A 00 01 24 F8 20 3A 00 ........:..$. :.
000000D0 01 24 F9 19 30 00 3A 00 01 24 FD 82 A5 01 63 53 .$..0.:..$....cS
000000E0 50 45 04 65 30 2E 30 2E 30 05 58 20 BF E6 D8 6F PE.e0.0.0.X ...o
000000F0 88 26 F4 FF 97 FB 96 C4 E6 FB C4 99 3E 46 19 FC .&..........>F..
00000100 56 5D A2 6A DF 34 C3 29 48 9A DC 38 06 66 53 48 V].j.4.)H..8.fSH
00000110 41 32 35 36 02 58 20 6D E1 0F 82 E0 CF FC 84 5A A256.X m.......Z
00000120 24 25 2B EB 70 D7 2C 6B FC 92 CD BE 5B 65 9E C7 $%+.p.,k....[e..
00000130 34 1E 1C D2 80 5D A3 A5 01 64 4E 53 50 45 04 65 4....]...dNSPE.e
00000140 30 2E 30 2E 30 05 58 20 B3 60 CA F5 C9 8C 6B 94 0.0.0.X .`....k.
00000150 2A 48 82 FA 9D 48 23 EF B1 66 A9 EF 6A 6E 4A A3 *H...H#..f..jnJ.
00000160 7C 19 19 ED 1F CC C0 49 06 66 53 48 41 32 35 36 |......I.fSHA256
00000170 02 58 20 01 4C F2 64 0D 49 F8 23 69 57 FE F3 73 .X .L.d.I.#iW..s
00000180 97 7E 73 C2 2C 4F D2 95 25 D8 BE 29 32 14 23 5D .~s.,O..%..)2.#]
00000190 A9 22 AD 3A 00 01 25 01 77 77 77 77 2E 74 72 75 .".:..%.wwww.tru
000001A0 73 74 65 64 66 69 72 6D 77 61 72 65 2E 6F 72 67 stedfirmware.org
000001B0 3A 00 01 24 F7 71 50 53 41 5F 49 4F 54 5F 50 52 :..$.qPSA_IOT_PR
000001C0 4F 46 49 4C 45 5F 31 3A 00 01 24 FC 72 30 36 30 OFILE_1:..$.r060
000001D0 34 35 36 35 32 37 32 38 32 39 31 30 30 31 30 58 456527282910010X
000001E0 40 59 23 3E 80 5E E0 9F FA E3 F4 14 62 D3 15 A5 @Y#>.^......b...
000001F0 B0 95 B5 E5 CB 79 92 F8 F1 A0 FE 14 0C 6C 84 2A .....y.......l.*
00000200 41 97 BC 6F C6 7D 9C A5 21 BB 4C 2C D1 2C F3 66 A..o.}..!.L,.,.f
00000210 4E D4 85 D2 57 15 72 11 E8 9E 06 4F C4 46 D0 58 N...W.r....O.F.X
00000220 26 &
[00:00:01.905,000] <inf> app: Persisting SECP256R1 key as #1
[00:00:02.458,000] <inf> app: Retrieving public key for key #1
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 04 07 93 39 CD 42 53 7B 18 8C 8A F1 05 7F 49 D1 ...9.BS{......I.
00000010 6B 30 D5 39 0D 1A 6E 95 BA 0C CD FE DB 59 A3 03 k0.9..n......Y..
00000020 02 61 B4 CF 13 CC 70 15 67 30 83 FE A0 D4 2A 19 .a....p.g0....*.
00000030 72 82 3E 3F 90 00 91 C6 5E 43 DC E9 B4 C4 0E F3 r.>?....^C......
00000040 79 y
[00:00:03.020,000] <inf> app: Calculating SHA-256 hash of value
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 50 6C 65 61 73 65 20 68 61 73 68 20 61 6E 64 20 Please hash and
00000010 73 69 67 6E 20 74 68 69 73 20 6D 65 73 73 61 67 sign this messag
00000020 65 2E e.
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 9D 08 E3 E6 DB 1C 12 39 C0 9B 9A 83 84 83 72 7A .......9......rz
00000010 EA 96 9E 1D 13 72 1E 4D 35 75 CC D4 C8 01 41 9C .....r.M5u....A.
[00:00:03.032,000] <inf> app: Signing SHA-256 hash
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 EE F1 FE A6 A8 41 5F CC A6 3A 73 A7 C1 33 B4 78 .....A_..:s..3.x
00000010 BF B7 38 78 2A 91 C8 82 32 F8 73 85 56 08 D2 A0 ..8x*...2.s.V...
00000020 A6 22 2C 64 7A C7 E4 0A FB 99 D1 8B 67 37 F7 13 .",dz.......g7..
00000030 E6 6C 54 7B 29 1D 3B A2 D8 E3 C4 79 17 BA 34 A8 .lT{).;....y..4.
[00:00:03.658,000] <inf> app: Verifying signature for SHA-256 hash
[00:00:06.339,000] <inf> app: Signature verified.
[00:00:06.349,000] <inf> app: Destroyed persistent key #1
[00:00:06.354,000] <inf> app: Generating 256 bytes of random data.
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 24 5C B3 EB 88 D2 80 76 23 B3 07 CA 16 92 8F 3D $\.....v#......=
00000010 27 AC C2 42 59 15 5E 3C EB 11 20 3C 14 A6 EB 60 '..BY.^<.. <...`
00000020 C0 92 12 97 4D D7 62 BC A0 0A 34 A7 CE A8 78 18 ....M.b...4...x.
00000030 1B 30 6E 3C DA 80 F2 55 F7 FA 10 8B F5 78 CE 92 .0n<...U.....x..
00000040 92 FF F2 A3 22 4D 2D F6 62 39 6D A5 DD E1 E1 C4 ...."M-.b9m.....
00000050 67 67 30 19 98 D7 E4 AD A2 6A 27 1C A4 C2 A2 C6 gg0......j'.....
00000060 8A B5 98 26 D3 1A 84 75 55 52 4F E1 6D 4B 84 99 ...&...uURO.mK..
00000070 0F C2 5E 88 D5 8B E6 AA 2F 61 DC 63 79 5B 69 3F ..^...../a.cy[i?
00000080 19 79 5A 78 49 29 22 92 9D F5 F3 FD 16 60 E2 72 .yZxI)"......`.r
00000090 EA F8 8E 32 7D 81 A0 21 0C 82 4A A8 4C EE 9C 0E ...2}..!..J.L...
000000A0 D7 BF 50 60 6C 65 8A 7C A6 CD C5 98 8B 15 EA F0 ..P`le.|........
000000B0 26 D0 15 F4 EB DE A0 FD 88 2F 72 8B ED 07 44 5C &......../r...D\
000000C0 91 46 17 8C 26 46 F2 7C BF 6B 45 63 B6 71 E7 51 .F..&F.|.kEc.q.Q
000000D0 E4 34 A2 5A 01 F4 6E FF A2 67 82 7B F3 36 34 54 .4.Z..n..g.{.64T
000000E0 80 ED 7E 9D 0A 21 09 9C 9C 55 A9 14 AF A2 66 65 ..~..!...U....fe
000000F0 DE 8D BE C2 8B 31 B8 ED 06 AE A9 0B 7E 62 75 87 .....1......~bu.
[00:00:06.385,000] <inf> app: Initialising PSA crypto
[00:00:06.386,000] <inf> app: PSA crypto init completed
[00:00:06.387,000] <inf> app: Persisting SECP256R1 key as #1
[00:00:06.938,000] <inf> app: Retrieving public key for key #1
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 04 34 B7 2F D5 EC 41 71 B1 04 D9 BE 1C E7 DD F7 .4./..Aq........
00000010 C4 C0 B1 E9 64 CB 45 1F E3 4A 95 52 A8 75 B2 8C ....d.E..J.R.u..
00000020 4D F1 CB 4F C2 26 2C 90 C9 05 B2 E4 4C 2A E9 9D M..O.&,.....L*..
00000030 11 DF 35 1B 0E 86 D5 9C A1 1F FC FA ED 21 9A B5 ..5..........!..
00000040 28 (
[00:00:07.495,000] <inf> app: Adding subject name to CSR
[00:00:07.496,000] <inf> app: Adding subject name to CSR completed
[00:00:07.497,000] <inf> app: Adding EC key to PK container
[00:00:07.499,000] <inf> app: Adding EC key to PK container completed
[00:00:07.500,000] <inf> app: Create device Certificate Signing Request
[00:00:08.692,000] <inf> app: Create device Certificate Signing Request completed
[00:00:08.693,000] <inf> app: Certificate Signing Request:
-----BEGIN CERTIFICATE REQUEST-----
MIHrMIGQAgEAMC4xDzANBgNVBAoMBkxpbmFybzEbMBkGA1UEAwwSRGV2aWNlIENl
cnRpZmljYXRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENLcv1exBcbEE2b4c
5933xMCx6WTLRR/jSpVSqHWyjE3xy0/CJiyQyQWy5Ewq6Z0R3zUbDobVnKEf/Prt
IZq1KKAAMAwGCCqGSM49BAMCBQADSAAwRQIgaAlTPmrIaRO7myM2Qr+LNk9sagdO
jPGUqbz4oUWhUsICIQCuHADW6F2l4czv78BO5Nf+FHZEpjbI1+fA2aLzglOaiA==
-----END CERTIFICATE REQUEST-----
[00:00:08.696,000] <inf> app: Encoding CSR as json
[00:00:08.699,000] <inf> app: Encoding CSR as json completed
[00:00:08.700,000] <inf> app: Certificate Signing Request in JSON:
{"CSR":"-----BEGIN CERTIFICATE REQUEST-----\nMIHrMIGQAgEAMC4xDzANBgNVBAoMBkxpbmFybzEbMBkGA1UEAwwSRGV2aWNlIENl\ncnRpZmljYXRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENLcv1exBcbEE2b4c\n5933xMCx6WTLRR/jSpVSqHWyjE3xy0/CJiyQyQWy5Ewq6Z0R3zUbDobVnKEf/Prt\nIZq1KKAAMAwGCCqGSM49BAMCBQADSAAwRQIgaAlTPmrIaRO7myM2Qr+LNk9sagdO\njPGUqbz4oUWhUsICIQCuHADW6F2l4czv78BO5Nf+FHZEpjbI1+fA2aLzglOaiA==\n-----END CERTIFICATE REQUEST-----\n"}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Modify the SRAM partitioning to accommodate the requirements
* for the Secure (TF-M) firmware for the configuration that is
* used in this sample.
*/
/* Increase the size of the Secure Firmware (TF-M).
* This modification is not required at the moment,
* since TF-M region definitions are configured
* statically in the TF-M project.
*/
&sram0_s {
reg = <0x20000000 DT_SIZE_K(88)>;
};
/* Decrease the size of the Non-Secure Firmware (Zephyr),
* and move its starting address to the offset expected by
* TF-M.
*/
/delete-node/ &sram0_ns;
/ {
reserved-memory {
sram0_ns: image_ns@20016000 {
reg = <0x20016000 DT_SIZE_K(168)>;
};
};
};
/* Disable UART1, because it is used by default in TF-M */
&uart1 {
status = "disabled";
};

View file

@ -0,0 +1,38 @@
CONFIG_LOG=y
CONFIG_LOG_RUNTIME_FILTERING=y
CONFIG_LOG_BUFFER_SIZE=2048
CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD=0
CONFIG_LOG_DEFAULT_LEVEL=3
#CONFIG_SHELL=n
#CONFIG_SHELL_HISTORY=y
#CONFIG_SHELL_VT100_COLORS=y
#CONFIG_SHELL_CMDS=n
#CONFIG_PSA_SHELL=y
CONFIG_BUILD_WITH_TFM=y
CONFIG_TFM_PROFILE_TYPE_NOT_SET=y
CONFIG_TFM_IPC=y
# The Zephyr CMSIS emulation assumes that ticks are ms, currently
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000
CONFIG_MAIN_STACK_SIZE=8192
CONFIG_HEAP_MEM_POOL_SIZE=4096
# Mbed TLS
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=32768
CONFIG_MBEDTLS_USER_CONFIG_ENABLE=y
CONFIG_MBEDTLS_USER_CONFIG_FILE="user-tls-conf.h"
# JSON
CONFIG_JSON_LIBRARY=y
# Enable the initial attestation
CONFIG_TFM_PARTITION_INITIAL_ATTESTATION=y
CONFIG_TFM_QCBOR_PATH="DOWNLOAD"
CONFIG_NEWLIB_LIBC=y

View file

@ -0,0 +1,25 @@
sample:
description: This app provides an example of using PSA crypto APIs
to generate device certificate signing request in Zephyr
using IPC mode.
name: PSA crypto example
tests:
sample.psa_crypto:
tags: introduction tfm crypto csr
platform_allow: mps2_an521_ns v2m_musca_s1_ns
nrf5340dk_nrf5340_cpuapp_ns nrf9160dk_nrf9160_ns
stm32l562e_dk_ns bl5340_dvk_cpuapp_ns
harness: console
harness_config:
type: multi_line
regex:
- "System IAT size is: (.*)"
- "Requesting IAT with (.*) byte challenge."
- "IAT data received: (.*)"
- "Retrieving public key for key #1"
- "Signature verified"
- "Destroyed persistent key #1"
- "Generating 256 bytes of random data."
- "Create device Certificate Signing Request completed"
- "BEGIN CERTIFICATE REQUEST"
- "END CERTIFICATE REQUEST"

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log_ctrl.h>
#include <zephyr/logging/log.h>
#include "tfm_ns_interface.h"
#include "psa_attestation.h"
#include "psa_crypto.h"
#include "util_app_cfg.h"
#include "util_app_log.h"
#include "util_sformat.h"
/** Declare a reference to the application logging interface. */
LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL);
/* Create an instance of the system config struct for the application. */
static struct cfg_data cfg;
int main(void)
{
/* Initialise the logger subsys and dump the current buffer. */
log_init();
/* Load app config struct from secure storage (create if missing). */
if (cfg_load_data(&cfg)) {
LOG_ERR("Error loading/generating app config data in SS.");
}
/* Get the entity attestation token (requires ~1kB stack memory!). */
att_test();
/* Crypto tests */
crp_test();
crp_test_rng();
/* Generate Certificate Signing Request using Mbed TLS */
crp_generate_csr();
/* Dump any queued log messages, and wait for system events. */
al_dump_log();
return 0;
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <stdio.h>
#include <zephyr/logging/log.h>
#include "psa/initial_attestation.h"
#include "psa_attestation.h"
#include "util_app_log.h"
#include "util_sformat.h"
LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL);
psa_status_t att_get_pub_key(void)
{
psa_status_t err = PSA_SUCCESS;
/* TODO: How to retrieve this?!? */
/* Log any eventual errors via app_log */
return err ? al_psa_status(err, __func__) : err;
}
psa_status_t att_get_iat(uint8_t *ch_buffer, uint32_t ch_sz,
uint8_t *token_buffer, uint32_t *token_sz)
{
psa_status_t err = PSA_SUCCESS;
uint32_t sys_token_sz;
size_t token_buf_size = ATT_MAX_TOKEN_SIZE;
/* Call with with bigger challenge object than allowed */
/*
* First determine how large the token is on this system.
* We don't need to compare with the size of ATT_MAX_TOKEN_SIZE here
* since a check will be made in 'psa_initial_attest_get_token' and the
* error return code will indicate a mismatch.
*/
switch (ch_sz) {
case 32:
err = psa_initial_attest_get_token(
ch_buffer,
PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32,
token_buffer,
token_buf_size,
&sys_token_sz);
break;
case 48:
err = psa_initial_attest_get_token(
ch_buffer,
PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48,
token_buffer,
token_buf_size,
&sys_token_sz);
break;
case 64:
err = psa_initial_attest_get_token(
ch_buffer,
PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64,
token_buffer,
token_buf_size,
&sys_token_sz);
break;
default:
err = -EINVAL;
break;
}
if (err) {
goto err;
}
LOG_INF("att: System IAT size is: %u bytes.", sys_token_sz);
/* Request the initial attestation token w/the challenge data. */
LOG_INF("att: Requesting IAT with %u byte challenge.", ch_sz);
err = psa_initial_attest_get_token(
ch_buffer, /* Challenge/nonce input buffer. */
ch_sz, /* Challenge size (32, 48 or 64). */
token_buffer, /* Token output buffer. */
token_buf_size,
token_sz /* Post exec output token size. */
);
LOG_INF("att: IAT data received: %u bytes.", *token_sz);
err:
/* Log any eventual errors via app_log */
return err ? al_psa_status(err, __func__) : err;
}
psa_status_t att_test(void)
{
psa_status_t err = PSA_SUCCESS;
/* 64-byte nonce/challenge, encrypted using the default public key;
*
* 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
* 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
* 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
* 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
*/
uint32_t nonce_sz = 64;
uint8_t nonce_buf[ATT_MAX_TOKEN_SIZE] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0
};
/* IAT response buffer. */
uint32_t iat_sz = ATT_MAX_TOKEN_SIZE;
uint8_t iat_buf[ATT_MAX_TOKEN_SIZE] = { 0 };
/* String format output config. */
struct sf_hex_tbl_fmt fmt = {
.ascii = true,
.addr_label = true,
.addr = 0
};
/* Request the IAT from the initial attestation service. */
err = att_get_iat(nonce_buf, nonce_sz, iat_buf, &iat_sz);
if (err) {
goto err;
}
/* Display queued log messages before dumping the IAT. */
al_dump_log();
/* Dump the IAT for debug purposes. */
sf_hex_tabulate_16(&fmt, iat_buf, (size_t)iat_sz);
err:
return err;
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "psa/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Maximum buffer size for an initial attestation token instance. */
#define ATT_MAX_TOKEN_SIZE (0x240)
/**
* @brief Gets the public key portion of the attestation service's securely
* stored key pair. This public key can be provided to external
* verification services for device verification purposes.
*
* @return Returns error code as specified in \ref psa_status_t
*/
psa_status_t att_get_pub_key(void);
/**
* @brief Gets an initial attestation token (IAT) from the TF-M secure
* processing environment (SPE). This data will be provided in CBOR
* format and is encrypted using the private key held on the SPE.
*
* The initial attestation token (IAT) is composed of a series of 'claims' or
* data points used to uniquely identify this device to an external
* verification entity (the IAT consumer).
*
* The generated IAT should be cryptographically verifiable by the IAT consumer.
*
* For details on IAT see https://tools.ietf.org/html/draft-mandyam-eat-01
*
* @param ch_buffer Pointer to the buffer containing the nonce or
* challenge data to be validated with the private key.
* @param ch_sz The number of bytes in the challenge. 32, 48 or 64.
* @param token_buffer Pointer to the buffer where the IAT will be written.
* Must be equal in size to the system IAT output, which
* can be determined via a call to
* 'psa_initial_attest_get_token_size'.
* @param token_sz Pointer to the size of token_buffer, this value will be
* updated in this function to contain the number of bytes
* actually retrieved during the IAT request.
*
* @return Returns error code as specified in \ref psa_status_t
*/
psa_status_t att_get_iat(uint8_t *ch_buffer, uint32_t ch_sz,
uint8_t *token_buffer, uint32_t *token_sz);
/**
* @brief TODO!
*
* @return Returns error code as specified in \ref psa_status_t
*/
psa_status_t att_test(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,871 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log_ctrl.h>
#include <zephyr/logging/log.h>
#include <zephyr/data/json.h>
#include "mbedtls/pk.h"
#include "mbedtls/x509.h"
#include "mbedtls/x509_csr.h"
#include "psa_crypto.h"
#include "util_app_log.h"
#include "util_sformat.h"
/** Declare a reference to the application logging interface. */
LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL);
/* Formatting details for displaying hex dumps. */
struct sf_hex_tbl_fmt crp_fmt = {
.ascii = true,
.addr_label = true,
.addr = 0
};
struct csr_json_struct {
const char *CSR;
};
static const struct json_obj_descr csr_json_descr[] = {
JSON_OBJ_DESCR_PRIM(struct csr_json_struct, CSR, JSON_TOK_STRING)
};
/**
* @brief Extracts the public key from the specified persistent key id.
*
* @param key_id The permanent identifier for the generated key.
* @param key Pointer to the buffer where the public key data
* will be written.
* @param key_buf_size Size of key buffer in bytes.
* @param key_len Number of bytes written into key by this function.
*/
static psa_status_t crp_get_pub_key(psa_key_id_t key_id,
uint8_t *key, size_t key_buf_size,
size_t *key_len)
{
psa_status_t status;
psa_key_handle_t key_handle;
LOG_INF("Retrieving public key for key #%d", key_id);
al_dump_log();
/* Now try to re-open the persisted key based on the key ID. */
status = al_psa_status(
psa_open_key(key_id, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_id);
goto err;
}
/* Export the persistent key's public key part. */
status = al_psa_status(
psa_export_public_key(key_handle, key, key_buf_size, key_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to export public key.");
goto err;
}
/* Display the binary key data for debug purposes. */
sf_hex_tabulate_16(&crp_fmt, key, *key_len);
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
return status;
err:
al_dump_log();
return status;
}
#if CONFIG_PSA_IMPORT_KEY
/**
* @brief Stores a new persistent secp256r1 key (usage: ecdsa-with-SHA256)
* in ITS, associating it with the specified unique key identifier.
*
* This function will store a new persistent secp256r1 key in internal trusted
* storage. Cryptographic operations can then be performed using the key
* identifier (key_id) associated with this persistent key. Only the 32-byte
* private key needs to be supplied, the public key can be derived using
* the supplied private key value.
*
* @param key_id The permament identifier for the generated key.
* @param key_usage The usage policy for the key.
* @param key_data Pointer to the 32-byte private key data.
*/
static psa_status_t crp_imp_key_secp256r1(psa_key_id_t key_id,
psa_key_usage_t key_usage,
uint8_t *key_data)
{
psa_status_t status = PSA_SUCCESS;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_type_t key_type =
PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
psa_key_handle_t key_handle;
size_t key_len = 32;
size_t data_len;
uint8_t data_out[65] = { 0 }; /* ECDSA public key = 65 bytes. */
int comp_result;
LOG_INF("Persisting SECP256R1 key as #%d", (uint32_t)key_id);
al_dump_log();
/* Setup the key's attributes before the creation request. */
psa_set_key_id(&key_attributes, key_id);
psa_set_key_usage_flags(&key_attributes, key_usage);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_algorithm(&key_attributes, alg);
psa_set_key_type(&key_attributes, key_type);
/* Import the private key, creating the persistent key on success */
status = al_psa_status(
psa_import_key(&key_attributes, key_data, key_len, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to import key.");
goto err;
}
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
/* Try to retrieve the public key. */
status = crp_get_pub_key(key_id, data_out, sizeof(data_out), &data_len);
/* Export the private key if usage includes PSA_KEY_USAGE_EXPORT. */
if (key_usage & PSA_KEY_USAGE_EXPORT) {
/* Re-open the persisted key based on the key ID. */
status = al_psa_status(
psa_open_key(key_id, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_id);
goto err;
}
/* Read the original (private) key data back. */
status = al_psa_status(
psa_export_key(key_handle, data_out,
sizeof(data_out), &data_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to export key.");
goto err;
}
/* Check key len. */
if (data_len != key_len) {
LOG_ERR("Unexpected number of bytes in exported key.");
goto err;
}
/* Verify that the exported private key matches input data. */
comp_result = memcmp(data_out, key_data, key_len);
if (comp_result != 0) {
LOG_ERR("Imported/exported private key mismatch.");
goto err;
}
/* Display the private key. */
LOG_INF("Private key data:");
al_dump_log();
sf_hex_tabulate_16(&crp_fmt, data_out, data_len);
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
}
return status;
err:
al_dump_log();
return status;
}
#else /* !CONFIG_PSA_IMPORT_KEY */
/**
* @brief Generates a new permanent, persistent prime256v1 (ecdsa-with-SHA256)
* key in ITS, associating it with the specified unique key identifier.
*
* This function will generate a new permanent prime256v1 key in internal trusted
* storage. Cryptographic operations can then be performed using the key
* identifier (key_id) associated with this persistent key.
*
* @param key_id The permanent identifier for the generated key.
* @param key_usage The usage policy for the key.
*/
static psa_status_t crp_gen_key_secp256r1(psa_key_id_t key_id,
psa_key_usage_t key_usage)
{
psa_status_t status = PSA_SUCCESS;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_type_t key_type =
PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
psa_key_handle_t key_handle;
size_t key_len = 32;
size_t data_len;
uint8_t data_out[65] = { 0 }; /* ECDSA public key = 65 bytes. */
LOG_INF("Persisting SECP256R1 key as #%d", (uint32_t)key_id);
al_dump_log();
/* Setup the key's attributes before the creation request. */
psa_set_key_id(&key_attributes, key_id);
psa_set_key_usage_flags(&key_attributes, key_usage);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_algorithm(&key_attributes, alg);
psa_set_key_type(&key_attributes, key_type);
psa_set_key_bits(&key_attributes, 256);
/* Generate the private key, creating the persistent key on success */
status = al_psa_status(
psa_generate_key(&key_attributes, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to generate key.");
goto err;
}
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
/* Try to retrieve the public key. */
status = crp_get_pub_key(key_id, data_out, sizeof(data_out), &data_len);
/* Export the private key if usage includes PSA_KEY_USAGE_EXPORT. */
if (key_usage & PSA_KEY_USAGE_EXPORT) {
/* Re-open the persisted key based on the key ID. */
status = al_psa_status(
psa_open_key(key_id, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_id);
goto err;
}
/* Read the original (private) key data back. */
status = al_psa_status(
psa_export_key(key_handle, data_out,
sizeof(data_out), &data_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to export key.");
goto err;
}
/* Check key len. */
if (data_len != key_len) {
LOG_ERR("Unexpected number of bytes in exported key.");
goto err;
}
/* Display the private key. */
LOG_INF("Private key data:");
al_dump_log();
sf_hex_tabulate_16(&crp_fmt, data_out, data_len);
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
}
return status;
err:
al_dump_log();
return status;
}
#endif /* CONFIG_PSA_IMPORT_KEY */
/**
* @brief PSA Random number generator wrapper for Mbed TLS
*/
static int psa_rng_for_mbedtls(void *p_rng,
unsigned char *output, size_t output_len)
{
(void)p_rng;
return psa_generate_random(output, output_len);
}
/**
* @brief Generates device certificate signing request (CSR) using Mbed TLS
* X.509 and TF-M crypto service.
*/
void crp_generate_csr(void)
{
psa_status_t status;
psa_key_id_t key_slot = 1;
psa_key_handle_t key_handle;
unsigned char output_buf[1024];
unsigned char json_encoded_buf[1024];
mbedtls_pk_context pk_key_container;
mbedtls_x509write_csr req;
struct csr_json_struct csr_json = {
.CSR = output_buf
};
/* Initialize Mbed TLS structures. */
mbedtls_x509write_csr_init(&req);
mbedtls_pk_init(&pk_key_container);
memset(output_buf, 0, sizeof(output_buf));
/* Initialize crypto API. */
LOG_INF("Initialising PSA crypto");
al_dump_log();
status = al_psa_status(psa_crypto_init(), __func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Crypto init failed.");
goto err;
}
LOG_INF("PSA crypto init completed");
al_dump_log();
/* prime256v1 (ecdsa-with-SHA256) private key. */
#if CONFIG_PSA_IMPORT_KEY
#if CONFIG_PRIVATE_KEY_STATIC
/* This value is based on the private key in user.pem,
* which can be viewed viw the following command:
*
* $ openssl ec -in user.pem -text -noout
*/
uint8_t priv_key_data[32] = {
0x14, 0xbc, 0xb9, 0x53, 0xa4, 0xee, 0xed, 0x50,
0x09, 0x36, 0x92, 0x07, 0x1d, 0xdb, 0x24, 0x2c,
0xef, 0xf9, 0x57, 0x92, 0x40, 0x4f, 0x49, 0xaa,
0xd0, 0x7c, 0x5b, 0x3f, 0x26, 0xa7, 0x80, 0x48
};
#else /* !CONFIG_PRIVATE_KEY_STATIC */
/* Randomly generate the private key. */
uint8_t priv_key_data[32] = { 0 };
LOG_INF("Generate rnadom data for private key");
al_dump_log();
psa_generate_random(priv_key_data, sizeof(priv_key_data));
LOG_INF("Random data generation for private key completed");
al_dump_log();
#endif /* CONFIG_PRIVATE_KEY_STATIC */
/* Generate persistent prime256v1 (ecdsa-with-SHA256) key w/ID #1. */
/* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */
status = al_psa_status(
crp_imp_key_secp256r1(key_slot,
PSA_KEY_USAGE_SIGN_HASH |
PSA_KEY_USAGE_VERIFY_HASH,
priv_key_data),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to create persistent key #%d", key_slot);
goto err;
}
#else /* !CONFIG_PSA_IMPORT_KEY */
/* NOTE: The certificate signing request (CSR) can be generated using
* openssl by using following commands:
*
* Generate a new key:
*
* $ openssl ecparam -name secp256k1 -genkey -out USER.key
*
* Generate a certificate signing request, containing the user public key
* and required details to be inserted into the user certificate.
* openssl req -new -key USER.key -out USER.csr \
* -subj "/O=Linaro/CN=$(uuidgen | tr '[:upper:]' '[:lower:]')"
*
*/
/* Generate persistent prime256v1 (ecdsa-with-SHA256) key w/ID #1. */
/* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */
status = al_psa_status(
crp_gen_key_secp256r1(key_slot,
PSA_KEY_USAGE_SIGN_HASH |
PSA_KEY_USAGE_VERIFY_HASH),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to create persistent key #%d", key_slot);
goto err;
}
#endif /* CONFIG_PSA_IMPORT_KEY */
status = al_psa_status(
psa_open_key(key_slot, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_slot);
goto err;
}
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_get_key_attributes(key_handle, &attributes);
mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256);
LOG_INF("Adding subject name to CSR");
al_dump_log();
status = mbedtls_x509write_csr_set_subject_name(&req, "O=Linaro,CN=Device Certificate");
if (status != 0) {
LOG_ERR("failed! mbedtls_x509write_csr_set_subject_name returned %d", status);
goto err;
}
LOG_INF("Adding subject name to CSR completed");
al_dump_log();
LOG_INF("Adding EC key to PK container");
al_dump_log();
status = mbedtls_pk_setup_opaque(&pk_key_container, key_handle);
if (status != 0) {
LOG_ERR("failed! mbedtls_pk_setup_opaque returned -0x%04x", (unsigned int) -status);
goto err;
}
LOG_INF("Adding EC key to PK container completed");
al_dump_log();
mbedtls_x509write_csr_set_key(&req, &pk_key_container);
LOG_INF("Create device Certificate Signing Request");
al_dump_log();
status = mbedtls_x509write_csr_pem(&req, output_buf, sizeof(output_buf),
psa_rng_for_mbedtls, NULL);
if (status < 0) {
LOG_ERR("failed! mbedtls_x509write_csr_pem returned -0x%04x",
(unsigned int) -status);
goto err;
}
LOG_INF("Create device Certificate Signing Request completed");
al_dump_log();
LOG_INF("Certificate Signing Request:\n");
al_dump_log();
printf("%s\n", output_buf);
/*
* 1.3. Encoding CSR as JSON
*/
LOG_INF("Encoding CSR as json");
al_dump_log();
status = json_obj_encode_buf(csr_json_descr, ARRAY_SIZE(csr_json_descr),
&csr_json, json_encoded_buf, sizeof(json_encoded_buf));
if (status != 0) {
LOG_ERR("failed! json_obj_encode_buf returned 0x%04x", status);
goto err;
}
LOG_INF("Encoding CSR as json completed");
al_dump_log();
LOG_INF("Certificate Signing Request in JSON:\n");
al_dump_log();
printf("%s\n", json_encoded_buf);
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
err:
al_dump_log();
mbedtls_x509write_csr_free(&req);
mbedtls_pk_free(&pk_key_container);
}
/**
* @brief Calculates the SHA256 hash for the supplied message.
*
* @param msg Pointer to the buffer to read when generating the hash.
* @param msg_len Number of bytes in msg.
* @param hash Pointer to the buffer where the hash should be written.
* @param hash_buf_size Size of hash in bytes.
* @param hash_len Placeholder for the number of hash bytes written.
*/
static psa_status_t crp_hash_payload(uint8_t *msg, size_t msg_len,
uint8_t *hash, size_t hash_buf_size,
size_t *hash_len)
{
psa_status_t status;
psa_hash_operation_t hash_handle = psa_hash_operation_init();
psa_algorithm_t alg = PSA_ALG_SHA_256;
LOG_INF("Calculating SHA-256 hash of value");
al_dump_log();
/* Display the input message */
sf_hex_tabulate_16(&crp_fmt, msg, msg_len);
/* Setup the hash object. */
status = al_psa_status(psa_hash_setup(&hash_handle, alg),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to setup hash op.");
goto err;
}
/* Update object with all the message chunks. */
/* For the moment, the message is passed in a single operation, */
/* but this can be broken up in chunks for larger messages. */
status = al_psa_status(psa_hash_update(&hash_handle, msg, msg_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to update hash.");
goto err;
}
/* Finalize the hash calculation. */
status = al_psa_status(psa_hash_finish(&hash_handle,
hash, hash_buf_size, hash_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to finalize hash op.");
goto err;
}
/* Display the SHA-256 hash for debug purposes */
sf_hex_tabulate_16(&crp_fmt, hash, (size_t)(PSA_HASH_MAX_SIZE));
return status;
err:
psa_hash_abort(&hash_handle);
al_dump_log();
return status;
}
/**
* @brief Signs the supplied hash using the specified persistent key.
*
* @param key_id The identifier of the key to use when signing.
* @param hash Pointer to the buffer where the hash should be written.
* @param hash_buf_size Size of hash in bytes.
* @param sig Pointer to the buffer to read when generating the sig.
* @param sig_buf_size Size of sig buffer in bytes.
* @param sig_len Number of bytes written to sig.
*/
static psa_status_t 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)
{
psa_status_t status;
psa_key_handle_t key_handle;
LOG_INF("Signing SHA-256 hash");
al_dump_log();
/* Try to open the persisted key based on the key ID. */
status = al_psa_status(
psa_open_key(key_id, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_id);
goto err;
}
/* Sign using psa_sign_hash. */
status = al_psa_status(
psa_sign_hash(key_handle,
PSA_ALG_ECDSA(PSA_ALG_SHA_256),
hash, hash_buf_size,
sig, sig_buf_size, sig_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to sign hash w/persistent key #%d", key_id);
goto err;
}
/* Display the ECDSA signature for debug purposes */
sf_hex_tabulate_16(&crp_fmt, sig, *sig_len);
/* You can test this same operation with openssl as follows:
*
* $ openssl dgst -sha256 -sign
*/
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
return status;
err:
al_dump_log();
return status;
}
/**
* @brief Verifies the hash signature using the public key associated
* with key_id.
*
* @param key_id The identifier for the persistent key.
* @param hash Pointer to the hash data to verify.
* @param hash_len Size of the hash buffer in bytes.
* @param sig Pointer to the signature buffer.
* @param sig_len Size of the signature buffer in bytes.
*/
static psa_status_t crp_verify_sign(psa_key_id_t key_id,
uint8_t *hash, size_t hash_len,
uint8_t *sig, size_t sig_len)
{
psa_status_t status;
psa_key_handle_t key_handle;
LOG_INF("Verifying signature for SHA-256 hash");
al_dump_log();
/* Try to open the persisted key based on the key ID. */
status = al_psa_status(
psa_open_key(key_id, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_id);
goto err;
}
/* Verify the hash signature. */
status = al_psa_status(
psa_verify_hash(key_handle,
PSA_ALG_ECDSA(PSA_ALG_SHA_256),
hash, hash_len,
sig, sig_len),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Signature verification failed!");
goto err;
}
LOG_INF("Signature verified.");
al_dump_log();
/* Close the key to free up the volatile slot. */
status = al_psa_status(
psa_close_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to close persistent key.");
goto err;
}
return status;
err:
al_dump_log();
return status;
}
/**
* @brief Destroys the specified persistent key.
*
* @param key_id The identifier for the persistent key.
*/
static psa_status_t crp_dest_key(psa_key_id_t key_id)
{
psa_status_t status;
psa_key_handle_t key_handle;
/* Try to open the persisted key based on the key ID. */
status = al_psa_status(
psa_open_key(key_id, &key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to open persistent key #%d", key_id);
goto err;
}
/* Destroy the persistent key */
status = al_psa_status(
psa_destroy_key(key_handle),
__func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Failed to destroy a persistent key");
goto err;
}
LOG_INF("Destroyed persistent key #%d", (uint32_t)key_id);
al_dump_log();
return status;
err:
al_dump_log();
return status;
}
void crp_test(void)
{
psa_status_t status;
uint8_t msg[] = "Please hash and sign this message.";
uint8_t hash[PSA_HASH_MAX_SIZE] = { 0 };
size_t hash_len;
uint8_t sig[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE] = { 0 };
size_t sig_len;
/* secp256r1 private key. */
#if CONFIG_PSA_IMPORT_KEY
#if CONFIG_PRIVATE_KEY_STATIC
/* This value is based on the private key in user.pem,
* which can be viewed viw the following command:
*
* $ openssl ec -in user.pem -text -noout
*/
uint8_t priv_key_data[32] = {
0x14, 0xbc, 0xb9, 0x53, 0xa4, 0xee, 0xed, 0x50,
0x09, 0x36, 0x92, 0x07, 0x1d, 0xdb, 0x24, 0x2c,
0xef, 0xf9, 0x57, 0x92, 0x40, 0x4f, 0x49, 0xaa,
0xd0, 0x7c, 0x5b, 0x3f, 0x26, 0xa7, 0x80, 0x48
};
#else /* !CONFIG_PRIVATE_KEY_STATIC */
/* Randomly generate the private key. */
uint8_t priv_key_data[32] = { 0 };
psa_generate_random(priv_key_data, sizeof(priv_key_data));
#endif /* CONFIG_PRIVATE_KEY_STATIC */
#endif /* CONFIG_PSA_IMPORT_KEY */
/* Initialize crypto API. */
status = al_psa_status(psa_crypto_init(), __func__);
if (status != PSA_SUCCESS) {
LOG_ERR("Crypto init failed.");
return;
}
/* NOTE: The same key generation, SHA256 hash, sign, and verify
* operations performed in this file can be also performed with
* openssl using the commands described below.
*
* Generate a new key:
*
* The curve `prime256v1` is same as `secp256r1` in OpenSSL
* (https://github.com/openssl/openssl/blob/master/apps/ecparam.c#L216)
* $ openssl ecparam -name prime256v1 -genkey -out user.pem
*
* Display the public and private keys in hexadecimal format:
*
* $ openssl ec -in user.pem -text -noout
*
* Update the private key value in priv_key_data with the hexadecimal
* values from "priv:" to be able to compare the PSA API and openssl
* output.
*
* Generate a PEM file with the public key (which will be used to
* verify any data signed with the private key):
*
* $ openssl ec -in user.pem -pubout -out user_pub.pem
*
* Hash the message with SHA256, and sign it with the private key:
*
* $ echo "Please hash and sign this message." > message.txt
* $ openssl dgst -sha256 -sign user.pem message.txt > signature.der
*
* Verify the signature using the public key and message file:
*
* $ openssl dgst -sha256 -verify user_pub.pem \
* -signature signature.der message.txt
*
* If everything ws OK you should see "Verified OK".
*/
/* Generate persistent secp256r1 key w/ID #1. */
/* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */
#if CONFIG_PSA_IMPORT_KEY
status = crp_imp_key_secp256r1(1,
PSA_KEY_USAGE_SIGN_HASH |
PSA_KEY_USAGE_VERIFY_HASH,
priv_key_data);
#else /* !CONFIG_PSA_IMPORT_KEY */
status = crp_gen_key_secp256r1(1,
PSA_KEY_USAGE_SIGN_HASH |
PSA_KEY_USAGE_VERIFY_HASH);
#endif
/* Hash some data with the key using SHA256. */
status = crp_hash_payload(msg, strlen(msg),
hash, sizeof(hash), &hash_len);
/* Sign the hash using key #1. */
status = crp_sign_hash(1,
hash, hash_len,
sig, sizeof(sig), &sig_len);
/* Verify the hash signature using the public key. */
status = crp_verify_sign(1, hash, hash_len, sig, sig_len);
/* Destroy the key. */
status = crp_dest_key(1);
}
/**
* @brief Generates random values using the TF-M crypto service.
*/
void crp_test_rng(void)
{
psa_status_t status;
uint8_t outbuf[256] = { 0 };
struct sf_hex_tbl_fmt fmt = {
.ascii = true,
.addr_label = true,
.addr = 0
};
status = al_psa_status(psa_generate_random(outbuf, 256), __func__);
LOG_INF("Generating 256 bytes of random data.");
al_dump_log();
sf_hex_tabulate_16(&fmt, outbuf, 256);
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "psa/crypto.h"
#include "psa/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generates random values using the TF-M crypto service.
*/
void crp_test_rng(void);
/**
* @brief Runs a series of PSA Cryptography API test functions.
*/
void crp_test(void);
/**
* @brief Generates device certificate signing request (CSR) using Mbed TLS
* X.509 and TF-M crypto service.
*/
void crp_generate_csr(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <ctype.h>
#include <zephyr/shell/shell.h>
#if CONFIG_PSA_SHELL
static int
psa_shell_invalid_arg(const struct shell *sh, char *arg_name)
{
shell_print(sh, "Error: invalid argument \"%s\"\n", arg_name);
return -EINVAL;
}
static int
psa_shell_cmd_version(const struct shell *sh, size_t argc, char **argv)
{
shell_print(sh, "%s", "0.0.0");
return 0;
}
/* Subcommand array for "psa" (level 1). */
SHELL_STATIC_SUBCMD_SET_CREATE(sub_psa,
/* 'version' command handler. */
SHELL_CMD(version, NULL, "app version", psa_shell_cmd_version),
/* Array terminator. */
SHELL_SUBCMD_SET_END
);
/* Root command "psa" (level 0). */
SHELL_CMD_REGISTER(psa, &sub_psa, "PSA commands", NULL);
#endif

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#define MBEDTLS_USE_PSA_CRYPTO
#define MBEDTLS_PSA_CRYPTO_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_TEST_NULL_ENTROPY
#define MBEDTLS_ECP_C
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECDSA_C
#define MBEDTLS_X509_CSR_WRITE_C
#define MBEDTLS_X509_CREATE_C
#define MBEDTLS_PEM_WRITE_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_OID_C
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_PK_WRITE_C
#define MBEDTLS_PK_C

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/logging/log.h>
#include "psa/error.h"
#include "psa/protected_storage.h"
#include "util_app_cfg.h"
#include "util_app_log.h"
/** The 64-bit UID associated with the config record in secure storage. */
static psa_storage_uid_t cfg_data_uid = 0x0000000055CFDA7A;
LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL);
/**
* @brief Default config settings. These settings will be used when a new
* config record is created, or when the config record is reset.
*/
static struct cfg_data cfg_data_dflt = {
.magic = 0x55CFDA7A,
.version = 1,
.scratch = { 0 }
};
psa_status_t cfg_create_data(void)
{
psa_status_t status;
LOG_INF("app_cfg: Creating new config file with UID 0x%llX",
(uint64_t)cfg_data_uid);
/*
* psa_ps_create can also be used here, which enables to the use of
* the psa_ps_set_extended function, but this requires us to set a
* maximum file size for resource allocation. Since the upper limit
* isn't known at present, we opt here for the simpler psa_ps_set
* call which also creates the secure storage record if necessary,
* but precludes the use of psa_ps_set_extended.
*/
status = psa_ps_set(cfg_data_uid, sizeof(cfg_data_dflt),
(void *)&cfg_data_dflt, 0);
if (status) {
goto err;
}
err:
return (status ? al_psa_status(status, __func__) : status);
}
psa_status_t cfg_load_data(struct cfg_data *p_cfg_data)
{
psa_status_t status;
struct psa_storage_info_t p_info;
memset(&p_info, 0, sizeof(p_info));
/* Check if the config record exists, if not create it. */
status = psa_ps_get_info(cfg_data_uid, &p_info);
if (status == PSA_ERROR_DOES_NOT_EXIST) {
/* Create a new config file. */
status = cfg_create_data();
/* Copy default values to the cfg_data placeholder. */
memcpy(p_cfg_data, &cfg_data_dflt, sizeof(cfg_data_dflt));
}
if (status) {
goto err;
}
err:
return (status ? al_psa_status(status, __func__) : status);
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "psa/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The struct used to persist config data to secure storage.
*
* The first 6 bytes of this struct should remain consistent in any future
* firmware updates, since they can be used to identify to layout of the rest
* of the struct in cases where config data version management becomes
* a necessity.
*/
struct cfg_data {
/**
* @brief Magic number for config data payloads (0x55CFDA7A).
*/
uint32_t magic;
/**
* @brief The version number for the stored config record.
*
* This number should be incremented any time the config_data struct
* definition changes to allow version management of config data at
* the application level.
*/
uint16_t version;
/** @brief 256-byte debug scratch area. */
uint8_t scratch[256];
};
/**
* @brief Creates a new config record in secure storage.
*
* @return #PSA_SUCCESS on success, otherwise a appropriate psa_status_t code.
*/
psa_status_t cfg_create_data(void);
/**
* @brief Attempts to load the config record from secure storage. If the
* record is not found in secure storage, a new record will be created
* using default config settings.
*
* @param p_cfg_data Pointer to the cfg_data struct where the config data
* should be assigned once loaded.
*
* @return #PSA_SUCCESS on success, otherwise a appropriate psa_status_t code.
*/
psa_status_t cfg_load_data(struct cfg_data *p_cfg_data);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log_ctrl.h>
#include <zephyr/logging/log.h>
#include "psa/crypto.h"
#include "util_app_log.h"
LOG_MODULE_REGISTER(app, CONFIG_LOG_DEFAULT_LEVEL);
psa_status_t al_psa_status(psa_status_t status, const char *func_name)
{
switch (status) {
case PSA_SUCCESS:
break;
/* Generic PSA errors (psa/error.h). */
case PSA_ERROR_PROGRAMMER_ERROR:
LOG_ERR("Programmer error");
break;
case PSA_ERROR_CONNECTION_REFUSED:
LOG_ERR("Connection refused");
break;
case PSA_ERROR_CONNECTION_BUSY:
LOG_ERR("Connection busy");
break;
case PSA_ERROR_GENERIC_ERROR:
LOG_ERR("Generic error");
break;
case PSA_ERROR_NOT_PERMITTED:
LOG_ERR("Not permitted");
break;
case PSA_ERROR_NOT_SUPPORTED:
LOG_ERR("Unsupported operation");
break;
case PSA_ERROR_INVALID_ARGUMENT:
LOG_ERR("Invalid argument");
break;
case PSA_ERROR_INVALID_HANDLE:
LOG_ERR("Invalid handle");
break;
case PSA_ERROR_BAD_STATE:
LOG_ERR("Bad state");
break;
case PSA_ERROR_BUFFER_TOO_SMALL:
LOG_ERR("Buffer too small");
break;
case PSA_ERROR_ALREADY_EXISTS:
LOG_ERR("Already exists");
break;
case PSA_ERROR_DOES_NOT_EXIST:
LOG_ERR("Does not exist");
break;
case PSA_ERROR_INSUFFICIENT_MEMORY:
LOG_ERR("Insufficient memory");
break;
case PSA_ERROR_INSUFFICIENT_STORAGE:
LOG_ERR("Insufficient storage");
break;
case PSA_ERROR_INSUFFICIENT_DATA:
LOG_ERR("Insufficient memory data");
break;
case PSA_ERROR_SERVICE_FAILURE:
LOG_ERR("Service failure");
break;
case PSA_ERROR_COMMUNICATION_FAILURE:
LOG_ERR("Communication failure");
break;
case PSA_ERROR_STORAGE_FAILURE:
LOG_ERR("Storage failure");
break;
case PSA_ERROR_HARDWARE_FAILURE:
LOG_ERR("Hardware failure");
break;
case PSA_ERROR_INVALID_SIGNATURE:
LOG_ERR("Invalid signature");
break;
/* PSA crypto errors (psa/crypto_values.h). */
case PSA_ERROR_INSUFFICIENT_ENTROPY:
LOG_ERR("CRYPTO: Insufficient entropy");
break;
case PSA_ERROR_CORRUPTION_DETECTED:
LOG_ERR("CRYPTO: Tampering detected");
break;
/* Catch-all error handler. */
default:
LOG_ERR("Unhandled status response: %d", status);
break;
}
/* Display the calling function name for debug purposes. */
if (status != PSA_SUCCESS) {
LOG_ERR("Function: '%s'", func_name);
}
return status;
}
void al_dump_log(void)
{
while (log_process()) {
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "psa/error.h"
#include "psa/initial_attestation.h"
#include "psa/protected_storage.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Logs PSA response messages other than PSA_SUCCESS for debugging
* purposes.
*
* @param status The psa_status_t value to log.
* @param func_name The name of the function that made this function call.
*
* @return Returns the psa_status_t value passed into the function.
*/
psa_status_t al_psa_status(psa_status_t status, const char *func_name);
/**
* @brief Calls 'log_process' in Zephyr to dump any queued log messages.
*/
void al_dump_log(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ctype.h>
#include <stdio.h>
#include "util_sformat.h"
static void sf_hex_ascii(unsigned char *data, size_t len, unsigned char nonvis)
{
uint32_t idx;
/* Render printable characters. */
idx = 0;
while (len) {
printf("%c", isprint(data[idx]) ? data[idx] : nonvis);
idx++;
len--;
}
}
void sf_hex_tabulate_16(struct sf_hex_tbl_fmt *fmt, unsigned char *data,
size_t len)
{
uint32_t idx;
uint32_t cpos; /* Current position. */
uint32_t ca; /* Current address. */
uint32_t ea; /* End address. */
if (!len) {
return;
}
/* Set the end address (since we modify len in the write loop). */
ea = fmt->addr + len;
/* Check if we need to render the top address bar. */
if (fmt->addr_label) {
/* Render the top address bar. */
printf("\n");
printf(" ");
printf("0 1 2 3 4 5 6 7 8 9 ");
printf("A B C D E F\n");
printf("%08X ", fmt->addr - (fmt->addr % 16));
}
/* Insert offset padding for first row if necessary. */
cpos = fmt->addr % 16;
if (cpos != 0) {
for (idx = 0; idx < cpos; idx++) {
printf(" ");
}
}
/* Print data row by row. */
idx = 0;
ca = fmt->addr;
while (len) {
/* Print the current value. */
printf("%02X ", data[idx++]);
cpos++;
ca++;
/* Wrap around to the next line if necessary. */
if (cpos == 16 || ca == ea) {
/* Render ASCII equiv at end of row if requested. */
if (fmt->ascii) {
if (ca == ea) {
/* Handle last/single row. */
if (ca % 16) {
/* PARTIAL row (< 16 vals). */
printf("%.*s",
(16 - ca % 16) * 3,
" "
" "
" ");
sf_hex_ascii(
&data[idx - (ca % 16)],
ca - fmt->addr < 16 ?
idx % 16 : ca % 16,
'.');
} else {
/* FULL row. */
sf_hex_ascii(
&data[idx - 16],
16, '.');
}
} else if (ca < fmt->addr + 15) {
/* Handle first row. */
printf("%.*s", fmt->addr % 16,
" ");
sf_hex_ascii(data,
16 - fmt->addr % 16, '.');
} else {
/* Full row. */
sf_hex_ascii(&data[idx - 16], 16, '.');
}
}
/* Wrap around if this isn't the last row. */
printf("\n");
if (ca != ea) {
/* Render the next base row addr. */
if (fmt->addr_label) {
printf("%08X ", ca);
}
}
cpos = 0;
}
len--;
}
printf("\n");
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019,2020 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SFORMAT_H_
#define _SFORMAT_H_
#include <stdint.h>
#include <zephyr/kernel.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Indicates how hex data should be rendered in tabular format.
*/
struct sf_hex_tbl_fmt {
/** Whether or not to render ASCII equivalents. */
uint8_t ascii : 1;
/** Whether or not to add address labels to the output. */
uint8_t addr_label : 1;
/** The starting value for the address labels. */
uint32_t addr;
} __packed;
/**
* @brief Prints a 16-value wide tabular rendering of 8-bit hex data, with
* optional ascii equivalents and address labels.
*
* @param fmt Pointer to thee sf_hex_tbl_fmt struct indicating how the
* table should be rendered.
* @param data Pointer to the data to render.
* @param len The number of bytes to render from data.
*/
void sf_hex_tabulate_16(struct sf_hex_tbl_fmt *fmt, unsigned char *data,
size_t len);
#ifdef __cplusplus
}
#endif
#endif /* _SFORMAT_H_ */

View file

@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBS8uVOk7u1QCTaSBx3bJCzv+VeSQE9JqtB8Wz8mp4BIoAoGCCqGSM49
AwEHoUQDQgAER+qu2dZtLh1lBfUE/swhmb5eWlZrTx4MQ+Jbzht9BtezceIKPEft
hJ9lDtv5PdIHu4Gmc+Y7FpUZrAECyxz1NQ==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER+qu2dZtLh1lBfUE/swhmb5eWlZr
Tx4MQ+Jbzht9BtezceIKPEfthJ9lDtv5PdIHu4Gmc+Y7FpUZrAECyxz1NQ==
-----END PUBLIC KEY-----