From ea3cd06a1e1921c0b401754213a2615c7ec37da5 Mon Sep 17 00:00:00 2001 From: jaypit02 <38027331+jaypit02@users.noreply.github.com> Date: Fri, 5 Oct 2018 12:22:38 +0530 Subject: [PATCH] Support for PSA Firmware Framework Test Suite. (#14) - PSA IPC tests - PSA Crypto tests Signed-off-by: Jaykumar Pitambarbhai Patel --- README.md | 3 + psa-ff/README.md | 131 ++++ .../Arm_PSA_FF_Arch_Test_Porting_Guide.md | 100 +++ ...SA_FF_Arch_Test_Validation_Methodology.pdf | Bin 0 -> 506854 bytes psa-ff/docs/psa_crypto_testlist.md | 56 ++ psa-ff/docs/psa_ipc_testlist.md | 100 +++ .../common/driver_partition_psa.json | 69 ++ .../manifests/ipc/client_partition_psa.json | 49 ++ .../manifests/ipc/server_partition_psa.json | 119 ++++ psa-ff/platform/nspe/Makefile | 54 ++ psa-ff/platform/nspe/pal_baremetal_ns_intf.c | 40 ++ psa-ff/platform/nspe/pal_common.h | 38 ++ psa-ff/platform/nspe/pal_crypto_intf.c | 150 +++++ psa-ff/platform/nspe/pal_crypto_intf.h | 165 +++++ psa-ff/platform/nspe/pal_sid.h | 35 ++ psa-ff/platform/spe/drivers/nvmem/pal_nvmem.c | 69 ++ psa-ff/platform/spe/drivers/nvmem/pal_nvmem.h | 26 + psa-ff/platform/spe/drivers/uart/pal_uart.c | 114 ++++ psa-ff/platform/spe/drivers/uart/pal_uart.h | 48 ++ .../spe/drivers/watchdog/pal_wd_cmsdk.c | 83 +++ .../spe/drivers/watchdog/pal_wd_cmsdk.h | 87 +++ psa-ff/platform/spe/pal_common.h | 41 ++ psa-ff/platform/spe/pal_driver_intf.c | 114 ++++ .../targets/fvp_mps2_cm4_mbedos/target.cfg | 54 ++ psa-ff/test_suites/crypto/test_c001/source.mk | 20 + .../test_suites/crypto/test_c001/test_c001.c | 84 +++ .../test_suites/crypto/test_c001/test_c001.h | 37 ++ .../test_suites/crypto/test_c001/test_entry.c | 52 ++ psa-ff/test_suites/crypto/test_c002/source.mk | 20 + .../test_suites/crypto/test_c002/test_c002.c | 225 +++++++ .../test_suites/crypto/test_c002/test_c002.h | 34 + .../test_suites/crypto/test_c002/test_data.h | 285 +++++++++ .../test_suites/crypto/test_c002/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c003/source.mk | 20 + .../test_suites/crypto/test_c003/test_c003.c | 218 +++++++ .../test_suites/crypto/test_c003/test_c003.h | 34 + .../test_suites/crypto/test_c003/test_data.h | 274 ++++++++ .../test_suites/crypto/test_c003/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c004/source.mk | 20 + .../test_suites/crypto/test_c004/test_c004.c | 225 +++++++ .../test_suites/crypto/test_c004/test_c004.h | 34 + .../test_suites/crypto/test_c004/test_data.h | 309 +++++++++ .../test_suites/crypto/test_c004/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c005/source.mk | 20 + .../test_suites/crypto/test_c005/test_c005.c | 199 ++++++ .../test_suites/crypto/test_c005/test_c005.h | 34 + .../test_suites/crypto/test_c005/test_data.h | 261 ++++++++ .../test_suites/crypto/test_c005/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c006/source.mk | 20 + .../test_suites/crypto/test_c006/test_c006.c | 169 +++++ .../test_suites/crypto/test_c006/test_c006.h | 34 + .../test_suites/crypto/test_c006/test_data.h | 262 ++++++++ .../test_suites/crypto/test_c006/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c007/source.mk | 20 + .../test_suites/crypto/test_c007/test_c007.c | 174 ++++++ .../test_suites/crypto/test_c007/test_c007.h | 35 ++ .../test_suites/crypto/test_c007/test_data.h | 270 ++++++++ .../test_suites/crypto/test_c007/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c008/source.mk | 20 + .../test_suites/crypto/test_c008/test_c008.c | 186 ++++++ .../test_suites/crypto/test_c008/test_c008.h | 34 + .../test_suites/crypto/test_c008/test_data.h | 256 ++++++++ .../test_suites/crypto/test_c008/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c009/source.mk | 20 + .../test_suites/crypto/test_c009/test_c009.c | 276 ++++++++ .../test_suites/crypto/test_c009/test_c009.h | 34 + .../test_suites/crypto/test_c009/test_data.h | 284 +++++++++ .../test_suites/crypto/test_c009/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c010/source.mk | 20 + .../test_suites/crypto/test_c010/test_c010.c | 182 ++++++ .../test_suites/crypto/test_c010/test_c010.h | 34 + .../test_suites/crypto/test_c010/test_data.h | 279 +++++++++ .../test_suites/crypto/test_c010/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c011/source.mk | 20 + .../test_suites/crypto/test_c011/test_c011.c | 68 ++ .../test_suites/crypto/test_c011/test_c011.h | 34 + .../test_suites/crypto/test_c011/test_data.h | 90 +++ .../test_suites/crypto/test_c011/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c012/source.mk | 20 + .../test_suites/crypto/test_c012/test_c012.c | 164 +++++ .../test_suites/crypto/test_c012/test_c012.h | 35 ++ .../test_suites/crypto/test_c012/test_data.h | 64 ++ .../test_suites/crypto/test_c012/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c013/source.mk | 20 + .../test_suites/crypto/test_c013/test_c013.c | 153 +++++ .../test_suites/crypto/test_c013/test_c013.h | 34 + .../test_suites/crypto/test_c013/test_data.h | 109 ++++ .../test_suites/crypto/test_c013/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c014/source.mk | 20 + .../test_suites/crypto/test_c014/test_c014.c | 219 +++++++ .../test_suites/crypto/test_c014/test_c014.h | 35 ++ .../test_suites/crypto/test_c014/test_data.h | 95 +++ .../test_suites/crypto/test_c014/test_entry.c | 69 ++ psa-ff/test_suites/crypto/test_c015/source.mk | 20 + .../test_suites/crypto/test_c015/test_c015.c | 142 +++++ .../test_suites/crypto/test_c015/test_c015.h | 34 + .../test_suites/crypto/test_c015/test_data.h | 62 ++ .../test_suites/crypto/test_c015/test_entry.c | 69 ++ psa-ff/test_suites/crypto/testsuite.db | 39 ++ psa-ff/test_suites/ipc/test_i001/source.mk | 20 + psa-ff/test_suites/ipc/test_i001/test_entry.c | 60 ++ psa-ff/test_suites/ipc/test_i001/test_i001.c | 93 +++ psa-ff/test_suites/ipc/test_i001/test_i001.h | 33 + .../ipc/test_i001/test_supp_i001.c | 41 ++ psa-ff/test_suites/ipc/test_i002/source.mk | 20 + psa-ff/test_suites/ipc/test_i002/test_entry.c | 60 ++ psa-ff/test_suites/ipc/test_i002/test_i002.c | 286 +++++++++ psa-ff/test_suites/ipc/test_i002/test_i002.h | 42 ++ .../ipc/test_i002/test_supp_i002.c | 373 +++++++++++ psa-ff/test_suites/ipc/test_i003/source.mk | 20 + psa-ff/test_suites/ipc/test_i003/test_entry.c | 60 ++ psa-ff/test_suites/ipc/test_i003/test_i003.c | 327 ++++++++++ psa-ff/test_suites/ipc/test_i003/test_i003.h | 37 ++ .../ipc/test_i003/test_supp_i003.c | 520 +++++++++++++++ psa-ff/test_suites/ipc/test_i004/source.mk | 20 + psa-ff/test_suites/ipc/test_i004/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i004/test_i004.c | 79 +++ psa-ff/test_suites/ipc/test_i004/test_i004.h | 32 + .../ipc/test_i004/test_supp_i004.c | 33 + psa-ff/test_suites/ipc/test_i005/source.mk | 20 + psa-ff/test_suites/ipc/test_i005/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i005/test_i005.c | 79 +++ psa-ff/test_suites/ipc/test_i005/test_i005.h | 32 + .../ipc/test_i005/test_supp_i005.c | 44 ++ psa-ff/test_suites/ipc/test_i006/source.mk | 20 + psa-ff/test_suites/ipc/test_i006/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i006/test_i006.c | 77 +++ psa-ff/test_suites/ipc/test_i006/test_i006.h | 32 + .../ipc/test_i006/test_supp_i006.c | 44 ++ psa-ff/test_suites/ipc/test_i007/source.mk | 20 + psa-ff/test_suites/ipc/test_i007/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i007/test_i007.c | 77 +++ psa-ff/test_suites/ipc/test_i007/test_i007.h | 32 + .../ipc/test_i007/test_supp_i007.c | 44 ++ psa-ff/test_suites/ipc/test_i008/source.mk | 20 + psa-ff/test_suites/ipc/test_i008/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i008/test_i008.c | 93 +++ psa-ff/test_suites/ipc/test_i008/test_i008.h | 32 + .../ipc/test_i008/test_supp_i008.c | 50 ++ psa-ff/test_suites/ipc/test_i009/source.mk | 20 + psa-ff/test_suites/ipc/test_i009/test_entry.c | 52 ++ psa-ff/test_suites/ipc/test_i009/test_i009.c | 73 +++ psa-ff/test_suites/ipc/test_i009/test_i009.h | 32 + .../ipc/test_i009/test_supp_i009.c | 44 ++ psa-ff/test_suites/ipc/test_i010/source.mk | 20 + psa-ff/test_suites/ipc/test_i010/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i010/test_i010.c | 77 +++ psa-ff/test_suites/ipc/test_i010/test_i010.h | 32 + .../ipc/test_i010/test_supp_i010.c | 44 ++ psa-ff/test_suites/ipc/test_i011/source.mk | 20 + psa-ff/test_suites/ipc/test_i011/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i011/test_i011.c | 77 +++ psa-ff/test_suites/ipc/test_i011/test_i011.h | 32 + .../ipc/test_i011/test_supp_i011.c | 44 ++ psa-ff/test_suites/ipc/test_i012/source.mk | 20 + psa-ff/test_suites/ipc/test_i012/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i012/test_i012.c | 74 +++ psa-ff/test_suites/ipc/test_i012/test_i012.h | 32 + .../ipc/test_i012/test_supp_i012.c | 33 + psa-ff/test_suites/ipc/test_i013/source.mk | 20 + psa-ff/test_suites/ipc/test_i013/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i013/test_i013.c | 47 ++ psa-ff/test_suites/ipc/test_i013/test_i013.h | 32 + .../ipc/test_i013/test_supp_i013.c | 77 +++ psa-ff/test_suites/ipc/test_i014/source.mk | 20 + psa-ff/test_suites/ipc/test_i014/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i014/test_i014.c | 47 ++ psa-ff/test_suites/ipc/test_i014/test_i014.h | 32 + .../ipc/test_i014/test_supp_i014.c | 81 +++ psa-ff/test_suites/ipc/test_i015/source.mk | 20 + psa-ff/test_suites/ipc/test_i015/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i015/test_i015.c | 47 ++ psa-ff/test_suites/ipc/test_i015/test_i015.h | 32 + .../ipc/test_i015/test_supp_i015.c | 74 +++ psa-ff/test_suites/ipc/test_i016/source.mk | 20 + psa-ff/test_suites/ipc/test_i016/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i016/test_i016.c | 47 ++ psa-ff/test_suites/ipc/test_i016/test_i016.h | 32 + .../ipc/test_i016/test_supp_i016.c | 74 +++ psa-ff/test_suites/ipc/test_i017/source.mk | 20 + psa-ff/test_suites/ipc/test_i017/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i017/test_i017.c | 47 ++ psa-ff/test_suites/ipc/test_i017/test_i017.h | 32 + .../ipc/test_i017/test_supp_i017.c | 82 +++ psa-ff/test_suites/ipc/test_i018/source.mk | 20 + psa-ff/test_suites/ipc/test_i018/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i018/test_i018.c | 47 ++ psa-ff/test_suites/ipc/test_i018/test_i018.h | 32 + .../ipc/test_i018/test_supp_i018.c | 83 +++ psa-ff/test_suites/ipc/test_i019/source.mk | 20 + psa-ff/test_suites/ipc/test_i019/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i019/test_i019.c | 47 ++ psa-ff/test_suites/ipc/test_i019/test_i019.h | 32 + .../ipc/test_i019/test_supp_i019.c | 72 +++ psa-ff/test_suites/ipc/test_i020/source.mk | 20 + psa-ff/test_suites/ipc/test_i020/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i020/test_i020.c | 51 ++ psa-ff/test_suites/ipc/test_i020/test_i020.h | 32 + .../ipc/test_i020/test_supp_i020.c | 80 +++ psa-ff/test_suites/ipc/test_i021/source.mk | 20 + psa-ff/test_suites/ipc/test_i021/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i021/test_i021.c | 61 ++ psa-ff/test_suites/ipc/test_i021/test_i021.h | 32 + .../ipc/test_i021/test_supp_i021.c | 92 +++ psa-ff/test_suites/ipc/test_i022/source.mk | 20 + psa-ff/test_suites/ipc/test_i022/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i022/test_i022.c | 47 ++ psa-ff/test_suites/ipc/test_i022/test_i022.h | 32 + .../ipc/test_i022/test_supp_i022.c | 80 +++ psa-ff/test_suites/ipc/test_i023/source.mk | 20 + psa-ff/test_suites/ipc/test_i023/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i023/test_i023.c | 47 ++ psa-ff/test_suites/ipc/test_i023/test_i023.h | 32 + .../ipc/test_i023/test_supp_i023.c | 82 +++ psa-ff/test_suites/ipc/test_i024/source.mk | 20 + psa-ff/test_suites/ipc/test_i024/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i024/test_i024.c | 89 +++ psa-ff/test_suites/ipc/test_i024/test_i024.h | 32 + .../ipc/test_i024/test_supp_i024.c | 52 ++ psa-ff/test_suites/ipc/test_i025/source.mk | 20 + psa-ff/test_suites/ipc/test_i025/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i025/test_i025.c | 89 +++ psa-ff/test_suites/ipc/test_i025/test_i025.h | 32 + .../ipc/test_i025/test_supp_i025.c | 52 ++ psa-ff/test_suites/ipc/test_i026/source.mk | 20 + psa-ff/test_suites/ipc/test_i026/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i026/test_i026.c | 96 +++ psa-ff/test_suites/ipc/test_i026/test_i026.h | 37 ++ .../ipc/test_i026/test_supp_i026.c | 52 ++ psa-ff/test_suites/ipc/test_i027/source.mk | 20 + psa-ff/test_suites/ipc/test_i027/test_entry.c | 59 ++ psa-ff/test_suites/ipc/test_i027/test_i027.c | 119 ++++ psa-ff/test_suites/ipc/test_i027/test_i027.h | 32 + .../ipc/test_i027/test_supp_i027.c | 65 ++ psa-ff/test_suites/ipc/test_i028/source.mk | 20 + psa-ff/test_suites/ipc/test_i028/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i028/test_i028.c | 47 ++ psa-ff/test_suites/ipc/test_i028/test_i028.h | 32 + .../ipc/test_i028/test_supp_i028.c | 78 +++ psa-ff/test_suites/ipc/test_i029/source.mk | 20 + psa-ff/test_suites/ipc/test_i029/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i029/test_i029.c | 51 ++ psa-ff/test_suites/ipc/test_i029/test_i029.h | 32 + .../ipc/test_i029/test_supp_i029.c | 85 +++ psa-ff/test_suites/ipc/test_i030/source.mk | 20 + psa-ff/test_suites/ipc/test_i030/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i030/test_i030.c | 61 ++ psa-ff/test_suites/ipc/test_i030/test_i030.h | 32 + .../ipc/test_i030/test_supp_i030.c | 97 +++ psa-ff/test_suites/ipc/test_i031/source.mk | 20 + psa-ff/test_suites/ipc/test_i031/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i031/test_i031.c | 60 ++ psa-ff/test_suites/ipc/test_i031/test_i031.h | 32 + .../ipc/test_i031/test_supp_i031.c | 97 +++ psa-ff/test_suites/ipc/test_i032/source.mk | 20 + psa-ff/test_suites/ipc/test_i032/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i032/test_i032.c | 60 ++ psa-ff/test_suites/ipc/test_i032/test_i032.h | 32 + .../ipc/test_i032/test_supp_i032.c | 97 +++ psa-ff/test_suites/ipc/test_i033/source.mk | 20 + psa-ff/test_suites/ipc/test_i033/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i033/test_i033.c | 60 ++ psa-ff/test_suites/ipc/test_i033/test_i033.h | 32 + .../ipc/test_i033/test_supp_i033.c | 97 +++ psa-ff/test_suites/ipc/test_i034/source.mk | 20 + psa-ff/test_suites/ipc/test_i034/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i034/test_i034.c | 47 ++ psa-ff/test_suites/ipc/test_i034/test_i034.h | 32 + .../ipc/test_i034/test_supp_i034.c | 76 +++ psa-ff/test_suites/ipc/test_i035/source.mk | 20 + psa-ff/test_suites/ipc/test_i035/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i035/test_i035.c | 50 ++ psa-ff/test_suites/ipc/test_i035/test_i035.h | 32 + .../ipc/test_i035/test_supp_i035.c | 82 +++ psa-ff/test_suites/ipc/test_i036/source.mk | 20 + psa-ff/test_suites/ipc/test_i036/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i036/test_i036.c | 60 ++ psa-ff/test_suites/ipc/test_i036/test_i036.h | 32 + .../ipc/test_i036/test_supp_i036.c | 94 +++ psa-ff/test_suites/ipc/test_i037/source.mk | 20 + psa-ff/test_suites/ipc/test_i037/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i037/test_i037.c | 60 ++ psa-ff/test_suites/ipc/test_i037/test_i037.h | 32 + .../ipc/test_i037/test_supp_i037.c | 82 +++ psa-ff/test_suites/ipc/test_i038/source.mk | 20 + psa-ff/test_suites/ipc/test_i038/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i038/test_i038.c | 60 ++ psa-ff/test_suites/ipc/test_i038/test_i038.h | 32 + .../ipc/test_i038/test_supp_i038.c | 96 +++ psa-ff/test_suites/ipc/test_i039/source.mk | 20 + psa-ff/test_suites/ipc/test_i039/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i039/test_i039.c | 60 ++ psa-ff/test_suites/ipc/test_i039/test_i039.h | 32 + .../ipc/test_i039/test_supp_i039.c | 96 +++ psa-ff/test_suites/ipc/test_i040/source.mk | 20 + psa-ff/test_suites/ipc/test_i040/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i040/test_i040.c | 47 ++ psa-ff/test_suites/ipc/test_i040/test_i040.h | 32 + .../ipc/test_i040/test_supp_i040.c | 79 +++ psa-ff/test_suites/ipc/test_i041/source.mk | 20 + psa-ff/test_suites/ipc/test_i041/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i041/test_i041.c | 50 ++ psa-ff/test_suites/ipc/test_i041/test_i041.h | 32 + .../ipc/test_i041/test_supp_i041.c | 86 +++ psa-ff/test_suites/ipc/test_i042/source.mk | 20 + psa-ff/test_suites/ipc/test_i042/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i042/test_i042.c | 60 ++ psa-ff/test_suites/ipc/test_i042/test_i042.h | 32 + .../ipc/test_i042/test_supp_i042.c | 82 +++ psa-ff/test_suites/ipc/test_i043/source.mk | 20 + psa-ff/test_suites/ipc/test_i043/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i043/test_i043.c | 60 ++ psa-ff/test_suites/ipc/test_i043/test_i043.h | 32 + .../ipc/test_i043/test_supp_i043.c | 98 +++ psa-ff/test_suites/ipc/test_i044/source.mk | 20 + psa-ff/test_suites/ipc/test_i044/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i044/test_i044.c | 60 ++ psa-ff/test_suites/ipc/test_i044/test_i044.h | 32 + .../ipc/test_i044/test_supp_i044.c | 86 +++ psa-ff/test_suites/ipc/test_i045/source.mk | 20 + psa-ff/test_suites/ipc/test_i045/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i045/test_i045.c | 60 ++ psa-ff/test_suites/ipc/test_i045/test_i045.h | 32 + .../ipc/test_i045/test_supp_i045.c | 98 +++ psa-ff/test_suites/ipc/test_i046/source.mk | 20 + psa-ff/test_suites/ipc/test_i046/test_entry.c | 51 ++ psa-ff/test_suites/ipc/test_i046/test_i046.c | 63 ++ psa-ff/test_suites/ipc/test_i046/test_i046.h | 32 + .../ipc/test_i046/test_supp_i046.c | 98 +++ psa-ff/test_suites/ipc/testsuite.db | 70 +++ .../partition/common/driver_partition.c | 192 ++++++ .../partition/ipc/client_partition.c | 99 +++ .../partition/ipc/client_partition.h | 124 ++++ .../partition/ipc/server_partition.c | 102 +++ .../partition/ipc/server_partition.h | 122 ++++ psa-ff/tools/makefiles/Makefile | 65 ++ psa-ff/tools/makefiles/test.linker | 55 ++ psa-ff/tools/makefiles/testbuild.mk | 51 ++ psa-ff/tools/makefiles/toolchain.mk | 66 ++ psa-ff/tools/makefiles/valbuild.mk | 48 ++ .../tools/scripts/process_test_linker_file.pl | 65 ++ psa-ff/tools/scripts/setup.sh | 242 +++++++ psa-ff/tools/scripts/targetConfigGen.pl | 235 +++++++ psa-ff/tools/scripts/test_elf_combine.pl | 89 +++ psa-ff/val/common/val.h | 258 ++++++++ psa-ff/val/common/val_client_defs.h | 81 +++ psa-ff/val/nspe/pal_interfaces_ns.h | 47 ++ psa-ff/val/nspe/val_crypto.c | 53 ++ psa-ff/val/nspe/val_crypto.h | 305 +++++++++ psa-ff/val/nspe/val_dispatcher.c | 436 +++++++++++++ psa-ff/val/nspe/val_dispatcher.h | 70 +++ psa-ff/val/nspe/val_entry.c | 63 ++ psa-ff/val/nspe/val_entry.h | 32 + psa-ff/val/nspe/val_framework.c | 590 ++++++++++++++++++ psa-ff/val/nspe/val_framework.h | 46 ++ psa-ff/val/nspe/val_interfaces.c | 59 ++ psa-ff/val/nspe/val_interfaces.h | 82 +++ psa-ff/val/nspe/val_peripherals.c | 395 ++++++++++++ psa-ff/val/nspe/val_peripherals.h | 33 + psa-ff/val/nspe/val_target.c | 146 +++++ psa-ff/val/nspe/val_target.h | 198 ++++++ psa-ff/val/spe/pal_interfaces_s.h | 93 +++ psa-ff/val/spe/val_driver_service_apis.c | 142 +++++ psa-ff/val/spe/val_driver_service_apis.h | 41 ++ psa-ff/val/spe/val_partition_common.h | 496 +++++++++++++++ psa-ff/val/spe/val_service_defs.h | 104 +++ 366 files changed, 27012 insertions(+) create mode 100644 psa-ff/README.md create mode 100644 psa-ff/docs/Arm_PSA_FF_Arch_Test_Porting_Guide.md create mode 100644 psa-ff/docs/Arm_PSA_FF_Arch_Test_Validation_Methodology.pdf create mode 100644 psa-ff/docs/psa_crypto_testlist.md create mode 100644 psa-ff/docs/psa_ipc_testlist.md create mode 100644 psa-ff/platform/manifests/common/driver_partition_psa.json create mode 100644 psa-ff/platform/manifests/ipc/client_partition_psa.json create mode 100644 psa-ff/platform/manifests/ipc/server_partition_psa.json create mode 100644 psa-ff/platform/nspe/Makefile create mode 100644 psa-ff/platform/nspe/pal_baremetal_ns_intf.c create mode 100644 psa-ff/platform/nspe/pal_common.h create mode 100644 psa-ff/platform/nspe/pal_crypto_intf.c create mode 100644 psa-ff/platform/nspe/pal_crypto_intf.h create mode 100644 psa-ff/platform/nspe/pal_sid.h create mode 100644 psa-ff/platform/spe/drivers/nvmem/pal_nvmem.c create mode 100644 psa-ff/platform/spe/drivers/nvmem/pal_nvmem.h create mode 100644 psa-ff/platform/spe/drivers/uart/pal_uart.c create mode 100644 psa-ff/platform/spe/drivers/uart/pal_uart.h create mode 100644 psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.c create mode 100644 psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.h create mode 100644 psa-ff/platform/spe/pal_common.h create mode 100644 psa-ff/platform/spe/pal_driver_intf.c create mode 100644 psa-ff/platform/targets/fvp_mps2_cm4_mbedos/target.cfg create mode 100644 psa-ff/test_suites/crypto/test_c001/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c001/test_c001.c create mode 100644 psa-ff/test_suites/crypto/test_c001/test_c001.h create mode 100644 psa-ff/test_suites/crypto/test_c001/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c002/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c002/test_c002.c create mode 100644 psa-ff/test_suites/crypto/test_c002/test_c002.h create mode 100644 psa-ff/test_suites/crypto/test_c002/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c002/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c003/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c003/test_c003.c create mode 100644 psa-ff/test_suites/crypto/test_c003/test_c003.h create mode 100644 psa-ff/test_suites/crypto/test_c003/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c003/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c004/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c004/test_c004.c create mode 100644 psa-ff/test_suites/crypto/test_c004/test_c004.h create mode 100644 psa-ff/test_suites/crypto/test_c004/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c004/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c005/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c005/test_c005.c create mode 100644 psa-ff/test_suites/crypto/test_c005/test_c005.h create mode 100644 psa-ff/test_suites/crypto/test_c005/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c005/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c006/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c006/test_c006.c create mode 100644 psa-ff/test_suites/crypto/test_c006/test_c006.h create mode 100644 psa-ff/test_suites/crypto/test_c006/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c006/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c007/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c007/test_c007.c create mode 100644 psa-ff/test_suites/crypto/test_c007/test_c007.h create mode 100644 psa-ff/test_suites/crypto/test_c007/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c007/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c008/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c008/test_c008.c create mode 100644 psa-ff/test_suites/crypto/test_c008/test_c008.h create mode 100644 psa-ff/test_suites/crypto/test_c008/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c008/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c009/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c009/test_c009.c create mode 100644 psa-ff/test_suites/crypto/test_c009/test_c009.h create mode 100644 psa-ff/test_suites/crypto/test_c009/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c009/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c010/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c010/test_c010.c create mode 100644 psa-ff/test_suites/crypto/test_c010/test_c010.h create mode 100644 psa-ff/test_suites/crypto/test_c010/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c010/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c011/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c011/test_c011.c create mode 100644 psa-ff/test_suites/crypto/test_c011/test_c011.h create mode 100644 psa-ff/test_suites/crypto/test_c011/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c011/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c012/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c012/test_c012.c create mode 100644 psa-ff/test_suites/crypto/test_c012/test_c012.h create mode 100644 psa-ff/test_suites/crypto/test_c012/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c012/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c013/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c013/test_c013.c create mode 100644 psa-ff/test_suites/crypto/test_c013/test_c013.h create mode 100644 psa-ff/test_suites/crypto/test_c013/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c013/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c014/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c014/test_c014.c create mode 100644 psa-ff/test_suites/crypto/test_c014/test_c014.h create mode 100644 psa-ff/test_suites/crypto/test_c014/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c014/test_entry.c create mode 100644 psa-ff/test_suites/crypto/test_c015/source.mk create mode 100644 psa-ff/test_suites/crypto/test_c015/test_c015.c create mode 100644 psa-ff/test_suites/crypto/test_c015/test_c015.h create mode 100644 psa-ff/test_suites/crypto/test_c015/test_data.h create mode 100644 psa-ff/test_suites/crypto/test_c015/test_entry.c create mode 100644 psa-ff/test_suites/crypto/testsuite.db create mode 100644 psa-ff/test_suites/ipc/test_i001/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i001/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i001/test_i001.c create mode 100644 psa-ff/test_suites/ipc/test_i001/test_i001.h create mode 100644 psa-ff/test_suites/ipc/test_i001/test_supp_i001.c create mode 100644 psa-ff/test_suites/ipc/test_i002/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i002/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i002/test_i002.c create mode 100644 psa-ff/test_suites/ipc/test_i002/test_i002.h create mode 100644 psa-ff/test_suites/ipc/test_i002/test_supp_i002.c create mode 100644 psa-ff/test_suites/ipc/test_i003/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i003/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i003/test_i003.c create mode 100644 psa-ff/test_suites/ipc/test_i003/test_i003.h create mode 100644 psa-ff/test_suites/ipc/test_i003/test_supp_i003.c create mode 100644 psa-ff/test_suites/ipc/test_i004/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i004/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i004/test_i004.c create mode 100644 psa-ff/test_suites/ipc/test_i004/test_i004.h create mode 100644 psa-ff/test_suites/ipc/test_i004/test_supp_i004.c create mode 100644 psa-ff/test_suites/ipc/test_i005/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i005/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i005/test_i005.c create mode 100644 psa-ff/test_suites/ipc/test_i005/test_i005.h create mode 100644 psa-ff/test_suites/ipc/test_i005/test_supp_i005.c create mode 100644 psa-ff/test_suites/ipc/test_i006/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i006/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i006/test_i006.c create mode 100644 psa-ff/test_suites/ipc/test_i006/test_i006.h create mode 100644 psa-ff/test_suites/ipc/test_i006/test_supp_i006.c create mode 100644 psa-ff/test_suites/ipc/test_i007/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i007/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i007/test_i007.c create mode 100644 psa-ff/test_suites/ipc/test_i007/test_i007.h create mode 100644 psa-ff/test_suites/ipc/test_i007/test_supp_i007.c create mode 100644 psa-ff/test_suites/ipc/test_i008/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i008/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i008/test_i008.c create mode 100644 psa-ff/test_suites/ipc/test_i008/test_i008.h create mode 100644 psa-ff/test_suites/ipc/test_i008/test_supp_i008.c create mode 100644 psa-ff/test_suites/ipc/test_i009/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i009/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i009/test_i009.c create mode 100644 psa-ff/test_suites/ipc/test_i009/test_i009.h create mode 100644 psa-ff/test_suites/ipc/test_i009/test_supp_i009.c create mode 100644 psa-ff/test_suites/ipc/test_i010/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i010/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i010/test_i010.c create mode 100644 psa-ff/test_suites/ipc/test_i010/test_i010.h create mode 100644 psa-ff/test_suites/ipc/test_i010/test_supp_i010.c create mode 100644 psa-ff/test_suites/ipc/test_i011/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i011/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i011/test_i011.c create mode 100644 psa-ff/test_suites/ipc/test_i011/test_i011.h create mode 100644 psa-ff/test_suites/ipc/test_i011/test_supp_i011.c create mode 100644 psa-ff/test_suites/ipc/test_i012/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i012/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i012/test_i012.c create mode 100644 psa-ff/test_suites/ipc/test_i012/test_i012.h create mode 100644 psa-ff/test_suites/ipc/test_i012/test_supp_i012.c create mode 100644 psa-ff/test_suites/ipc/test_i013/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i013/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i013/test_i013.c create mode 100644 psa-ff/test_suites/ipc/test_i013/test_i013.h create mode 100644 psa-ff/test_suites/ipc/test_i013/test_supp_i013.c create mode 100644 psa-ff/test_suites/ipc/test_i014/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i014/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i014/test_i014.c create mode 100644 psa-ff/test_suites/ipc/test_i014/test_i014.h create mode 100644 psa-ff/test_suites/ipc/test_i014/test_supp_i014.c create mode 100644 psa-ff/test_suites/ipc/test_i015/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i015/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i015/test_i015.c create mode 100644 psa-ff/test_suites/ipc/test_i015/test_i015.h create mode 100644 psa-ff/test_suites/ipc/test_i015/test_supp_i015.c create mode 100644 psa-ff/test_suites/ipc/test_i016/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i016/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i016/test_i016.c create mode 100644 psa-ff/test_suites/ipc/test_i016/test_i016.h create mode 100644 psa-ff/test_suites/ipc/test_i016/test_supp_i016.c create mode 100644 psa-ff/test_suites/ipc/test_i017/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i017/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i017/test_i017.c create mode 100644 psa-ff/test_suites/ipc/test_i017/test_i017.h create mode 100644 psa-ff/test_suites/ipc/test_i017/test_supp_i017.c create mode 100644 psa-ff/test_suites/ipc/test_i018/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i018/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i018/test_i018.c create mode 100644 psa-ff/test_suites/ipc/test_i018/test_i018.h create mode 100644 psa-ff/test_suites/ipc/test_i018/test_supp_i018.c create mode 100644 psa-ff/test_suites/ipc/test_i019/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i019/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i019/test_i019.c create mode 100644 psa-ff/test_suites/ipc/test_i019/test_i019.h create mode 100644 psa-ff/test_suites/ipc/test_i019/test_supp_i019.c create mode 100644 psa-ff/test_suites/ipc/test_i020/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i020/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i020/test_i020.c create mode 100644 psa-ff/test_suites/ipc/test_i020/test_i020.h create mode 100644 psa-ff/test_suites/ipc/test_i020/test_supp_i020.c create mode 100644 psa-ff/test_suites/ipc/test_i021/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i021/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i021/test_i021.c create mode 100644 psa-ff/test_suites/ipc/test_i021/test_i021.h create mode 100644 psa-ff/test_suites/ipc/test_i021/test_supp_i021.c create mode 100644 psa-ff/test_suites/ipc/test_i022/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i022/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i022/test_i022.c create mode 100644 psa-ff/test_suites/ipc/test_i022/test_i022.h create mode 100644 psa-ff/test_suites/ipc/test_i022/test_supp_i022.c create mode 100644 psa-ff/test_suites/ipc/test_i023/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i023/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i023/test_i023.c create mode 100644 psa-ff/test_suites/ipc/test_i023/test_i023.h create mode 100644 psa-ff/test_suites/ipc/test_i023/test_supp_i023.c create mode 100644 psa-ff/test_suites/ipc/test_i024/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i024/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i024/test_i024.c create mode 100644 psa-ff/test_suites/ipc/test_i024/test_i024.h create mode 100644 psa-ff/test_suites/ipc/test_i024/test_supp_i024.c create mode 100644 psa-ff/test_suites/ipc/test_i025/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i025/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i025/test_i025.c create mode 100644 psa-ff/test_suites/ipc/test_i025/test_i025.h create mode 100644 psa-ff/test_suites/ipc/test_i025/test_supp_i025.c create mode 100644 psa-ff/test_suites/ipc/test_i026/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i026/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i026/test_i026.c create mode 100644 psa-ff/test_suites/ipc/test_i026/test_i026.h create mode 100644 psa-ff/test_suites/ipc/test_i026/test_supp_i026.c create mode 100644 psa-ff/test_suites/ipc/test_i027/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i027/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i027/test_i027.c create mode 100644 psa-ff/test_suites/ipc/test_i027/test_i027.h create mode 100644 psa-ff/test_suites/ipc/test_i027/test_supp_i027.c create mode 100644 psa-ff/test_suites/ipc/test_i028/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i028/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i028/test_i028.c create mode 100644 psa-ff/test_suites/ipc/test_i028/test_i028.h create mode 100644 psa-ff/test_suites/ipc/test_i028/test_supp_i028.c create mode 100644 psa-ff/test_suites/ipc/test_i029/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i029/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i029/test_i029.c create mode 100644 psa-ff/test_suites/ipc/test_i029/test_i029.h create mode 100644 psa-ff/test_suites/ipc/test_i029/test_supp_i029.c create mode 100644 psa-ff/test_suites/ipc/test_i030/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i030/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i030/test_i030.c create mode 100644 psa-ff/test_suites/ipc/test_i030/test_i030.h create mode 100644 psa-ff/test_suites/ipc/test_i030/test_supp_i030.c create mode 100644 psa-ff/test_suites/ipc/test_i031/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i031/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i031/test_i031.c create mode 100644 psa-ff/test_suites/ipc/test_i031/test_i031.h create mode 100644 psa-ff/test_suites/ipc/test_i031/test_supp_i031.c create mode 100644 psa-ff/test_suites/ipc/test_i032/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i032/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i032/test_i032.c create mode 100644 psa-ff/test_suites/ipc/test_i032/test_i032.h create mode 100644 psa-ff/test_suites/ipc/test_i032/test_supp_i032.c create mode 100644 psa-ff/test_suites/ipc/test_i033/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i033/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i033/test_i033.c create mode 100644 psa-ff/test_suites/ipc/test_i033/test_i033.h create mode 100644 psa-ff/test_suites/ipc/test_i033/test_supp_i033.c create mode 100644 psa-ff/test_suites/ipc/test_i034/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i034/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i034/test_i034.c create mode 100644 psa-ff/test_suites/ipc/test_i034/test_i034.h create mode 100644 psa-ff/test_suites/ipc/test_i034/test_supp_i034.c create mode 100644 psa-ff/test_suites/ipc/test_i035/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i035/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i035/test_i035.c create mode 100644 psa-ff/test_suites/ipc/test_i035/test_i035.h create mode 100644 psa-ff/test_suites/ipc/test_i035/test_supp_i035.c create mode 100644 psa-ff/test_suites/ipc/test_i036/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i036/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i036/test_i036.c create mode 100644 psa-ff/test_suites/ipc/test_i036/test_i036.h create mode 100644 psa-ff/test_suites/ipc/test_i036/test_supp_i036.c create mode 100644 psa-ff/test_suites/ipc/test_i037/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i037/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i037/test_i037.c create mode 100644 psa-ff/test_suites/ipc/test_i037/test_i037.h create mode 100644 psa-ff/test_suites/ipc/test_i037/test_supp_i037.c create mode 100644 psa-ff/test_suites/ipc/test_i038/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i038/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i038/test_i038.c create mode 100644 psa-ff/test_suites/ipc/test_i038/test_i038.h create mode 100644 psa-ff/test_suites/ipc/test_i038/test_supp_i038.c create mode 100644 psa-ff/test_suites/ipc/test_i039/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i039/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i039/test_i039.c create mode 100644 psa-ff/test_suites/ipc/test_i039/test_i039.h create mode 100644 psa-ff/test_suites/ipc/test_i039/test_supp_i039.c create mode 100644 psa-ff/test_suites/ipc/test_i040/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i040/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i040/test_i040.c create mode 100644 psa-ff/test_suites/ipc/test_i040/test_i040.h create mode 100644 psa-ff/test_suites/ipc/test_i040/test_supp_i040.c create mode 100644 psa-ff/test_suites/ipc/test_i041/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i041/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i041/test_i041.c create mode 100644 psa-ff/test_suites/ipc/test_i041/test_i041.h create mode 100644 psa-ff/test_suites/ipc/test_i041/test_supp_i041.c create mode 100644 psa-ff/test_suites/ipc/test_i042/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i042/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i042/test_i042.c create mode 100644 psa-ff/test_suites/ipc/test_i042/test_i042.h create mode 100644 psa-ff/test_suites/ipc/test_i042/test_supp_i042.c create mode 100644 psa-ff/test_suites/ipc/test_i043/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i043/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i043/test_i043.c create mode 100644 psa-ff/test_suites/ipc/test_i043/test_i043.h create mode 100644 psa-ff/test_suites/ipc/test_i043/test_supp_i043.c create mode 100644 psa-ff/test_suites/ipc/test_i044/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i044/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i044/test_i044.c create mode 100644 psa-ff/test_suites/ipc/test_i044/test_i044.h create mode 100644 psa-ff/test_suites/ipc/test_i044/test_supp_i044.c create mode 100644 psa-ff/test_suites/ipc/test_i045/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i045/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i045/test_i045.c create mode 100644 psa-ff/test_suites/ipc/test_i045/test_i045.h create mode 100644 psa-ff/test_suites/ipc/test_i045/test_supp_i045.c create mode 100644 psa-ff/test_suites/ipc/test_i046/source.mk create mode 100644 psa-ff/test_suites/ipc/test_i046/test_entry.c create mode 100644 psa-ff/test_suites/ipc/test_i046/test_i046.c create mode 100644 psa-ff/test_suites/ipc/test_i046/test_i046.h create mode 100644 psa-ff/test_suites/ipc/test_i046/test_supp_i046.c create mode 100644 psa-ff/test_suites/ipc/testsuite.db create mode 100644 psa-ff/test_suites/partition/common/driver_partition.c create mode 100644 psa-ff/test_suites/partition/ipc/client_partition.c create mode 100644 psa-ff/test_suites/partition/ipc/client_partition.h create mode 100644 psa-ff/test_suites/partition/ipc/server_partition.c create mode 100644 psa-ff/test_suites/partition/ipc/server_partition.h create mode 100644 psa-ff/tools/makefiles/Makefile create mode 100644 psa-ff/tools/makefiles/test.linker create mode 100644 psa-ff/tools/makefiles/testbuild.mk create mode 100644 psa-ff/tools/makefiles/toolchain.mk create mode 100644 psa-ff/tools/makefiles/valbuild.mk create mode 100755 psa-ff/tools/scripts/process_test_linker_file.pl create mode 100755 psa-ff/tools/scripts/setup.sh create mode 100755 psa-ff/tools/scripts/targetConfigGen.pl create mode 100755 psa-ff/tools/scripts/test_elf_combine.pl create mode 100644 psa-ff/val/common/val.h create mode 100644 psa-ff/val/common/val_client_defs.h create mode 100644 psa-ff/val/nspe/pal_interfaces_ns.h create mode 100644 psa-ff/val/nspe/val_crypto.c create mode 100644 psa-ff/val/nspe/val_crypto.h create mode 100644 psa-ff/val/nspe/val_dispatcher.c create mode 100644 psa-ff/val/nspe/val_dispatcher.h create mode 100644 psa-ff/val/nspe/val_entry.c create mode 100644 psa-ff/val/nspe/val_entry.h create mode 100644 psa-ff/val/nspe/val_framework.c create mode 100644 psa-ff/val/nspe/val_framework.h create mode 100644 psa-ff/val/nspe/val_interfaces.c create mode 100644 psa-ff/val/nspe/val_interfaces.h create mode 100644 psa-ff/val/nspe/val_peripherals.c create mode 100644 psa-ff/val/nspe/val_peripherals.h create mode 100644 psa-ff/val/nspe/val_target.c create mode 100644 psa-ff/val/nspe/val_target.h create mode 100644 psa-ff/val/spe/pal_interfaces_s.h create mode 100644 psa-ff/val/spe/val_driver_service_apis.c create mode 100644 psa-ff/val/spe/val_driver_service_apis.h create mode 100644 psa-ff/val/spe/val_partition_common.h create mode 100644 psa-ff/val/spe/val_service_defs.h diff --git a/README.md b/README.md index 01772a9b..b51fa1a3 100755 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ The tests are available as open source. The tests and the corresponding abstract ### TBSA-v8M The test suite for this specification is located in the tbsa-v8m directory of this repository. See [TBSA-v8m Readme](tbsa-v8m/README.md) file for more details. +### Firmware Framework +The test suite for this specification is located in the psa-ff directory of this repository. See [Firmware Framework Readme](psa-ff/README.md) file for more details. + ## License diff --git a/psa-ff/README.md b/psa-ff/README.md new file mode 100644 index 00000000..395c0b7f --- /dev/null +++ b/psa-ff/README.md @@ -0,0 +1,131 @@ + +# PSA Firmware Framework : Architecture Test Suite + +## Introduction + +### PSA FF Specification + +Arm Platform Security Architecture (PSA) is a holistic set of threat models, security analysis, hardware and firmware architecture specifications, and an open source firmware reference implementation. PSA provides a recipe, based on industry best practice, that allows security to be consistently designed in, at both a hardware and firmware level. + +The PSA Firmware Framework defines a standard programming environment and firmware interfaces for implementing and accessing security services within a device's Root of Trust. + +The Firmware Framework specification:
+- provides requirements for the SPM +- defines a standard runtime environment for developing protected RoT Services, including the programming interfaces + provided by the SPM for implementing and using RoT Services +- defines the standard interfaces for the PSA RoT Services. + +To receive a copy of the PSA FF specification, Arm Licensees may contact Arm through their partner managers. + +### Architecture Test Suite + +The Architecture Test Suite is a set of examples of the invariant behaviours that are specified by the PSA FF Specification. Use this suite to verify that these behaviours are implemented correctly in your system. + +PSA FF Architecture Test Suite contains the tests that are self-checking, portable C-based tests with directed stimulus. + +The tests are available as open source. The tests and the corresponding abstraction layers are available with an Apache v2.0 license allowing for external contribution. + +## Release Update + - Release Version - 0.5 + - Code Quality: Alpha
+ This indicates the suite is in development and it contains tests which have not been validated on any platform. Please use this opportunity to suggest enhancements and point out errors. + - Current release contains the test suite for IPC and Crypto tests. + - This test suite is not a substitute for design verification. + - To review the test logs, Arm licensees can contact Arm directly through their partner managers. + +## Layers + +PSA FF Architecture Test Suite uses a layered software-stack approach to enable porting across different test platforms. The constituents of the layered stack are:
+- Test suite +- Validation Abstraction Layer (VAL) +- Platform Abstraction Layer (PAL) + +The tests are written on top of Validation Abstraction Layer (VAL) and Platform Abstraction Layer (PAL). The abstraction layers provide platform information and runtime environment to enable execution of the tests. + +In this release, PAL is written on top of baremetal drivers. It is expected that this layer will be ported to specific software stack of the platform. + +## Scenarios + +The mapping of the rules in the specification to the test cases and the steps followed in the tests are mentioned in the [Scenario document](docs/) present in the docs/ folder. + + +## Getting Started + +Follow the instructions in the subsequent sections to get a copy of the source code on your local machine and build the tests.
+ +### Prerequisite + +Before starting the test suite build, ensure that the following requirements are met:
+ +- Host Operating System : Ubuntu 16.04 +- Scripting tools : Perl 5.12.3 +- Other open-source tools : GNU Arm Embedded Toolchain 6.3.1 + +### Download source +To download the master branch of the repository, type the following command:
+ + git clone https://github.com/ARM-software/psa-arch-tests.git + +### Porting steps + +Refer to the [Porting Guide](docs/Arm_PSA_FF_Arch_Test_Porting_Guide.md) document for porting steps. +Refer to the [Validation Methodology](docs/Arm_PSA_FF_Arch_Test_Validation_Methodology.pdf) document in the docs folder for additional details. + +### Build steps + +To build PSA FF test suite for a given platform, execute the following commands: + +1. cd psa-ff +2. ./tools/scripts/setup.sh --target --cpu_arch --suite --build
+ + Examples:
+ To compile IPC tests: ./tools/scripts/setup.sh --target fvp_mps2_cm4_mbedos --cpu_arch armv7m --suite ipc --include --build BUILD_IPC
+ To compile Crypto tests: ./tools/scripts/setup.sh --target fvp_mps2_cm4_mbedos --cpu_arch armv7m --suite crypto --build BUILD_CRYPTO + +
where: + +- is the same as the name of the target specific directory created in the platform/targets/ directory.
+- is the Arm Architecture version name for which compliance binaries should be compiled the binaries. For example, Armv7M, Armv8M-Baseline and Armv8M-Mainline Architecture.
+- is the suite name and it is same as the suite name available in test_suites/ directory.
+- is an additional directory to be included into compiler search path. For IPC tests, it must point to path where psa_client.h and psa_service.h files are located.
+- is an output directory to keep build files. + +Refer ./tools/scripts/setup.sh --help to know more about options. + +### Build output +PSA FF test suite build generates following output binaries: + + - /BUILD/val/val_nspe.a + - /BUILD/platform/pal_nspe.a + - /BUILD//test_elf_combine.bin + +### Binaries integration into your platform + +1. Compile compliance tests partition code using your build tool. You must integrate compliance partition code with your software stack containing SPM so that partition code get access to PSA defined client and RoT service APIs. This forms a SPE binary. Refer to the [Porting Guide](docs/Arm_PSA_FF_Arch_Test_Porting_Guide.md) for more detail on compliance partition code compilation. +2. Integrate BUILD/val/val_nspe.a and BUILD/platform/pal_nspe.a libraries with your Non-Secure OS so that these libraries get access to PSA client APIs. For Crypto tests, these libraries would require to get access to PSA Crypto APIs as well. This will form a NSPE binary. +3. Load NSPE binary and test_elf_combine.bin to NS memory +4. Load SPE binary into S memory + +## Test Suite Execution +The following steps describe the execution flow prior to the start of test execution:
+ +1. The target platform must load above binaries into appropriate memory.
+2. The System Under Test (SUT) would boot to an environment which intializes SPM and PSA FF partitions are ready to accept requests.
+3. On the non-secure side, the SUT - boot software would give control to the compliance tests entry point- *void val_entry(void);* as an application entry point.
+4. The tests are executed sequentially in a loop in the test_dispatcher function.
+ +## License + +Arm PSA FF Architecture test suite is distributed under Apache v2.0 License. + + +## Feedback, contributions, and support + + - For feedback, use the GitHub Issue Tracker that is associated with this repository. + - For support, send an email to support-psa-arch-tests@arm.com with details. + - Arm licensees can contact Arm directly through their partner managers. + - Arm welcomes code contributions through GitHub pull requests. + +-------------- + +*Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.* diff --git a/psa-ff/docs/Arm_PSA_FF_Arch_Test_Porting_Guide.md b/psa-ff/docs/Arm_PSA_FF_Arch_Test_Porting_Guide.md new file mode 100644 index 00000000..e6c2d048 --- /dev/null +++ b/psa-ff/docs/Arm_PSA_FF_Arch_Test_Porting_Guide.md @@ -0,0 +1,100 @@ + +# Porting Guide - PSA FF Architecture Test Suite +----------------------------------------------------- + +## Introduction +The PSA FF Architecture test suite contains a platform abstraction layer (PAL) which abstracts platform specific information from the tests. + - The PAL layer interface functions need to be implemented/ported to the target platform. + - The target config file must be created/updated to match the details of the target platform. + +This document provides details on the porting steps and the PAL APIs. + +## Porting steps + +### Target configuration + + You must populate your system configuration and provide it as an input to test suite. + +This is captured in a single static input configuration file that is named as target.cfg. This file is available at psa-ff/platform/targets//.
+ +An example of the input configuration file is as shown. + + // UART device info + uart.num=1; + uart.0.base = 0x40004000; + uart.0.size = 0xFFF; + uart.0.intr_id = 0xFF; + uart.0.permission = TYPE_READ_WRITE; + + // Watchdog device info + watchdog.num = 1; + watchdog.0.base = 0x40008000; + watchdog.0.size = 0xFFF; + watchdog.0.intr_id = 0xFF; + watchdog.0.permission = TYPE_READ_WRITE; + + More details on the structure of the input can be obtained from val/nspe/val_target.h. + +**Note**: + Test suite needs access to the following peripherals and the services of these peripherals are implemented as RoT services in the driver partition. + - One UART to print nspe and spe messages + - One Watchdog timer to help recovery from any fatal error conditions + - Non-volatile memory support to preserve test status over watchdog timer reset + +### Create a new target + + Since PSA FF test suite is agnostic to various system targets, before building the tests, you must port the files mentioned in the following steps. + +**Procedure** +---------------- + + - Create a new directory in platform/targets/. For reference, see the existing platform fvp_mps2_cm4_mbedos directory. + - Copy platform/targets/fvp_mps2_cm4_mbedos/target.cfg file into your folder and update it with your platform detail. Refer val/nspe/val_target.h for structure details. + - Update mmio_regions information available in platform/manifests/common/driver_partition_psa.json manifest file with your platform information. mmio_regions detail must match with device detail provided in the target.cfg file. + - The test suite specified "sid" and partition "id" are provided in manifest files available in platform/manifests/ directory. You can update them if they are clashing with your system defined sid and partition-id values. You may need to update platform/nspe/pal_sid.h file for any change in test suite provided SID values. + - Refer "PAL API list" section to view list of PAL API that must be ported for your target platform. These APIs definitions are available in nspe/pal_\*\_intf.c and spe/pal_\*_intf.c files. These APIs are written for fvp_mps2_cm4_mbedos platform. You can reuse the code if it works for your platform. Otherwise you must port them for your platform specific peripherals. + - Update platform/nspe/Makefile appropriately for platform/nspe/ code compilation. This make file is invoked as part of test suite build tool(./setup.sh) step and it creates /BUILD/platform/pal_nspe.a archive. + - The code available in platform/spe/ is part of driver partition and it must be compiled by your partition build tool by processing the platform/manifests/common/driver_partition_psa.json manifest file. See "Compiling compliance partition sources" section for more detail. + +## PAL API list + These functions will require implementation for the target platform.
+ +- Following are the list of PAL APIs used in NSPE:
+ +| No | Prototype | Description | Parameters | +|----|-----------------------------------------------------------|------------------------------------------------------|-------------------------------------| +| 01 | int pal_spi_read(addr_t addr, uint8_t *data, uint32_t len);| This function will read peripherals using SPI commands| addr : address of the peripheral
data : read buffer
len : length of the read buffer in bytes
| +| 02 | void *pal_target_get_cfg_start(void); | provides the database source location. | void
| +| 03 | uint32_t pal_crypto_function(int type, va_list valist); | This API will call the requested crypto function | type : function code
valist : variable argument list
| + +- Following are the list of PAL APIs used in SPE:
+ +| No | Prototype | Description | Parameters | +|----|-----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|----------------------------------------------------------| +| 01 | void pal_uart_init(addr_t uart_base_addr); | This function initializes the uart | uart_base_addr : Base address of the UART
| +| 02 | void pal_print(char *str, uint32_t data); | This function parses the input string and writes byte by byte to print | str : Input String
data : Value for Format specifier
| +| 03 | int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us);| Initializes an hardware watchdog timer | base_addr : Base address of the watchdog module
time_us : Time in micro seconds
timer_tick_us : Number of ticks per micro second
| +| 04 | int pal_wd_timer_enable(addr_t base_addr); | Enables a hardware watchdog timer | base_addr : Base address of the watchdog module
| +| 05 | int pal_wd_timer_disable(addr_t base_addr); | Disables a hardware watchdog timer | base_addr : Base address of the watchdog module
| +| 06 | int pal_wd_timer_is_enabled(addr_t base_addr); | Checks whether hardware watchdog timer is enabled | base_addr : Base address of the watchdog module
| +| 07 | int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); | Writes 'size' bytes from buffer into non-volatile memory at a given 'base + offset'| base : Base address of NV MEM
offset : Offset
buffer : Pointer to source address
size : Number of bytes
| +| 08 | int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); | Reads 'size' bytes from non-volatile memory at a given | base : Base address of NV MEM
offset : Offset
buffer : Pointer to source address
size : Number of bytes
| + +## Compiling compliance partition sources + +- Your build tool must conform to manifest rules specified in PSA FF specification. Refer PSA FF specification for more details. +- The compliance required partition manifest files are available in platform/manifests/common and platform/manifests/ipc directory. Use these paths to search and process compliance provided partition manifest files using your build tool. +- Compliance partition source code requires your build tool to gererate following header files when your process the manifest files and those must be available when compiling partition source code:
+ - A private header file with \ naming convention per manifest containing macros that maps names to signals
+ - A global header file with \ as name that contains SIDs macros of all manifests
+- Your build tool must provide the implementation of PSA defined psa_client.h and psa_service.h header files to partition sources. +- Compliance partition source code includes header files with path relative to psa-ff directory. Therefore, when you compile the sources, your build tool must supply the full path of psa-ff directory to the compiler header file seach path. +- You must integrate compliance partition code with your software stack containing SPM so that partition code get access to PSA defined client and RoT service APIs. + + +## License +Arm PSA FF Architecture test suite is distributed under Apache v2.0 License. + +-------------- + +*Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.* diff --git a/psa-ff/docs/Arm_PSA_FF_Arch_Test_Validation_Methodology.pdf b/psa-ff/docs/Arm_PSA_FF_Arch_Test_Validation_Methodology.pdf new file mode 100644 index 0000000000000000000000000000000000000000..127f090586a25a8dc81f718367e202c397a7e345 GIT binary patch literal 506854 zcmagEV|1m<_Qo6A?r_DnZQHifvDHz>ww-ir+qP|X?4;wHefBx`oIA!I_rE@@SM|(@zRS*%QWu#+=C*9kho`;8FW@G>`0PKvc;CXoHWh`w?4IM2#O-%rd^fCZe z1~v{>7J7LAGb1y-D1eiZg@J=!?(2$P<*#RqoQ(7m0Bry(GdqBtnN63E58l+)n$tcJnA|lAh z$j-?k#LOTf!p9@ZEF#3fDkvzzA;!eW!XhZd%)%@z${@_iBFM}p#=yv@OD}0_V(PB_ zS0q?j|GF}>128ji{>!b5sja!Q1%QM7ul~9IJ!&R;B|E#nddBpxCdyy&0x+|&(yMsb zo6>72nVQk7Ycm3v0gPV}adLJvHMD_;vDh*)GBH3hFfcMO@PH=zRvj?v$xsSGls4_Z zIthq>pbj+wssFYoa9~w~EryigkjEGM)~%4`9~S3-gQWovN^BUvk6B9vN&B4RfAa$B z2LuWvgN_gZ5AzlAfBX1%?46tq9i81BP0iq87~o+@Nk!$w;9>qZAOCp%l_WNn|0YQk zz`?=vk1+>uaB|T9J1T4}j9;-V5_j3hkB>KDY0wfaZk3&l;a45H zu0o@V?*T{tu~vE%rtgXlr_$4{8OiNH^OyG$BZcyG&Xyb9Mfk95@t4ZK(5AWTrMR!V z2CMBv<#&3l;xAM`mgPV*fzaL*qzkCAF6a%F+eWH08gc4Eg{-BJT|QaAB^cq4Z3FKS zlQ;DGiFm+s2!>*|z?pY#(Jy_|VoudV@P{Tp1!~8ij*O8V1-Fh;+4q|F^9V17&#J?c z+^&{|YFskOF)S);jJ{{En2Sk{?n>BmLO)bYaJ%lgAE}PEbqCXSyuek1B5E(x3)^}( z!C;_NAm7pZPC-YBB?_afVK8te&n3>NXZM%w`P7DCf~}JSN}Pw-GI&v&QNg0cxwf&4 ze!-up##lRQnr�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 z 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
    N}C1MdeU=VPPR)#4C@yf=UER<3TvwaHBNg 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�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

    9Fwwl(Y{$ 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 g8~<|r9G&zXoZK9YP2gdenc3lC$jL?J#Nc864{ 2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get basic metadata about a key
    7. Export a key in binary format
    8. Check if original key data matches with the exported data | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. 2048 RSA public key
    5. 2048 RSA keypair
    6. DES 64 bit key
    7. Triple DES 2-Key
    8. Triple DES 3-Key
    9. EC Public key
    10. EC keypair | +| | | | | PSA_ERROR_NOT_SUPPORTED | Calling this function with incorrect key type | Incorrect key type | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Key data greater than the algorithm size
    2. Incorrect key data size
    3. Invalid key slot
    4. Zero key slot | +| | | | | PSA_ERROR_OCCUPIED_SLOT | Pass the key slot to store data which is already occupied | Already occupied key slot | +| | test_c003 | psa_export_key | Export a key in binary format | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get basic metadata about a key
    7. Export a key in binary format
    8. Check if original key data matches with the exported data | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. 2048 RSA public key
    5. 2048 RSA keypair
    6. DES 64 bit key
    7. Triple DES 2-Key
    8. Triple DES 3-Key
    9. EC Public key
    10. EC keypair | +| | | | | PSA_ERROR_BUFFER_TOO_SMALL | Calling this function with buffer size less than required | Less buffer size | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Zero key slot
    2. Invalid key slot | +| | | | | PSA_ERROR_BAD_STATE | Calling this function with key policy that cannot be exported | Invalid key policy usage | +| | | | | PSA_ERROR_EMPTY_SLOT | Calling this function with empty key slot | Empty key slot | +| | test_c004 | psa_export_public_key | Export a public key or the public part of a key pair in binary format. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get basic metadata about a key
    7. Export a key in binary format
    8. Check if original key data matches with the exported data | 1. 2048 RSA public key
    2. 2048 RSA keypair
    3. EC Public key
    4. EC keypair | +| | | | | PSA_ERROR_INVALID_ARGUMENT | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get basic metadata about a key
    7. Export a key in binary format
    8. Check if original key data matches with the exported data | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. DES 64 bit key
    5. Triple DES 2-Key
    6. Triple DES 3-Key | +| | | | | PSA_ERROR_BUFFER_TOO_SMALL | Calling this function with buffer size less than required | Less buffer size | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Zero key slot
    2. Invalid key slot | +| | | | | PSA_ERROR_BAD_STATE | Calling this function with key policy that cannot be exported | Invalid key policy usage | +| | test_c005 | psa_destroy_key | Destroy a key and restore the slot to its default state. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get basic metadata about a key
    7. Destroy a key and restore the slot to its default state
    8. Check that if the key metadata are destroyed | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. 2048 RSA public key
    5. 2048 RSA keypair
    6. DES 64 bit key
    7. Triple DES 2-Key
    8. Triple DES 3-Key
    9. EC Public key
    10. EC keypair | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Invalid key slot
    2. Zero key slot
    3. Empty key slot | +| | test_c006 | psa_get_key_information | Get basic metadata about a key. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get basic metadata about a key | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. 2048 RSA public key
    5. 2048 RSA keypair
    6. DES 64 bit key
    7. Triple DES 2-Key
    8. Triple DES 3-Key
    9. EC Public key
    10. EC keypair | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Zero key slot
    2. Invalid key slot | +| | | | | PSA_ERROR_EMPTY_SLOT | Pass the key slot number which has the key type as none | Empty key slot | +| | NO TEST | psa_key_policy_set_usage | Set the standard fields of a policy structure. | void | Void function. Covered as part of other cases | | +| | | | | | | | +| **Key Policies** | test_c007 | psa_set_key_policy | Set the usage policy on a key slot. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Import the key data into the key slot
    6. Get the usage policy for a key slot
    7. Check if the policy matches the original input | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. 2048 RSA public key
    5. 2048 RSA keypair
    6. DES 64 bit key
    7. Triple DES 2-Key
    8. Triple DES 3-Key
    9. EC Public key
    10. EC keypair | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Invalid key policy
    2. Zero key slot
    3. Invalid key slot | +| | | | | PSA_ERROR_OCCUPIED_SLOT | Pass the key slot to store data which is already occupied | Already occupied key slot | +| | test_c008 | psa_get_key_policy | Get the usage policy for a key slot | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Change the lifetime of a key slot
    6. Import the key data into the key slot
    7. Get the usage policy for a key slot
    8. Retrieve the usage field of a policy structure
    9. Retrieve the algorithm field of a policy structure
    10. Make sure they match the original value | 1. 16 Byte AES
    2. 24 Byte AES
    3. 32 Byte AES
    4. 2048 RSA public key
    5. 2048 RSA keypair
    6. DES 64 bit key
    7. Triple DES 2-Key
    8. Triple DES 3-Key
    9. EC Public key
    10. EC keypair | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Zero key slot
    2. Invalid key slot | +| | test_c009 | psa_set_key_lifetime | Change the lifetime of a key slot. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Change the lifetime of a key slot
    6. Import the key data into the key slot
    7. Reset the system
    8. After reboot check if the contents of persistent and write once key data are preserved | 1. Volatile keys
    2. Persistent keys
    3. Write once keys | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with incorrect argument should return failure | 1. Zero key slot
    2. Invalid key slot
    3. Invalid key lifetime | +| | | | | PSA_ERROR_OCCUPIED_SLOT | If implementation does not supports changing the lifetime of preoccupied slot, calling this function with already occupied slot should return failure(Impdef) | Pre occupied key slot | +| | test_c010 | psa_get_key_lifetime | Retrieve the lifetime of a key slot. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Initialize a key policy structure to a default that forbids all usage of the key
    3. Set the standard fields of a policy structure
    4. Set the usage policy on a key slot
    5. Change the lifetime of a key slot
    6. Import the key data into the key slot
    7. Get the lifetime of a key slot | Testing only volatile keys as other key types are currently not supported | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. Invalid key policy
    2. Zero key slot
    3. Invalid key slot
    4. Empty key slot | +| | | | | | | | +| **Message Authentication Codes** | test_c011 | psa_hash_start | Start a multipart hash operation. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Start a multipart hash operation | 1. MD2
    2. MD4
    3. MD5
    4. RIPEMD160
    5. SHA1
    6. SHA224
    7. SHA256
    8. SHA512
    9. SHA512_224
    10. SHA512_256
    11. SHA3_224 1
    2. SHA3_256 1
    3. SHA3_384 1
    4. SHA3_512 | +| | | | | PSA_ERROR_NOT_SUPPORTED | Calling this function with unsupported algorithm should return error | Invalid hash algorithm | +| | test_c012 | psa_hash_update | Add a message fragment to a multipart hash operation. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Start a multipart hash operation
    3. Add a message fragment to a multipart hash operation | 1. MD2
    2. MD4
    3. MD5
    4. RIPEMD160
    5. SHA1
    6. SHA224
    7. SHA256
    8. SHA384
    9. SHA512 | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function without calling the psa_hash_start() should return error | Inactive operation handle | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with completed operation handle should return error | Completed operation handle | +| | test_c013 | psa_hash_verify | Finish the calculation of the hash of a message and compare it with an expected value. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Start a multipart hash operation
    3. Add a message fragment to a multipart hash operation
    4. Finish the calculation of the hash of a message and compare it with an expected value | 1. MD2
    2. MD4
    3. MD5
    4. RIPEMD160
    5. SHA1
    6. SHA224
    7. SHA256
    8. SHA384
    9. SHA512 | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with inactive operation handle should return error | Inactive operation handle | +| | | | | PSA_ERROR_INVALID_SIGNATURE | Calling this function with incorrect expected value should return error | 1. Incorrect expected hash value
    2. Incorrect expected hash length | +| | test_c014 | psa_hash_finish | Finish the calculation of the hash of a message. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Start a multipart hash operation
    3. Add a message fragment to a multipart hash operation
    4. Finish the calculation of the hash of a message
    5. Compare it with the expected value | 1. MD2
    2. MD4
    3. MD5
    4. RIPEMD160
    5. SHA1
    6. SHA224
    7. SHA256
    8. SHA384
    9. SHA512 | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with an inactive operation handle should return error | Inactive operation handle | +| | | | | PSA_ERROR_BUFFER_TOO_SMALL | Calling this function with a hash buffer whose size is less than the algorithm output should return error | Buffer size less than required | +| | test_c015 | psa_hash_abort | Abort a hash operation. | PSA_SUCCESS | 1. Initialize the PSA crypto library
    2. Start a multipart hash operation
    3. Abort a hash operation | 1. MD2
    2. MD4
    3. MD5
    4. RIPEMD160
    5. SHA1
    6. SHA224
    7. SHA256
    8. SHA384
    9. SHA512 | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling psa_hash_finish after calling psa_hash_abort should return error | | diff --git a/psa-ff/docs/psa_ipc_testlist.md b/psa-ff/docs/psa_ipc_testlist.md new file mode 100644 index 00000000..8d46d451 --- /dev/null +++ b/psa-ff/docs/psa_ipc_testlist.md @@ -0,0 +1,100 @@ +# PSA FF IPC Testcase checklist + +| Tests | Scenario rules | Client-Server Test Functions Pair | Test_algorithm/Comments/Test_limitation | PSA API crosses | Covered or to be covered in release | +|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|--------------------------------------| +| test_i001 | 1. psa_framework_version(): Retrieve the version of the PSA Framework API that is implemented. | [client/server]_test_psa_framework_version() | Call the psa_framework_version() API at both side SPE and NSPE and check the return value. | psa_framework_version | Alpha | +| | 1. psa_version() returns PSA_VERSION_NONE when the RoT Service is not implemented, or the caller is not permitted to access the service OR return > 0 with the minor version of the implemented RoT Service. | [client/server]_test_psa_version() | 1. Pass un-implemented SID and expects PSA_VERSION_NONE as return value.

    2. Call to API from NSPE with a SID who doesn't provide service to NSPE and expects PSA_VERSION_NONE as return value

    3. Pass SID who is implemented and provides service to NSPE and expect minor number(>0) of given RoT service as return Perform all above checks from SPE too. | psa_version | Alpha | +| test_i002 | 1. psa_connect() returns the PSA_CONNECTION_BUSY when RoT Service cannot make the connection at the moment (transient error).

    2. psa_connect() returns the PSA_CONNECTION_REFUSED when RoT Service has refused the connection

    3.psa_wait_any() returns the set of signals that have been asserted for a Secure Partition. Returns > 0 when at least one signal is asserted

    4.psa_get():Get the message which corresponds to a given RoT Service signal and remove the message from the queue.

    5.psa_get() returns PSA_SUCCESS when \*msg will contain the delivered message.

    6.The type member of the psa_msg_t object should return PSA_IPC_CONNECT for a new connection request following a psa_get() call to psa_connect() and msg.handle must be positive.

    7.psa_reply(): If the message type is PSA_IPC_CONNECT then server can reject the connection by sending PSA_CONNECTION_REFUSED (permanent error) or PSA_CONNECTION_BUSY (transient error) status code. | [client/server]_test_connection_busy_and_reject() | 1. Client tries to connect to ROT service using psa_connect()

    2. RoT service checks the delivery of PSA_IPC_CONNECT message type and positive handle value returned by psa_get()

    3.RoT service rejects the connection by executing psa_reply(handle,PSA_CONNECTION_BUSY)

    4.Client tries to connect to same ROT service using psa_connect() again

    5.This time, RoT service rejects the connection by executing psa_reply(handle,PSA_CONNECTION_REFUSED)

    Perform all above steps from NSPE and SPE both. | psa_connect, psa_wait_any, psa_get, psa_reply | Alpha | +| | 1. psa_connect(): When SID is implemented and client is permitted to access the service, SPM delivers a PSA_IPC_CONNECT message from the client to the Secure Partition that implements the RoT Service and positive handle is returned to client.

    2. psa_get():Get the message which corresponds to a given RoT Service signal and remove the message from the queue.

    3.psa_get() returns PSA_SUCCESS when \*msg will contain the delivered message

    4.psa_get():The type member of the psa_msg_t object should return PSA_IPC_CONNECT for a new connection request following a psa_get() call to psa_connect() and msg.handle must be positive.

    5.psa_get(): The type member of the psa_msg_t object should return PSA_IPC_DISCONNECT for a new connection request following a psa_get() call to psa_close() and msg.handle must be positive.

    6.psa_reply(): If the message type is PSA_IPC_CONNECT then server can accept the connection by sending PSA_SUCCESS status code. If the message type is PSA_IPC_DISCONNECT then the status code is ignored.

    7.psa_close(): Closes a connection to a RoT Service. Sends the PSA_IPC_DISCONNECT message to the RoT Service. This function will have no effect if called with the null handle. | [client/server]_test_accept_and_close_connect() | 1.Client tries to connect to ROT service

    2. RoT service checks the delivery of PSA_IPC_CONNECT message type and positive handle value return by psa_get()

    3.RoT service accepts the connection by executing psa_reply(.., PSA_CONNECTION_ACCEPTED).

    4.Client calls the psa_close with PSA_NULL_HANDLE and later it closes the connection using psa_close() with actual handle value

    5.RoT service checks the delivery of PSA_IPC_DISCONNECT message type, checks handle value eqaul to NULL returned by psa_get() and completes the PSA_IPC_DISCONNECT by executing psa_reply() .

    Perform all above steps from NSPE and SPE both. | psa_connect, psa_wait_any, psa_get, psa_reply, psa_close | Alpha | +| | Following are allowed minor version condition to psa_connect(): 1. Version policy is not mentioned and requested version is 1 which is default minimum version

    2. Version policy is STRICT and requested version equals minimum version

    3.Version policy is relaxed and requested version is smaller than the minimum version

    4.Version policy is relaxed and requested version is equal to the minimum version | [client/server]_test_connect_with_allowed_minor_version_policy() | Client tries connecting ROT service of following properties and expects connection to astablish:

    - Version policy is not mentioned and requested version is 1 which is default minimum version

    - Version policy is STRICT and requested version equals minimum version

    - Version policy is relaxed and requested version is smaller than the minimum version

    - Version policy is relaxed and requested version is equal to the minimum version

    Perform all above steps from NSPE and SPE both. | psa_connect | Alpha | +| | 1. psa_call(): The valid psa_call, make SPM to send PSA_IPC_CALL msg.type to the RoT servicee partition and the successful call should return >=0 handle value.

    2. psa_call() receives return value >= 0 for RoT-specific return code

    3.psa_call() receive return value < 0 for RoT-specific error code

    4.psa_reply(): If the message type is PSA_IPC_CALL and ROT service want to end call with success then the return code must be PSA_SUCCESS or and positive integers are used to indicate RoT Service specific result values(this must be reported to client). All other return codes (INT32_MIN+128 to -1) are reported to the client. | [client/server]_test_psa_call_with_allowed_status_code() | 1. Client connects to RoT service

    2. Client sends message using psa_call()

    3.RoT service checks the delivery of PSA_IPC_CALL message type and positive handle value return by psa_get()

    4.RoT service ends the call using psa_reply(status_code)

    5.Client checks the return value of psa_call() and Client closes the connection Repeat (1) to (5) for different status code, SUCCESS, +ve, -ve..

    Perform all above steps from NSPE and SPE both. | psa_call, psa_reply | Alpha | +| | 1. A msg.client_id that has a positive value indicates that the client is in the SPE and this the Secure Partition ID of the client. A negative client_id indicates that the client is in the NSPE.

    2. Client_id is valid during PSA_IPC_CONNECT, PSA_IPC_CALL and PSA_IPC_DISCONNECT msg type.

    3.Manifest Parameter- name (required, unique) A Partition must have an alphanumeric name for source code to directly refer to a specific Partition. The format of the name must follow the rules of a C macro.

    4.Manifest Parameter - id (required, unique) It must be represented by a hexadecimal string. The Secure Partition ID can be referenced in Secure Partition source code via the symbolic name specified as name attribute. The symbol must have the value of the Secure Partition ID. | [client/server]_test_identity() | - Check the value returned by msg.client_id during PSA_IPC_CONNECT, PSA_IPC_CALL and PSA_IPC_DISCONNECT. For NSPE connection, client_id should be <0 and for SPE, it should be >0.

    Perform all above steps from NSPE and SPE both. - Access the Partition ID of client partition and compare the value with expected ID value. | psa_get | Alpha | +| | 1. psa_connect() will return the PSA_CONNECTION_REFUSED OR PSA_CONNECTION_BUSY when the SPM has reached the limit of concurrent connections | [client/server]_test_spm_concurrent_connect_limit() | Execute psa_connect() API in a loop until it returns PSA_CONNECTION_REFUSED or PSA_CONNECTION_BUSY

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| | 1. psa_wait_any(): When MODE(PSA_BLOCK) is one, the function will block the caller until one of the requested signals is asserted.

    2.psa_wait_any(): RES timeout[30:0] is reserved for future use. Callers must set RES to zero, implementations must ignore the value of RES.These requirements ensure compatibility for the intended future use of this field. | [client/server]_test_psa_wait_any_with_psa_block() | 1. Client connects to a RoT service mutilpe times.

    2. RoT service psa_wait_any(PSA_BLOCK|(Non-zero value for timeout[30:0])) API is executed in a while loop.

    3.Count the number of times while loop iterates over and number of times psa_wait_any API returns(signal value must be zero).

    4.At the end, match both counts to check API's BLOCK behvaiour.

    Perform above steps from NSPE and SPE both. | psa_wait_any | Alpha | +| | 1. psa_wait_any(): When MODE is zero, the function will return immediately with the current signal state, which can be zero if no signals are active. | [client/server]_test_psa_wait_any_with_psa_poll() | 1. Client connects to a RoT service mutilpe times.

    2. RoT service executes psa_wait_any(PSA_POLL) in a while loop and checks the API's polling behaviour.

    3.Call psa_wait_any(PSA_POLL) when no request is made by client and checks the return value

    Perform above steps from NSPE and SPE both. | psa_wait_any | Alpha | +| test_i003 | 1. The reverse handle for a connection is NULL until psa_set_rhandle() is used. psa_set_rhandle() can be used to associate some caller-provided private data with a specified client connection. And SPM must provide same rhandle for msg.rhandle with all subsequent messages delivered on this connection. On success the rhandle is retained by the implementation and provided in all future messages on that connection as part of the psa_msg_t structure.

    2. Setting the rhandle for a connection during disconnection has no observable effect. | [client/server]_test_psa_set_rhandle() | 1. Client connects to RoT service and RoT service checks the value of msg.rhanlde value duing PSA_IPC_CONNECT

    2. Client send call msg to RoT service and RoT service checks the value of msg.rhanlde value duing PSA_IPC_CONNECT

    3.ROT service sets the rhandle with known value.

    4.Client send call msg to RoT service and RoT service now compare the value of msg.rhanndle with previously set value

    5.ROT service sets the rhandle with known value other than previously set value

    6.Client send call msg to RoT service and RoT service now compare the value of msg.rhanndle with last set value

    7.The reverse handle for a connection is NULL until psa_set_rhandle() is used

    Perform above steps from NSPE and SPE both. | psa_set_rhandle, psa_get | Alpha | +| | 1. psa_call():The caller can optionally provide one or more buffers to receive a response (out_vec).

    2. psa_get(): The array in_size provides the size of each client input vector in bytes. The array out_size provides the size of each client output vector in bytes.

    3.psa_read(): parameter from the client input vector. Streams up to the next num_bytes bytes of client input vector invec_idx in the message identified by msg_handle to the Secure Partition buffer. Returns the number of bytes copied.

    4.psa_read(): If num_bytes is less than or equal to the available data in the input vector then num_bytes are copied to buffer, and the remaining data in the input vector can be read by subsequent calls to psa_read() with the same msg_handle and invec_idx.

    5.psa_read(): RoT Services can determine how much data is available to read from the message based on the in_size[] attribute of the psa_msg_t message returned from psa_get(). If an input vector has not been passed by the client then the corresponding in_size[] for that vector is zero.

    6.psa_read(): If num_bytes is greater than the remaining data in the input vector then the remaining input bytes are copied to buffer and the call returns the number of bytes copied. Any space after this in buffer is not modified. Subsequent calls of psa_read() or psa_skip() with the same message input vector will report that there is no more data in the vector.

    7.psa_skip(): Skip over part of a client input vector. Advances the current read offset by skipping up to num_bytes bytes for input vector invec_idx in the message identified by msg_handle. psa_skip(): When psa_skip returns, it returns with the number of bytes skipped

    8.psa_skip(): If There was no remaining data in this input vector, return zero 9.psa_skip(): If num_bytes is greater than the remaining size of the input vector then the remaining size of the input vector is returned. Subsequent calls of psa_read() or psa_skip() with the same message input vector will report that there is no more data in the vector. | [client/server]_test_call_read_and_skip() | 1.Client connects to RoT service

    2. Client sends four input vectors to RoT service using psa_call

    3.RoT service checks following
    - Reporting of input vectors size through msg.in_size
    - Input vectors content reading through psa_read
    -Inbound /Outbound offset reading
    -Inbound/Outbound offset skipping
    -Zero byte read and skip
    -out_len=0 check

    4.Client recieves the status of RoT service checks and closes the connection

    Perform above steps from NSPE and SPE both. | psa_call, psa_read, psa_skip, psa_get, | Alpha | +| | 1. psa_call(): The caller can optionally provide one or more buffers to receive a response (out_vec). On return from psa_call() the len value will have been updated to indicate the number of bytes of data written to the buffer by the RoT Service.

    2. psa_write(): Appends num_bytes of data from buffer to the client output vector outvec_idx in the message identified by msg_handle. Sequential calls using the same msg_handle and outvec_idx will be concatenated in the output vector | [client/server]_test_call_and_write() | 1. Client connects to RoT service

    2. Client sends four output vectors to RoT service using psa_call

    3.RoT service checks following
    - Reporting of output vectors size through msg.out_size
    - in_len=0 check
    - Zero byte write
    - Out vector writes using psa_write
    - Vector write concatenation

    4.Client recieves the status of RoT service checks, cross check the content of out vectors and closes the connection

    Perform above steps from NSPE and SPE both. | psa_call, psa_get, psa_write | Alpha | +| | 1. psa_call(): Any I/O vector of length zero is permitted and will be treated as an empty or non-existent vector by the framework. When less than four vectors are provided to psa_call() for either input or output, then the remaining vectors have zero length and the in_size and out_size elements for these vectors will be zero.

    2.psa_call(): If in_len is zero then in_vec is ignored

    3. psa_call(): If out_len is zero then out_vec is ignored.

    4.psa_get(): If an input and output vector has not been passed by the client then the corresponding in_size[] and out_size[] for that vector is zero. | [client/server]_test_zero_length_invec() [client/server]_test_zero_length_outvec() | Test zero lenth input vector 1. Client connects to RoT service

    2.Client sends three input and one outvec vectors to RoT service using psa_call - invec 0 as zero length vector, invec 1 as NULL and invec 2 and outvec 0 as non-zero length vector

    3.RoT service checks size of each vectors reported through msg.in_size and msg.out_size

    4.Client recieves the status of checks and closes the connection Test zero lenth output vector 1. Client connects to RoT service

    2.Client sends one input and three outvec vectorsto RoT service using psa_call - outvec 0 as zero length vector, outvec 0 as NULL and invec 0 and outvec 2 as non-zero length vector

    3.RoT service checks size of each vectors reported through msg.in_size and msg.out_size

    4.Client recieves the status of checks and closes the connection

    Perform above steps from NSPE and SPE both. | psa_call, psa_get | Alpha | +| | 1. When client provides an input and output vectors which are referencing to same memory location, a psa_read after psa_write to the same memory location can return original or modified value.

    2.When client provides an input and output vectors which are referencing to same memory location, a psa_write(s) to both memory vectors can return either the 1st or the 2nd value written. | [client/server]_test_overlapping_vectors | 1. Client provides one input and 2 output vectors which are pointing to same location.

    2.RoT service performs read after write operation on to outvec-0 and expects return value either modified one or the original

    3.RoT service performs write operation to outvec-1 and unblock the connection. The write performed is to mimic write after write operatio to the overlapping vector.

    4.Client check the value of outvec. The value should be either the value written by first psa_write or the second psa_write. | psa_call, psa_write, psa_read | Alpha | +| test_i004 | psa_connect() does not return if RoT Service does not exist on platform | [client/server]_test_sid_does_not_exists() | Call psa_connect with SID which does not exist on a platform

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i005 | psa_connect() does not return if Version policy is STRICT and requested version is HIGHER than minimum version | [client/server]_test_strict_policy_higher_minor_version() | call psa_connect with SID whose Version policy is STRICT and requested minor version is HIGHER than minimum version.

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i006 | psa_connect() does not return if Version policy is STRICT and requested version lower than minimum version | [client/server]_test_strict_policy_lower_minor_version() | call psa_connect with SID whose Version policy is STRICT and requested minor version is Lower than minimum version.

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i007 | psa_connect() does not return if Version policy is RELAXED and requested version is bigger than minimum version | [client/server]_test_relax_policy_higher_minor_version() | call psa_connect with SID whose Version policy is RELAXED and requested minor version is HIGHER than minimum version.

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i008 | 1. psa_connect() does not return if Service to non_secure_client is not avaiable

    2.Manifest parameter - The non_secure_clients field contains a boolean to indicate if it is accessible to NSPE clients. RoT Services are always accessible to SPE clients. | [client/server]_test_secure_access_only_connection() | Call psa_connect with SID which allow secure only connection.

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i009 | 1. psa_connect() does not return if External SID is not mentioned.

    2.Manifest parameter- extern_sids (optional) If access between a Secure Partition (acting as client) and a RoT Service (acting as server) is not specified in the manifest, then the client is not permitted to connect to the RoT Service. | [client/server]_test_unextern_sid_connection() | Call psa_connect with SID which is not mentioned as external SID in manifest. | psa_connect | Alpha | +| test_i010 | It is not required for the minor_version or minor_policy attributes to be specified. If they are not specified in the manifest, the RoT Service will have default attributes of minor_version=1 and minor_policy= STRICT . psa_connect() does not return if requested version higher than minimum version | [client/server]_test_unspecified_policy_with_higher_minor_ver() | Call psa_connect with SID whose Version policy is not mentioned and requested minor version is HIGHER than minimum version.

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i011 | It is not required for the minor_version or minor_policy attributes to be specified. If they are not specified in the manifest, the RoT Service will have default attributes of minor_version=1 and minor_policy= STRICT . psa_connect() does not return if requested version lower than minimum version | [client/server]_test_unspecified_policy_with_lower_minor_ver() | Call psa_connect with SID whose Version policy is not mentioned and requested minor version is lower than minimum version.

    Perform above step from NSPE and SPE both. | psa_connect | Alpha | +| test_i012 | psa_close() does not return if an invalid handle was provided that is not the null handle | [client/server]_test_psa_close_with_invalid_handle() | Call psa_close with INVALID_HANDLE which is not NULL.

    Perform above step from NSPE and SPE both. | psa_close | Alpha | +| test_i013 | psa_get() does not return if signal has more than a single bit set | [client/server]_test_psa_get_with_more_than_one_signal() | Call psa_get with a signal who has more than a single bit set | psa_get | Alpha | +| test_i014 | After a RoT Service message is signaled, psa_get() function is used to retrieve the message details. Each message can only be retrieved once. | [client/server]_test_psa_get_called_twice() | Call psa_get with a valid signal back to back. | psa_get | Alpha | +| test_i015 | psa_get() does not return if signal does not correspond to a RoT Service | [client/server]_test_psa_get_with_non_rot_signal() | Call psa_get with DOORBELL signal | psa_get | Alpha | +| test_i016 | psa_get() does not return if The RoT Service signal is not currently asserted | [client/server]_test_psa_get_with_unasserted_signal() | Call psa_get with singal which is currently not asserted | psa_get | Alpha | +| test_i017 | Each RoT Service listed creates a dependency from the client Partition to a server Partition. Within the resulting network of dependencies, there must be no circular dependencies between Secure Partitions. This would result in deadlock because the Service requests are always synchronous. For the same reason, a Secure Partition must not use a RoT Service that is defined within itself. Direct function calls must be used instead of IPC where there is a dependency between RoT Services within a single Secure Partition. | [client/server]_test_partition_calling_its_own_rot_service() | Partition calling its own ROT service using psa_connect. | psa_connect | Alpha | +| test_i018 | psa_set_rhandle() does not return if an invalid message handle was provided | [client/server]_test_psa_set_rhandle_with_invalid_handle() | Call psa_set_rhanlde with an INVALID_HANDLE which is not NULL | psa_set_rhandle | Alpha | +| test_i019 | psa_set_rhandle() does not return if Null handle was passed | [client/server]_test_psa_set_rhandle_with_null_handle() | Call psa_set_rhanlde with NULL handle | psa_set_rhandle | Alpha | +| test_i020 | If messgae type if is PSA_IPC_CONNECT then use of status values in psa_reply() other than PSA_SUCCESS or PSA_CONNECTION_REFUSED is a fatal programming error | [client/server]_test_psa_reply_with_invalid_connect_status_code() | Call to psa_reply during PSA_IPC_CONNECT with status code other than PSA_SUCCESS and PSA_CONNECTION_REFUSED | psa_reply | Alpha | +| test_i021 | If message type if PSA_IPC_CALL , the use of other reserved status codes in the range INT32_MIN+1 to INT32_MIN+127 for psa_reply() is a fatal programming error. | [client/server]_test_psa_reply_with_invalid_call_status_code() | Call to psa_reply during PSA_IPC_CALL with status code other than allowed code | psa_reply | Alpha | +| test_i022 | psa_reply() does not return if the message handle is invalid | [client/server]_test_psa_reply_with_invalid_handle() | Call psa_reply with an INVALID_HANDLE which is not NULL | psa_reply | Alpha | +| test_i023 | psa_reply() does not return if the message handle is Null handle | [client/server]_test_psa_reply_with_null_handle() | Call psa_reply with a NULL HANDLE | psa_reply | Alpha | +| test_i024 | psa_call() does not return if an invalid handle was passed | [client/server]_test_psa_call_with_invalid_handle() | Call psa_call with an INVALID_HANDLE which is not NULL.

    Perform this step from NSPE and SPE both. | psa_call | Alpha | +| test_i025 | psa_call() does not return if an null handle was passed | [client/server]_test_psa_call_with_null_handle() | Call psa_call with a NULL HANDLE. Perform this step from NSPE and SPE both. | psa_call | Alpha | +| test_i026 | psa_call() does not return if in_len + out_len > PSA_MAX_IOVEC | [client/server]_test_psa_call_with_iovec_more_than_max_limit() | Call psa_call with more than four IOVECs. Perform this step from NSPE and SPE both. | psa_call | Alpha | +| test_i027 | 1. psa_call() returns PSA_DROP_CONNECTION when the connection has been dropped by the RoT Service. This indicates that either this or a previous message was invalid (The SPM will implement one of the following behaviors in this situation: • this is a client fatal error and psa_call() will not return • psa_call() returning PSA_DROP_CONNECTION. In this case, all subsequent calls to psa_call() on this connection will immediately return PSA_DROP_CONNECTION and the connection must be closed)

    2.psa_reply(): If the message type is PSA_IPC_CALL and the client has made an invalid request, then the RoT Service can request for the connection to be terminated by calling psa_reply() with return value PSA_DROP_CONNECTION. After this, the RoT Service will receive no further PSA_IPC_CALL messages on that connection. The SPM will deliver a PSA_IPC_DISCONNECT to the RoT Service to release any resources associated with that connection. | [client/server]_test_psa_drop_connection() | RoT service executes psa_reply with status code eqaul to PSA_DROP_CONNECTION during PSA_IPC_CALL. Client expects either PSA_DROP_CONNECTION as returned status code or does not return condition for psa_call. Perform above steps from NSPE and SPE both. | psa_call | Alpha | +| test_i028 | psa_read() does not return if msg_handle does not refer to a PSA_IPC_CALL message | [client/server]_test_psa_read_at_ipc_connect() | Call psa_read during PSA_IPC_CONNECT | psa_read | Alpha | +| test_i029 | psa_read() does not return if msg_handle does not refer to a PSA_IPC_CALL message | [client/server]_test_psa_read_at_ipc_disconnect() | Call psa_read during PSA_IPC_DISCONNECT | psa_read | Alpha | +| test_i030 | psa_read() does not return if Null handle was passed | [client/server]_test_psa_read_with_null_handle() | Call psa_read with NULL handle | psa_read | Alpha | +| test_i031 | psa_read() does not return if Invalid handle was passed | [client/server]_test_psa_read_with_invalid_handle() | Calll psa_read with INVALID_HANDLE which is not NULL | psa_read | Alpha | +| test_i032 | psa_read() does not return if invec_idx is equal to PSA_MAX_IOVEC | [client/server]_test_psa_read_with_invec_equal_to_max_iovec() | Call psa_read with invec_idx=PSA_MAX_IOVEC | psa_read | Alpha | +| test_i033 | psa_read() does not return if invec_idx is greater than PSA_MAX_IOVEC | [client/server]_test_psa_read_with_invec_greater_than_max_iovec() | Call psa_read with invec_idx>PSA_MAX_IOVEC | psa_read | Alpha | +| test_i034 | psa_skip() does not return if msg_handle does not refer to a PSA_IPC_CALL message | [client/server]_test_psa_skip_at_ipc_connect() | Call psa_skip during PSA_IPC_CONNECT | psa_skip | Alpha | +| test_i035 | psa_skip() does not return if msg_handle does not refer to a PSA_IPC_CALL message | [client/server]_test_psa_skip_at_ipc_disconnect() | Call psa_skip during PSA_IPC_DISCONNECT | psa_skip | Alpha | +| test_i036 | psa_skip() does not return if Null handle was passed | [client/server]_test_psa_skip_with_null_handle() | Call psa_skipwith NULL handle | psa_skip | Alpha | +| test_i037 | psa_skip() does not return if Invalid handle was passed | [client/server]_test_psa_skip_with_invalid_handle() | Calll psa_skip with INVALID_HANDLE which is not NULL | psa_skip | Alpha | +| test_i038 | psa_skip() does not return if invec_idx is equal to PSA_MAX_IOVEC | [client/server]_test_psa_skip_with_invec_equal_to_max_iovec() | Call psa_skip with invec_idx=PSA_MAX_IOVEC | psa_skip | Alpha | +| test_i039 | psa_skip() does not return if invec_idx is greater than PSA_MAX_IOVEC | [client/server]_test_psa_skip_with_invec_greater_than_max_iovec() | Call psa_skip with invec_idx>PSA_MAX_IOVEC | psa_skip | Alpha | +| test_i040 | psa_write() does not return if msg_handle does not refer to a PSA_IPC_CALL message | [client/server]_test_psa_write_at_ipc_connect() | Call psa_write during PSA_IPC_CONNECT | psa_write | Alpha | +| test_i041 | psa_write() does not return if msg_handle does not refer to a PSA_IPC_CALL message | [client/server]_test_psa_write_at_ipc_disconnect() | Call psa_write during PSA_IPC_DISCONNECT | psa_write | Alpha | +| test_i042 | psa_write() does not return if Null handle was passed | [client/server]_test_psa_write_with_null_handle() | Call psa_write with NULL handle | psa_write | Alpha | +| test_i043 | psa_write() does not return if Invalid handle was passed | [client/server]_test_psa_write_with_invalid_handle() | Calll psa_write with INVALID_HANDLE which is not NULL | psa_write | Alpha | +| test_i044 | psa_write() does not return if invec_idx is equal to PSA_MAX_IOVEC | [client/server]_test_psa_write_with_invec_equal_to_max_iovec() | Call psa_write with invec_idx=PSA_MAX_IOVEC | psa_write | Alpha | +| test_i045 | psa_write() does not return if invec_idx is greater than PSA_MAX_IOVEC | [client/server]_test_psa_write_with_invec_greater_than_max_iovec() | Call psa_write with invec_idx>PSA_MAX_IOVEC | psa_write | Alpha | +| test_i046 | psa_write() does not return if the call attempts to write data past the end of the client output vector | [client/server]_test_psa_write_with_size_overflow() | Call psa_write with a size input one byte bigger than allowed size | psa_write | Alpha | +| | psa_get does not return if The msg pointer provided is not a valid memory reference | | | | To be covered in Beta | +| | psa_call does not return if address of in_vec is invalid for client | | | | To be covered in Beta | +| | psa_call does not return if address of out_vec is invalid for client | | | | To be covered in Beta | +| | psa_call does not return if psa_invec.base address is invalid for client | | | | To be covered in Beta | +| | psa_call does not return if psa_outvec.base address is invalid for client | | | | To be covered in Beta | +| | psa_call does not return if psa_invec.base addr is valid but psa_invec.base+size address is invalid for client | | | | To be covered in Beta | +| | psa_call does not return if psa_outvec.base addr is valid but psa_invec.base+sizeaddress is invalid for client | | | | To be covered in Beta | +| | psa_get() returns PSA_ERR_NOMSG if the SPM cannot deliver a message to the Secure Partition following the assertion of the RoT Service signal. | | | | To be covered in Beta | +| | psa_close doesnot return if The connection is already handling a request | | | | To be covered in Beta | +| | psa_call doesnot return if The connection is already handling a request | | | | To be covered in Beta | +| | psa_read does not return if the memory reference for buffer is invalid or not writable | | | | To be covered in Beta | +| | psa_write does not return if the memory reference for buffer is invalid | | | | To be covered in Beta | +| | 1. psa_notify(): Sends a PSA_DOORBELL signal to a specific Secure Partition. The receiving partition should be expecting the assertion of their PSA_DOORBELL signal using psa_wait_interrupt() or psa_wait_any().

    2.psa_clear() clears the PSA_DOORBELL signal.

    3.psa_clear(): The target Partition doorbell will remain asserted until it calls psa_clear()

    4.psa_wait_interrupt() returns the Secure Partition interrupt signals that have been asserted from the subset of signals indicated in the bitmask provided. The mask must contain the set of signals the caller is interested in handling. Signals that are not in this set will be ignored.

    5.psa_wait_interrupt() : When MODE(PSA_BLOCK) is one, the function will block the caller until one of the requested signals is asserted.

    6.psa_wait_interrupt() : When MODE is zero, the function will return immediately with the current signal state, which can be zero if no signals are active.

    7.psa_wait_interrupt(): RES timeout[30:0] Reserved for future use. Callers must set RES to zero, implementations must ignore the value of RES. | | | | To be covered in Beta | +| | psa_notify does not return if Partition ID does not correspond to a Secure Partition | | | | To be covered in Beta | +| | psa_clear does not return if The Secure PartitionÂ’s doorbell signal is not currently asserted | | | | To be covered in Beta | +| | 1. psa_wait_interrupt() returns the Secure Partition interrupt signals that have been asserted from the subset of signals indicated in the bitmask provided.

    2.psa_eoi():Informs the SPM that an interrupt has been handled (end of interrupt). This will re-enable the interrupt line. psa_eoi(): A signal remains active until it is processed by psa_eoi

    3.Manifest- irqs (optional, unique) A Partition can claim exclusive access to IRQ lines. A list can be provided of IRQ lines which the partition must exclusively handle. When doing so, a signal name must be provided so that a Partition can reference the associated signal. | | | | To be covered in Beta | +| | psa_wait_interrupt does not return if signal_mask does not include any interrupt or doorbell signals | | | | To be covered in Beta | +| | psa_wait_interrupt does not return if signal_mask includes one or more RoT Service signals | | | | To be covered in Beta | +| | psa_eoi does not return if irq_signal is not an interrupt signal | | | | To be covered in Beta | +| | psa_eoi does not return if irq_signal indicates more than one signal | | | | To be covered in Beta | +| | psa_eoi does not return if irq_signal is not currently asserted | | | | To be covered in Beta | +| No explicit test | The SPM must eventually deliver all signals and IPC messages. | | No explicit test written to cover this rule. All PSA tests are written with the expectation that the SPM delivers all requested signals and IPC message in a timely fashion. Failure to provide this will result in simulation time out. | | Alpha | +| | A Secure Partition is guaranteed to be able to execute and read its own code regions, read its own read-only data regions and read and write its private stack, data, and heap regions | | | | To be covered in Beta | +| | Isolation Level-1 rules - The SPM must ensure that no runtime state, data or devices that are part of the SPE can be accessed (read or written) by firmware or hardware in the NSPE. Assets considered for verifying isolation rules: - Local static data (static variable declared inside function) - Global data and variables - Heap data and Stack data - MMIO regions | | From non-secure application, access(read/write) above assets of Application RoT and PSA RoT and expect transaction fail. | | To be covered in Beta | +| | Isolation Level-2 rules- In addition to the Level 1 requirements, Level 2 firmware isolation requires - SPM must ensure that no runtime state, data or devices that are part of the PSA Root of Trust can be accessed (read or written) by firmware or hardware in the Application Root of Trust. | | In Addition to level-1 tests, perform following checks: From Application RoT partition, access(read/write) PSA RoT (SPM and PSA RoT Partition) assets and expect transaction fail. | | To be covered in Beta | +| | Isolation Level-3 rules - In addition to the Level 1 requirements, Level 3 firmware isolation requires: • The SPM must ensure that no runtime state, data or devices that are part of the SPM can be accessed (read or written) by firmware or hardware in any Secure Partition. • The SPM must ensure that no runtime state, data or devices that are part of a Secure Partition can be accessed (read or written) by firmware or hardware in any other Secure Partition. | | In Addition to level-1 and level 2 tests, perform following checks: From Application secure partition, access(read/write) other Application secure partition asserts and expect transaction fail. | | To be covered in Beta | +| No explicit test | Manifest Parameter - id (required, unique) A Secure Partition ID must be a non-zero positive 32-bit value. | | PSA IPC tests partition files will be provided with unique Partition ID which is non-zero positive and name field compliant to manifest rules. | | Alpha | +| Out of scope | Secure Partition IDs must be fixed across updates | | Checking for Partition IDs across SPM updates is out of scope for PSA IPC tests. | | | +| | Manifest Parameter- type (required) Whether the Partition is a part of the PSA Root of Trust Services or is part of the Application Root of Trust Services. Type must be assigned one of the following values: - APPLICATION-ROT - PSA-ROT | | PSA IPC tests partition files will be provided with these field. Access permission related to these fields will be verified as part of tests covering isolation level rules. | | To be covered in Beta | +| No explicit test | Manifest Parameter - description (optional) Field for human readable descriptions and comments. | | PSA IPC tests partition files will be provided with these field with adhere to manifest rules | | To be covered in Beta | +| Out of scope | Manifest Parameter - priority (required) Partitions must be assigned one of the following priority groups: • HIGH • NORMAL • LOW LOW is the lowest priority. Priority is ignored by SPMs that do not implement any priority-based scheduling | | PSA IPC tests partition files are provided with priority field equal to LOW. Use of these fields are higly dependent on type of SPM scheduling policy and verifying scheduling policy is out of scope for IPC tests | | | +| No explicit test | Manifest Parameter -entry_point (required, unique) The Partition entry point in the form of an C function symbol. A single entry point must be provided. | | No explicit test written to cover this rule. PSA IPC tests manifests are provided with tests partition entry_point. A successful luanch and run of tests partition code indirectly verify this field. | | Alpha | +| No explicit test | Manifest Parameter- stack_size (required) Partition's stack size in bytes. The size value must be represented either as a positive integer or as a hexadecimal string. | | No explicit test written to cover this rule. PSA IPC tests manifests are provided with tests partition required stack_size. A successful run of tests partition code indirectly verify this field. | | Alpha | +| | Manifest Parameter- heap_size (optional) Partition's heap size in bytes. The size value must be represented either as a positive integer or as a hexadecimal string. If this field is not specified then the heap size must be set to 0. | | | | To be covered in Beta | +| No explicit test | Manifest Parameter- source_files (required) Explicit list of file paths to files containing the Partition's source code. Source file paths must be relative path names. | | No explicit test written to cover this rule. PSA IPC tests manifests are provided with source_files filled with tests source files. A successful compilation and run of tests partition code indirectly verify this field. | | Alpha | +| No explicit test | Manifest Parameter- RoT Services (optional, unique) A Partition must declare all of the RoT Services that it implements. This includes at least four identifying pieces of information: • The sid field contains the SID value. • The signal field contains an alphanumeric string for use with psa_get(). • The name field contains an alphanumeric string for use with psa_connect(). | | PSA IPC tests manifests are provided with these fields. A successful compilation and run of tests partition code indirectly verify this field. | | Alpha | +| No explicit test | mmio_regions (optional, unique) List of memory-mapped I/O region objects which the Secure Partition needs access to. This may be required by a Secure Partition in order to implement a device driver. A Secure Partition always has exclusive access to an MMIO region. Secure Partitions are not permitted to share MMIO regions with other Secure Partitions. An MMIO region can be defined either as a: • numbered_region • named_region | | PSA IPC tests device driver partition manifests are provided with these fields. A successful compilation and run of device driver partition code indirectly verify this field. Rules around sharing of MMIO regions can't be test as sharing of MMIO regions between secure partition will result into compilation fail. PSA tests partition relies on numbered_region only as named_region is subject to resolved in Implementation defined manner. | | To be covered in Beta | +| No explicit test | Manifest Input - psa_framework_version - Version of the PSA Firmware Framework specification this manifest conforms to. | | PSA IPC tests manifests are provided with this field. A successful compilation and run of tests partition code indirectly verify this field. | | Alpha | diff --git a/psa-ff/platform/manifests/common/driver_partition_psa.json b/psa-ff/platform/manifests/common/driver_partition_psa.json new file mode 100644 index 00000000..878c923d --- /dev/null +++ b/psa-ff/platform/manifests/common/driver_partition_psa.json @@ -0,0 +1,69 @@ +{ + "psa_framework_version": 0.10, + "name": "DRIVER_PARTITION", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000003", + "description": "Implements device services such print, flash read/write,. etc.", + "entry_point": "driver_main", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "DRIVER_UART_SID", + "sid": "0x0000FC01", + "signal": "DRIVER_UART_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "DRIVER_WATCHDOG_SID", + "sid": "0x0000FC02", + "signal": "DRIVER_WATCHDOG_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "DRIVER_NVMEM_SID", + "sid": "0x0000FC03", + "signal": "DRIVER_NVMEM_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "DRIVER_TARGET_INIT_SID", + "sid": "0x0000FC04", + "signal": "DRIVER_TARGET_INIT_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + } + ], + "mmio_regions" : [ + { + "name": "UART_REGION", + "base": "0x40004000", + "size": "0xFFF", + "permission": "READ-WRITE" + }, + { + "name": "WATCHDOG_REGION", + "base": "0x40008000", + "size": "0xFFF", + "permission": "READ-WRITE" + }, + { + "name": "NVMEM_REGION", + "base": "0x2002F000", + "size": "0xFFF", + "permission": "READ-WRITE" + } + ], + "source_files": [ + "../../../test_suites/partition/common/driver_partition.c", + "../../../val/spe/val_driver_service_apis.c", + "../../../platform/spe/pal_driver_intf.c" + ] +} diff --git a/psa-ff/platform/manifests/ipc/client_partition_psa.json b/psa-ff/platform/manifests/ipc/client_partition_psa.json new file mode 100644 index 00000000..d202d4d6 --- /dev/null +++ b/psa-ff/platform/manifests/ipc/client_partition_psa.json @@ -0,0 +1,49 @@ +{ + "psa_framework_version": 0.10, + "name": "CLIENT_PARTITION", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000001", + "description": "Client partition executing client test func from SPE", + "entry_point": "client_main", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "CLIENT_TEST_DISPATCHER_SID", + "sid": "0x0000FA01", + "signal": "CLIENT_TEST_DISPATCHER_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + } + ], + "extern_sids": [ + "DRIVER_UART_SID", + "DRIVER_NVMEM_SID", + "SERVER_TEST_DISPATCHER_SID", + "SERVER_UNSPECIFED_MINOR_V_SID", + "SERVER_STRICT_MINOR_VERSION_SID", + "SERVER_RELAX_MINOR_VERSION_SID", + "SERVER_SECURE_CONNECT_ONLY_SID", + "SERVER_CONNECTION_DROP_SID" + ], + "source_files": [ + "../../../test_suites/partition/ipc/client_partition.c", + "../../../test_suites/ipc/test_i001/test_i001.c", + "../../../test_suites/ipc/test_i002/test_i002.c", + "../../../test_suites/ipc/test_i003/test_i003.c", + "../../../test_suites/ipc/test_i004/test_i004.c", + "../../../test_suites/ipc/test_i005/test_i005.c", + "../../../test_suites/ipc/test_i006/test_i006.c", + "../../../test_suites/ipc/test_i007/test_i007.c", + "../../../test_suites/ipc/test_i008/test_i008.c", + "../../../test_suites/ipc/test_i009/test_i009.c", + "../../../test_suites/ipc/test_i010/test_i010.c", + "../../../test_suites/ipc/test_i011/test_i011.c", + "../../../test_suites/ipc/test_i012/test_i012.c", + "../../../test_suites/ipc/test_i024/test_i024.c", + "../../../test_suites/ipc/test_i025/test_i025.c", + "../../../test_suites/ipc/test_i026/test_i026.c", + "../../../test_suites/ipc/test_i027/test_i027.c" + ] +} diff --git a/psa-ff/platform/manifests/ipc/server_partition_psa.json b/psa-ff/platform/manifests/ipc/server_partition_psa.json new file mode 100644 index 00000000..907fb07a --- /dev/null +++ b/psa-ff/platform/manifests/ipc/server_partition_psa.json @@ -0,0 +1,119 @@ +{ + "psa_framework_version": 0.10, + "name": "SERVER_PARTITION", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "id": "0x00000002", + "description": "Server partition executing server test func", + "entry_point": "server_main", + "stack_size": "0x400", + "heap_size": "0x400", + "services": [{ + "name": "SERVER_TEST_DISPATCHER_SID", + "sid": "0x0000FB01", + "signal": "SERVER_TEST_DISPATCHER_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + }, + { + "name": "SERVER_SECURE_CONNECT_ONLY_SID", + "sid": "0x0000FB02", + "signal": "SERVER_SECURE_CONNECT_ONLY_SIG", + "non_secure_clients": false, + "minor_version": 2, + "minor_policy": "RELAXED" + }, + { + "name": "SERVER_STRICT_MINOR_VERSION_SID", + "sid": "0x0000FB03", + "signal": "SERVER_STRICT_MINOR_VERSION_SIG", + "non_secure_clients": true, + "minor_version": 2, + "minor_policy": "STRICT" + }, + { + "name": "SERVER_UNSPECIFED_MINOR_V_SID", + "sid": "0x0000FB04", + "signal": "SERVER_UNSPECIFED_MINOR_V_SIG", + "non_secure_clients": true + }, + { + "name": "SERVER_RELAX_MINOR_VERSION_SID", + "sid": "0x0000FB05", + "signal": "SERVER_RELAX_MINOR_VERSION_SIG", + "non_secure_clients": true, + "minor_version": 2, + "minor_policy": "RELAXED" + }, + { + "name": "SERVER_UNEXTERN_SID", + "sid": "0x0000FB06", + "signal": "SERVER_UNEXTERN_SIG", + "non_secure_clients": true, + "minor_version": 2, + "minor_policy": "RELAXED" + }, + { + "name": "SERVER_CONNECTION_DROP_SID", + "sid": "0x0000FB07", + "signal": "SERVER_CONNECTION_DROP_SIG", + "non_secure_clients": true, + "minor_version": 2, + "minor_policy": "RELAXED" + } + ], + "extern_sids": [ + "DRIVER_UART_SID", + "DRIVER_NVMEM_SID" + ], + "source_files": [ + "../../../test_suites/partition/ipc/server_partition.c", + "../../../test_suites/ipc/test_i001/test_supp_i001.c", + "../../../test_suites/ipc/test_i002/test_supp_i002.c", + "../../../test_suites/ipc/test_i003/test_supp_i003.c", + "../../../test_suites/ipc/test_i004/test_supp_i004.c", + "../../../test_suites/ipc/test_i005/test_supp_i005.c", + "../../../test_suites/ipc/test_i006/test_supp_i006.c", + "../../../test_suites/ipc/test_i007/test_supp_i007.c", + "../../../test_suites/ipc/test_i008/test_supp_i008.c", + "../../../test_suites/ipc/test_i009/test_supp_i009.c", + "../../../test_suites/ipc/test_i010/test_supp_i010.c", + "../../../test_suites/ipc/test_i011/test_supp_i011.c", + "../../../test_suites/ipc/test_i012/test_supp_i012.c", + "../../../test_suites/ipc/test_i013/test_supp_i013.c", + "../../../test_suites/ipc/test_i014/test_supp_i014.c", + "../../../test_suites/ipc/test_i015/test_supp_i015.c", + "../../../test_suites/ipc/test_i016/test_supp_i016.c", + "../../../test_suites/ipc/test_i017/test_supp_i017.c", + "../../../test_suites/ipc/test_i018/test_supp_i018.c", + "../../../test_suites/ipc/test_i019/test_supp_i019.c", + "../../../test_suites/ipc/test_i020/test_supp_i020.c", + "../../../test_suites/ipc/test_i021/test_supp_i021.c", + "../../../test_suites/ipc/test_i022/test_supp_i022.c", + "../../../test_suites/ipc/test_i023/test_supp_i023.c", + "../../../test_suites/ipc/test_i024/test_supp_i024.c", + "../../../test_suites/ipc/test_i025/test_supp_i025.c", + "../../../test_suites/ipc/test_i026/test_supp_i026.c", + "../../../test_suites/ipc/test_i027/test_supp_i027.c", + "../../../test_suites/ipc/test_i028/test_supp_i028.c", + "../../../test_suites/ipc/test_i029/test_supp_i029.c", + "../../../test_suites/ipc/test_i030/test_supp_i030.c", + "../../../test_suites/ipc/test_i031/test_supp_i031.c", + "../../../test_suites/ipc/test_i032/test_supp_i032.c", + "../../../test_suites/ipc/test_i033/test_supp_i033.c", + "../../../test_suites/ipc/test_i034/test_supp_i034.c", + "../../../test_suites/ipc/test_i035/test_supp_i035.c", + "../../../test_suites/ipc/test_i036/test_supp_i036.c", + "../../../test_suites/ipc/test_i037/test_supp_i037.c", + "../../../test_suites/ipc/test_i038/test_supp_i038.c", + "../../../test_suites/ipc/test_i039/test_supp_i039.c", + "../../../test_suites/ipc/test_i040/test_supp_i040.c", + "../../../test_suites/ipc/test_i041/test_supp_i041.c", + "../../../test_suites/ipc/test_i042/test_supp_i042.c", + "../../../test_suites/ipc/test_i043/test_supp_i043.c", + "../../../test_suites/ipc/test_i044/test_supp_i044.c", + "../../../test_suites/ipc/test_i045/test_supp_i045.c", + "../../../test_suites/ipc/test_i046/test_supp_i046.c" + ] +} diff --git a/psa-ff/platform/nspe/Makefile b/psa-ff/platform/nspe/Makefile new file mode 100644 index 00000000..dd34d44f --- /dev/null +++ b/psa-ff/platform/nspe/Makefile @@ -0,0 +1,54 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +include $(SOURCE)/tools/makefiles/toolchain.mk + +# Add C source files for compilation +SRC_C= pal_baremetal_ns_intf.c pal_crypto_intf.c + +# Add ASM source files for compilation +SRC_ASM= + +INCLUDE= -I$(SOURCE)/platform/targets/$(TARGET)/ \ + -I$(BUILD)/platform/$(TARGET)/ \ + -I$(SOURCE)/platform/nspe/drivers/ \ + -I$(SOURCE)/platform/nspe/ + +VPATH=$(SOURCE)/platform/targets/$(TARGET)/: \ + $(SOURCE)/platform/nspe/drivers/: \ + $(SOURCE)/platform/nspe/: \ + +all: build + +build: mkdir build_c build_asm pal_nspe.a + +mkdir: + @mkdir -p $(BUILD)/platform/nspe/ + +build_c: $(SRC_C:%.c=$(BUILD)/platform/nspe/%.o) +build_asm: $(SRC_ASM:%.s=$(BUILD)/platform/nspe/%.o) + +$(BUILD)/platform/nspe/%.o : %.c + $(CC) $(INCLUDE) -o $@ -c $< + +$(BUILD)/platform/nspe/%.o : %.s + $(AS) $(INCLUDE) -o $@ $< + +pal_nspe.a: + $(AR) $(AR_OPTIONS) $(BUILD)/platform/pal_nspe.a $(BUILD)/platform/nspe/*.o + +clean: + @rm -rf $(BUILD)/platform/nspe/*.o $(BUILD)/platform/nspe/*.a diff --git a/psa-ff/platform/nspe/pal_baremetal_ns_intf.c b/psa-ff/platform/nspe/pal_baremetal_ns_intf.c new file mode 100644 index 00000000..a34afc73 --- /dev/null +++ b/psa-ff/platform/nspe/pal_baremetal_ns_intf.c @@ -0,0 +1,40 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "pal_common.h" +#include "pal_database.h" + +/** + @brief - provides the database source location. + @return - Returns base address of database +**/ +void *pal_target_get_cfg_start(void) +{ + return (void *)database; +} + +/** + @brief - This function will read peripherals using SPI commands + @param - addr : address of the peripheral + data : read buffer + len : length of the read buffer in bytes + @return - error status +**/ +int pal_spi_read(addr_t addr, uint8_t *data, uint32_t len) +{ + return 0xFF; +} diff --git a/psa-ff/platform/nspe/pal_common.h b/psa-ff/platform/nspe/pal_common.h new file mode 100644 index 00000000..ffc15c79 --- /dev/null +++ b/psa-ff/platform/nspe/pal_common.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_COMMON_H_ +#define _PAL_COMMON_H_ + +/* typedef's */ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; +typedef uint8_t bool_t; +typedef uint32_t addr_t; +typedef uint32_t test_id_t; +typedef char char8_t; +typedef uint32_t cfg_id_t; + +#define addr_t uint32_t +#define bool_t uint8_t + +#endif /* _PAL_COMMON_H_ */ diff --git a/psa-ff/platform/nspe/pal_crypto_intf.c b/psa-ff/platform/nspe/pal_crypto_intf.c new file mode 100644 index 00000000..a992779c --- /dev/null +++ b/psa-ff/platform/nspe/pal_crypto_intf.c @@ -0,0 +1,150 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + + +#include "pal_common.h" +#include "pal_crypto_intf.h" + +/** + @brief - This API will call the requested crypto function + @param - type : function code + valist : variable argument list + @return - error status +**/ +#ifdef CRYPTO_SUITE +psa_status_t pal_crypto_function(int type, va_list valist) +{ + size_t size, *length; + uint8_t *buffer; + uint32_t status; + psa_key_slot_t key_slot; + psa_key_type_t key_type, *key_type_out; + psa_key_policy_t *policy; + psa_key_usage_t usage, *usage_out; + psa_key_lifetime_t lifetime, *lifetime_out; + psa_algorithm_t alg, *alg_out; + psa_hash_operation_t *operation; + + switch (type) + { + case PAL_CRYPTO_INIT: + return psa_crypto_init(); + case PAL_CRYPTO_GENERATE_RANDOM: + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, int); + return psa_generate_random(buffer, size); + case PAL_CRYPTO_IMPORT_KEY: + key_slot = va_arg(valist, psa_key_slot_t); + key_type = va_arg(valist, psa_key_type_t); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, int); + status = psa_import_key(key_slot, key_type, buffer, size); + return status; + case PAL_CRYPTO_EXPORT_KEY: + key_slot = va_arg(valist, psa_key_slot_t); + buffer = (uint8_t *)(va_arg(valist, uint8_t*)); + size = va_arg(valist, int); + length = (size_t *)va_arg(valist, size_t*); + status = psa_export_key(key_slot, buffer, size, length); + return status; + case PAL_CRYPTO_EXPORT_PUBLIC_KEY: + key_slot = va_arg(valist, psa_key_slot_t); + buffer = (uint8_t *)(va_arg(valist, uint8_t*)); + size = va_arg(valist, int); + length = (size_t *)va_arg(valist, size_t*); + status = psa_export_public_key(key_slot, buffer, size, length); + return status; + case PAL_CRYPTO_KEY_POLICY_INIT: + policy = va_arg(valist, psa_key_policy_t*); + psa_key_policy_init(policy); + return 0; + case PAL_CRYPTO_KEY_POLICY_SET_USAGE: + policy = va_arg(valist, psa_key_policy_t*); + usage = va_arg(valist, psa_key_usage_t); + alg = va_arg(valist, psa_algorithm_t); + psa_key_policy_set_usage(policy, usage, alg); + return 0; + case PAL_CRYPTO_SET_KEY_POLICY: + key_slot = va_arg(valist, psa_key_slot_t); + policy = va_arg(valist, psa_key_policy_t*); + return psa_set_key_policy(key_slot, policy); + case PAL_CRYPTO_DESTROY_KEY: + key_slot = va_arg(valist, psa_key_slot_t); + status = psa_destroy_key(key_slot); + return status; + case PAL_CRYPTO_GET_KEY_INFORMATION: + key_slot = va_arg(valist, psa_key_slot_t); + key_type_out = va_arg(valist, psa_key_type_t*); + length = (size_t *)va_arg(valist, size_t*); + status = psa_get_key_information(key_slot, key_type_out, length); + return status; + case PAL_CRYPTO_GET_KEY_POLICY: + key_slot = va_arg(valist, psa_key_slot_t); + policy = va_arg(valist, psa_key_policy_t*); + return psa_get_key_policy(key_slot, policy); + case PAL_CRYPTO_KEY_POLICY_GET_USAGE: + policy = va_arg(valist, psa_key_policy_t*); + usage_out = va_arg(valist, psa_key_usage_t*); + *usage_out = psa_key_policy_get_usage(policy); + return 0; + case PAL_CRYPTO_KEY_POLICY_GET_ALGORITHM: + policy = va_arg(valist, psa_key_policy_t*); + alg_out = va_arg(valist, psa_algorithm_t*); + *alg_out = psa_key_policy_get_algorithm(policy); + return 0; + case PAL_CRYPTO_GET_KEY_LIFETIME: + key_slot = va_arg(valist, psa_key_slot_t); + lifetime_out = va_arg(valist, psa_key_lifetime_t*); + return psa_get_key_lifetime(key_slot, lifetime_out); + case PAL_CRYPTO_SET_KEY_LIFETIME: + key_slot = va_arg(valist, psa_key_slot_t); + lifetime = va_arg(valist, psa_key_lifetime_t); + return psa_set_key_lifetime(key_slot, lifetime); + case PAL_CRYPTO_HASH_SETUP: + operation = va_arg(valist, psa_hash_operation_t*); + alg = va_arg(valist, psa_algorithm_t); + return psa_hash_setup(operation, alg); + case PAL_CRYPTO_HASH_UPDATE: + operation = va_arg(valist, psa_hash_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_hash_update(operation, buffer, size); + case PAL_CRYPTO_HASH_VERIFY: + operation = va_arg(valist, psa_hash_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_hash_verify(operation, buffer, size); + case PAL_CRYPTO_HASH_FINISH: + operation = va_arg(valist, psa_hash_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_hash_finish(operation, buffer, size, length); + case PAL_CRYPTO_HASH_ABORT: + operation = va_arg(valist, psa_hash_operation_t*); + return psa_hash_abort(operation); + default: + return PAL_STATUS_UNSUPPORTED_FUNC; + } +} + +#else +psa_status_t pal_crypto_function(int type, va_list valist) +{ + return PAL_STATUS_UNSUPPORTED_FUNC; +} +#endif diff --git a/psa-ff/platform/nspe/pal_crypto_intf.h b/psa-ff/platform/nspe/pal_crypto_intf.h new file mode 100644 index 00000000..79158df0 --- /dev/null +++ b/psa-ff/platform/nspe/pal_crypto_intf.h @@ -0,0 +1,165 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_CRYPTO_H_ +#define _PAL_CRYPTO_H_ + +#include + +#define PAL_STATUS_UNSUPPORTED_FUNC 0xFF + +typedef uint32_t psa_key_usage_t; +typedef uint32_t psa_algorithm_t; +typedef int32_t psa_status_t; +typedef uint32_t psa_key_type_t; +typedef uint32_t psa_key_slot_t; +typedef uint32_t psa_key_lifetime_t; +typedef uint32_t size_t; + +enum crypto_function_code { + PAL_CRYPTO_INIT = 0x1, + PAL_CRYPTO_GENERATE_RANDOM = 0x2, + PAL_CRYPTO_IMPORT_KEY = 0x3, + PAL_CRYPTO_EXPORT_KEY = 0x4, + PAL_CRYPTO_EXPORT_PUBLIC_KEY = 0x5, + PAL_CRYPTO_DESTROY_KEY = 0x6, + PAL_CRYPTO_GET_KEY_INFO = 0x7, + PAL_CRYPTO_KEY_POLICY_INIT = 0x8, + PAL_CRYPTO_KEY_POLICY_SET_USAGE = 0x9, + PAL_CRYPTO_KEY_POLICY_GET_USAGE = 0xA, + PAL_CRYPTO_KEY_POLICY_GET_ALGORITHM = 0xB, + PAL_CRYPTO_SET_KEY_POLICY = 0xC, + PAL_CRYPTO_GET_KEY_POLICY = 0xD, + PAL_CRYPTO_GET_KEY_INFORMATION = 0xE, + PAL_CRYPTO_GET_KEY_LIFETIME = 0xF, + PAL_CRYPTO_SET_KEY_LIFETIME = 0x10, + PAL_CRYPTO_HASH_SETUP = 0x11, + PAL_CRYPTO_HASH_UPDATE = 0x12, + PAL_CRYPTO_HASH_VERIFY = 0x13, + PAL_CRYPTO_HASH_FINISH = 0x14, + PAL_CRYPTO_HASH_ABORT = 0x15, +}; + +struct psa_key_policy_s { + psa_key_usage_t usage; + psa_algorithm_t alg; +}; + +typedef struct { + unsigned char cksum[16]; /*!< checksum of the data block */ + unsigned char state[48]; /*!< intermediate digest state */ + unsigned char buffer[16]; /*!< data block being processed */ + size_t left; /*!< amount of data in buffer */ +} mbedtls_md2_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} mbedtls_md4_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} mbedtls_md5_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} mbedtls_ripemd160_context; + +typedef struct { + uint32_t total[2]; /*!< The number of Bytes processed. */ + uint32_t state[5]; /*!< The intermediate digest state. */ + unsigned char buffer[64]; /*!< The data block being processed. */ +} mbedtls_sha1_context; + +typedef struct { + uint32_t total[2]; /*!< The number of Bytes processed. */ + uint32_t state[8]; /*!< The intermediate digest state. */ + unsigned char buffer[64]; /*!< The data block being processed. */ + int is224; /*!< Determines which function to use: + 0: Use SHA-256, or 1: Use SHA-224. */ +} mbedtls_sha256_context; + +typedef struct { + uint64_t total[2]; /*!< The number of Bytes processed. */ + uint64_t state[8]; /*!< The intermediate digest state. */ + unsigned char buffer[128]; /*!< The data block being processed. */ + int is384; /*!< Determines which function to use: + 0: Use SHA-512, or 1: Use SHA-384. */ +} mbedtls_sha512_context; + +struct psa_hash_operation_s { + psa_algorithm_t alg; + union + { + unsigned dummy; /* Make the union non-empty even with no supported algorithms. */ + mbedtls_md2_context md2; + mbedtls_md4_context md4; + mbedtls_md5_context md5; + mbedtls_ripemd160_context ripemd160; + mbedtls_sha1_context sha1; + mbedtls_sha256_context sha256; + mbedtls_sha512_context sha512; + } ctx; +}; + +typedef struct psa_hash_operation_s psa_hash_operation_t; +typedef struct psa_key_policy_s psa_key_policy_t; + +psa_status_t psa_crypto_init(void); +psa_status_t psa_import_key(psa_key_slot_t key, psa_key_type_t type, + const uint8_t *data, size_t data_length); +psa_status_t psa_export_key(psa_key_slot_t key, uint8_t *data, + size_t data_size, size_t *data_length); +void psa_key_policy_init(psa_key_policy_t *policy); +void psa_key_policy_set_usage(psa_key_policy_t *policy, psa_key_usage_t usage, + psa_algorithm_t alg); +psa_status_t psa_set_key_policy(psa_key_slot_t key, const psa_key_policy_t *policy); +psa_status_t psa_destroy_key(psa_key_slot_t key); +psa_status_t psa_export_public_key(psa_key_slot_t key, uint8_t *data, + size_t data_size, size_t *data_length); +psa_status_t psa_generate_key(psa_key_slot_t key, psa_key_type_t type, + size_t bits, const void *extra, size_t extra_size); +psa_status_t psa_get_key_information(psa_key_slot_t key, psa_key_type_t *type, size_t *bits); +psa_status_t psa_get_key_policy(psa_key_slot_t key, psa_key_policy_t *policy); +psa_key_usage_t psa_key_policy_get_usage(const psa_key_policy_t *policy); +psa_algorithm_t psa_key_policy_get_algorithm(const psa_key_policy_t *policy); +psa_status_t psa_get_key_lifetime(psa_key_slot_t key, + psa_key_lifetime_t *lifetime); +psa_status_t psa_set_key_lifetime(psa_key_slot_t key, + psa_key_lifetime_t lifetime); +psa_status_t psa_hash_setup(psa_hash_operation_t *operation, + psa_algorithm_t alg); + +psa_status_t psa_hash_update(psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length); +psa_status_t psa_hash_verify(psa_hash_operation_t *operation, + const uint8_t *hash, + size_t hash_length); +psa_status_t psa_hash_finish(psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); +psa_status_t psa_hash_abort(psa_hash_operation_t *operation); +psa_status_t psa_generate_random(uint8_t *output, size_t output_size); +psa_status_t pal_crypto_function(int type, va_list valist); +#endif /* _PAL_CRYPTO_H_ */ diff --git a/psa-ff/platform/nspe/pal_sid.h b/psa-ff/platform/nspe/pal_sid.h new file mode 100644 index 00000000..988b3bb4 --- /dev/null +++ b/psa-ff/platform/nspe/pal_sid.h @@ -0,0 +1,35 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_SID_H_ +#define _PAL_SID_H_ + +/* SID Constant defined and used by test suite */ +#define CLIENT_TEST_DISPATCHER_SID 0x0000FA01 +#define SERVER_TEST_DISPATCHER_SID 0x0000FB01 +#define SERVER_SECURE_CONNECT_ONLY_SID 0x0000FB02 +#define SERVER_STRICT_MINOR_VERSION_SID 0x0000FB03 +#define SERVER_UNSPECIFED_MINOR_V_SID 0x0000FB04 +#define SERVER_RELAX_MINOR_VERSION_SID 0x0000FB05 +#define SERVER_UNEXTERN_SID 0x0000FB06 +#define SERVER_CONNECTION_DROP_SID 0x0000FB07 +#define DRIVER_UART_SID 0x0000FC01 +#define DRIVER_WATCHDOG_SID 0x0000FC02 +#define DRIVER_NVMEM_SID 0x0000FC03 +#define DRIVER_TARGET_INIT_SID 0x0000FC04 + +#endif /* _PAL_SID_H_ */ diff --git a/psa-ff/platform/spe/drivers/nvmem/pal_nvmem.c b/psa-ff/platform/spe/drivers/nvmem/pal_nvmem.c new file mode 100644 index 00000000..65a9442b --- /dev/null +++ b/psa-ff/platform/spe/drivers/nvmem/pal_nvmem.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "pal_nvmem.h" + +/** + @brief - Writes into given non-volatile address. + @param - base : Base address of non-volatile memory + offset : Offset + buffer : Pointer to source address + size : Number of bytes + @return - 1/0 +**/ +int nvmem_write(addr_t base, uint32_t offset, void *buffer, int size) +{ + int b_cnt; + + if (buffer == NULL) { + return 0; + } + + for (b_cnt = 0; b_cnt < size; b_cnt++) + { + /* If flash interface just emulated over SRAM, use simple memory write */ + *(uint8_t *)(base + offset + b_cnt) = *((uint8_t *)buffer + b_cnt); + } + + return 1; +} + +/** + @brief - Reads from given non-volatile address. + @param - base : Base address of non-volatile memory + offset : Offset + buffer : Pointer to source address + size : Number of bytes + @return - 1/0 +**/ +int nvmem_read(addr_t base, uint32_t offset, void *buffer, int size) +{ + int b_cnt; + + if (buffer == NULL) { + return 0; + } + + for (b_cnt = 0; b_cnt < size; b_cnt++) + { + /* If flash interface just emulated over SRAM, use simple memory read */ + *((uint8_t *)buffer + b_cnt) = *(uint8_t *)(base + offset + b_cnt); + } + + return 1; +} + diff --git a/psa-ff/platform/spe/drivers/nvmem/pal_nvmem.h b/psa-ff/platform/spe/drivers/nvmem/pal_nvmem.h new file mode 100644 index 00000000..df55c029 --- /dev/null +++ b/psa-ff/platform/spe/drivers/nvmem/pal_nvmem.h @@ -0,0 +1,26 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_NVMEM_H_ +#define _PAL_NVMEM_H_ + +#include "platform/spe/pal_common.h" + +int nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); +int nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); + +#endif /* PAL_NVMEM_H */ diff --git a/psa-ff/platform/spe/drivers/uart/pal_uart.c b/psa-ff/platform/spe/drivers/uart/pal_uart.c new file mode 100644 index 00000000..48515c19 --- /dev/null +++ b/psa-ff/platform/spe/drivers/uart/pal_uart.c @@ -0,0 +1,114 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "pal_uart.h" + +volatile uint32_t g_uart; + +/** + @brief - This function initializes the UART +**/ +void pal_uart_cmsdk_init(uint32_t uart_base_addr) +{ + g_uart = uart_base_addr; + + /* Disable RX and TX interrupts, disable overrun interrupts, enable TX, + * disable RX */ + ((uart_t *) g_uart)->CTRL = CMSDK_UART_CTRL_TXEN_Msk; + /* However, the UART resets to a BAUDDIV of 0, which is invalid. Set it to + * the minimum valid number. */ + ((uart_t *) g_uart)->BAUDDIV = 16; + +} + +/** + @brief - This function checks for empty TX FIFO +**/ +static int pal_uart_cmsdk_is_tx_empty(void) +{ + /* Note: Check for empty TX FIFO */ + return (!((uart_t *) g_uart)->STATE & CMSDK_UART_STATE_TXBF_Msk); +} + +/** + @brief - This function checks for empty TX FIFO and writes to FIFO register +**/ +static void pal_uart_cmsdk_putc(uint8_t c) +{ + /* ensure TX buffer to be empty */ + while (!pal_uart_cmsdk_is_tx_empty()); + + /* write the data (upper 24 bits are reserved) */ + ((uart_t *) g_uart)->DATA = c; + if (c == '\n') + { + pal_uart_cmsdk_putc('\r'); + } +} + +/** + @brief - This function parses the input string and writes bytes into UART TX FIFO + @param - str : Input String + - data : Value for format specifier +**/ +void pal_cmsdk_print(char *str, uint32_t data) +{ + uint8_t j, buffer[16]; + int8_t i = 0; + + for (; *str != '\0'; ++str) + { + if (*str == '%') + { + ++str; + if (*str == 'd') + { + while (data != 0) + { + j = data % 10; + data = data /10; + buffer[i] = j + 48; + i += 1; + } + } + else if (*str == 'x' || *str == 'X') + { + while (data != 0) + { + j = data & 0xf; + data >>= 4; + buffer[i] = j + ((j > 9) ? 55 : 48); + i += 1; + } + } + if (i > 0) + { + while (i > 0) { + pal_uart_cmsdk_putc(buffer[--i]); + } + } + else + { + pal_uart_cmsdk_putc(48); + } + } + else + { + pal_uart_cmsdk_putc(*str); + } + } +} diff --git a/psa-ff/platform/spe/drivers/uart/pal_uart.h b/psa-ff/platform/spe/drivers/uart/pal_uart.h new file mode 100644 index 00000000..7e82d417 --- /dev/null +++ b/psa-ff/platform/spe/drivers/uart/pal_uart.h @@ -0,0 +1,48 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_UART_CMSDK_H_ +#define _PAL_UART_CMSDK_H_ + +#include + +/* CMSDK_UART DATA Register Definitions */ +#define CMSDK_UART_STATE_TXBF_Pos 0 /* CMSDK_UART STATE: TXBF Position */ +#define CMSDK_UART_STATE_TXBF_Msk (0x1UL) /* CMSDK_UART STATE: TXBF Mask */ + +/* CMSDK_UART CTRL Register Definitions */ +#define CMSDK_UART_CTRL_TXEN_Pos 0 /* CMSDK_UART CTRL: TXEN Position */ +#define CMSDK_UART_CTRL_TXEN_Msk (0x01UL) /* CMSDK_UART CTRL: TXEN Mask */ + +/* typedef's */ +typedef struct { + uint32_t DATA; /* Offset: 0x000 (R/W) Data Register */ + uint32_t STATE; /* Offset: 0x004 (R/W) Status state */ + uint32_t CTRL; /* Offset: 0x008 (R/W) Control Register */ + union { + uint32_t INTSTATUS; /* Offset: 0x00C (R/ ) Interrupt Status Register */ + uint32_t INTCLEAR; /* Offset: 0x00C ( /W) Interrupt Clear Register */ + }; + uint32_t BAUDDIV; /* Offset: 0x010 (R/W) Baud rate divider */ +} uart_t; + + +/* function prototypes */ +void pal_uart_cmsdk_init(uint32_t uart_base_addr); +void pal_cmsdk_print(char *str, uint32_t data); + +#endif /* _PAL_UART_CMSDK_H_ */ diff --git a/psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.c b/psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.c new file mode 100644 index 00000000..e9d20bb4 --- /dev/null +++ b/psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.c @@ -0,0 +1,83 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "pal_wd_cmsdk.h" + +/** + @brief - Initializes an hardware watchdog timer + @param - base_addr : Base address of the watchdog module + - time_us : Time in micro seconds + - timer_tick_us : Number of ticks per micro second + @return - SUCCESS/FAILURE +**/ +int pal_wd_cmsdk_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us) +{ + /* Disable Timer */ + ((wd_timer_t *)base_addr)->CTRL = 0x0; + + if (time_us == 0 ) + { + ((wd_timer_t *)base_addr)->LOAD = 0; + } + else + { + ((wd_timer_t *)base_addr)->LOAD = time_us * timer_tick_us; + } + + return 0; +} + +/** + @brief - Enables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_cmsdk_enable(addr_t base_addr) +{ + /* Enable Interrupt */ + ((wd_timer_t *)base_addr)->CTRL = Watchdog_CTRL_INTEN_Msk; + /* Enable Reset */ + ((wd_timer_t *)base_addr)->CTRL |= Watchdog_CTRL_RESEN_Msk; + + return 0; +} + +/** + @brief - Disables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_cmsdk_disable(addr_t base_addr) +{ + /* Disable Timer */ + ((wd_timer_t *)base_addr)->CTRL = 0x0; + ((wd_timer_t *)base_addr)->INTCLR = 0x1; + + return 0; +} + +/** + @brief - Checks whether hardware watchdog timer is enabled + @param - base_addr : Base address of the watchdog module + @return - Enabled : 1, Disabled : 0 +**/ +int pal_wd_cmsdk_is_enabled(addr_t base_addr) +{ + return (((wd_timer_t *)base_addr)->CTRL & Watchdog_CTRL_INTEN_Msk ? \ + (((wd_timer_t *)base_addr)->CTRL & Watchdog_CTRL_RESEN_Msk ? 1 : 0) : 0); +} + diff --git a/psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.h b/psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.h new file mode 100644 index 00000000..f73d6987 --- /dev/null +++ b/psa-ff/platform/spe/drivers/watchdog/pal_wd_cmsdk.h @@ -0,0 +1,87 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_WD_CMSDK_H_ +#define _PAL_WD_CMSDK_H_ + +#include "platform/spe/pal_common.h" + +#define WDOG_TIMER_MAX_VALUE 0xFFFFFFFF + +typedef struct { + uint32_t LOAD; /* Offset: 0x000 (R/W) Watchdog Load Register */ + uint32_t VALUE; /* Offset: 0x004 (R/ ) Watchdog Value Register */ + uint32_t CTRL; /* Offset: 0x008 (R/W) Watchdog Control Register */ + uint32_t INTCLR; /* Offset: 0x00C ( /W) Watchdog Clear Interrupt Register */ + uint32_t RAWINTSTAT; /* Offset: 0x010 (R/ ) Watchdog Raw Interrupt Status Register */ + uint32_t MASKINTSTAT; /* Offset: 0x014 (R/ ) Watchdog Interrupt Status Register */ + uint32_t RESERVED0[762]; + uint32_t LOCK; /* Offset: 0xC00 (R/W) Watchdog Lock Register */ + uint32_t RESERVED1[191]; + uint32_t ITCR; /* Offset: 0xF00 (R/W) Watchdog Integration Test Control Register */ + uint32_t ITOP; /* Offset: 0xF04 ( /W) Watchdog Integration Test Output Set Register */ +} wd_timer_t; + +/* WATCHDOG LOAD Register Definitions */ +#define Watchdog_LOAD_Pos 0 /* Watchdog LOAD: LOAD Position */ +#define Watchdog_LOAD_Msk (0xFFFFFFFFUL) /* Watchdog LOAD: LOAD Mask */ + +/* WATCHDOG VALUE Register Definitions */ +#define Watchdog_VALUE_Pos 0 /* Watchdog VALUE: VALUE Position */ +#define Watchdog_VALUE_Msk (0xFFFFFFFFUL) /* Watchdog VALUE: VALUE Mask */ + +/* WATCHDOG CTRL Register Definitions */ +/* Watchdog CTRL_RESEN: Enable Reset Output Position */ +#define Watchdog_CTRL_RESEN_Pos 1 + /* Watchdog CTRL_RESEN: Enable Reset Output Mask */ +#define Watchdog_CTRL_RESEN_Msk (0x1UL << Watchdog_CTRL_RESEN_Pos) + +#define Watchdog_CTRL_INTEN_Pos 0 /* Watchdog CTRL_INTEN: Int Enable Position */ +#define Watchdog_CTRL_INTEN_Msk (0x1UL) /* Watchdog CTRL_INTEN: Int Enable Mask */ + +/* WATCHDOG INTCLR Register Definitions */ +#define Watchdog_INTCLR_Pos 0 /* Watchdog INTCLR: Int Clear Position */ +#define Watchdog_INTCLR_Msk (0x1UL) /* Watchdog INTCLR: Int Clear Mask */ + +/* WATCHDOG RAWINTSTAT Register Definitions */ +#define Watchdog_RAWINTSTAT_Pos 0 /* Watchdog RAWINTSTAT: Raw Int Status Position */ +#define Watchdog_RAWINTSTAT_Msk (0x1UL) /* Watchdog RAWINTSTAT: Raw Int Status Mask */ + +/* WATCHDOG MASKINTSTAT Register Definitions */ +#define Watchdog_MASKINTSTAT_Pos 0 /* Watchdog MASKINTSTAT: Mask Int Status Position */ +#define Watchdog_MASKINTSTAT_Msk (0x1UL) /* Watchdog MASKINTSTAT: Mask Int Status Mask */ + +/* WATCHDOG LOCK Register Definitions */ +#define Watchdog_LOCK_Pos 0 /* Watchdog LOCK: LOCK Position */ +#define Watchdog_LOCK_Msk (0x1UL) /* Watchdog LOCK: LOCK Mask */ + +/* WATCHDOG INTEGTESTEN Register Definitions */ +#define Watchdog_INTEGTESTEN_Pos 0 /* Watchdog INTEGTESTEN: Integration Test Enable Pos*/ +#define Watchdog_INTEGTESTEN_Msk (0x1UL)/*Watchdog INTEGTESTEN: Integration Test Enable Mask*/ + +/* WATCHDOG INTEGTESTOUTSET Register Definitions */ +/* Watchdog INTEGTESTOUTSET: Integration Test Output Set Position */ +#define Watchdog_INTEGTESTOUTSET_Pos 1 +/* Watchdog INTEGTESTOUTSET: Integration Test Output Set Mask */ +#define Watchdog_INTEGTESTOUTSET_Msk (0x1UL) + +int pal_wd_cmsdk_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); +int pal_wd_cmsdk_enable(addr_t base_addr); +int pal_wd_cmsdk_disable(addr_t base_addr); +int pal_wd_cmsdk_is_enabled(addr_t base_addr); + +#endif /* PAL_WD_CMSDK_H */ diff --git a/psa-ff/platform/spe/pal_common.h b/psa-ff/platform/spe/pal_common.h new file mode 100644 index 00000000..43b2b4d7 --- /dev/null +++ b/psa-ff/platform/spe/pal_common.h @@ -0,0 +1,41 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _PAL_COMMON_H_ +#define _PAL_COMMON_H_ + +#include +#include +#include +/* typedef's */ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; +typedef uint8_t bool_t; +typedef uint32_t addr_t; +typedef uint32_t test_id_t; +typedef char char8_t; +typedef uint32_t cfg_id_t; + +#define addr_t uint32_t +#define bool_t uint8_t + +#endif /* _PAL_COMMON_H_ */ diff --git a/psa-ff/platform/spe/pal_driver_intf.c b/psa-ff/platform/spe/pal_driver_intf.c new file mode 100644 index 00000000..ca15e0bf --- /dev/null +++ b/psa-ff/platform/spe/pal_driver_intf.c @@ -0,0 +1,114 @@ + /** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#include "drivers/uart/pal_uart.c" +#include "drivers/nvmem/pal_nvmem.c" +#include "drivers/watchdog/pal_wd_cmsdk.c" + +/** + @brief - This function initializes the UART + @param - uart base addr + @return - void +**/ +void pal_uart_init(uint32_t uart_base_addr) +{ + pal_uart_cmsdk_init(uart_base_addr); +} + +/** + @brief - This function parses the input string and writes bytes into UART TX FIFO + @param - str : Input String + - data : Value for format specifier +**/ + +void pal_print(char *str, uint32_t data) +{ + pal_cmsdk_print(str,data); + +} + + +/** + @brief - Writes into given non-volatile address. + @param - base : Base address of nvmem + offset : Offset + buffer : Pointer to source address + size : Number of bytes + @return - 1/0 +**/ +int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size) +{ + return nvmem_write(base, offset, buffer, size); +} + +/** + @brief - Reads from given non-volatile address. + @param - base : Base address of nvmem + offset : Offset + buffer : Pointer to source address + size : Number of bytes + @return - 1/0 +**/ +int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size) +{ + return nvmem_read(base, offset, buffer, size); +} + + +/** + @brief - Initializes an hardware watchdog timer + @param - base_addr : Base address of the watchdog module + - time_us : Time in micro seconds + - timer_tick_us : Number of ticks per micro second + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us) +{ + return(pal_wd_cmsdk_init(base_addr,time_us, timer_tick_us)); + +} + +/** + @brief - Enables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_enable(addr_t base_addr) +{ + return(pal_wd_cmsdk_enable(base_addr)); +} + +/** + @brief - Disables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_disable(addr_t base_addr) +{ + return (pal_wd_cmsdk_disable(base_addr)); +} + +/** + @brief - Checks whether hardware watchdog timer is enabled + @param - base_addr : Base address of the watchdog module + @return - Enabled : 1, Disabled : 0 +**/ +int pal_wd_timer_is_enabled(addr_t base_addr) +{ + return (pal_wd_cmsdk_is_enabled(base_addr)); +} + diff --git a/psa-ff/platform/targets/fvp_mps2_cm4_mbedos/target.cfg b/psa-ff/platform/targets/fvp_mps2_cm4_mbedos/target.cfg new file mode 100644 index 00000000..8feb2a36 --- /dev/null +++ b/psa-ff/platform/targets/fvp_mps2_cm4_mbedos/target.cfg @@ -0,0 +1,54 @@ +///** @file +// * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +// * SPDX-License-Identifier : Apache-2.0 +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +//**/ + +// UART device info +uart.num=1; +uart.0.base = 0x40004000; +uart.0.size = 0xFFF; +uart.0.intr_id = 0xFF; +uart.0.permission = TYPE_READ_WRITE; + +// Watchdog device info +watchdog.num = 1; +watchdog.0.base = 0x40008000; +watchdog.0.size = 0xFFF; +watchdog.0.intr_id = 0xFF; +watchdog.0.permission = TYPE_READ_WRITE; +watchdog.0.num_of_tick_per_micro_sec = 0x3; //(sys_feq/1000000) +watchdog.0.timeout_in_micro_sec_low = 0xF4240; //1.0 sec : 1 * 1000 * 1000 +watchdog.0.timeout_in_micro_sec_medium = 0x1E8480; //2.0 sec : 2 * 1000 * 1000 +watchdog.0.timeout_in_micro_sec_high = 0x4C4B40; //5.0 sec : 5 * 1000 * 1000 + +// Range of 1KB Non-volatile memory to preserve data over reset. Ex, NVRAM and FLASH +nvmem.num =1; +nvmem.0.start = 0x2002F000; +nvmem.0.end = 0x2002FFFF; +nvmem.0.permission = TYPE_READ_WRITE; + +//Miscellaneous +dut.num = 1; +dut.0.implemented_psa_firmware_isolation_level = LEVEL1; + +// Start address of combine_test_binary in memory. Memory can be main memory or secondary memory. +// Size of combine_test_binary = Summation of size of each test ELF file +dut.0.ns_start_addr_of_combine_test_binary = 0x2003F000; + +// Is combine_test_binary available in RAM? +dut.0.combine_test_binary_in_ram = AVAILABLE; + +// Start address of 8KB NS memory for test ELF +dut.0.ns_test_addr = 0x2007F000; diff --git a/psa-ff/test_suites/crypto/test_c001/source.mk b/psa-ff/test_suites/crypto/test_c001/source.mk new file mode 100644 index 00000000..c24e740a --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c001/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c001.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c001/test_c001.c b/psa-ff/test_suites/crypto/test_c001/test_c001.c new file mode 100644 index 00000000..834f4422 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c001/test_c001.c @@ -0,0 +1,84 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c001.h" + +client_test_t test_c001_crypto_list[] = { + NULL, + psa_generate_random_without_init_test, + psa_crypto_init_test, + multiple_psa_crypto_init_test, + NULL, +}; + +int32_t psa_generate_random_without_init_test(security_t caller) +{ + uint8_t output[GENERATE_SIZE]; + + val->print(PRINT_TEST, "[Check1] Test calling crypto functions before psa_crypto_init \n", 0); + + /* Generate random bytes */ + if (val->crypto_function(VAL_CRYPTO_GENERATE_RANDOM, output, GENERATE_SIZE) == PSA_SUCCESS) + { + val->print(PRINT_ERROR, "The crypto function should have failed but succeeded\n", 0); + return VAL_STATUS_CRYPTO_FAILURE; + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_crypto_init_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val->print(PRINT_TEST, "[Check2] Test psa_crypto_init\n", status); + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "Crypto init failed\n", 0); + status = VAL_STATUS_INIT_FAILED; + } + + return status; +} + +int32_t multiple_psa_crypto_init_test(security_t caller) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + + val->print(PRINT_TEST, "[Check3] Test multiple psa_crypto_init \n", 0); + for (i = 0; i < 5; i++) + { + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + status = VAL_STATUS_INIT_FAILED; + return status; + } + } + + return status; +} diff --git a/psa-ff/test_suites/crypto/test_c001/test_c001.h b/psa-ff/test_suites/crypto/test_c001/test_c001.h new file mode 100644 index 00000000..028755fe --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c001/test_c001.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C001_CLIENT_TESTS_H_ +#define _TEST_C001_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +#define GENERATE_SIZE 32 + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c001_crypto_list[]; + +int32_t psa_crypto_init_test(security_t caller); +int32_t multiple_psa_crypto_init_test(security_t caller); +int32_t psa_generate_random_without_init_test(security_t caller); +#endif /* _TEST_C001_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c001/test_entry.c b/psa-ff/test_suites/crypto/test_c001/test_entry.c new file mode 100644 index 00000000..1b3b985a --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c001/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c001.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 1) +#define TEST_DESC "Testing psa_crypto_init API: Basic\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c001_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c002/source.mk b/psa-ff/test_suites/crypto/test_c002/source.mk new file mode 100644 index 00000000..d56ef668 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c002/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c002.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c002/test_c002.c b/psa-ff/test_suites/crypto/test_c002/test_c002.c new file mode 100644 index 00000000..df4de842 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c002/test_c002.c @@ -0,0 +1,225 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c002.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c002_crypto_list[] = { + NULL, + psa_import_key_test, + psa_import_key_negative_test, + NULL, +}; + +int g_test_count; + +int32_t psa_import_key_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint32_t length, i, j; + uint8_t data[BUFFER_SIZE]; + uint8_t *key_data; + psa_key_policy_t policy; + psa_key_type_t key_type; + size_t bits; + int num_checks = sizeof(check1)/sizeof(check1[0]); + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_slot, + &key_type, &bits); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key information failed\n", 0); + return status; + } + + if (key_type != check1[i].key_type) + { + val->print(PRINT_ERROR, "\tPSA mismatch key type\n", 0); + return status; + } + + if (bits != check1[i].expected_bit_length) + { + val->print(PRINT_ERROR, "\tStored key length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check1[i].key_slot, data, + BUFFER_SIZE, &length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA export key failed\n", 0); + return status; + } + + if (length != check1[i].expected_key_length) + { + val->print(PRINT_ERROR, "\tKey length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + if (val->crypto_key_type_is_raw(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (check1[i].key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type) || PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else + { + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_import_key_negative_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + psa_key_policy_t policy; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the usage policy on a key slot */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check2[i].usage, + check2[i].key_alg); + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check2[i].key_slot, check2[i].key_type, + check2[i].key_data, check2[i].key_length); + if (check2[i].expected_status != status) + { + val->print(PRINT_ERROR, "\tPSA import key should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c002/test_c002.h b/psa-ff/test_suites/crypto/test_c002/test_c002.h new file mode 100644 index 00000000..1cc089ed --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c002/test_c002.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C002_CLIENT_TESTS_H_ +#define _TEST_C002_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c002_crypto_list[]; + +int32_t psa_import_key_test(security_t caller); +int32_t psa_import_key_negative_test(security_t caller); +#endif /* _TEST_C002_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c002/test_data.h b/psa-ff/test_suites/crypto/test_c002/test_data.h new file mode 100644 index 00000000..54924631 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c002/test_data.h @@ -0,0 +1,285 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_import_key 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_import_key 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_import_key 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_import_key 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_import_key with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_import_key with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_import_key with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_import_key with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_import_key with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_import_key with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 97, PSA_SUCCESS +}, +}; + + +test_data check2[] = { +{"Test psa_import_key with key data greater than the algorithm size\n", 11, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_34B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_34B_KEY_SIZE), AES_34B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_import_key with incorrect key data size\n", 12, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90}, +AES_18B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_18B_KEY_SIZE), AES_18B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_import_key with incorrect key type\n", 13, PSA_KEY_TYPE_VENDOR_FLAG, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, +AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_import_key with already occupied key slot\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_OCCUPIED_SLOT +}, + +{"Test psa_import_key with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_import_key with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c002/test_entry.c b/psa-ff/test_suites/crypto/test_c002/test_entry.c new file mode 100644 index 00000000..e52be28f --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c002/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c002.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 2) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c002_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c003/source.mk b/psa-ff/test_suites/crypto/test_c003/source.mk new file mode 100644 index 00000000..350748f1 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c003/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c003.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c003/test_c003.c b/psa-ff/test_suites/crypto/test_c003/test_c003.c new file mode 100644 index 00000000..661391b9 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c003/test_c003.c @@ -0,0 +1,218 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c003.h" +#include "test_data.h" + +client_test_t test_c003_crypto_list[] = { + NULL, + psa_export_key_test, + psa_export_key_negative_test, + NULL, +}; + +int g_test_count; + +int32_t psa_export_key_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint32_t length, i, j; + uint8_t data[BUFFER_SIZE]; + uint8_t *key_data; + psa_key_policy_t policy; + psa_key_type_t key_type; + size_t bits; + int num_checks = sizeof(check1)/sizeof(check1[0]); + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_slot, + &key_type, &bits); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key information failed\n", 0); + return status; + } + + if (key_type != check1[i].key_type) + { + val->print(PRINT_ERROR, "\tPSA mismatch key type\n", 0); + return status; + } + + if (bits != check1[i].expected_bit_length) + { + val->print(PRINT_ERROR, "\tStored key length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check1[i].key_slot, data, + BUFFER_SIZE, &length); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA export key failed\n", 0); + return VAL_STATUS_INVALID; + } + + if (check1[i].expected_status != PSA_SUCCESS) + continue; + + if (length != check1[i].expected_key_length) + { + val->print(PRINT_ERROR, "\tKey length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Check if original key data matches with the exported data */ + if (val->crypto_key_type_is_raw(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (check1[i].key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type) || PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else + { + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_export_key_negative_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i, length; + uint8_t data[BUFFER_SIZE]; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check2[i].key_slot, data, + check2[i].key_length, &length); + if (check2[i].expected_status != status) + { + val->print(PRINT_ERROR, "\tPSA export key should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + } + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c003/test_c003.h b/psa-ff/test_suites/crypto/test_c003/test_c003.h new file mode 100644 index 00000000..0f81b555 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c003/test_c003.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C003_CLIENT_TESTS_H_ +#define _TEST_C003_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c003_crypto_list[]; + +int32_t psa_export_key_test(security_t caller); +int32_t psa_export_key_negative_test(security_t caller); +#endif /* _TEST_C003_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c003/test_data.h b/psa-ff/test_suites/crypto/test_c003/test_data.h new file mode 100644 index 00000000..a560c635 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c003/test_data.h @@ -0,0 +1,274 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +#define EMPTY_KEY_SLOT 31 + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_export_key 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_export_key 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_export_key 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_export_key 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_export_key with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_export_key with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_export_key with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_export_key with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_export_key with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_export_key with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 97, PSA_SUCCESS +}, + +{"Test psa_export_key with key policy verify\n", 11, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_VERIFY, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_NOT_PERMITTED +}, +}; + + +test_data check2[] = { +{"Test psa_export_key with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_key with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_key with less buffer size\n", 1, PSA_KEY_TYPE_AES, +{0}, +14, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_BUFFER_TOO_SMALL +}, + +{"Test psa_export_key with empty key slot\n", EMPTY_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_EMPTY_SLOT +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c003/test_entry.c b/psa-ff/test_suites/crypto/test_c003/test_entry.c new file mode 100644 index 00000000..cba7066a --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c003/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c003.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 3) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c003_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c004/source.mk b/psa-ff/test_suites/crypto/test_c004/source.mk new file mode 100644 index 00000000..559bf61c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c004/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c004.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c004/test_c004.c b/psa-ff/test_suites/crypto/test_c004/test_c004.c new file mode 100644 index 00000000..66c6839c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c004/test_c004.c @@ -0,0 +1,225 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c004.h" +#include "test_data.h" + +client_test_t test_c004_crypto_list[] = { + NULL, + test_psa_export_public_key, + test_psa_export_public_key_slot, + NULL, +}; + +int g_test_count; + +int32_t test_psa_export_public_key(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint32_t length, i, j; + uint8_t data[BUFFER_SIZE]; + uint8_t *key_data; + psa_key_policy_t policy; + psa_key_type_t key_type; + size_t bits; + int num_checks = sizeof(check1)/sizeof(check1[0]); + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_slot, + &key_type, &bits); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key information failed\n", 0); + return status; + } + + if (key_type != check1[i].key_type) + { + val->print(PRINT_ERROR, "\tPSA mismatch key type\n", 0); + return status; + } + + if (bits != check1[i].expected_bit_length) + { + val->print(PRINT_ERROR, "\tStored key length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_PUBLIC_KEY, check1[i].key_slot, data, + BUFFER_SIZE, &length); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA export public key failed\n", 0); + return VAL_STATUS_INVALID; + } + + if (check1[i].expected_status != PSA_SUCCESS) + continue; + + if (length != check1[i].expected_key_length) + { + val->print(PRINT_ERROR, "\tKey length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Check if original key data matches with the exported data */ + if (val->crypto_key_type_is_raw(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (check1[i].key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type) || PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + key_data = expected_rsa_256_pubprv; + else if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = expected_ec_pubprv; + + for (j = 0; j < length; j++) + { + if (key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else + { + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; + +} + +int32_t test_psa_export_public_key_slot(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i, length; + uint8_t data[BUFFER_SIZE]; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_PUBLIC_KEY, check2[i].key_slot, data, + check2[i].key_length, &length); + if (check2[i].expected_status != status) + { + val->print(PRINT_ERROR, "\tPSA export publickey should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + } + return VAL_STATUS_SUCCESS; +} + diff --git a/psa-ff/test_suites/crypto/test_c004/test_c004.h b/psa-ff/test_suites/crypto/test_c004/test_c004.h new file mode 100644 index 00000000..b355d33e --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c004/test_c004.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C004_CLIENT_TESTS_H_ +#define _TEST_C004_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c004_crypto_list[]; + +int32_t test_psa_export_public_key(security_t caller); +int32_t test_psa_export_public_key_slot(security_t caller); +#endif /* _TEST_C004_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c004/test_data.h b/psa-ff/test_suites/crypto/test_c004/test_data.h new file mode 100644 index 00000000..b4463505 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c004/test_data.h @@ -0,0 +1,309 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +#define EMPTY_KEY_SLOT 31 + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; +uint8_t expected_rsa_256_pubprv[] = { +0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, +0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, +0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, 0x95, 0x08, 0xE1, 0x57, 0x41, +0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, 0x01, 0x65, 0xC6, 0x45, 0xAE, +0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, 0x4E, 0xD6, 0xF6, 0x1C, 0x88, +0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, 0x5C, 0x9C, 0x51, 0x75, 0xF7, +0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, +0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, 0xBA, 0xE0, 0x21, 0xE5, 0x72, +0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, 0xAC, 0x1B, 0x53, 0xB9, 0x5F, +0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, 0x63, 0x51, 0x8B, 0x0B, 0x64, +0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, 0xB3, 0xAE, 0x00, 0xA0, 0x63, +0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, 0xA6, 0x8C, 0x18, 0xA9, 0x02, +0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, 0xE3, 0xC6, 0xCC, 0x40, 0xB4, +0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, 0xBB, 0x17, 0xA6, 0xF3, 0xE8, +0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, 0xFD, 0x66, 0x51, 0x0C, 0xBD, +0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, 0x73, 0xD1, 0x09, 0x03, 0x89, +0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, 0xA1, 0x96, 0x4A, 0xBC, 0xE1, +0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, 0x78, 0x0F, 0x44, 0x37, 0x30, +0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, 0xBA, 0x13, 0xD2, 0x97, 0x73, +0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, 0x60, 0xA4, 0xB4, 0xB0, 0x69, +0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, 0x20, 0xB3, 0x58, 0x22, 0xA7, +0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, 0xD2, 0x96, 0xDF, 0xD9, 0xD0, +0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t expected_ec_pubprv[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, + 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, 0xF3, 0x9D, 0x53, 0x93, 0xE6, + 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, 0xB7, 0x76, 0xC6, 0x74, + 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, 0x68, 0x54, 0xB5, + 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_export_public_key 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_export_public_key with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_export_public_key with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_export_public_key with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_export_public_key with invalid key usage policy\n", 11, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_VERIFY, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +}; + + +test_data check2[] = { +{"Test psa_export_public_key with invalid keyslot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_RSA_PUBLIC_KEY, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_RSA_PUBLIC_KEY, +{0}, +294, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, +2048, 294, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_export_public_key with less buffer size\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, +{0}, +200, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, +2048, 294, PSA_ERROR_BUFFER_TOO_SMALL +}, + +{"Test psa_export_public_key with empty key slot\n", EMPTY_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_EMPTY_SLOT +}, + +}; diff --git a/psa-ff/test_suites/crypto/test_c004/test_entry.c b/psa-ff/test_suites/crypto/test_c004/test_entry.c new file mode 100644 index 00000000..562aa430 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c004/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c004.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 4) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c004_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c005/source.mk b/psa-ff/test_suites/crypto/test_c005/source.mk new file mode 100644 index 00000000..0aa393b8 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c005/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c005.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c005/test_c005.c b/psa-ff/test_suites/crypto/test_c005/test_c005.c new file mode 100644 index 00000000..83f01af2 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c005/test_c005.c @@ -0,0 +1,199 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c005.h" +#include "test_data.h" + +client_test_t test_c005_crypto_list[] = { + NULL, + psa_destroy_key_test, + psa_destroy_invalid_key_test, + NULL, +}; + +int g_test_count; + +int32_t psa_destroy_key_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint32_t i; + uint8_t *key_data; + psa_key_policy_t policy; + psa_key_type_t key_type; + size_t bits; + int num_checks = sizeof(check1)/sizeof(check1[0]); + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_slot, + &key_type, &bits); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key information failed\n", 0); + return status; + } + + if (key_type != check1[i].key_type) + { + val->print(PRINT_ERROR, "\tPSA mismatch key type\n", 0); + return status; + } + + if (bits != check1[i].expected_bit_length) + { + val->print(PRINT_ERROR, "\tStored key length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Destroy a key and restore the slot to its default state */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_slot); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed\n", 0); + return status; + } + + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_slot, + &key_type, &bits); + if (status == PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key info succeeded but should have failed\n", 0); + return VAL_STATUS_ERROR; + } + + /* Check that if the key metadata are destroyed */ + if (key_type == check1[i].key_type) + { + val->print(PRINT_ERROR, "\tPSA key meta data not destroyed\n", 0); + return VAL_STATUS_ERROR; + } + + if (check1[i].expected_bit_length == bits) + { + val->print(PRINT_ERROR, "\tPSA key meta data not destroyed\n", 0); + return status; + } + + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_destroy_invalid_key_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Destroy a key and restore the slot to its default state */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check2[i].key_slot); + if (check2[i].expected_status != status) + { + val->print(PRINT_ERROR, "\tPSA destroy key should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + } + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c005/test_c005.h b/psa-ff/test_suites/crypto/test_c005/test_c005.h new file mode 100644 index 00000000..9a18031c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c005/test_c005.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C005_CLIENT_TESTS_H_ +#define _TEST_C005_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c005_crypto_list[]; + +int32_t psa_destroy_key_test(security_t caller); +int32_t psa_destroy_invalid_key_test(security_t caller); +#endif /* _TEST_C005_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c005/test_data.h b/psa-ff/test_suites/crypto/test_c005/test_data.h new file mode 100644 index 00000000..37f7e1f9 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c005/test_data.h @@ -0,0 +1,261 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +#define EMPTY_KEY_SLOT 31 + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_destroy_key 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_destroy_key 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_destroy_key 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_destroy_key 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_destroy_key with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_destroy_key with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_destroy_key with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_destroy_key with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_destroy_key with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_destroy_key with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 97, PSA_SUCCESS +}, +}; + + +test_data check2[] = { +{"Test psa_destroy_key with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_destroy_key with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_destroy_key with empty key slot\n", EMPTY_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c005/test_entry.c b/psa-ff/test_suites/crypto/test_c005/test_entry.c new file mode 100644 index 00000000..76b6ec91 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c005/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c005.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 5) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c005_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c006/source.mk b/psa-ff/test_suites/crypto/test_c006/source.mk new file mode 100644 index 00000000..e2e1be7a --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c006/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c006.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c006/test_c006.c b/psa-ff/test_suites/crypto/test_c006/test_c006.c new file mode 100644 index 00000000..bc558a15 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c006/test_c006.c @@ -0,0 +1,169 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c006.h" +#include "test_data.h" + +client_test_t test_c006_crypto_list[] = { + NULL, + psa_get_key_information_test, + psa_get_key_information_invalid_test, + NULL, +}; + +int g_test_count; + +int32_t psa_get_key_information_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint32_t i; + uint8_t *key_data; + psa_key_policy_t policy; + psa_key_type_t key_type; + size_t bits; + int num_checks = sizeof(check1)/sizeof(check1[0]); + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_slot, + &key_type, &bits); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key information failed\n", 0); + return status; + } + + if (key_type != check1[i].key_type) + { + val->print(PRINT_ERROR, "\tPSA mismatch key type\n", 0); + return status; + } + + if (bits != check1[i].expected_bit_length) + { + val->print(PRINT_ERROR, "\tStored key length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_get_key_information_invalid_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check2[i].key_slot, + &check2[i].key_type, &check2[i].key_length); + if (status != check2[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA get key info should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + + } + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c006/test_c006.h b/psa-ff/test_suites/crypto/test_c006/test_c006.h new file mode 100644 index 00000000..ba8b2b7f --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c006/test_c006.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C006_CLIENT_TESTS_H_ +#define _TEST_C006_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c006_crypto_list[]; + +int32_t psa_get_key_information_test(security_t caller); +int32_t psa_get_key_information_invalid_test(security_t caller); +#endif /* _TEST_C006_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c006/test_data.h b/psa-ff/test_suites/crypto/test_c006/test_data.h new file mode 100644 index 00000000..ae1ee8fc --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c006/test_data.h @@ -0,0 +1,262 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +#define EMPTY_KEY_SLOT 31 + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_get_key_information 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_information 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_information 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_information 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_get_key_information with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_EXPORT, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_get_key_information with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_information with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_information with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_information with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_get_key_information with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 97, PSA_SUCCESS +}, +}; + + +test_data check2[] = { +{"Test psa_get_key_information with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_get_key_information with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_get_key_information with empty key slot\n", EMPTY_KEY_SLOT, PSA_KEY_TYPE_AES, +{0}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_EMPTY_SLOT +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c006/test_entry.c b/psa-ff/test_suites/crypto/test_c006/test_entry.c new file mode 100644 index 00000000..5e254cfb --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c006/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c006.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 6) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c006_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c007/source.mk b/psa-ff/test_suites/crypto/test_c007/source.mk new file mode 100644 index 00000000..0144796d --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c007/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c007.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c007/test_c007.c b/psa-ff/test_suites/crypto/test_c007/test_c007.c new file mode 100644 index 00000000..2e59b1c5 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c007/test_c007.c @@ -0,0 +1,174 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c007.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c007_crypto_list[] = { + NULL, + psa_set_key_policy_test, + psa_set_key_policy_negative_test, + NULL, +}; + +int g_test_count; + +int32_t psa_set_key_policy_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint8_t *key_data; + psa_key_policy_t policy, expected_policy; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get the usage policy for a key slot */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_POLICY, check1[i].key_slot, + &expected_policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key policy failed\n", 0); + return status; + } + + if ((expected_policy.usage != check1[i].usage) || + (expected_policy.alg != check1[i].key_alg)) + { + val->print(PRINT_ERROR, "\tPolicy data Mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_set_key_policy_negative_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + psa_key_policy_t policy; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Set the usage policy on a key slot */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check2[i].usage, + check2[i].key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check2[i].key_slot, &policy); + if (status != check2[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA set key policy should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c007/test_c007.h b/psa-ff/test_suites/crypto/test_c007/test_c007.h new file mode 100644 index 00000000..93f5e391 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c007/test_c007.h @@ -0,0 +1,35 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C007_CLIENT_TESTS_H_ +#define _TEST_C007_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c007_crypto_list[]; + +int32_t psa_set_key_policy_test(security_t caller); +int32_t psa_set_key_policy_negative_test(security_t caller); +#endif /* _TEST_C007_CLIENT_TESTS_H_ */ + diff --git a/psa-ff/test_suites/crypto/test_c007/test_data.h b/psa-ff/test_suites/crypto/test_c007/test_data.h new file mode 100644 index 00000000..c5401098 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c007/test_data.h @@ -0,0 +1,270 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_set_key_policy 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_policy 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_ENCRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_policy 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_DECRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_policy 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_SIGN, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_set_key_policy with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_VERIFY, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_set_key_policy with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_policy with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_policy with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_policy with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_set_key_policy with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 97, PSA_SUCCESS +}, +}; + + +test_data check2[] = { +{"Test psa_set_key_policy with already occupied key slot\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_OCCUPIED_SLOT +}, + +{"Test psa_set_key_policy with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_set_key_policy with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_set_key_policy with invalid usage\n", 13, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_INVALID, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c007/test_entry.c b/psa-ff/test_suites/crypto/test_c007/test_entry.c new file mode 100644 index 00000000..2a11f27d --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c007/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c007.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 7) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c007_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c008/source.mk b/psa-ff/test_suites/crypto/test_c008/source.mk new file mode 100644 index 00000000..3bc136e7 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c008/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c008.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c008/test_c008.c b/psa-ff/test_suites/crypto/test_c008/test_c008.c new file mode 100644 index 00000000..247a826c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c008/test_c008.c @@ -0,0 +1,186 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c008.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c008_crypto_list[] = { + NULL, + psa_get_key_policy_test, + psa_get_key_policy_negative_test, + NULL, +}; + +int g_test_count; + +int32_t psa_get_key_policy_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint8_t *key_data; + psa_key_policy_t policy, expected_policy; + psa_key_usage_t expected_usage; + psa_algorithm_t expected_alg; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, check1[i].key_type, + key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get the usage policy for a key slot */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_POLICY, check1[i].key_slot, + &expected_policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key policy failed\n", 0); + return status; + } + + if ((expected_policy.usage != check1[i].usage) || + (expected_policy.alg != check1[i].key_alg)) + { + val->print(PRINT_ERROR, "\tPolicy data Mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + + /* Retrieve the usage field of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_GET_USAGE, &policy, &expected_usage); + + /* Retrieve the algorithm field of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_GET_ALGORITHM, &policy, &expected_alg); + + if ((expected_usage != check1[i].usage) || + (expected_alg != check1[i].key_alg)) + { + val->print(PRINT_ERROR, "\tPolicy data Mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_get_key_policy_negative_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + psa_key_policy_t policy; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Get the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_POLICY, check2[i].key_slot, &policy); + if (status != check2[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA get key policy should have failed but succeeded\n", 0); + return VAL_STATUS_ERROR; + } + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c008/test_c008.h b/psa-ff/test_suites/crypto/test_c008/test_c008.h new file mode 100644 index 00000000..8a6c12ef --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c008/test_c008.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C008_CLIENT_TESTS_H_ +#define _TEST_C008_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c008_crypto_list[]; + +int32_t psa_get_key_policy_test(security_t caller); +int32_t psa_get_key_policy_negative_test(security_t caller); +#endif /* _TEST_C008_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c008/test_data.h b/psa-ff/test_suites/crypto/test_c008/test_data.h new file mode 100644 index 00000000..eeec0291 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c008/test_data.h @@ -0,0 +1,256 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_get_key_policy 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_policy 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_ENCRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_policy 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_DECRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_policy 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_SIGN, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 294, PSA_SUCCESS +}, + +{"Test psa_get_key_policy with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_VERIFY, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_get_key_policy with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_policy with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_policy with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_policy with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 75, PSA_SUCCESS +}, + +{"Test psa_get_key_policy with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + 192, 97, PSA_SUCCESS +}, +}; + + +test_data check2[] = { +{"Test psa_get_key_policy with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_get_key_policy with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, +AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c008/test_entry.c b/psa-ff/test_suites/crypto/test_c008/test_entry.c new file mode 100644 index 00000000..c03c24bd --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c008/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c008.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 8) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c008_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c009/source.mk b/psa-ff/test_suites/crypto/test_c009/source.mk new file mode 100644 index 00000000..e5f4c6f6 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c009/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c009.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c009/test_c009.c b/psa-ff/test_suites/crypto/test_c009/test_c009.c new file mode 100644 index 00000000..18297adb --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c009/test_c009.c @@ -0,0 +1,276 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c009.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c009_crypto_list[] = { + NULL, + psa_set_key_lifetime_test, + psa_set_key_lifetime_negative_test, + NULL, +}; + +int g_test_count; + +int32_t psa_set_key_lifetime_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint8_t *key_data, data[BUFFER_SIZE]; + psa_key_policy_t policy; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i, j, length; + boot_state_t boot_state; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + status = val->get_boot_flag(&boot_state); + if (status != VAL_STATUS_SUCCESS) + { + val->print(PRINT_ERROR, "\tGet boot flag failed\n", 0); + return status; + } + + if (boot_state == BOOT_NOT_EXPECTED) + { + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Change the lifetime of a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_LIFETIME, check1[i].key_slot, + check1[i].lifetime); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA set key lifetime failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, + check1[i].key_type, key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + } + + status = val->set_boot_flag(BOOT_EXPECTED_CRYPTO); + if (status != VAL_STATUS_SUCCESS) + { + val->print(PRINT_ERROR, "\tSet boot flag failed\n", 0); + return status; + } + + /*Waiting here for watchdog expiry */ + while (1); + } + else if (boot_state == BOOT_EXPECTED_CRYPTO) + { + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + if (check1[i].lifetime == PSA_KEY_LIFETIME_VOLATILE) + continue; + + /* Set the key data buffer to the input base on algorithm */ + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check1[i].key_slot, data, + BUFFER_SIZE, &length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA export key failed\n", 0); + return status; + } + + if (length != check1[i].expected_key_length) + { + val->print(PRINT_ERROR, "\tKey length mismatch\n", 0); + return VAL_STATUS_INVALID_SIZE; + } + + /* Check if original key data matches with the exported data */ + if (val->crypto_key_type_is_raw(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (check1[i].key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + for (j = 0; j < length; j++) + { + if (key_data[j] != data[j]) + { + val->print(PRINT_ERROR, "\tKey data mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + } + } + else + { + return VAL_STATUS_INVALID; + } + } + } + else + { + val->print(PRINT_ERROR, "\tInvalid boot state\n", 0); + return VAL_STATUS_INVALID; + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_set_key_lifetime_negative_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + psa_key_policy_t policy; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Change the lifetime of a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_LIFETIME, check2[i].key_slot, + check2[i].lifetime); + if (status != check2[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA set key lifetime failed\n", 0); + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c009/test_c009.h b/psa-ff/test_suites/crypto/test_c009/test_c009.h new file mode 100644 index 00000000..e0a4c258 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c009/test_c009.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C009_CLIENT_TESTS_H_ +#define _TEST_C009_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c009_crypto_list[]; + +int32_t psa_set_key_lifetime_test(security_t caller); +int32_t psa_set_key_lifetime_negative_test(security_t caller); +#endif /* _TEST_C009_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c009/test_data.h b/psa-ff/test_suites/crypto/test_c009/test_data.h new file mode 100644 index 00000000..6de98e43 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c009/test_data.h @@ -0,0 +1,284 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + psa_key_lifetime_t lifetime; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + + +test_data check1[] = { +{"Test psa_set_key_lifetime 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_VOLATILE, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_set_key_lifetime 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_ENCRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_PERSISTENT, BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_set_key_lifetime 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_DECRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_WRITE_ONCE, BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +{"Test psa_set_key_lifetime 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_SIGN, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_KEY_LIFETIME_VOLATILE, 2048, 294, PSA_SUCCESS +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_set_key_lifetime with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_VERIFY, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_KEY_LIFETIME_PERSISTENT, 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_set_key_lifetime with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_WRITE_ONCE, BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +{"Test psa_set_key_lifetime with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_VOLATILE, BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_set_key_lifetime with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_PERSISTENT, BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, +#endif + +{"Test psa_set_key_lifetime with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + PSA_KEY_LIFETIME_VOLATILE, 192, 75, PSA_SUCCESS +}, + +{"Test psa_set_key_lifetime with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + PSA_KEY_LIFETIME_VOLATILE, 192, 97, PSA_SUCCESS +}, +}; + + +test_data check2[] = { +{"Test psa_set_key_lifetime with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_VOLATILE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_set_key_lifetime with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_VOLATILE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_set_key_lifetime with invalid lifetime\n", 11, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_INVALID, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +#ifdef NO_SUPPORT +{"Test psa_set_key_lifetime with occupied slot\n", 11, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_VOLATILE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_OCCUPIED_SLOT +}, +#endif + +}; diff --git a/psa-ff/test_suites/crypto/test_c009/test_entry.c b/psa-ff/test_suites/crypto/test_c009/test_entry.c new file mode 100644 index 00000000..d7037fe5 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c009/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c009.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 9) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c009_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c010/source.mk b/psa-ff/test_suites/crypto/test_c010/source.mk new file mode 100644 index 00000000..da0bb911 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c010/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c010.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c010/test_c010.c b/psa-ff/test_suites/crypto/test_c010/test_c010.c new file mode 100644 index 00000000..083d5a9e --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c010/test_c010.c @@ -0,0 +1,182 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c010.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c010_crypto_list[] = { + NULL, + psa_get_key_lifetime_test, + psa_get_key_lifetime_negative_test, + NULL, +}; + +int g_test_count; + +int32_t psa_get_key_lifetime_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint8_t *key_data; + psa_key_policy_t policy; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + psa_key_lifetime_t lifetime; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + /* Set the key data buffer to the input base on algorithm */ + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type)) + { + if (check1[i].key_type == PSA_KEY_TYPE_RSA_KEYPAIR) + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keypair; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keypair; + else + return VAL_STATUS_INVALID; + } + else + { + if (check1[i].expected_bit_length == BYTES_TO_BITS(384)) + key_data = rsa_384_keydata; + else if (check1[i].expected_bit_length == BYTES_TO_BITS(256)) + key_data = rsa_256_keydata; + else + return VAL_STATUS_INVALID; + } + } + else if (PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + if (PSA_KEY_TYPE_IS_ECC_KEYPAIR(check1[i].key_type)) + key_data = ec_keypair; + else + key_data = ec_keydata; + } + else + key_data = check1[i].key_data; + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check1[i].usage, + check1[i].key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_slot, &policy); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key policy failed\n", 0); + return status; + } + + /* Change the lifetime of a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_LIFETIME, check1[i].key_slot, + check1[i].lifetime); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA set key lifetime failed\n", 0); + return status; + } + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_slot, + check1[i].key_type, key_data, check1[i].key_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA import key failed\n", 0); + return status; + } + + /* Get the lifetime of a key slot */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_LIFETIME, check1[i].key_slot, + &lifetime); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA get key lifetime failed\n", 0); + return status; + } + + if (lifetime != check1[i].lifetime) + { + val->print(PRINT_ERROR, "\tKey lifetime mismatch\n", 0); + return VAL_STATUS_DATA_MISMATCH; + } + + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_get_key_lifetime_negative_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check2)/sizeof(check2[0]); + uint32_t i; + psa_key_lifetime_t lifetime; + psa_key_policy_t policy; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check2[i].test_desc, 0); + + /* Get the lifetime of a key slot */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_LIFETIME, check2[i].key_slot, + &lifetime); + if (status != check2[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA get key lifetime failed\n", 0); + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c010/test_c010.h b/psa-ff/test_suites/crypto/test_c010/test_c010.h new file mode 100644 index 00000000..df1e4f73 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c010/test_c010.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C010_CLIENT_TESTS_H_ +#define _TEST_C010_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c010_crypto_list[]; + +int32_t psa_get_key_lifetime_test(security_t caller); +int32_t psa_get_key_lifetime_negative_test(security_t caller); +#endif /* _TEST_C010_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c010/test_data.h b/psa-ff/test_suites/crypto/test_c010/test_data.h new file mode 100644 index 00000000..42d3a001 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c010/test_data.h @@ -0,0 +1,279 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +#define EMPTY_KEY_SLOT 31 + +typedef struct { + char test_desc[75]; + psa_key_slot_t key_slot; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_algorithm_t key_alg; + psa_key_lifetime_t lifetime; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +uint8_t rsa_384_keypair[1]; +uint8_t rsa_384_keydata[1]; + +uint8_t rsa_256_keypair[] = { + 0x30, 0x82, 0x04, 0xA5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, + 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2, 0x71, 0x6D, 0xB7, 0xD2, 0x45, 0x41, 0x27, + 0x01, 0x65, 0xC6, 0x45, 0xAE, 0xF2, 0xBC, 0x24, 0x30, 0xB8, 0x95, 0xCE, 0x2F, + 0x4E, 0xD6, 0xF6, 0x1C, 0x88, 0xBC, 0x7C, 0x9F, 0xFB, 0xA8, 0x67, 0x7F, 0xFE, + 0x5C, 0x9C, 0x51, 0x75, 0xF7, 0x8A, 0xCA, 0x07, 0xE7, 0x35, 0x2F, 0x8F, 0xE1, + 0xBD, 0x7B, 0xC0, 0x2F, 0x7C, 0xAB, 0x64, 0xA8, 0x17, 0xFC, 0xCA, 0x5D, 0x7B, + 0xBA, 0xE0, 0x21, 0xE5, 0x72, 0x2E, 0x6F, 0x2E, 0x86, 0xD8, 0x95, 0x73, 0xDA, + 0xAC, 0x1B, 0x53, 0xB9, 0x5F, 0x3F, 0xD7, 0x19, 0x0D, 0x25, 0x4F, 0xE1, 0x63, + 0x63, 0x51, 0x8B, 0x0B, 0x64, 0x3F, 0xAD, 0x43, 0xB8, 0xA5, 0x1C, 0x5C, 0x34, + 0xB3, 0xAE, 0x00, 0xA0, 0x63, 0xC5, 0xF6, 0x7F, 0x0B, 0x59, 0x68, 0x78, 0x73, + 0xA6, 0x8C, 0x18, 0xA9, 0x02, 0x6D, 0xAF, 0xC3, 0x19, 0x01, 0x2E, 0xB8, 0x10, + 0xE3, 0xC6, 0xCC, 0x40, 0xB4, 0x69, 0xA3, 0x46, 0x33, 0x69, 0x87, 0x6E, 0xC4, + 0xBB, 0x17, 0xA6, 0xF3, 0xE8, 0xDD, 0xAD, 0x73, 0xBC, 0x7B, 0x2F, 0x21, 0xB5, + 0xFD, 0x66, 0x51, 0x0C, 0xBD, 0x54, 0xB3, 0xE1, 0x6D, 0x5F, 0x1C, 0xBC, 0x23, + 0x73, 0xD1, 0x09, 0x03, 0x89, 0x14, 0xD2, 0x10, 0xB9, 0x64, 0xC3, 0x2A, 0xD0, + 0xA1, 0x96, 0x4A, 0xBC, 0xE1, 0xD4, 0x1A, 0x5B, 0xC7, 0xA0, 0xC0, 0xC1, 0x63, + 0x78, 0x0F, 0x44, 0x37, 0x30, 0x32, 0x96, 0x80, 0x32, 0x23, 0x95, 0xA1, 0x77, + 0xBA, 0x13, 0xD2, 0x97, 0x73, 0xE2, 0x5D, 0x25, 0xC9, 0x6A, 0x0D, 0xC3, 0x39, + 0x60, 0xA4, 0xB4, 0xB0, 0x69, 0x42, 0x42, 0x09, 0xE9, 0xD8, 0x08, 0xBC, 0x33, + 0x20, 0xB3, 0x58, 0x22, 0xA7, 0xAA, 0xEB, 0xC4, 0xE1, 0xE6, 0x61, 0x83, 0xC5, + 0xD2, 0x96, 0xDF, 0xD9, 0xD0, 0x4F, 0xAD, 0xD7, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9A, 0xD0, 0x34, 0x0F, 0x52, 0x62, 0x05, 0x50, + 0x01, 0xEF, 0x9F, 0xED, 0x64, 0x6E, 0xC2, 0xC4, 0xDA, 0x1A, 0xF2, 0x84, 0xD7, + 0x92, 0x10, 0x48, 0x92, 0xC4, 0xE9, 0x6A, 0xEB, 0x8B, 0x75, 0x6C, 0xC6, 0x79, + 0x38, 0xF2, 0xC9, 0x72, 0x4A, 0x86, 0x64, 0x54, 0x95, 0x77, 0xCB, 0xC3, 0x9A, + 0x9D, 0xB7, 0xD4, 0x1D, 0xA4, 0x00, 0xC8, 0x9E, 0x4E, 0xE4, 0xDD, 0xC7, 0xBA, + 0x67, 0x16, 0xC1, 0x74, 0xBC, 0xA9, 0xD6, 0x94, 0x8F, 0x2B, 0x30, 0x1A, 0xFB, + 0xED, 0xDF, 0x21, 0x05, 0x23, 0xD9, 0x4A, 0x39, 0xBD, 0x98, 0x6B, 0x65, 0x9A, + 0xB8, 0xDC, 0xC4, 0x7D, 0xEE, 0xA6, 0x43, 0x15, 0x2E, 0x3D, 0xBE, 0x1D, 0x22, + 0x60, 0x2A, 0x73, 0x30, 0xD5, 0x3E, 0xD8, 0xA2, 0xAC, 0x86, 0x43, 0x2E, 0xC4, + 0xF5, 0x64, 0x5E, 0x3F, 0x89, 0x75, 0x0F, 0x11, 0xD8, 0x51, 0x25, 0x4E, 0x9F, + 0xD8, 0xAA, 0xA3, 0xCE, 0x60, 0xB3, 0xE2, 0x8A, 0xD9, 0x7E, 0x1B, 0xF0, 0x64, + 0xCA, 0x9A, 0x5B, 0x05, 0x0B, 0x5B, 0xAA, 0xCB, 0xE5, 0xE3, 0x3F, 0x6E, 0x32, + 0x22, 0x05, 0xF3, 0xD0, 0xFA, 0xEF, 0x74, 0x52, 0x81, 0xE2, 0x5F, 0x74, 0xD3, + 0xBD, 0xFF, 0x31, 0x83, 0x45, 0x75, 0xFA, 0x63, 0x7A, 0x97, 0x2E, 0xD6, 0xB6, + 0x19, 0xC6, 0x92, 0x26, 0xE4, 0x28, 0x06, 0x50, 0x50, 0x0E, 0x78, 0x2E, 0xA9, + 0x78, 0x0D, 0x14, 0x97, 0xB4, 0x12, 0xD8, 0x31, 0x40, 0xAB, 0xA1, 0x01, 0x41, + 0xC2, 0x30, 0xF8, 0x07, 0x5F, 0x16, 0xE4, 0x61, 0x77, 0xD2, 0x60, 0xF2, 0x9F, + 0x8D, 0xE8, 0xF4, 0xBA, 0xEB, 0x63, 0xDE, 0x2A, 0x97, 0x81, 0xEF, 0x4C, 0x6C, + 0xE6, 0x55, 0x34, 0x51, 0x2B, 0x28, 0x34, 0xF4, 0x53, 0x1C, 0xC4, 0x58, 0x0A, + 0x3F, 0xBB, 0xAF, 0xB5, 0xF7, 0x4A, 0x85, 0x43, 0x2D, 0x3C, 0xF1, 0x58, 0x58, + 0x81, 0x02, 0x81, 0x81, 0x00, 0xF2, 0x2C, 0x54, 0x76, 0x39, 0x23, 0x63, 0xC9, + 0x10, 0x32, 0xB7, 0x93, 0xAD, 0xAF, 0xBE, 0x19, 0x75, 0x96, 0x81, 0x64, 0xE6, + 0xB5, 0xB8, 0x89, 0x42, 0x41, 0xD1, 0x6D, 0xD0, 0x1C, 0x1B, 0xF8, 0x1B, 0xAC, + 0x69, 0xCB, 0x36, 0x3C, 0x64, 0x7D, 0xDC, 0xF4, 0x19, 0xB8, 0xC3, 0x60, 0xB1, + 0x57, 0x48, 0x5F, 0x52, 0x4F, 0x59, 0x3A, 0x55, 0x7F, 0x32, 0xC0, 0x19, 0x43, + 0x50, 0x3F, 0xAE, 0xCE, 0x6F, 0x17, 0xF3, 0x0E, 0x9F, 0x40, 0xCA, 0x4E, 0xAD, + 0x15, 0x3B, 0xC9, 0x79, 0xE9, 0xC0, 0x59, 0x38, 0x73, 0x70, 0x9C, 0x0A, 0x7C, + 0xC9, 0x3A, 0x48, 0x32, 0xA7, 0xD8, 0x49, 0x75, 0x0A, 0x85, 0xC2, 0xC2, 0xFD, + 0x15, 0x73, 0xDA, 0x99, 0x09, 0x2A, 0x69, 0x9A, 0x9F, 0x0A, 0x71, 0xBF, 0xB0, + 0x04, 0xA6, 0x8C, 0x7A, 0x5A, 0x6F, 0x48, 0x5A, 0x54, 0x3B, 0xC6, 0xB1, 0x53, + 0x17, 0xDF, 0xE7, 0x02, 0x81, 0x81, 0x00, 0xCB, 0x93, 0xDE, 0x77, 0x15, 0x5D, + 0xB7, 0x5C, 0x5C, 0x7C, 0xD8, 0x90, 0xA9, 0x98, 0x2D, 0xD6, 0x69, 0x0E, 0x63, + 0xB3, 0xA3, 0xDC, 0xA6, 0xCC, 0x8B, 0x6A, 0xA4, 0xA2, 0x12, 0x8C, 0x8E, 0x7B, + 0x48, 0x2C, 0xB2, 0x4B, 0x37, 0xDC, 0x06, 0x18, 0x7D, 0xEA, 0xFE, 0x76, 0xA1, + 0xD4, 0xA1, 0xE9, 0x3F, 0x0D, 0xCD, 0x1B, 0x5F, 0xAF, 0x5F, 0x9E, 0x96, 0x5B, + 0x5B, 0x0F, 0xA1, 0x7C, 0xAF, 0xB3, 0x9B, 0x90, 0xDB, 0x57, 0x73, 0x3A, 0xED, + 0xB0, 0x23, 0x44, 0xAE, 0x41, 0x4F, 0x1F, 0x07, 0x42, 0x13, 0x23, 0x4C, 0xCB, + 0xFA, 0xF4, 0x14, 0xA4, 0xD5, 0xF7, 0x9E, 0x36, 0x7C, 0x5B, 0x9F, 0xA8, 0x3C, + 0xC1, 0x85, 0x5F, 0x74, 0xD2, 0x39, 0x2D, 0xFF, 0xD0, 0x84, 0xDF, 0xFB, 0xB3, + 0x20, 0x7A, 0x2E, 0x9B, 0x17, 0xAE, 0xE6, 0xBA, 0x0B, 0xAE, 0x5F, 0x53, 0xA4, + 0x52, 0xED, 0x1B, 0xC4, 0x91, 0x02, 0x81, 0x81, 0x00, 0xEC, 0x98, 0xDA, 0xBB, + 0xD5, 0xFE, 0xF9, 0x52, 0x4A, 0x7D, 0x02, 0x55, 0x49, 0x6F, 0x55, 0x6E, 0x52, + 0x2F, 0x84, 0xA3, 0x2B, 0xB3, 0x86, 0x62, 0xB3, 0x54, 0xD2, 0x63, 0x52, 0xDA, + 0xE3, 0x88, 0x76, 0xA0, 0xEF, 0x8B, 0x15, 0xA5, 0xD3, 0x18, 0x14, 0x72, 0x77, + 0x5E, 0xC7, 0xA3, 0x04, 0x1F, 0x9E, 0x19, 0x62, 0xB5, 0x1B, 0x1B, 0x9E, 0xC3, + 0xF2, 0xB5, 0x32, 0xF9, 0x4C, 0xC1, 0xAA, 0xEB, 0x0C, 0x26, 0x7D, 0xD4, 0x5F, + 0x4A, 0x51, 0x5C, 0xA4, 0x45, 0x06, 0x70, 0x44, 0xA7, 0x56, 0xC0, 0xD4, 0x22, + 0x14, 0x76, 0x9E, 0xD8, 0x63, 0x50, 0x89, 0x90, 0xD3, 0xE2, 0xBF, 0x81, 0x95, + 0x92, 0x31, 0x41, 0x87, 0x39, 0x1A, 0x43, 0x0B, 0x18, 0xA5, 0x53, 0x1F, 0x39, + 0x1A, 0x5F, 0x1F, 0x43, 0xBC, 0x87, 0x6A, 0xDF, 0x6E, 0xD3, 0x22, 0x00, 0xFE, + 0x22, 0x98, 0x70, 0x4E, 0x1A, 0x19, 0x29, 0x02, 0x81, 0x81, 0x00, 0x8A, 0x41, + 0x56, 0x28, 0x51, 0x9E, 0x5F, 0xD4, 0x9E, 0x0B, 0x3B, 0x98, 0xA3, 0x54, 0xF2, + 0x6C, 0x56, 0xD4, 0xAA, 0xE9, 0x69, 0x33, 0x85, 0x24, 0x0C, 0xDA, 0xD4, 0x0C, + 0x2D, 0xC4, 0xBF, 0x4F, 0x02, 0x69, 0x38, 0x7C, 0xD4, 0xE6, 0xDC, 0x4C, 0xED, + 0xD7, 0x16, 0x11, 0xC3, 0x3E, 0x00, 0xE7, 0xC3, 0x26, 0xC0, 0x51, 0x02, 0xDE, + 0xBB, 0x75, 0x9C, 0x6F, 0x56, 0x9C, 0x7A, 0xF3, 0x8E, 0xEF, 0xCF, 0x8A, 0xC5, + 0x2B, 0xD2, 0xDA, 0x06, 0x6A, 0x44, 0xC9, 0x73, 0xFE, 0x6E, 0x99, 0x87, 0xF8, + 0x5B, 0xBE, 0xF1, 0x7C, 0xE6, 0x65, 0xB5, 0x4F, 0x6C, 0xF0, 0xC9, 0xC5, 0xFF, + 0x16, 0xCA, 0x8B, 0x1B, 0x17, 0xE2, 0x58, 0x3D, 0xA2, 0x37, 0xAB, 0x01, 0xBC, + 0xBF, 0x40, 0xCE, 0x53, 0x8C, 0x8E, 0xED, 0xEF, 0xEE, 0x59, 0x9D, 0xE0, 0x63, + 0xE6, 0x7C, 0x5E, 0xF5, 0x8E, 0x4B, 0xF1, 0x3B, 0xC1, 0x02, 0x81, 0x80, 0x4D, + 0x45, 0xF9, 0x40, 0x8C, 0xC5, 0x5B, 0xF4, 0x2A, 0x1A, 0x8A, 0xB4, 0xF2, 0x1C, + 0xAC, 0x6B, 0xE9, 0x0C, 0x56, 0x36, 0xB7, 0x4E, 0x72, 0x96, 0xD5, 0xE5, 0x8A, + 0xD2, 0xE2, 0xFF, 0xF1, 0xF1, 0x18, 0x13, 0x3D, 0x86, 0x09, 0xB8, 0xD8, 0x76, + 0xA7, 0xC9, 0x1C, 0x71, 0x52, 0x94, 0x30, 0x43, 0xE0, 0xF1, 0x78, 0x74, 0xFD, + 0x61, 0x1B, 0x4C, 0x09, 0xCC, 0xE6, 0x68, 0x2A, 0x71, 0xAD, 0x1C, 0xDF, 0x43, + 0xBC, 0x56, 0xDB, 0xA5, 0xA4, 0xBE, 0x35, 0x70, 0xA4, 0x5E, 0xCF, 0x4F, 0xFC, + 0x00, 0x55, 0x99, 0x3A, 0x3D, 0x23, 0xCF, 0x67, 0x5A, 0xF5, 0x22, 0xF8, 0xB5, + 0x29, 0xD0, 0x44, 0x11, 0xEB, 0x35, 0x2E, 0x46, 0xBE, 0xFD, 0x8E, 0x18, 0xB2, + 0x5F, 0xA8, 0xBF, 0x19, 0x32, 0xA1, 0xF5, 0xDC, 0x03, 0xE6, 0x7C, 0x9A, 0x1F, + 0x0C, 0x7C, 0xA9, 0xB0, 0x0E, 0x21, 0x37, 0x3B, 0xF1, 0xB0}; + +uint8_t rsa_256_keydata[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xDB, 0x1C, 0x7F, 0x2E, 0x0B, 0xCD, 0xBF, 0xCE, 0xD1, + 0x75, 0x10, 0xA0, 0xA2, 0xB8, 0xCE, 0x7D, 0xAA, 0xE2, 0x05, 0xE0, 0x7A, 0xD8, 0x44, + 0x63, 0x8F, 0xB5, 0xBD, 0xC0, 0xB0, 0x19, 0xB9, 0x37, 0xB8, 0x19, 0x4A, 0x0E, 0xF1, + 0x5D, 0x74, 0x80, 0x67, 0x46, 0x87, 0x06, 0xDE, 0x5B, 0x7F, 0x06, 0x03, 0xBD, 0xC1, + 0x8D, 0x5E, 0x07, 0x15, 0xD4, 0x5B, 0xF4, 0xDC, 0xE5, 0xCF, 0x3D, 0xF9, 0xC1, 0x11, + 0x2C, 0xAE, 0x6A, 0xB9, 0x8A, 0xBD, 0x1D, 0x67, 0x66, 0x17, 0xEA, 0x4E, 0xBD, 0xDB, + 0x15, 0x9A, 0x82, 0x87, 0xE4, 0xF0, 0x78, 0xC3, 0xA3, 0x85, 0x87, 0xB0, 0xFD, 0x9F, + 0xA9, 0x99, 0x5F, 0xE3, 0x33, 0xEC, 0xCC, 0xEA, 0x0B, 0xB5, 0x61, 0x5E, 0xF1, 0x49, + 0x7E, 0x3F, 0xA3, 0x2D, 0xEA, 0x01, 0x0C, 0xCC, 0x42, 0x9A, 0x76, 0x9B, 0xC4, 0xD0, + 0x37, 0xD3, 0xB1, 0x17, 0x01, 0x61, 0x01, 0x16, 0x59, 0x7E, 0x1C, 0x17, 0xC3, 0x53, + 0xFD, 0xD1, 0x72, 0xCB, 0x4C, 0x60, 0x15, 0xDA, 0x7D, 0xE2, 0xEA, 0xAD, 0x50, 0xEF, + 0x8E, 0xE2, 0x8B, 0xD4, 0x6A, 0x77, 0x55, 0xD6, 0x70, 0xD9, 0x6B, 0xBB, 0xF1, 0xEE, + 0x39, 0x04, 0x38, 0xA3, 0xBD, 0xE2, 0xD1, 0xE0, 0x66, 0x6B, 0xE2, 0x9C, 0x47, 0x99, + 0xE9, 0x28, 0xE6, 0xB6, 0xFC, 0x2E, 0xCA, 0x67, 0x43, 0x84, 0xE8, 0xD5, 0x83, 0xD6, + 0x9D, 0x98, 0x6B, 0x01, 0x3E, 0x81, 0xDC, 0x3C, 0x7A, 0xCA, 0xF9, 0xF3, 0x9C, 0xF7, + 0xD6, 0x28, 0x1B, 0x27, 0x78, 0x7C, 0xC3, 0xD0, 0xD5, 0x63, 0xA7, 0x81, 0x34, 0x89, + 0xAD, 0x25, 0x6A, 0xBD, 0xF2, 0xEA, 0xED, 0xFA, 0x57, 0xFC, 0xE5, 0x34, 0xC6, 0xC1, + 0x0F, 0x71, 0x2D, 0xD2, 0x08, 0x10, 0x1B, 0xAD, 0x44, 0x41, 0xE0, 0xFE, 0x79, 0xA0, + 0x63, 0x93, 0x8A, 0xB1, 0x5D, 0xE9, 0xB0, 0xEE, 0x6F, 0x02, 0x03, 0x01, 0x00, 0x01}; + +uint8_t ec_keydata[] = { + 0x30, 0x49, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x03, 0x32, 0x00, 0x04, 0xBC, + 0x79, 0x7D, 0xB3, 0xAE, 0x7F, 0x08, 0xEC, 0x3D, 0x49, 0x6B, 0x4F, 0xB4, 0x11, 0xB3, + 0xF6, 0x20, 0xA5, 0x58, 0xA5, 0x01, 0xE0, 0x22, 0x2D, 0x08, 0xCF, 0xE0, 0xDC, 0x8A, + 0xEC, 0x8B, 0x1A, 0x7B, 0xF2, 0x4B, 0xE9, 0x29, 0x51, 0xCC, 0x5B, 0xA1, 0xBE, 0xBB, + 0x24, 0x74, 0x90, 0x9A, 0xE0}; + +uint8_t ec_keypair[] = { + 0x30, 0x5F, 0x02, 0x01, 0x01, 0x04, 0x18, 0x33, 0x8E, 0x86, 0xA8, 0x81, 0xE2, 0x38, + 0xF5, 0x49, 0xBD, 0x6F, 0x05, 0x53, 0x49, 0x4B, 0x73, 0xE3, 0xD6, 0x11, 0x30, 0xFD, + 0xC6, 0xC9, 0x6D, 0xA0, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x01, 0xA1, 0x34, 0x03, 0x32, 0x00, 0x04, 0x51, 0x75, 0xBC, 0xDF, 0x30, 0xA3, 0x70, + 0xF3, 0x9D, 0x53, 0x93, 0xE6, 0x12, 0x72, 0x88, 0xD8, 0x01, 0x67, 0xB5, 0xF4, 0xB4, + 0xB7, 0x76, 0xC6, 0x74, 0xF7, 0xC6, 0xF3, 0x54, 0xB7, 0xD2, 0x24, 0x06, 0x2C, 0x1F, + 0x68, 0x54, 0xB5, 0xA7, 0xAF, 0x0F, 0xE5, 0x78, 0xEA, 0xF2, 0x58, 0xF0, 0x27}; + +test_data check1[] = { +{"Test psa_get_key_lifetime 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_VOLATILE, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_get_key_lifetime 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, +{0x24, 0x13, 0x61, 0x47, 0x61, 0xB8, 0xC8, 0xF0, 0xDF, 0xAB, 0x5A, 0x0E, 0x87, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9}, + AES_24B_KEY_SIZE, PSA_KEY_USAGE_ENCRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_PERSISTENT, BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, + +{"Test psa_get_key_lifetime 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, +{0xEA, 0xD5, 0xE6, 0xC8, 0x51, 0xF9, 0xEC, 0xBB, 0x9B, 0x57, 0x7C, 0xED, 0xD2, + 0x4B, 0x82, 0x84, 0x9F, 0x9F, 0xE6, 0x73, 0x21, 0x3D, 0x1A, 0x05, 0xC9, 0xED, + 0xDF, 0x25, 0x17, 0x68, 0x86, 0xAE}, + AES_32B_KEY_SIZE, PSA_KEY_USAGE_DECRYPT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_WRITE_ONCE, BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +{"Test psa_get_key_lifetime 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 294, PSA_KEY_USAGE_SIGN, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_KEY_LIFETIME_VOLATILE, 2048, 294, PSA_SUCCESS +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_get_key_lifetime with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, PSA_KEY_USAGE_VERIFY, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_KEY_LIFETIME_PERSISTENT, 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_get_key_lifetime with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_WRITE_ONCE, BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +{"Test psa_get_key_lifetime with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + DES3_2KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_VOLATILE, BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_get_key_lifetime with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, +{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xF1, 0xE0, 0xD3, 0xC2, 0xB5, 0xA4, 0x97, 0x86, + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, + DES3_3KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, + PSA_KEY_LIFETIME_PERSISTENT, BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, +#endif + +{"Test psa_get_key_lifetime with EC Public key\n", 9, + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 75, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + PSA_KEY_LIFETIME_VOLATILE, 192, 75, PSA_SUCCESS +}, + +{"Test psa_get_key_lifetime with EC keypair\n", 10, + PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, + {0}, + 97, PSA_KEY_USAGE_EXPORT, PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION, + PSA_KEY_LIFETIME_VOLATILE, 192, 97, PSA_SUCCESS +}, + +}; + + +test_data check2[] = { +{"Test psa_get_key_lifetime with invalid key slot\n", INVALID_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_VOLATILE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_get_key_lifetime with zero key slot\n", ZERO_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_VOLATILE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, + +#ifdef NO_SUPPORT +/* PSA crypto doesn't support these test scenarios */ +{"Test psa_get_key_lifetime with empty key slot\n", EMPTY_KEY_SLOT, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_BLOCK_CIPHER_PAD_NONE, PSA_KEY_LIFETIME_VOLATILE, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, +#endif +}; diff --git a/psa-ff/test_suites/crypto/test_c010/test_entry.c b/psa-ff/test_suites/crypto/test_c010/test_entry.c new file mode 100644 index 00000000..f1644d15 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c010/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c010.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 10) +#define TEST_DESC "Testing crypto key management APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c010_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c011/source.mk b/psa-ff/test_suites/crypto/test_c011/source.mk new file mode 100644 index 00000000..10a5d4ba --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c011/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c011.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c011/test_c011.c b/psa-ff/test_suites/crypto/test_c011/test_c011.c new file mode 100644 index 00000000..de2f79b7 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c011/test_c011.c @@ -0,0 +1,68 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c011.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c011_crypto_list[] = { + NULL, + psa_hash_setup_test, + NULL, +}; + +int g_test_count; + +int32_t psa_hash_setup_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + psa_hash_operation_t operation; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed", 0); + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c011/test_c011.h b/psa-ff/test_suites/crypto/test_c011/test_c011.h new file mode 100644 index 00000000..e78b46d5 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c011/test_c011.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C011_CLIENT_TESTS_H_ +#define _TEST_C011_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c011_crypto_list[]; + +int32_t psa_hash_setup_test(security_t caller); +int32_t psa_get_key_lifetime_negative_test(security_t caller); +#endif /* _TEST_C011_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c011/test_data.h b/psa-ff/test_suites/crypto/test_c011/test_data.h new file mode 100644 index 00000000..e3eead4d --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c011/test_data.h @@ -0,0 +1,90 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[50]; + psa_algorithm_t alg; + psa_status_t expected_status; +} test_data; + +test_data check1[] = { +{"Test psa_hash_setup with MD2 algorithm\n", + PSA_ALG_MD2, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with MD4 algorithm\n", + PSA_ALG_MD4, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with MD5 algorithm\n", + PSA_ALG_MD5, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with RIPEMD160 algorithm\n", + PSA_ALG_RIPEMD160, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with SHA1 algorithm\n", + PSA_ALG_SHA_1, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with SHA224 algorithm\n", + PSA_ALG_SHA_224, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with SHA256 algorithm\n", + PSA_ALG_SHA_256, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with SHA384 algorithm\n", + PSA_ALG_SHA_384, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with SHA512 algorithm\n", + PSA_ALG_SHA_512, PSA_SUCCESS, +}, + +{"Test psa_hash_setup with SHA512_224 algorithm\n", + PSA_ALG_SHA_512_224, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_hash_setup with SHA512_256 algorithm\n", + PSA_ALG_SHA_512_256, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_hash_setup with SHA3_224 algorithm\n", + PSA_ALG_SHA3_224, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_hash_setup with SHA3_256 algorithm\n", + PSA_ALG_SHA3_256, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_hash_setup with SHA3_384 algorithm\n", + PSA_ALG_SHA3_384, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_hash_setup with SHA3_512 algorithm\n", + PSA_ALG_SHA3_512, PSA_ERROR_NOT_SUPPORTED, +}, + +{"Test psa_hash_setup with Invalid algorithm\n", + PSA_ALG_INVALID, PSA_ERROR_INVALID_ARGUMENT, +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c011/test_entry.c b/psa-ff/test_suites/crypto/test_c011/test_entry.c new file mode 100644 index 00000000..498cc648 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c011/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c011.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 11) +#define TEST_DESC "Testing crypto hash functions APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c011_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c012/source.mk b/psa-ff/test_suites/crypto/test_c012/source.mk new file mode 100644 index 00000000..5379fd9f --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c012/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c012.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c012/test_c012.c b/psa-ff/test_suites/crypto/test_c012/test_c012.c new file mode 100644 index 00000000..2b67f9aa --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c012/test_c012.c @@ -0,0 +1,164 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c012.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c012_crypto_list[] = { + NULL, + psa_hash_update_test, + psa_hash_update_invalid_handle, + psa_hash_update_with_completed_handle, + NULL, +}; + +int g_test_count; + +int32_t psa_hash_update_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + psa_hash_operation_t operation; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + check1[i].input, check1[i].input_length); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_hash_update_invalid_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_hash_operation_t operation; + uint8_t input[] = "Hello World"; + size_t input_length = sizeof(input)/sizeof(input[0]); + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "Test psa_hash_update without hash setup\n", 0); + + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + input, input_length); + if (status != PSA_ERROR_INVALID_ARGUMENT) + { + val->print(PRINT_ERROR, "\tPSA hash update should have failed but succeeded\n", 0); + return VAL_STATUS_INVALID; + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_hash_update_with_completed_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_hash_operation_t operation; + uint8_t input[] = {0xbd}; + size_t input_length = sizeof(input)/sizeof(input[0]); + psa_algorithm_t alg = PSA_ALG_SHA_256; + uint8_t hash[] = {0x68, 0x32, 0x57, 0x20, 0xAA, 0xBD, 0x7C, 0x82, 0xF3, 0x0F, + 0x55, 0x4B, 0x31, 0x3D, 0x05, 0x70, 0xC9, 0x5A, 0xCC, 0xBB, + 0x7D, 0xC4, 0xB5, 0xAA, 0xE1, 0x12, 0x04, 0xC0, 0x8F, 0xFE, + 0x73, 0x2B}; + size_t hash_length = sizeof(hash)/sizeof(hash[0]); + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "Test psa_hash_update with completed opertaion handle \n", 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + input, input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message and compare it with an expected value*/ + status = val->crypto_function(VAL_CRYPTO_HASH_VERIFY, &operation, hash, hash_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash verify failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + input, input_length); + if (status != PSA_ERROR_INVALID_ARGUMENT) + { + val->print(PRINT_ERROR, "\tPSA hash update should have failed but succeeded\n", 0); + return VAL_STATUS_INVALID; + } + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c012/test_c012.h b/psa-ff/test_suites/crypto/test_c012/test_c012.h new file mode 100644 index 00000000..c4aea187 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c012/test_c012.h @@ -0,0 +1,35 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C012_CLIENT_TESTS_H_ +#define _TEST_C012_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c012_crypto_list[]; + +int32_t psa_hash_update_test(security_t caller); +int32_t psa_hash_update_invalid_handle(security_t caller); +int32_t psa_hash_update_with_completed_handle(security_t caller); +#endif /* _TEST_C012_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c012/test_data.h b/psa-ff/test_suites/crypto/test_c012/test_data.h new file mode 100644 index 00000000..e7072b03 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c012/test_data.h @@ -0,0 +1,64 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[50]; + psa_algorithm_t alg; + char input[15]; + size_t input_length; + psa_status_t expected_status; +} test_data; + +test_data check1[] = { +{"Test psa_hash_update with MD2 algorithm\n", + PSA_ALG_MD2, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with MD4 algorithm\n", + PSA_ALG_MD4, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with MD5 algorithm\n", + PSA_ALG_MD5, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with RIPEMD160 algorithm\n", + PSA_ALG_RIPEMD160, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with SHA1 algorithm\n", + PSA_ALG_SHA_1, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with SHA224 algorithm\n", + PSA_ALG_SHA_224, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with SHA256 algorithm\n", + PSA_ALG_SHA_256, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with SHA384 algorithm\n", + PSA_ALG_SHA_384, "Hello World", 11, PSA_SUCCESS, +}, + +{"Test psa_hash_update with SHA512 algorithm\n", + PSA_ALG_SHA_512, "Hello World", 11, PSA_SUCCESS, +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c012/test_entry.c b/psa-ff/test_suites/crypto/test_c012/test_entry.c new file mode 100644 index 00000000..221d9daf --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c012/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c012.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 12) +#define TEST_DESC "Testing crypto hash functions APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c012_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c013/source.mk b/psa-ff/test_suites/crypto/test_c013/source.mk new file mode 100644 index 00000000..3039e72f --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c013/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c013.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c013/test_c013.c b/psa-ff/test_suites/crypto/test_c013/test_c013.c new file mode 100644 index 00000000..5ba2878c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c013/test_c013.c @@ -0,0 +1,153 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c013.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c013_crypto_list[] = { + NULL, + psa_hash_verify_test, + psa_hash_verify_inactive_operation_handle, + NULL, +}; + +int g_test_count; + +int32_t psa_hash_verify_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + psa_hash_operation_t operation; + char *hash; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + if (check1[i].alg == PSA_ALG_SHA_384) + hash = sha384_hash; + else if (check1[i].alg == PSA_ALG_SHA_512) + hash = sha512_hash; + else + hash = check1[i].hash; + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + &check1[i].input, check1[i].input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message and compare it with an expected value*/ + status = val->crypto_function(VAL_CRYPTO_HASH_VERIFY, &operation, hash, + check1[i].hash_length); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA hash verify failed\n", 0); + return VAL_STATUS_INVALID; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_hash_verify_inactive_operation_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_hash_operation_t operation; + char input = 0xbd; + size_t input_length = 1; + psa_algorithm_t alg = PSA_ALG_SHA_256; + size_t hash_length = PSA_HASH_SIZE(alg); + char hash[] = {0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, + 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, + 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, + 0x73, 0x2b}; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "test psa_hash_verify with inactive operation handle\n", 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + &input, input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message and compare it with an expected value*/ + status = val->crypto_function(VAL_CRYPTO_HASH_VERIFY, &operation, hash, hash_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash verify failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Retry the operation with completed operation handle */ + status = val->crypto_function(VAL_CRYPTO_HASH_VERIFY, &operation, hash, hash_length); + if (status != PSA_ERROR_INVALID_ARGUMENT) + { + val->print(PRINT_ERROR, "\tPSA hash verify should have failed but succeeded\n", 0); + return VAL_STATUS_INVALID; + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c013/test_c013.h b/psa-ff/test_suites/crypto/test_c013/test_c013.h new file mode 100644 index 00000000..60a16ee8 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c013/test_c013.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C013_CLIENT_TESTS_H_ +#define _TEST_C013_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c013_crypto_list[]; + +int32_t psa_hash_verify_test(security_t caller); +int32_t psa_hash_verify_inactive_operation_handle(security_t caller); +#endif /* _TEST_C013_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c013/test_data.h b/psa-ff/test_suites/crypto/test_c013/test_data.h new file mode 100644 index 00000000..a5d2df6b --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c013/test_data.h @@ -0,0 +1,109 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[50]; + psa_algorithm_t alg; + char input; + size_t input_length; + char hash[32]; + size_t hash_length; + psa_status_t expected_status; +} test_data; + +char sha384_hash[] = {0x43, 0x72, 0xe3, 0x8a, 0x92, 0xa2, 0x8b, 0x5d, 0x2c, 0x39, 0x1e, 0x62, +0x45, 0x2a, 0x86, 0xd5, 0x0e, 0x02, 0x67, 0x22, 0x8b, 0xe1, 0x76, 0xc7, 0x7d, 0x24, 0x02, 0xef, +0xfe, 0x9f, 0xa5, 0x0d, 0xe4, 0x07, 0xbb, 0xb8, 0x51, 0xb3, 0x7d, 0x59, 0x04, 0xab, 0xa2, 0xde, +0xde, 0x74, 0xda, 0x2a}; + +char sha512_hash[] = {0x29, 0x6e, 0x22, 0x67, 0xd7, 0x4c, 0x27, 0x8d, 0xaa, 0xaa, 0x94, 0x0d, +0x17, 0xb0, 0xcf, 0xb7, 0x4a, 0x50, 0x83, 0xf8, 0xe0, 0x69, 0x72, 0x6d, 0x8c, 0x84, 0x1c, 0xbe, +0x59, 0x6e, 0x04, 0x31, 0xcb, 0x77, 0x41, 0xa5, 0xb5, 0x0f, 0x71, 0x66, 0x6c, 0xfd, 0x54, 0xba, +0xcb, 0x7b, 0x00, 0xae, 0xa8, 0x91, 0x49, 0x9c, 0xf4, 0xef, 0x6a, 0x03, 0xc8, 0xa8, 0x3f, 0xe3, +0x7c, 0x3f, 0x7b, 0xaf}; + +test_data check1[] = { +{"Test psa_hash_verify with MD2 algorithm\n", + PSA_ALG_MD2, 0xbd, 1, + {0x8c, 0x9c, 0x17, 0x66, 0x5d, 0x25, 0xb3, 0x5f, 0xc4, 0x13, 0xc4, 0x18, 0x05, 0xc6, 0x79, 0xcf}, + 16, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with MD4 algorithm\n", + PSA_ALG_MD4, 0xbd, 1, + {0x18, 0xc3, 0x3f, 0x97, 0x29, 0x7e, 0xfe, 0x5f, 0x8a, 0x73, 0x22, 0x58, 0x28, 0x9f, 0xda, 0x25}, + 16, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with MD5 algorithm\n", + PSA_ALG_MD5, 0xbd, 1, + {0xab, 0xae, 0x57, 0xcb, 0x56, 0x2e, 0xcf, 0x29, 0x5b, 0x4a, 0x37, 0xa7, 0x6e, 0xfe, 0x61, 0xfb}, + 16, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with RIPEMD160 algorithm\n", + PSA_ALG_RIPEMD160, 0xbd, 1, + {0x50, 0x89, 0x26, 0x5e, 0xe5, 0xd9, 0xaf, 0x75, 0xd1, 0x2d, 0xbf, 0x7e, 0xa2, 0xf2, 0x7d, 0xbd, + 0xee, 0x43, 0x5b, 0x37}, + 20, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with SHA1 algorithm\n", + PSA_ALG_SHA_1, 0xbd, 1, + {0x90, 0x34, 0xaa, 0xf4, 0x51, 0x43, 0x99, 0x6a, 0x2b, 0x14, 0x46, 0x5c, 0x35, 0x2a, 0xb0, 0xc6, + 0xfa, 0x26, 0xb2, 0x21}, + 20, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with SHA224 algorithm\n", + PSA_ALG_SHA_224, 0xbd, 1, + {0xb1, 0xe4, 0x6b, 0xb9, 0xef, 0xe4, 0x5a, 0xf5, 0x54, 0x36, 0x34, 0x49, 0xc6, 0x94, 0x5a, 0x0d, + 0x61, 0x69, 0xfc, 0x3a, 0x5a, 0x39, 0x6a, 0x56, 0xcb, 0x97, 0xcb, 0x57}, + 28, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with SHA256 algorithm\n", + PSA_ALG_SHA_256, 0xbd, 1, + {0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, + 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b}, + 32, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with SHA384 algorithm\n", + PSA_ALG_SHA_384, 0xbd, 1, {0}, 48, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with SHA512 algorithm\n", + PSA_ALG_SHA_512, 0xbd, 1, {0}, 64, PSA_SUCCESS, +}, + +{"Test psa_hash_verify with incorrect expected hash\n", + PSA_ALG_SHA_256, 0xbd, 1, + {0x68, 0x32, 0x57, 0x20, 0xab, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, + 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x78}, + 32, PSA_ERROR_INVALID_SIGNATURE, +}, + +{"Test psa_hash_verify with incorrect hash length\n", + PSA_ALG_SHA_256, 0xbd, 1, + {0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, + 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b}, + 31, PSA_ERROR_INVALID_SIGNATURE, +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c013/test_entry.c b/psa-ff/test_suites/crypto/test_c013/test_entry.c new file mode 100644 index 00000000..08c8b2f5 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c013/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c013.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 13) +#define TEST_DESC "Testing crypto hash functions APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c013_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c014/source.mk b/psa-ff/test_suites/crypto/test_c014/source.mk new file mode 100644 index 00000000..4e75c890 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c014/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c014.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c014/test_c014.c b/psa-ff/test_suites/crypto/test_c014/test_c014.c new file mode 100644 index 00000000..e9a1196c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c014/test_c014.c @@ -0,0 +1,219 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c014.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c014_crypto_list[] = { + NULL, + psa_hash_finish_test, + psa_hash_finish_inactive_operation_handle, + psa_hash_finish_invalid_hash_buffer_size, + NULL, +}; + +int g_test_count; + +int32_t psa_hash_finish_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i, j; + psa_hash_operation_t operation; + char *expected_hash, hash[HASH_64B]; + size_t hash_length, hash_size = sizeof(hash)/sizeof(hash[0]); + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + if (check1[i].alg == PSA_ALG_SHA_384) + expected_hash = sha384_hash; + else if (check1[i].alg == PSA_ALG_SHA_512) + expected_hash = sha512_hash; + else + expected_hash = check1[i].hash; + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + &check1[i].input, check1[i].input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message */ + status = val->crypto_function(VAL_CRYPTO_HASH_FINISH, &operation, hash, hash_size, + &hash_length); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA hash finish failed\n", 0); + return VAL_STATUS_INVALID; + } + + if (status != PSA_SUCCESS) + continue; + + if (hash_length != PSA_HASH_SIZE(check1[i].alg)) + { + val->print(PRINT_ERROR, "\tHash length mismatch\n", 0); + return VAL_STATUS_INVALID; + } + for (j = 0; j < hash_length; j++) + { + if (hash[i] != expected_hash[i]) + { + val->print(PRINT_ERROR, "\tHash data mismatch\n", 0); + return VAL_STATUS_INVALID; + } + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_hash_finish_inactive_operation_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_hash_operation_t operation; + char input = 0xbd; + size_t input_length = 1; + psa_algorithm_t alg = PSA_ALG_SHA_256; + char hash[HASH_64B]; + size_t hash_length, hash_size = sizeof(hash)/sizeof(hash[0]); + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "test psa_hash_finish with inactive operation handle\n", 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + &input, input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message */ + status = val->crypto_function(VAL_CRYPTO_HASH_FINISH, &operation, hash, hash_size, + &hash_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash finish failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Retry the operation with completed operation handle */ + status = val->crypto_function(VAL_CRYPTO_HASH_FINISH, &operation, hash, hash_size, + &hash_length); + if (status != PSA_ERROR_INVALID_ARGUMENT) + { + val->print(PRINT_ERROR, "\tPSA hash finish should have failed\n", 0); + return VAL_STATUS_INVALID; + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_hash_finish_invalid_hash_buffer_size(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_hash_operation_t operation; + char input = 0xbd; + size_t input_length = 1; + psa_algorithm_t alg = PSA_ALG_SHA_256; + char hash[HASH_64B]; + size_t hash_length, hash_size = 10; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "test psa_hash_finish with invalid hash buffer size\n", 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + &input, input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message */ + status = val->crypto_function(VAL_CRYPTO_HASH_FINISH, &operation, hash, hash_size, + &hash_length); + if (status != PSA_ERROR_BUFFER_TOO_SMALL) + { + val->print(PRINT_ERROR, "\tPSA hash finish should have failed\n", 0); + return VAL_STATUS_INVALID; + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c014/test_c014.h b/psa-ff/test_suites/crypto/test_c014/test_c014.h new file mode 100644 index 00000000..4f0d26a2 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c014/test_c014.h @@ -0,0 +1,35 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C014_CLIENT_TESTS_H_ +#define _TEST_C014_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c014_crypto_list[]; + +int32_t psa_hash_finish_test(security_t caller); +int32_t psa_hash_finish_inactive_operation_handle(security_t caller); +int32_t psa_hash_finish_invalid_hash_buffer_size(security_t caller); +#endif /* _TEST_C014_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c014/test_data.h b/psa-ff/test_suites/crypto/test_c014/test_data.h new file mode 100644 index 00000000..6738c60c --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c014/test_data.h @@ -0,0 +1,95 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[50]; + psa_algorithm_t alg; + char input; + size_t input_length; + char hash[32]; + size_t hash_length; + psa_status_t expected_status; +} test_data; + +char sha384_hash[] = {0x43, 0x72, 0xe3, 0x8a, 0x92, 0xa2, 0x8b, 0x5d, 0x2c, 0x39, 0x1e, 0x62, +0x45, 0x2a, 0x86, 0xd5, 0x0e, 0x02, 0x67, 0x22, 0x8b, 0xe1, 0x76, 0xc7, 0x7d, 0x24, 0x02, 0xef, +0xfe, 0x9f, 0xa5, 0x0d, 0xe4, 0x07, 0xbb, 0xb8, 0x51, 0xb3, 0x7d, 0x59, 0x04, 0xab, 0xa2, 0xde, +0xde, 0x74, 0xda, 0x2a}; + +char sha512_hash[] = {0x29, 0x6e, 0x22, 0x67, 0xd7, 0x4c, 0x27, 0x8d, 0xaa, 0xaa, 0x94, 0x0d, +0x17, 0xb0, 0xcf, 0xb7, 0x4a, 0x50, 0x83, 0xf8, 0xe0, 0x69, 0x72, 0x6d, 0x8c, 0x84, 0x1c, 0xbe, +0x59, 0x6e, 0x04, 0x31, 0xcb, 0x77, 0x41, 0xa5, 0xb5, 0x0f, 0x71, 0x66, 0x6c, 0xfd, 0x54, 0xba, +0xcb, 0x7b, 0x00, 0xae, 0xa8, 0x91, 0x49, 0x9c, 0xf4, 0xef, 0x6a, 0x03, 0xc8, 0xa8, 0x3f, 0xe3, +0x7c, 0x3f, 0x7b, 0xaf}; + +test_data check1[] = { +{"Test psa_hash_finish with MD2 algorithm\n", + PSA_ALG_MD2, 0xbd, 1, + {0x8c, 0x9c, 0x17, 0x66, 0x5d, 0x25, 0xb3, 0x5f, 0xc4, 0x13, 0xc4, 0x18, 0x05, 0xc6, 0x79, 0xcf}, + 16, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with MD4 algorithm\n", + PSA_ALG_MD4, 0xbd, 1, + {0x18, 0xc3, 0x3f, 0x97, 0x29, 0x7e, 0xfe, 0x5f, 0x8a, 0x73, 0x22, 0x58, 0x28, 0x9f, 0xda, 0x25}, + 16, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with MD5 algorithm\n", + PSA_ALG_MD5, 0xbd, 1, + {0xab, 0xae, 0x57, 0xcb, 0x56, 0x2e, 0xcf, 0x29, 0x5b, 0x4a, 0x37, 0xa7, 0x6e, 0xfe, 0x61, 0xfb}, + 16, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with RIPEMD160 algorithm\n", + PSA_ALG_RIPEMD160, 0xbd, 1, + {0x50, 0x89, 0x26, 0x5e, 0xe5, 0xd9, 0xaf, 0x75, 0xd1, 0x2d, 0xbf, 0x7e, 0xa2, 0xf2, 0x7d, 0xbd, + 0xee, 0x43, 0x5b, 0x37}, + 20, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with SHA1 algorithm\n", + PSA_ALG_SHA_1, 0xbd, 1, + {0x90, 0x34, 0xaa, 0xf4, 0x51, 0x43, 0x99, 0x6a, 0x2b, 0x14, 0x46, 0x5c, 0x35, 0x2a, 0xb0, 0xc6, + 0xfa, 0x26, 0xb2, 0x21}, + 20, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with SHA224 algorithm\n", + PSA_ALG_SHA_224, 0xbd, 1, + {0xb1, 0xe4, 0x6b, 0xb9, 0xef, 0xe4, 0x5a, 0xf5, 0x54, 0x36, 0x34, 0x49, 0xc6, 0x94, 0x5a, 0x0d, + 0x61, 0x69, 0xfc, 0x3a, 0x5a, 0x39, 0x6a, 0x56, 0xcb, 0x97, 0xcb, 0x57}, + 28, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with SHA256 algorithm\n", + PSA_ALG_SHA_256, 0xbd, 1, + {0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, + 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b}, + 32, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with SHA384 algorithm\n", + PSA_ALG_SHA_384, 0xbd, 1, {0}, 48, PSA_SUCCESS, +}, + +{"Test psa_hash_finish with SHA512 algorithm\n", + PSA_ALG_SHA_512, 0xbd, 1, {0}, 64, PSA_SUCCESS, +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c014/test_entry.c b/psa-ff/test_suites/crypto/test_c014/test_entry.c new file mode 100644 index 00000000..bc174f8d --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c014/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c014.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 14) +#define TEST_DESC "Testing crypto hash functions APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c014_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/test_c015/source.mk b/psa-ff/test_suites/crypto/test_c015/source.mk new file mode 100644 index 00000000..66ef2a23 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c015/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_c015.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/crypto/test_c015/test_c015.c b/psa-ff/test_suites/crypto/test_c015/test_c015.c new file mode 100644 index 00000000..b31f560e --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c015/test_c015.c @@ -0,0 +1,142 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_c015.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c015_crypto_list[] = { + NULL, + psa_hash_abort_test, + psa_hash_abort_before_operation_finish, + NULL, +}; + +int g_test_count; + +int32_t psa_hash_abort_test(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i; + psa_hash_operation_t operation; + + g_test_count = 1; + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Abort a hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_ABORT, &operation); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA hash abort failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Multiple hash abort should succeed */ + status = val->crypto_function(VAL_CRYPTO_HASH_ABORT, &operation); + if (status != check1[i].expected_status) + { + val->print(PRINT_ERROR, "\tPSA hash abort failed\n", 0); + return VAL_STATUS_INVALID; + } + + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_hash_abort_before_operation_finish(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_hash_operation_t operation; + char input = 0xbd; + size_t input_length = 1; + psa_algorithm_t alg = PSA_ALG_SHA_256; + char hash[HASH_64B]; + size_t hash_length, hash_size = sizeof(hash)/sizeof(hash[0]); + + /* Initialize the PSA crypto library*/ + if (val->crypto_function(VAL_CRYPTO_INIT) != PSA_SUCCESS) + { + return VAL_STATUS_INIT_FAILED; + } + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "Test psa_hash_finish after calling psa_hash_abort\n", 0); + + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, alg); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash setup failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Add a message fragment to a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_UPDATE, &operation, + &input, input_length); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash update failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Abort a hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_ABORT, &operation); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA hash abort failed\n", 0); + return VAL_STATUS_INVALID; + } + + /* Finish the calculation of the hash of a message */ + status = val->crypto_function(VAL_CRYPTO_HASH_FINISH, &operation, hash, hash_size, + &hash_length); + if (status != PSA_ERROR_INVALID_ARGUMENT) + { + val->print(PRINT_ERROR, "\tPSA hash finish should have failed\n", 0); + return VAL_STATUS_INVALID; + } + + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/crypto/test_c015/test_c015.h b/psa-ff/test_suites/crypto/test_c015/test_c015.h new file mode 100644 index 00000000..ec3574d1 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c015/test_c015.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST_C015_CLIENT_TESTS_H_ +#define _TEST_C015_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#include "val_crypto.h" + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c015_crypto_list[]; + +int32_t psa_hash_abort_test(security_t caller); +int32_t psa_hash_abort_before_operation_finish(security_t caller); +#endif /* _TEST_C015_CLIENT_TESTS_H_ */ diff --git a/psa-ff/test_suites/crypto/test_c015/test_data.h b/psa-ff/test_suites/crypto/test_c015/test_data.h new file mode 100644 index 00000000..d06bf4a0 --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c015/test_data.h @@ -0,0 +1,62 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" + +typedef struct { + char test_desc[50]; + psa_algorithm_t alg; + psa_status_t expected_status; +} test_data; + +test_data check1[] = { +{"Test psa_hash_abort with MD2 algorithm\n", + PSA_ALG_MD2, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with MD4 algorithm\n", + PSA_ALG_MD4, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with MD5 algorithm\n", + PSA_ALG_MD5, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with RIPEMD160 algorithm\n", + PSA_ALG_RIPEMD160, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with SHA1 algorithm\n", + PSA_ALG_SHA_1, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with SHA224 algorithm\n", + PSA_ALG_SHA_224, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with SHA256 algorithm\n", + PSA_ALG_SHA_256, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with SHA384 algorithm\n", + PSA_ALG_SHA_384, PSA_SUCCESS, +}, + +{"Test psa_hash_abort with SHA512 algorithm\n", + PSA_ALG_SHA_512, PSA_SUCCESS, +}, +}; diff --git a/psa-ff/test_suites/crypto/test_c015/test_entry.c b/psa-ff/test_suites/crypto/test_c015/test_entry.c new file mode 100644 index 00000000..2b112ddb --- /dev/null +++ b/psa-ff/test_suites/crypto/test_c015/test_entry.c @@ -0,0 +1,69 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_c015.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 15) +#define TEST_DESC "Testing crypto hash functions APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t i, status = VAL_STATUS_SUCCESS; + bool_t destroy_status = TRUE; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_HIGH_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_crypto_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_c015_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->print(PRINT_DEBUG, "\tPSA destroy all the key slot\n", 0); + for (i = 1; i <= MAX_KEY_SLOT; i++) + { + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, i); + if (status != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tPSA destroy key failed for slot number: %d\n", i); + destroy_status = FALSE; + } + } + + if (destroy_status) + { + val->print(PRINT_DEBUG, "\tPSA all the key slot successfully destroyed\n", 0); + } + + val->test_exit(); +} diff --git a/psa-ff/test_suites/crypto/testsuite.db b/psa-ff/test_suites/crypto/testsuite.db new file mode 100644 index 00000000..8ad7c56f --- /dev/null +++ b/psa-ff/test_suites/crypto/testsuite.db @@ -0,0 +1,39 @@ +#/** @file +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + + +#List of tests to be compiled and run as part of crypto suite + +(START) + +test_c001 +test_c002 +test_c003 +test_c004 +test_c005 +test_c006 +test_c007 +test_c008 +test_c009 +test_c010 +test_c011 +test_c012 +test_c013 +test_c014 +test_c015 + +(END) diff --git a/psa-ff/test_suites/ipc/test_i001/source.mk b/psa-ff/test_suites/ipc/test_i001/source.mk new file mode 100644 index 00000000..eb23179e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i001/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i001.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i001/test_entry.c b/psa-ff/test_suites/ipc/test_i001/test_entry.c new file mode 100644 index 00000000..d194ca71 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i001/test_entry.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i001.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 1) +#define TEST_DESC "Testing psa_framework_version and psa_version APIs\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side */ + status = val->execute_non_secure_tests(TEST_NUM, test_i001_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i001/test_i001.c b/psa-ff/test_suites/ipc/test_i001/test_i001.c new file mode 100644 index 00000000..dd54077a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i001/test_i001.c @@ -0,0 +1,93 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i001.h" + +client_test_t test_i001_client_tests_list[] = { + NULL, + client_test_psa_framework_version, + client_test_psa_version, + NULL, +}; + +int32_t client_test_psa_framework_version(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val->print(PRINT_TEST, "[Check1] psa_framework_version\n", 0); + + /* Retrieve the version of the PSA Framework API that is implemented.*/ + if (psa->framework_version() != PSA_FRAMEWORK_VERSION) + { + status = VAL_STATUS_FRAMEWORK_VERSION_FAILED; + val->print(PRINT_ERROR, + "\tpsa_framework_version API failed, Returned=0x%x\n", (psa->framework_version())); + } + + return status; +} + +int32_t client_test_psa_version(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + uint32_t version; + + val->print(PRINT_TEST, "[Check2] psa_version\n", 0); + + /*Return PSA_VERSION_NONE when the RoT Service is not implemented, or the caller is not permitted + to access the service. Return minor version of the implemented and allowed RoT Service */ + /* psa_version() check for un-implemented SID */ + if (psa->version(INVALID_SID) != PSA_VERSION_NONE) + { + status = VAL_STATUS_VERSION_API_FAILED; + } + + if (val->err_check_set(TEST_CHECKPOINT_NUM(101), status)) + { + return status; + } + + /* psa_version() check for implemented SID but allows only secure connection */ + version = psa->version(SERVER_SECURE_CONNECT_ONLY_SID); + if (((caller == NONSECURE) && (version != PSA_VERSION_NONE)) + || ((caller == SECURE) && (version != 2))) + { + status = VAL_STATUS_VERSION_API_FAILED; + val->print(PRINT_ERROR, + "\tpsa_version API failed for SID which allows secure only connection\n", 0); + } + + if (val->err_check_set(TEST_CHECKPOINT_NUM(102), status)) + { + return status; + } + + /* psa_version() returns minor version of the implemented and allowed RoT Service */ + if (psa->version(SERVER_TEST_DISPATCHER_SID) != 1) + { + status = VAL_STATUS_VERSION_API_FAILED; + } + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i001/test_i001.h b/psa-ff/test_suites/ipc/test_i001/test_i001.h new file mode 100644 index 00000000..967f66e4 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i001/test_i001.h @@ -0,0 +1,33 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST001_CLIENT_TESTS_H_ +#define _TEST001_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i001_client_tests_list[]; + +int32_t client_test_psa_framework_version(security_t); +int32_t client_test_psa_version(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i001/test_supp_i001.c b/psa-ff/test_suites/ipc/test_i001/test_supp_i001.c new file mode 100644 index 00000000..ce6de385 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i001/test_supp_i001.c @@ -0,0 +1,41 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_framework_version(void); +int32_t server_test_psa_version(void); + +server_test_t test_i001_server_tests_list[] = { + NULL, + server_test_psa_framework_version, + server_test_psa_version, + NULL, +}; + +int32_t server_test_psa_framework_version(void) +{ + val_err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_psa_version(void) +{ + val_err_check_set(TEST_CHECKPOINT_NUM(202), VAL_STATUS_SUCCESS); + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/ipc/test_i002/source.mk b/psa-ff/test_suites/ipc/test_i002/source.mk new file mode 100644 index 00000000..daec7a18 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i002/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i002.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i002/test_entry.c b/psa-ff/test_suites/ipc/test_i002/test_entry.c new file mode 100644 index 00000000..8d23bd22 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i002/test_entry.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i002.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 2) +#define TEST_DESC "Testing RoT connect and disconnect cases\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i002_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i002/test_i002.c b/psa-ff/test_suites/ipc/test_i002/test_i002.c new file mode 100644 index 00000000..74be8198 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i002/test_i002.c @@ -0,0 +1,286 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i002.h" + +client_test_t test_i002_client_tests_list[] = { + NULL, + client_test_connection_busy_and_reject, + client_test_accept_and_close_connect, + client_test_connect_with_allowed_minor_version_policy, + client_test_psa_call_with_allowed_status_code, + client_test_identity, + client_test_spm_concurrent_connect_limit, + client_test_psa_wait_any_with_psa_block, + client_test_psa_wait_any_with_psa_poll, + NULL, +}; + +int32_t client_test_connection_busy_and_reject(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test busy and reject connect type\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* The RoT Service can't make connection at this moment. + * Expect PSA_CONNECTION_BUSY by RoT service. + */ + if (handle != PSA_CONNECTION_BUSY) + { + val->print(PRINT_ERROR, "Expected handle=PSA_CONNECTION_BUSY but handle=0x%x\n", handle); + return VAL_STATUS_INVALID_HANDLE; + } + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* The RoT Service rejected the client because of an application-specific case + * Expect PSA_CONNECTION_REFUSED by RoT service + */ + if (handle != PSA_CONNECTION_REFUSED) + { + val->print(PRINT_ERROR, "Expected handle=PSA_CONNECTION_REFUSED but handle=0x%x\n", handle); + status = VAL_STATUS_INVALID_HANDLE; + } + + return status; +} + +int32_t client_test_accept_and_close_connect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check2] Accept and close connection\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + /* RoT service accepts the connection. Expecting positive handle */ + if (handle < 0) + { + return VAL_STATUS_INVALID_HANDLE; + } + + /* psa_close with PSA_NULL_HANDLE. This shoudn't have any effect */ + psa->close(PSA_NULL_HANDLE); + + /* Close the connection */ + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_connect_with_allowed_minor_version_policy(security_t caller) +{ + psa_handle_t handle = 0; + uint32_t i = 0; + uint32_t sid[] = {SERVER_UNSPECIFED_MINOR_V_SID, + SERVER_STRICT_MINOR_VERSION_SID, + SERVER_RELAX_MINOR_VERSION_SID, + SERVER_RELAX_MINOR_VERSION_SID}; + uint32_t minor_v[] = {1, 2, 1, 2}; + + val->print(PRINT_TEST, "[Check3] Test psa_connect with allowed minor version policy\n", 0); + + /* Connect RoT service with following minor version numbers and expect positive handle for + * each connection: + * Case 1. Version policy is not mentioned and requested version is 1 (default minimum version) + * Case 2. Version policy is STRICT and requested version equals minimum version + * Case 3. Version policy is relaxed and requested version is smaller than the minimum version + * Case 4. Version policy is relaxed and requested version is euqal to the minimum version + */ + + for (i = 0; i < (sizeof(sid)/sizeof(sid[0])); i++) + { + handle = psa->connect(sid[i], minor_v[i]); + if (handle < 0) + { + val->print(PRINT_ERROR, + "\tpsa_connect failed for minor_v policy. Iteration No=%d\n", i); + return VAL_STATUS_INVALID_HANDLE; + } + + psa->close(handle); + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_call_with_null_msg(int32_t expected_status) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + /* Compare status code returned with expected status code */ + if (status_of_call != expected_status) + { + status = VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return status; +} + +int32_t client_test_psa_call_with_allowed_status_code(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_status_t expected_status_code[] = {PSA_SUCCESS, 1, 2, INT32_MAX, -1, -2, INT32_MIN+128}; + uint32_t i = 0; + + val->print(PRINT_TEST, "[Check4] Test psa_call with allowed status code\n", 0); + + for (i = 0; i < (sizeof(expected_status_code)/sizeof(expected_status_code[0])); i++) + { + /* Compare the status code sent by RoT service with expected code */ + status = psa_call_with_null_msg(expected_status_code[i]); + if (VAL_ERROR(status)) + { + val->print(PRINT_ERROR, + "psa_call failed for status code=0x%x\n", expected_status_code[i]); + return status; + } + } + return status; +} + +int32_t client_test_identity(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + int id_at_connect = 0, id_at_call = 0; + + val->print(PRINT_TEST, "[Check5] Test client_id\n", 0); + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + if (handle < 0) + { + return(VAL_STATUS_INVALID_HANDLE); + } + + psa_outvec resp[2] = {{&id_at_connect, sizeof(id_at_call)}, + {&id_at_call, sizeof(id_at_call)}}; + + status_of_call = psa->call(handle, NULL, 0, resp, 2); + + + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + } + /* For NSPE access, identity should be < 0 */ + else if ((caller == NONSECURE) && ((id_at_connect != id_at_call) + || (id_at_connect >=0) || (id_at_call >=0))) + { + status = VAL_STATUS_WRONG_IDENTITY; + } + /* For SPE access, identity should be > 0 */ + else if ((caller == SECURE) && ((id_at_connect != id_at_call) + || (id_at_connect <=0) || (id_at_call <=0))) + { + status = VAL_STATUS_WRONG_IDENTITY; + } + psa->close(handle); + + return status; +} + +int32_t client_test_spm_concurrent_connect_limit(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle[CONNECT_LIMIT] = {0}; + int i= 0, signture = 0; + + val->print(PRINT_TEST, "[Check6] Test connect limit\n", 0); + + /* Execute psa_connect in a loop until it returns + * PSA_CONNECTION_REFUSED OR PSA_CONNECTION_BUSY + */ + while (i < CONNECT_LIMIT) + { + handle[i] = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + /* Compare handle value */ + if ((handle[i] == PSA_CONNECTION_REFUSED) || (handle[i] == PSA_CONNECTION_BUSY)) + { + signture = 1; + break; + } + i++; + } + + if (signture != 1) + { + /* Didn't recieve expected return handle value */ + status = VAL_STATUS_SPM_FAILED; + i--; + } + + while (i >= 0) + { + if (handle[i] > 0) + { + /* Close all open connection to RoT service */ + psa->close(handle[i]); + } + i--; + } + return status; +} + +int32_t client_test_psa_wait_any(void) +{ + psa_handle_t handle[CONNECT_NUM] = {0}; + int i = 0; + + for(i = 0; i < CONNECT_NUM; i++) + { + handle[i] = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle[i] != PSA_CONNECTION_REFUSED) + { + return VAL_STATUS_INVALID_HANDLE; + } + } + + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_psa_wait_any_with_psa_block(security_t caller) +{ + val->print(PRINT_TEST, "[Check7] Test psa_wait_any with PSA_BLOCK\n", 0); + return (client_test_psa_wait_any()); +} + +int32_t client_test_psa_wait_any_with_psa_poll(security_t caller) +{ + val->print(PRINT_TEST, "[Check8] Test psa_wait_any with PSA_POLL\n", 0); + return (client_test_psa_wait_any()); +} diff --git a/psa-ff/test_suites/ipc/test_i002/test_i002.h b/psa-ff/test_suites/ipc/test_i002/test_i002.h new file mode 100644 index 00000000..2c425b50 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i002/test_i002.h @@ -0,0 +1,42 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST002_CLIENT_TESTS_H_ +#define _TEST002_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +#define CONNECT_LIMIT 20 +#define CONNECT_NUM 2 + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i002_client_tests_list[]; + +int32_t client_test_connection_busy_and_reject(security_t); +int32_t client_test_accept_and_close_connect(security_t); +int32_t client_test_connect_with_allowed_minor_version_policy(security_t); +int32_t client_test_psa_call_with_allowed_status_code(security_t); +int32_t client_test_identity(security_t); +int32_t client_test_spm_concurrent_connect_limit(security_t); +int32_t client_test_psa_wait_any_with_psa_block(security_t); +int32_t client_test_psa_wait_any_with_psa_poll(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i002/test_supp_i002.c b/psa-ff/test_suites/ipc/test_i002/test_supp_i002.c new file mode 100644 index 00000000..5fc2319f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i002/test_supp_i002.c @@ -0,0 +1,373 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define CONNECT_NUM 2 + +int32_t server_test_connection_busy_and_reject(void); +int32_t server_test_accept_and_close_connect(void); +int32_t server_test_connect_with_allowed_minor_version_policy(void); +int32_t server_test_psa_call_with_allowed_status_code(void); +int32_t server_test_identity(void); +int32_t server_test_spm_concurrent_connect_limit(void); +int32_t server_test_psa_wait_any_with_psa_block(void); +int32_t server_test_psa_wait_any_with_psa_poll(void); + +server_test_t test_i002_server_tests_list[] = { + NULL, + server_test_connection_busy_and_reject, + server_test_accept_and_close_connect, + server_test_connect_with_allowed_minor_version_policy, + server_test_psa_call_with_allowed_status_code, + server_test_identity, + server_test_spm_concurrent_connect_limit, + server_test_psa_wait_any_with_psa_block, + server_test_psa_wait_any_with_psa_poll, + NULL, +}; + +int32_t server_test_connection_busy_and_reject(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + /* Checks performed: + * psa_wait_any()- Returns > 0 when at least one signal is asserted + * check delivery of PSA_IPC_CONNECT when psa_connect called. + * And msg.handle must be positive. + */ + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + return status; + } + + /* Rejecting connection to check behaviour of PSA_CONNECTION_BUSY */ + psa_reply(msg.handle, PSA_CONNECTION_BUSY); + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return status; + } + + /* Rejecting connection to check behaviour of PSA_CONNECTION_REFUSED */ + psa_reply(msg.handle, PSA_CONNECTION_BUSY); + return status; +} + +int32_t server_test_accept_and_close_connect(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + /* Reject the connection if processing of connect request has failed */ + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Accepting connection to check behaviour of PSA_SUCCESS */ + psa_reply(msg.handle, PSA_SUCCESS); + + /* Checking delivery of PSA_IPC_DISCONNECT when psa_close called + * msg.handle must be positive + */ + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + return status; + } + + /* Sanity check - if the message type is PSA_IPC_DISCONNECT then the status code is ignored.*/ + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + /* Debug print for sanity check */ + val_err_check_set(TEST_CHECKPOINT_NUM(205), status); + return status; +} + +int32_t server_test_connect_with_allowed_minor_version_policy(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + int i = 0; + psa_signal_t signal[4] = {SERVER_UNSPECIFED_MINOR_V_SIG, + SERVER_STRICT_MINOR_VERSION_SIG, + SERVER_RELAX_MINOR_VERSION_SIG, + SERVER_RELAX_MINOR_VERSION_SIG}; + + for (i = 0; i < 4; i++) + { + status = ((val_process_connect_request(signal[i], &msg)) + ? VAL_STATUS_ERROR : status); + if (val_err_check_set(TEST_CHECKPOINT_NUM(206), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = ((val_process_disconnect_request(signal[i], &msg)) + ? VAL_STATUS_ERROR : status); + val_err_check_set(TEST_CHECKPOINT_NUM(207), status); + psa_reply(msg.handle, PSA_SUCCESS); + } + return status; +} + +int32_t server_test_psa_call_with_allowed_status_code(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint32_t i = 0; + psa_status_t status_code[] = {PSA_SUCCESS, 1, 2, INT32_MAX, -1, -2, INT32_MIN+128}; + + for (i = 0; i < (sizeof(status_code)/sizeof(status_code[0])); i++) + { + status = ((val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + if (val_err_check_set(TEST_CHECKPOINT_NUM(208), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(209), status)) + { + /* Send status code other than status_code[] to indicate failure + * in processing call request + */ + psa_reply(msg.handle, -3); + } + else + { + /* Send various status code available in status_code[] */ + psa_reply(msg.handle, status_code[i]); + } + + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + val_err_check_set(TEST_CHECKPOINT_NUM(210), status); + } + return status; +} + +int32_t server_test_identity(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + int id_at_connect = 0, id_at_call = 0; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + if (val_err_check_set(TEST_CHECKPOINT_NUM(211), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Client ID during connect */ + id_at_connect = msg.client_id; + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(212), status)) + { + psa_reply(msg.handle, -3); + } + else + { + /* Client ID during call */ + id_at_call = msg.client_id; + psa_write(msg.handle, 0, &id_at_connect, msg.out_size[0]); + psa_write(msg.handle, 1, &id_at_call, msg.out_size[1]); + psa_reply(msg.handle, PSA_SUCCESS); + } + + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + val_err_check_set(TEST_CHECKPOINT_NUM(213), status); + /* Client ID during disconnect. It should be equal to id_at_call */ + if (msg.client_id != id_at_call) + { + val_print(PRINT_ERROR, "\tmsg.client_id failed for IPC_DISCONNECT", 0); + status = VAL_STATUS_WRONG_IDENTITY; + } + + /* A Partition must have an alphanumeric name for source code to directly refer + * to a specific Partition. The Secure Partition ID can be referenced in + * Secure Partition source code via the symbolic name specified as name attribute. + * The symbol must have the value of the Secure Partition ID. + * Using CLIENT_PARTITION define to cover this rule. + */ + if ((msg.client_id > 0) && (msg.client_id != CLIENT_PARTITION)) + { + val_print(PRINT_ERROR, "\tmsg.client_id failed for CLIENT_PARTITION", 0); + status = VAL_STATUS_WRONG_IDENTITY; + } + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} + +int32_t server_test_spm_concurrent_connect_limit(void) +{ + psa_signal_t signals = 0; + psa_msg_t msg = {0}; + int count = 0; + int32_t status = VAL_STATUS_SUCCESS; + + while (1) + { + signals = psa_wait_any(PSA_BLOCK); + if ((signals & SERVER_UNSPECIFED_MINOR_V_SIG) == 0) + { + val_print(PRINT_ERROR, + "psa_wait_any returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } + + psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + switch(msg.type) + { + case PSA_IPC_CONNECT: + /* serve bulk connect cases to reach connect limit */ + count++; + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_DISCONNECT: + /* serve bulk disconnect cases */ + count--; + psa_reply(msg.handle, PSA_SUCCESS); + } + if (count == 0) + { + /* Closed all open connection. Come out of loop */ + break; + } + } + return status; +} + +int32_t server_test_psa_wait_any_with_psa_block(void) +{ + psa_signal_t signals = 0; + psa_msg_t msg = {0}; + int expected_connect_count = 0, while_count = 0; + + /* Calling psa_wait_any in a loop and comparing the count value + * to check whether PSA_BLOCK is not giving polling behaviour + */ + while (1) + { + /* Debug print */ + val_err_check_set(TEST_CHECKPOINT_NUM(214), VAL_STATUS_SUCCESS); + + /* Ored with 0xFF to check timeout[30:0]=RES is ignored by implementation */ + signals = psa_wait_any(PSA_BLOCK | 0xFF); + + /* Count how many times while loop executed */ + while_count++; + + /* When MODE is one(PSA_BLOCK), the psa_wait_any must return non-zero signal value */ + if ((signals & SERVER_UNSPECIFED_MINOR_V_SIG) == 0) + { + val_print(PRINT_ERROR, + "psa_wait_any returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } + else + { + psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* Count how many times non-zero signal value returned */ + expected_connect_count++; + } + + if (expected_connect_count == CONNECT_NUM) + { + /* Come out of loop as we reached required connect limit*/ + break; + } + } + + /* For PSA_BLOCK, expected_connect_count should be equal to while_count. */ + if (expected_connect_count != while_count) + { + return VAL_STATUS_SPM_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_psa_wait_any_with_psa_poll(void) +{ + psa_signal_t signals = 0, signals_temp = 0; + psa_msg_t msg = {0}; + int count = 0; + + while (1) + { + /* Debug print */ + val_err_check_set(TEST_CHECKPOINT_NUM(215), VAL_STATUS_SUCCESS); + + /* Loop to receive client request */ + while (signals == 0) + { + signals = psa_wait_any(PSA_POLL); + } + + /* When MODE is zero(PSA_POLL), the psa_wait_any will return immediately with the current + * signal state, which can be zero if no signals are active. Exepecting following call to + * return immediately as none of client is making request. + */ + signals_temp = psa_wait_any(PSA_POLL); + + if (signals_temp == 0) + { + val_print(PRINT_ERROR, + "psa_wait_any returned with invalid signals_temp = 0x%x\n", signals_temp); + return VAL_STATUS_ERROR; + } + else if ((signals & SERVER_UNSPECIFED_MINOR_V_SIG) == 0) + { + val_print(PRINT_ERROR, + "psa_wait_any returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } + else + { + psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + count++; + signals = 0; + } + + if (count == CONNECT_NUM) + { + /* Come out of loop as we reached required connect limit*/ + break; + } + } + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/ipc/test_i003/source.mk b/psa-ff/test_suites/ipc/test_i003/source.mk new file mode 100644 index 00000000..70ad2da2 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i003/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i003.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i003/test_entry.c b/psa-ff/test_suites/ipc/test_i003/test_entry.c new file mode 100644 index 00000000..e5131066 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i003/test_entry.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i003.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 3) +#define TEST_DESC "Testing IOVECS\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i003_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i003/test_i003.c b/psa-ff/test_suites/ipc/test_i003/test_i003.c new file mode 100644 index 00000000..114f6734 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i003/test_i003.c @@ -0,0 +1,327 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i003.h" + +client_test_t test_i003_client_tests_list[] = { + NULL, + client_test_zero_length_invec, + client_test_zero_length_outvec, + client_test_call_read_and_skip, + client_test_call_and_write, + client_test_psa_set_rhandle, + client_test_overlapping_vectors, + NULL, +}; + +int32_t client_test_zero_length_invec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + int data[4] = {0x11, 0x22}; + + val->print(PRINT_TEST, "[Check1] Test zero length invec\n", 0); + + if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + /* Invec 0 as zero length vector + * Invec 1 as NULL + * Invec 2 as valid msg pointer which is not zero length memory pointer + */ + psa_invec data1[3] = {{&data[0], 0}, + {NULL, 0}, + {&data[1], sizeof(data[1])}}; + + /* Outvec 0 as valid msg pointer which is not zero length memory pointer + * Outvec 1 to 3 are NULL + */ + psa_outvec resp[1] = {{&data[2], sizeof(data[2])}}; + + status = psa->call(handle, data1, 3, resp, 1); + + if (status < 0) + { + status = VAL_STATUS_CALL_FAILED; + } + else if (data[2] != data[1]) + { + val->print(PRINT_ERROR, "\tExpected data=%x\n", data[1]); + val->print(PRINT_ERROR, "\tBut actual data=%x\n", data[2]); + status = VAL_STATUS_WRITE_FAILED; + } + else if (resp[0].len != sizeof(data[1])) + { + val->print(PRINT_ERROR, "\tExpected size= %x\n", sizeof(data[1])); + val->print(PRINT_ERROR, "\tBut actual size=%x\n", resp[0].len); + status = VAL_STATUS_WRITE_FAILED; + } + + if (VAL_ERROR(status)) + { + val->print(PRINT_ERROR, "\tpsa_call failed. status=%x\n", status); + } + + val->ipc_close(handle); + return status; +} + +int32_t client_test_zero_length_outvec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + int data[4] = {0x11}; + + val->print(PRINT_TEST, "[Check2] Test zero length outvec\n", 0); + + if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + /* Test invec 1 to 3 are NULL + * Invec 1 should be ignored since in_len is 1 + */ + psa_invec data1[2] = {{&data[0], sizeof(data[0])}, + {&data[1], sizeof(data[1])}}; + + /* Test outvec 0 as zero length + * Test outvec 1 as NULL + * Test outvec 2 as valid msg pointer which is not zero length memory pointer + */ + psa_outvec resp[3] = {{&data[0], 0}, + {NULL, 0}, + {&data[2], sizeof(data[2])}}; + + status = psa->call(handle, data1, 1, resp, 3); + if (status < 0) + { + status = VAL_STATUS_CALL_FAILED; + } + /* Compare the outvec with expected data */ + else if (data[2] != data[0]) + { + val->print(PRINT_ERROR, "\tExpected data=%x\n", data[0]); + val->print(PRINT_ERROR, "\tBut actual data=%x\n", data[2]); + status = VAL_STATUS_WRITE_FAILED; + } + /* No. of bytes written by psa_write should update the psa_outvec.len param */ + else if (resp[2].len != sizeof(data[2])) + { + val->print(PRINT_ERROR, "\tExpected size=%x\n", sizeof(data[2])); + val->print(PRINT_ERROR, "\tBut actual size=%x\n", resp[2].len); + status = VAL_STATUS_WRITE_FAILED; + } + + if (VAL_ERROR(status)) + { + val->print(PRINT_ERROR, "\tpsa_call failed. status=%x\n", status); + } + + val->ipc_close(handle); + return status; +} + +int32_t client_test_call_read_and_skip(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int data1[2] = {0xaa, 0xbb}; + uint64_t data2 = 0x1122334455667788; + uint64_t data3 = 0x1020304050607080; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check3] Test psa_write, psa_read and psa_skip\n", 0); + + if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + /* Server test func checks the following: + * All iovec as input, out_len=0, inbound read, inbound skip, + * outbound read, outbound skip + */ + psa_invec data[4] = {{&data1[0], sizeof(data1[0])}, + {&data1[1], sizeof(data1[1])}, + {&data2, sizeof(data2)}, + {&data3, sizeof(data3)}}; + + status = psa->call(handle, data, 4, NULL, 0); + if (status < 0) + { + val->print(PRINT_ERROR, "\tpsa_call failed. status=%x\n",status); + status = VAL_STATUS_CALL_FAILED; + } + + val->ipc_close(handle); + return status; +} + +int32_t client_test_call_and_write(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + int data[4] = {0}, expected_data[4] = {0xaa, 0xbb, 0xcc, 0xeedd}, i; + size_t expected_size[] = {sizeof(expected_data[0]), + sizeof(expected_data[1]), + sizeof(expected_data[2]), + 2}; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check4] Test psa_call and psa_write\n", 0); + + if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + /* Check all iovec as output */ + /* Check for in_len=0 */ + /* check for iovec write using psa_write */ + /* check for write concatenation */ + psa_outvec resp[4] = {{&data[0], sizeof(data[0])}, + {&data[1], sizeof(data[1])}, + {&data[2], sizeof(data[2])}, + {&data[3], sizeof(data[3])}}; + + status = psa->call(handle, NULL, 0, resp, 4); + if (status < 0) + { + status = VAL_STATUS_CALL_FAILED; + } + + for (i = 0; i < 4 ; i++) + { + if (data[i] != expected_data[i]) + { + val->print(PRINT_ERROR, "\tIteration= %x\n", i); + val->print(PRINT_ERROR, "\tExpected data=%x\n", expected_data[i]); + val->print(PRINT_ERROR, "\tBut actual data=%x\n", data[i]); + status = VAL_STATUS_WRITE_FAILED; + } + /* No. of bytes written by psa_write should update the psa_outvec.len param */ + else if (resp[i].len != expected_size[i]) + { + val->print(PRINT_ERROR, "\tIteration= %x\n", i); + val->print(PRINT_ERROR, "\tExpected size=%x\n", expected_size[i]); + val->print(PRINT_ERROR, "\tBut actual size=%x\n", resp[i].len); + status = VAL_STATUS_WRITE_FAILED; + } + } + + if (VAL_ERROR(status)) + { + val->print(PRINT_ERROR, "\tpsa_call failed. status=%x\n", status); + } + + val->ipc_close(handle); + return status; +} + +int32_t client_test_psa_set_rhandle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + int i = 0; + + val->print(PRINT_TEST, "[Check5] Test psa_set_rhandle API\n", 0); + + /*rhandle value check when PSA_IPC_CONNECT */ + if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + for (i = 0; i < 3; i++) + { + /*i=0: rhandle value check when PSA_IPC_CALL */ + /*i=1: rhandle value check after calling psa_set_rhandle() */ + /*i:2: rhandle value check after resetting rhandle to other value */ + status = psa->call(handle, NULL, 0, NULL, 0); + if (status < 0) + { + val->print(PRINT_ERROR, "psa_call failed. status=%x\n", status); + status = VAL_STATUS_CALL_FAILED; + break; + } + } + + /*rhandle value check when PSA_IPC_DISCONNECT */ + val->ipc_close(handle); + return status; +} + +int32_t client_test_overlapping_vectors(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + uint8_t data = 0x11, expected_data[] = {0x22, 0x33}; + + val->print(PRINT_TEST, "[Check6] Test overlapping vectors\n", 0); + + if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + /* Rule- When client provides an input and output vectors which are referencing + * to same memory location, psa_read after psa_write to that memory location + * can return original or modified value. + * + * Rule- When client provides an input and output vectors which are referencing to same + * memory location, a psa_write(s) to both memory vectors can return either the 1st + * or the 2nd value written. + * + * Input [0] and output [0,1] vectors have been used to verify these rule. + * All of these vectors are pointing to same memory location. + */ + + psa_invec invec[1] = {{&data, sizeof(data)}}; + psa_outvec outvec[2] = {{&data, sizeof(data)}, + {&data, sizeof(data)}}; + + status = psa->call(handle, invec, 1, outvec, 2); + + if (status < 0) + { + status = VAL_STATUS_CALL_FAILED; + } + /* data should contain either value written by 1st psa_write or 2nd */ + else if ((data != expected_data[0]) && (data != expected_data[1])) + { + val->print(PRINT_ERROR, "\tInvalid data received=%x\n", data); + status = VAL_STATUS_CALL_FAILED; + } + + if (VAL_ERROR(status)) + { + val->print(PRINT_ERROR, "\tpsa_call failed. status=%x\n", status); + } + + val->ipc_close(handle); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i003/test_i003.h b/psa-ff/test_suites/ipc/test_i003/test_i003.h new file mode 100644 index 00000000..4c078088 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i003/test_i003.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST003_CLIENT_TESTS_H_ +#define _TEST003_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i003_client_tests_list[]; + +int32_t client_test_psa_set_rhandle(security_t); +int32_t client_test_call_read_and_skip(security_t); +int32_t client_test_call_and_write(security_t); +int32_t client_test_zero_length_invec(security_t); +int32_t client_test_zero_length_outvec(security_t); +int32_t client_test_overlapping_vectors(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i003/test_supp_i003.c b/psa-ff/test_suites/ipc/test_i003/test_supp_i003.c new file mode 100644 index 00000000..85a4240b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i003/test_supp_i003.c @@ -0,0 +1,520 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_set_rhandle(void); +int32_t server_test_call_read_and_skip(void); +int32_t server_test_call_and_write(void); +int32_t server_test_zero_length_invec(); +int32_t server_test_zero_length_outvec(void); +int32_t server_test_overlapping_vectors(void); + +server_test_t test_i003_server_tests_list[] = { + NULL, + server_test_zero_length_invec, + server_test_zero_length_outvec, + server_test_call_read_and_skip, + server_test_call_and_write, + server_test_psa_set_rhandle, + server_test_overlapping_vectors, + NULL, +}; + +static void exit_graceful(psa_handle_t msg_handle, int status_code, + int print_next_args, int expected_data, int actual_data) +{ + psa_msg_t msg={0}; + + if (print_next_args != 0) + { + val_print(PRINT_ERROR, "\tExpected data=%x\n", expected_data); + val_print(PRINT_ERROR, "\tActual data=%x\n", actual_data); + } + /* Negative status_code represents check failure and each check has + * uniq status_code to identify failing point + */ + psa_reply(msg_handle, status_code); + + if (val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + val_print(PRINT_ERROR, "\tdisconnect failed in exit_graceful func\n", 0); + } + psa_reply(msg.handle, PSA_SUCCESS); +} + +int32_t server_test_zero_length_invec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + int data[5] = {0}, actual_data = 0x22; + + if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_CONNECTION_FAILED; + } + psa_reply(msg.handle, PSA_SUCCESS); + + if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + exit_graceful(msg.handle, -2, 0, 0, 0); + return VAL_STATUS_CALL_FAILED; + } + + if (((msg.in_size[0] != 0)) || + ((msg.in_size[1] != 0)) || + ((msg.in_size[2] != sizeof(int))) || + ((msg.in_size[3] != 0))) + { + exit_graceful(msg.handle, -3, 0, 0, 0); + return VAL_STATUS_MSG_INSIZE_FAILED; + } + + if (psa_read(msg.handle, 2, &data[2], msg.in_size[2]) != msg.in_size[2]) + { + exit_graceful(msg.handle, -4, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + + if (actual_data != data[2]) + { + exit_graceful(msg.handle, -5, 1, data[2], actual_data); + return VAL_STATUS_CALL_FAILED; + } + + if (((msg.out_size[0] != sizeof(int))) || + ((msg.out_size[1] != 0)) || + ((msg.out_size[2] != 0)) || + ((msg.out_size[3] != 0))) + { + exit_graceful(msg.handle, -6, 0, 0, 0); + return VAL_STATUS_MSG_OUTSIZE_FAILED; + } + + psa_write(msg.handle, 0, &data[2], msg.out_size[0]); + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} + +int32_t server_test_zero_length_outvec(void) +{ + int32_t status=VAL_STATUS_SUCCESS; + psa_msg_t msg={0}; + int data[5] ={0}, actual_data=0x11; + + if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_CONNECTION_FAILED; + } + psa_reply(msg.handle, PSA_SUCCESS); + + if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + exit_graceful(msg.handle, -2, 0, 0, 0); + return VAL_STATUS_CALL_FAILED; + } + + if (((msg.in_size[0] != sizeof(int))) || + ((msg.in_size[1] != 0)) || + ((msg.in_size[2] != 0)) || + ((msg.in_size[3] != 0))) + { + exit_graceful(msg.handle, -3, 0, 0, 0); + return VAL_STATUS_MSG_INSIZE_FAILED; + } + + if (psa_read(msg.handle, 0, &data[0], msg.in_size[0]) != msg.in_size[0]) + { + exit_graceful(msg.handle, -4, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + + if (actual_data != data[0]) + { + exit_graceful(msg.handle, -5, 1, data[2], actual_data); + return VAL_STATUS_CALL_FAILED; + } + + if (((msg.out_size[0] != 0)) || + ((msg.out_size[1] != 0)) || + ((msg.out_size[2] != sizeof(int))) || + ((msg.out_size[3] != 0))) + { + exit_graceful(msg.handle, -6, 0, 0, 0); + return VAL_STATUS_MSG_OUTSIZE_FAILED; + } + psa_write(msg.handle, 2, &data[0], msg.out_size[2]); + + /* Dummy write with zero byte. This should not overwrite previously written data */ + psa_write(msg.handle, 2, &data[0], msg.out_size[0]); + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} + +int32_t server_test_call_read_and_skip(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + int expected_data1[2] = {0xaa, 0xbb}; + int expected_data2[4] = {0x7788, 0x2233, 0x2211, 0x50607080}, + actual_data[4] = {0}, i; + psa_msg_t msg = {0}; + + if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_CONNECTION_FAILED; + } + psa_reply(msg.handle, PSA_SUCCESS); + + if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + exit_graceful(msg.handle, -2, 0, 0, 0); + return VAL_STATUS_CALL_FAILED; + } + + /* msg.in_size should match with the size of psa_invec given to psa_call*/ + if (((msg.in_size[0] != sizeof(int))) || + ((msg.in_size[1] != sizeof(int))) || + ((msg.in_size[2] != sizeof(uint64_t))) || + ((msg.in_size[3] != sizeof(uint64_t)))) + { + exit_graceful(msg.handle, -3, 0, 0, 0); + return VAL_STATUS_MSG_INSIZE_FAILED; + } + + /* Full size read for invec 0 and invec 1 */ + for (i = 0; i < 2 ; i++) + { + if (psa_read(msg.handle, i, &actual_data[i], msg.in_size[i]) != msg.in_size[i]) + { + exit_graceful(msg.handle, -4, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + if (actual_data[i] != expected_data1[i]) + { + exit_graceful(msg.handle, -5, 1, expected_data1[i], actual_data[i]); + return VAL_STATUS_READ_FAILED; + } + } + + /* Inbound read of 2 bytes from invec 2 */ + if (psa_read(msg.handle, 2, &actual_data[0], 2) != 2) + { + exit_graceful(msg.handle, -6, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + if (actual_data[0] != expected_data2[0]) + { + exit_graceful(msg.handle, -7, 1, expected_data2[0], actual_data[0]); + return VAL_STATUS_READ_FAILED; + } + + /* Inbound read of 3 bytes from invec 2 */ + if (psa_skip(msg.handle, 2, 3) != 3) + { + exit_graceful(msg.handle, -8, 0, 0, 0); + return VAL_STATUS_SKIP_FAILED; + } + + /* Check previous psa_skip has actually skipped 3 bytes */ + if (psa_read(msg.handle, 2, &actual_data[0], 2) != 2) + { + exit_graceful(msg.handle, -9, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + + if (actual_data[0] != expected_data2[1]) + { + exit_graceful(msg.handle, -10, 1, expected_data2[1], actual_data[0]); + return VAL_STATUS_READ_FAILED; + } + + /* Outbound read of 3 bytes from invec 2 + * Only one byte should be updated in buffer. Remaining space should be untouched + */ + if (psa_read(msg.handle, 2, &actual_data[0], 3) != 1) + { + exit_graceful(msg.handle, -11, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + + if (actual_data[0] != expected_data2[2]) + { + exit_graceful(msg.handle, -12, 1, expected_data2[2], actual_data[0]); + return VAL_STATUS_READ_FAILED; + } + + actual_data[0] = 0xaa; + + /* After outbound read, subsequent read or skip to invec 2 should return 0 + * and memory buffer shouldn't be updated + */ + if ((psa_read(msg.handle, 2, &actual_data[0], 3) != 0) || + (psa_skip(msg.handle, 2, 3) != 0) || (actual_data[0] != 0xaa)) + { + exit_graceful(msg.handle, -13, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + + /* Read of zero bytes should not read anything */ + if ((psa_read(msg.handle, 3, &actual_data[0], 0) != 0) || (actual_data[0] != 0xaa)) + { + exit_graceful(msg.handle, -14, 0, 0, 0); + return VAL_STATUS_READ_FAILED; + } + + /* Skip of zero bytes should not skip anything */ + if (psa_skip(msg.handle, 3, 0) != 0) + { + exit_graceful(msg.handle, -15, 0, 0, 0); + return VAL_STATUS_SKIP_FAILED; + } + + /* Check effect of previous zero byte read and skip */ + psa_read(msg.handle, 3, &actual_data[0], 4); + if (actual_data[0] != expected_data2[3]) + { + exit_graceful(msg.handle, -16, 1, expected_data2[3], actual_data[0]); + return VAL_STATUS_READ_FAILED; + } + + /* Outbound skip to invec 3 */ + if (psa_skip(msg.handle, 3, 5) != 4) + { + exit_graceful(msg.handle, -17, 0, 0, 0); + return VAL_STATUS_SKIP_FAILED; + } + if (psa_skip(msg.handle, 3, 5) != 0) + { + exit_graceful(msg.handle, -18, 0, 0, 0); + return VAL_STATUS_SKIP_FAILED; + } + + /* Since out_len is 0, msg.out_size should be 0 */ + if ((msg.out_size[0] != 0) || + (msg.out_size[1] != 0) || + (msg.out_size[2] != 0) || + (msg.out_size[3] != 0)) + { + exit_graceful(msg.handle, -14, 0, 0, 0); + return VAL_STATUS_MSG_OUTSIZE_FAILED; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} + +int32_t server_test_call_and_write(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + int data[5] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee}, i; + psa_msg_t msg = {0}; + + if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_CONNECTION_FAILED; + } + psa_reply(msg.handle, PSA_SUCCESS); + + if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + { + exit_graceful(msg.handle, -2, 0, 0, 0); + return VAL_STATUS_CALL_FAILED; + } + + /* msg.out_size should match with size of psa_outvec given to psa_call */ + if (((msg.out_size[0] != sizeof(int))) || + ((msg.out_size[1] != sizeof(int))) || + ((msg.out_size[2] != sizeof(int))) || + ((msg.out_size[3] != sizeof(int)))) + { + exit_graceful(msg.handle, -3, 0, 0, 0); + return VAL_STATUS_MSG_OUTSIZE_FAILED; + } + + /* Since in_len=0, all msg.in_size should be 0 */ + if ((msg.in_size[0] != 0) || + (msg.in_size[1] != 0) || + (msg.in_size[2] != 0) || + (msg.in_size[3] != 0)) + + { + exit_graceful(msg.handle, -4, 0, 0, 0); + return VAL_STATUS_MSG_INSIZE_FAILED; + } + + for (i = 0; i < 3 ; i++) + { + psa_write(msg.handle, i, &data[i], msg.out_size[i]); + } + + /* Zero byte write shouldn't have any effect */ + psa_write(msg.handle, 3, &data[3], 0); + + /*Using invec 3 to test write concatenation behaviour */ + psa_write(msg.handle, 3, &data[3], 1); + psa_write(msg.handle, 3, &data[4], 1); + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} + +int32_t server_test_psa_set_rhandle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + int *num = NULL; + + /*rhandle value check when PSA_IPC_CONNECT */ + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (msg.rhandle != NULL) + { + status = VAL_STATUS_INVALID_HANDLE; + } + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + + /*rhandle value check when PSA_IPC_CALL */ + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (msg.rhandle != NULL) + { + status = VAL_STATUS_INVALID_HANDLE; + } + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + exit_graceful(msg.handle, -2, 0, 0, 0); + return status; + } + + /*set rhandle */ + *num = 5; + psa_set_rhandle(msg.handle, num); + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (msg.rhandle != num) + { + status = VAL_STATUS_INVALID_HANDLE; + } + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + exit_graceful(msg.handle, -3, 0, 0, 0); + return status; + } + + /*set rhandle other than previous value to see effect of + * updated value in next msg delivery. Next msg should + * return the updated value. + */ + *num = 10; + psa_set_rhandle(msg.handle, num); + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (msg.rhandle != num) + { + status = VAL_STATUS_INVALID_HANDLE; + } + if (val_err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + exit_graceful(msg.handle, -4, 0, 0, 0); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + /* rhandle should retain the value at PSA_IPC_DISCONNECT too */ + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (msg.rhandle != num) + { + status = VAL_STATUS_INVALID_HANDLE; + } + val_err_check_set(TEST_CHECKPOINT_NUM(205), status); + + /*Setting the rhandle for a connection during disconnection has no observable effect*/ + *num = 15; + psa_set_rhandle(msg.handle, num); + + /* Sanity check - previous call shouldn't hang */ + val_err_check_set(TEST_CHECKPOINT_NUM(206), status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} + +int32_t server_test_overlapping_vectors(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t wr_data[] = {0x22, 0x33}, + rd_data[] = {0x0, 0x0}, + expected_data[] = {0x11, 0x22}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(207), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(208), status)) + { + exit_graceful(msg.handle, -1, 0, 0, 0); + return status; + } + + /* Performing read after write to overlapping vector. */ + psa_write(msg.handle, 0, &wr_data[0], 1); + psa_read(msg.handle, 0, &rd_data[0], 1); + + /* rd_data[0] should either be original value or modified value */ + if ((rd_data[0] != expected_data[0]) && (rd_data[0] != expected_data[1])) + { + val_print(PRINT_ERROR, "\tReceived invalid data=%x\n", rd_data[0]); + exit_graceful(msg.handle, -2, 0, 0, 0); + return status; + } + + /* Performing write after write to overlapping vector. */ + psa_write(msg.handle, 1, &wr_data[1], 1); + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + val_err_check_set(TEST_CHECKPOINT_NUM(209), status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i004/source.mk b/psa-ff/test_suites/ipc/test_i004/source.mk new file mode 100644 index 00000000..515add7f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i004/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i004.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i004/test_entry.c b/psa-ff/test_suites/ipc/test_i004/test_entry.c new file mode 100644 index 00000000..991770af --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i004/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i004.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 4) +#define TEST_DESC "Testing psa_connect with invalid sid\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i004_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i004/test_i004.c b/psa-ff/test_suites/ipc/test_i004/test_i004.c new file mode 100644 index 00000000..1e94d474 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i004/test_i004.c @@ -0,0 +1,79 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i004.h" + +client_test_t test_i004_client_tests_list[] = { + NULL, + client_test_sid_does_not_exists, + NULL, +}; + +int32_t client_test_sid_does_not_exists(security_t caller) +{ + psa_handle_t handle = 0; + boot_state_t boot_state; + + val->print(PRINT_TEST, "[Check1] psa_connect with invalid sid \n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Test check - psa_connect with INVALID_SID, call must not return */ + handle = psa->connect(INVALID_SID, 1); + + /* Control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_connect with invalid sid should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + if (handle > 0) + { + psa->close(handle); + } + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i004/test_i004.h b/psa-ff/test_suites/ipc/test_i004/test_i004.h new file mode 100644 index 00000000..80d04564 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i004/test_i004.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST004_CLIENT_TESTS_H_ +#define _TEST004_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i004_client_tests_list[]; + +int32_t client_test_sid_does_not_exists(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i004/test_supp_i004.c b/psa-ff/test_suites/ipc/test_i004/test_supp_i004.c new file mode 100644 index 00000000..8d1abd28 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i004/test_supp_i004.c @@ -0,0 +1,33 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_sid_does_not_exists(); + +server_test_t test_i004_server_tests_list[] = { + NULL, + server_test_sid_does_not_exists, + NULL, +}; + +int32_t server_test_sid_does_not_exists() +{ + val_err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/ipc/test_i005/source.mk b/psa-ff/test_suites/ipc/test_i005/source.mk new file mode 100644 index 00000000..08441c7d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i005/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i005.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i005/test_entry.c b/psa-ff/test_suites/ipc/test_i005/test_entry.c new file mode 100644 index 00000000..e0e22955 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i005/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i005.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 5) +#define TEST_DESC "Testing STRICT policy with higher minor version\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i005_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i005/test_i005.c b/psa-ff/test_suites/ipc/test_i005/test_i005.c new file mode 100644 index 00000000..38b8536b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i005/test_i005.c @@ -0,0 +1,79 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i005.h" + +client_test_t test_i005_client_tests_list[] = { + NULL, + client_test_strict_policy_higher_minor_version, + NULL, +}; + +int32_t client_test_strict_policy_higher_minor_version(security_t caller) +{ + psa_handle_t handle = 0; + boot_state_t boot_state; + + val->print(PRINT_TEST, "[Check1] Test STRICT policy with higher minor version\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Test check- Version policy is strict and requested version is bigger than + * the minimum version. + */ + handle = psa->connect(SERVER_STRICT_MINOR_VERSION_SID, 3); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, + "\tSTRICT policy with higher minor version should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i005/test_i005.h b/psa-ff/test_suites/ipc/test_i005/test_i005.h new file mode 100644 index 00000000..c9d13531 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i005/test_i005.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST005_CLIENT_TESTS_H_ +#define _TEST005_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i005_client_tests_list[]; + +int32_t client_test_strict_policy_higher_minor_version(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i005/test_supp_i005.c b/psa-ff/test_suites/ipc/test_i005/test_supp_i005.c new file mode 100644 index 00000000..b50c5489 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i005/test_supp_i005.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_strict_policy_higher_minor_version(void); + +server_test_t test_i005_server_tests_list[] = { + NULL, + server_test_strict_policy_higher_minor_version, + NULL, +}; + +int32_t server_test_strict_policy_higher_minor_version(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_STRICT_MINOR_VERSION_SIG, &msg); + + /* Shouldn't have reached here */ + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return VAL_STATUS_INVALID; + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i006/source.mk b/psa-ff/test_suites/ipc/test_i006/source.mk new file mode 100644 index 00000000..51a2ebfa --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i006/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i006.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i006/test_entry.c b/psa-ff/test_suites/ipc/test_i006/test_entry.c new file mode 100644 index 00000000..2c83556a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i006/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i006.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 6) +#define TEST_DESC "Testing STRICT policy with lower minor version\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i006_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i006/test_i006.c b/psa-ff/test_suites/ipc/test_i006/test_i006.c new file mode 100644 index 00000000..3c896a97 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i006/test_i006.c @@ -0,0 +1,77 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i006.h" + +client_test_t test_i006_client_tests_list[] = { + NULL, + client_test_strict_policy_lower_minor_version, + NULL, +}; + +int32_t client_test_strict_policy_lower_minor_version(security_t caller) +{ + psa_handle_t handle = 0; + boot_state_t boot_state; + + val->print(PRINT_TEST, "[Check1] Test STRICT policy with lower minor version\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /*Version policy is strict and requested version is smaller than the minimum version */ + handle = psa->connect(SERVER_STRICT_MINOR_VERSION_SID, 1); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, + "\tSTRICT policy with lower minor version should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i006/test_i006.h b/psa-ff/test_suites/ipc/test_i006/test_i006.h new file mode 100644 index 00000000..2d0ed6d2 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i006/test_i006.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST006_CLIENT_TESTS_H_ +#define _TEST006_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i006_client_tests_list[]; + +int32_t client_test_strict_policy_lower_minor_version(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i006/test_supp_i006.c b/psa-ff/test_suites/ipc/test_i006/test_supp_i006.c new file mode 100644 index 00000000..b1cd95e9 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i006/test_supp_i006.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_strict_policy_lower_minor_version(void); + +server_test_t test_i006_server_tests_list[] = { + NULL, + server_test_strict_policy_lower_minor_version, + NULL, +}; + +int32_t server_test_strict_policy_lower_minor_version(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* Shouldn't have reached here */ + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return VAL_STATUS_INVALID; + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i007/source.mk b/psa-ff/test_suites/ipc/test_i007/source.mk new file mode 100644 index 00000000..ebe0ae4c --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i007/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i007.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i007/test_entry.c b/psa-ff/test_suites/ipc/test_i007/test_entry.c new file mode 100644 index 00000000..ddfe3cdd --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i007/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i007.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 7) +#define TEST_DESC "Testing RELAX policy with higher minor version\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i007_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i007/test_i007.c b/psa-ff/test_suites/ipc/test_i007/test_i007.c new file mode 100644 index 00000000..dfea14c0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i007/test_i007.c @@ -0,0 +1,77 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i007.h" + +client_test_t test_i007_client_tests_list[] = { + NULL, + client_test_relax_policy_higher_minor_version, + NULL, +}; + +int32_t client_test_relax_policy_higher_minor_version(security_t caller) +{ + psa_handle_t handle = 0; + boot_state_t boot_state; + + val->print(PRINT_TEST, "[Check1] Test RELAX policy with higher minor version\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /*Version policy is relaxed and requested version is higher than the minimum version */ + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 3); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, + "\tRELAXED policy with higher minor version should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i007/test_i007.h b/psa-ff/test_suites/ipc/test_i007/test_i007.h new file mode 100644 index 00000000..0f2c6913 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i007/test_i007.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST007_CLIENT_TESTS_H_ +#define _TEST007_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i007_client_tests_list[]; + +int32_t client_test_relax_policy_higher_minor_version(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i007/test_supp_i007.c b/psa-ff/test_suites/ipc/test_i007/test_supp_i007.c new file mode 100644 index 00000000..dd703f21 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i007/test_supp_i007.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_relax_policy_higher_minor_version(void); + +server_test_t test_i007_server_tests_list[] = { + NULL, + server_test_relax_policy_higher_minor_version, + NULL, +}; + +int32_t server_test_relax_policy_higher_minor_version(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + + /* Shouldn't have reached here */ + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return VAL_STATUS_INVALID; + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i008/source.mk b/psa-ff/test_suites/ipc/test_i008/source.mk new file mode 100644 index 00000000..7b408fff --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i008/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i008.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i008/test_entry.c b/psa-ff/test_suites/ipc/test_i008/test_entry.c new file mode 100644 index 00000000..9a9162a8 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i008/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i008.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 8) +#define TEST_DESC "Testing secure access only connection\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i008_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i008/test_i008.c b/psa-ff/test_suites/ipc/test_i008/test_i008.c new file mode 100644 index 00000000..decdf38c --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i008/test_i008.c @@ -0,0 +1,93 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i008.h" + +client_test_t test_i008_client_tests_list[] = { + NULL, + client_test_secure_access_only_connection, + NULL, +}; + +int32_t client_test_secure_access_only_connection(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test secure access only connection\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check for NS*/ + if(caller == NONSECURE) + { + status = val->set_boot_flag(BOOT_EXPECTED_NS); + } + if(VAL_ERROR(status)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return status; + } + /* Below psa_connect should not return for call from nspe and + * should succeed for call from spe + */ + handle = psa->connect(SERVER_SECURE_CONNECT_ONLY_SID, 1); + + if(caller == NONSECURE) + { + /* Shouldn't have reached here for NS run*/ + val->print(PRINT_ERROR, "\tSecure access only connection test failed for NS run\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + status = VAL_STATUS_SPM_FAILED; + } + + /* Should return positive handle for SPE connection */ + if(handle > 0) + { + psa->close(handle); + } + else + { + val->print(PRINT_ERROR, "\tSecure access only connection failed\n", 0); + status = VAL_STATUS_SPM_FAILED; + } + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i008/test_i008.h b/psa-ff/test_suites/ipc/test_i008/test_i008.h new file mode 100644 index 00000000..7eb7737f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i008/test_i008.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST008_CLIENT_TESTS_H_ +#define _TEST008_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i008_client_tests_list[]; + +int32_t client_test_secure_access_only_connection(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i008/test_supp_i008.c b/psa-ff/test_suites/ipc/test_i008/test_supp_i008.c new file mode 100644 index 00000000..711621e5 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i008/test_supp_i008.c @@ -0,0 +1,50 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_secure_access_only_connection(void); + +server_test_t test_i008_server_tests_list[] = { + NULL, + server_test_secure_access_only_connection, + NULL, +}; + +int32_t server_test_secure_access_only_connection(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_SECURE_CONNECT_ONLY_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_SECURE_CONNECT_ONLY_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i009/source.mk b/psa-ff/test_suites/ipc/test_i009/source.mk new file mode 100644 index 00000000..0607651a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i009/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i009.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i009/test_entry.c b/psa-ff/test_suites/ipc/test_i009/test_entry.c new file mode 100644 index 00000000..992e9e23 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i009/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i009.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 9) +#define TEST_DESC "Testing unextern SID connection\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i009/test_i009.c b/psa-ff/test_suites/ipc/test_i009/test_i009.c new file mode 100644 index 00000000..fde8cf3f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i009/test_i009.c @@ -0,0 +1,73 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i009.h" + +client_test_t test_i009_client_tests_list[] = { + NULL, + client_test_unextern_sid_connection, + NULL, +}; + +int32_t client_test_unextern_sid_connection(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test unextern SID connection\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_S)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + handle = psa->connect(SERVER_UNEXTERN_SID, 2); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tunextern SID connection should have failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i009/test_i009.h b/psa-ff/test_suites/ipc/test_i009/test_i009.h new file mode 100644 index 00000000..85f73967 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i009/test_i009.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST009_CLIENT_TESTS_H_ +#define _TEST009_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i009_client_tests_list[]; + +int32_t client_test_unextern_sid_connection(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i009/test_supp_i009.c b/psa-ff/test_suites/ipc/test_i009/test_supp_i009.c new file mode 100644 index 00000000..864f39b8 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i009/test_supp_i009.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_unextern_sid_connection(void); + +server_test_t test_i009_server_tests_list[] = { + NULL, + server_test_unextern_sid_connection, + NULL, +}; + +int32_t server_test_unextern_sid_connection(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_UNEXTERN_SIG, &msg); + + /* Shouldn't have reached here */ + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return VAL_STATUS_INVALID; + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i010/source.mk b/psa-ff/test_suites/ipc/test_i010/source.mk new file mode 100644 index 00000000..6986b18d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i010/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i010.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i010/test_entry.c b/psa-ff/test_suites/ipc/test_i010/test_entry.c new file mode 100644 index 00000000..fa42b059 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i010/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i010.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 10) +#define TEST_DESC "Testing un-specified minor_policy with higher minor version\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i010_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i010/test_i010.c b/psa-ff/test_suites/ipc/test_i010/test_i010.c new file mode 100644 index 00000000..9a3241fc --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i010/test_i010.c @@ -0,0 +1,77 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i010.h" + +client_test_t test_i010_client_tests_list[] = { + NULL, + client_test_unspecified_policy_with_higher_minor_ver, + NULL, +}; + +int32_t client_test_unspecified_policy_with_higher_minor_ver(security_t caller) +{ + psa_handle_t handle = 0; + boot_state_t boot_state; + + val->print(PRINT_TEST, + "[Check1] Test un-specified minor_policy with higher minor version\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 3); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, + "\tun-specified policy with higher minor version should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i010/test_i010.h b/psa-ff/test_suites/ipc/test_i010/test_i010.h new file mode 100644 index 00000000..89aaa91f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i010/test_i010.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST010_CLIENT_TESTS_H_ +#define _TEST010_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i010_client_tests_list[]; + +int32_t client_test_unspecified_policy_with_higher_minor_ver(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i010/test_supp_i010.c b/psa-ff/test_suites/ipc/test_i010/test_supp_i010.c new file mode 100644 index 00000000..406760c3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i010/test_supp_i010.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_unspecified_policy_with_higher_minor_ver(void); + +server_test_t test_i010_server_tests_list[] = { + NULL, + server_test_unspecified_policy_with_higher_minor_ver, + NULL, +}; + +int32_t server_test_unspecified_policy_with_higher_minor_ver(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* Shouldn't have reached here */ + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return VAL_STATUS_INVALID; + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i011/source.mk b/psa-ff/test_suites/ipc/test_i011/source.mk new file mode 100644 index 00000000..692cc780 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i011/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i011.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i011/test_entry.c b/psa-ff/test_suites/ipc/test_i011/test_entry.c new file mode 100644 index 00000000..ef4b6f5a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i011/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i011.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 11) +#define TEST_DESC "Testing un-specified minor_policy with lower minor version\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i011_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i011/test_i011.c b/psa-ff/test_suites/ipc/test_i011/test_i011.c new file mode 100644 index 00000000..e15d869b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i011/test_i011.c @@ -0,0 +1,77 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i011.h" + +client_test_t test_i011_client_tests_list[] = { + NULL, + client_test_unspecified_policy_with_lower_minor_ver, + NULL, +}; + +int32_t client_test_unspecified_policy_with_lower_minor_ver(security_t caller) +{ + psa_handle_t handle = 0; + boot_state_t boot_state; + + val->print(PRINT_TEST, + "[Check1] Test un-specified minor_policy with higher minor version\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 0); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, + "\tun-specified policy with lower minor version should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i011/test_i011.h b/psa-ff/test_suites/ipc/test_i011/test_i011.h new file mode 100644 index 00000000..b3ef30fe --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i011/test_i011.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST011_CLIENT_TESTS_H_ +#define _TEST011_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i011_client_tests_list[]; + +int32_t client_test_unspecified_policy_with_lower_minor_ver(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i011/test_supp_i011.c b/psa-ff/test_suites/ipc/test_i011/test_supp_i011.c new file mode 100644 index 00000000..a4a5e21b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i011/test_supp_i011.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_unspecified_policy_with_lower_minor_ver(void); + +server_test_t test_i011_server_tests_list[] = { + NULL, + server_test_unspecified_policy_with_lower_minor_ver, + NULL, +}; + +int32_t server_test_unspecified_policy_with_lower_minor_ver(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* Shouldn't have reached here */ + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + return VAL_STATUS_INVALID; + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i012/source.mk b/psa-ff/test_suites/ipc/test_i012/source.mk new file mode 100644 index 00000000..16f861bf --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i012/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i012.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i012/test_entry.c b/psa-ff/test_suites/ipc/test_i012/test_entry.c new file mode 100644 index 00000000..c319510a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i012/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i012.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 12) +#define TEST_DESC "Testing psa_close with invalid handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i012_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i012/test_i012.c b/psa-ff/test_suites/ipc/test_i012/test_i012.c new file mode 100644 index 00000000..7fe6a838 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i012/test_i012.c @@ -0,0 +1,74 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i012.h" + +client_test_t test_i012_client_tests_list[] = { + NULL, + client_test_psa_close_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_close_with_invalid_handle(security_t caller) +{ + boot_state_t boot_state; + + val->print(PRINT_TEST, "[Check1] Test psa_close with invalid handle\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* psa_close() with invalid handle */ + psa->close(INVALID_HANDLE); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_close(invalid_handle) should have failed but succeeded\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i012/test_i012.h b/psa-ff/test_suites/ipc/test_i012/test_i012.h new file mode 100644 index 00000000..b1f93f54 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i012/test_i012.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST012_CLIENT_TESTS_H_ +#define _TEST012_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i012_client_tests_list[]; + +int32_t client_test_psa_close_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i012/test_supp_i012.c b/psa-ff/test_suites/ipc/test_i012/test_supp_i012.c new file mode 100644 index 00000000..104552ce --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i012/test_supp_i012.c @@ -0,0 +1,33 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_close_with_invalid_handle(void); + +server_test_t test_i012_server_tests_list[] = { + NULL, + server_test_psa_close_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_close_with_invalid_handle(void) +{ + val_err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/test_suites/ipc/test_i013/source.mk b/psa-ff/test_suites/ipc/test_i013/source.mk new file mode 100644 index 00000000..c55c6822 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i013/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i013.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i013/test_entry.c b/psa-ff/test_suites/ipc/test_i013/test_entry.c new file mode 100644 index 00000000..9a8620b1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i013/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i013.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 13) +#define TEST_DESC "Testing psa_get with multiple signals\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i013_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i013/test_i013.c b/psa-ff/test_suites/ipc/test_i013/test_i013.c new file mode 100644 index 00000000..ac9752aa --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i013/test_i013.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i013.h" + +client_test_t test_i013_client_tests_list[] = { + NULL, + client_test_psa_get_with_more_than_one_signal, + NULL, +}; + +int32_t client_test_psa_get_with_more_than_one_signal(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_get with multiple signals\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i013/test_i013.h b/psa-ff/test_suites/ipc/test_i013/test_i013.h new file mode 100644 index 00000000..54ddf79f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i013/test_i013.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST013_CLIENT_TESTS_H_ +#define _TEST013_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i013_client_tests_list[]; + +int32_t client_test_psa_get_with_more_than_one_signal(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i013/test_supp_i013.c b/psa-ff/test_suites/ipc/test_i013/test_supp_i013.c new file mode 100644 index 00000000..f4e3f402 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i013/test_supp_i013.c @@ -0,0 +1,77 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_get_with_more_than_one_signal(void); + +server_test_t test_i013_server_tests_list[] = { + NULL, + server_test_psa_get_with_more_than_one_signal, + NULL, +}; + +int32_t server_test_psa_get_with_more_than_one_signal(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + if ((psa_wait_any(PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + { + /* Setting boot.state before test check */ + if (val_set_boot_flag(BOOT_EXPECTED_NS)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + /* Unblock client */ + if (psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_ERROR; + } + + /* multiple signals check */ + psa_get((SERVER_UNSPECIFED_MINOR_V_SIG | SERVER_RELAX_MINOR_VERSION_SID), &msg); + + /* shouldn't have reached here */ + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(203), status); + + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i014/source.mk b/psa-ff/test_suites/ipc/test_i014/source.mk new file mode 100644 index 00000000..ba29e7db --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i014/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i014.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i014/test_entry.c b/psa-ff/test_suites/ipc/test_i014/test_entry.c new file mode 100644 index 00000000..0ad7222d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i014/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i014.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 14) +#define TEST_DESC "Testing psa_get called twice\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i014_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i014/test_i014.c b/psa-ff/test_suites/ipc/test_i014/test_i014.c new file mode 100644 index 00000000..c11a8ab3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i014/test_i014.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i014.h" + +client_test_t test_i014_client_tests_list[] = { + NULL, + client_test_psa_get_called_twice, + NULL, +}; + +int32_t client_test_psa_get_called_twice(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_get called twice\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i014/test_i014.h b/psa-ff/test_suites/ipc/test_i014/test_i014.h new file mode 100644 index 00000000..13faa48e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i014/test_i014.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST014_CLIENT_TESTS_H_ +#define _TEST014_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i014_client_tests_list[]; + +int32_t client_test_psa_get_called_twice(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i014/test_supp_i014.c b/psa-ff/test_suites/ipc/test_i014/test_supp_i014.c new file mode 100644 index 00000000..31b29440 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i014/test_supp_i014.c @@ -0,0 +1,81 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_get_called_twice(void); + +server_test_t test_i014_server_tests_list[] = { + NULL, + server_test_psa_get_called_twice, + NULL, +}; + +int32_t server_test_psa_get_called_twice(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + +wait: + if ((psa_wait_any(PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + { + /* First psa_get call */ + if (psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } + /* Debug print */ + val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + + /* Setting boot.state before test check */ + if (val_set_boot_flag(BOOT_EXPECTED_NS)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + /* Unblock client */ + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_ERROR; + } + + /* Second psa_get call. This should not return */ + psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* shouldn't have reached here */ + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(202), status); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i015/source.mk b/psa-ff/test_suites/ipc/test_i015/source.mk new file mode 100644 index 00000000..ab2caf7f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i015/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i015.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i015/test_entry.c b/psa-ff/test_suites/ipc/test_i015/test_entry.c new file mode 100644 index 00000000..b6ddaa29 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i015/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i015.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 15) +#define TEST_DESC "Testing psa_get with non-RoT signal\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i015_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i015/test_i015.c b/psa-ff/test_suites/ipc/test_i015/test_i015.c new file mode 100644 index 00000000..715a9479 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i015/test_i015.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i015.h" + +client_test_t test_i015_client_tests_list[] = { + NULL, + client_test_psa_get_with_non_rot_signal, + NULL, +}; + +int32_t client_test_psa_get_with_non_rot_signal(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_get with non-RoT signal\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i015/test_i015.h b/psa-ff/test_suites/ipc/test_i015/test_i015.h new file mode 100644 index 00000000..9512bea7 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i015/test_i015.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST015_CLIENT_TESTS_H_ +#define _TEST015_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i015_client_tests_list[]; + +int32_t client_test_psa_get_with_non_rot_signal(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i015/test_supp_i015.c b/psa-ff/test_suites/ipc/test_i015/test_supp_i015.c new file mode 100644 index 00000000..08c91e36 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i015/test_supp_i015.c @@ -0,0 +1,74 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_get_with_non_rot_signal(void); + +server_test_t test_i015_server_tests_list[] = { + NULL, + server_test_psa_get_with_non_rot_signal, + NULL, +}; + +int32_t server_test_psa_get_with_non_rot_signal(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + if ((psa_wait_any(PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Call to psa_get should not return */ + psa_get(PSA_DOORBELL, &msg); + + /* shouldn't have reached here */ + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(203), status); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i016/source.mk b/psa-ff/test_suites/ipc/test_i016/source.mk new file mode 100644 index 00000000..acce71c4 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i016/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i016.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i016/test_entry.c b/psa-ff/test_suites/ipc/test_i016/test_entry.c new file mode 100644 index 00000000..62aaf758 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i016/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i016.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 16) +#define TEST_DESC "Testing psa_get with unasserted signal\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i016_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i016/test_i016.c b/psa-ff/test_suites/ipc/test_i016/test_i016.c new file mode 100644 index 00000000..6edbbe94 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i016/test_i016.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i016.h" + +client_test_t test_i016_client_tests_list[] = { + NULL, + client_test_psa_get_with_unasserted_signal, + NULL, +}; + +int32_t client_test_psa_get_with_unasserted_signal(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_get with unasserted signal\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i016/test_i016.h b/psa-ff/test_suites/ipc/test_i016/test_i016.h new file mode 100644 index 00000000..f5d9d2be --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i016/test_i016.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST015_CLIENT_TESTS_H_ +#define _TEST015_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i016_client_tests_list[]; + +int32_t client_test_psa_get_with_unasserted_signal(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i016/test_supp_i016.c b/psa-ff/test_suites/ipc/test_i016/test_supp_i016.c new file mode 100644 index 00000000..4b2e5a59 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i016/test_supp_i016.c @@ -0,0 +1,74 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_get_with_unasserted_signal(void); + +server_test_t test_i016_server_tests_list[] = { + NULL, + server_test_psa_get_with_unasserted_signal, + NULL, +}; + +int32_t server_test_psa_get_with_unasserted_signal(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + if ((psa_wait_any(PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Unasserted signal check, call to psa_get should not return */ + psa_get(SERVER_RELAX_MINOR_VERSION_SID, &msg); + + /* shouldn't have reached here */ + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(203), status); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i017/source.mk b/psa-ff/test_suites/ipc/test_i017/source.mk new file mode 100644 index 00000000..040be0d4 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i017/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i017.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i017/test_entry.c b/psa-ff/test_suites/ipc/test_i017/test_entry.c new file mode 100644 index 00000000..a53b16ed --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i017/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i017.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 17) +#define TEST_DESC "Testing partition calling its own RoT service\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i017_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i017/test_i017.c b/psa-ff/test_suites/ipc/test_i017/test_i017.c new file mode 100644 index 00000000..47c54d70 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i017/test_i017.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i017.h" + +client_test_t test_i017_client_tests_list[] = { + NULL, + client_test_partition_calling_its_own_rot_service, + NULL, +}; + +int32_t client_test_partition_calling_its_own_rot_service(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test partition calling its own RoT service\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i017/test_i017.h b/psa-ff/test_suites/ipc/test_i017/test_i017.h new file mode 100644 index 00000000..3ad60d00 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i017/test_i017.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST017_CLIENT_TESTS_H_ +#define _TEST017_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i017_client_tests_list[]; + +int32_t client_test_partition_calling_its_own_rot_service(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i017/test_supp_i017.c b/psa-ff/test_suites/ipc/test_i017/test_supp_i017.c new file mode 100644 index 00000000..50082602 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i017/test_supp_i017.c @@ -0,0 +1,82 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_partition_calling_its_own_rot_service(void); + +server_test_t test_i017_server_tests_list[] = { + NULL, + server_test_partition_calling_its_own_rot_service, + NULL, +}; + +int32_t server_test_partition_calling_its_own_rot_service(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Calling server partition RoT service using IPC and call should not return */ + handle = psa_connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + + (void)(handle); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i018/source.mk b/psa-ff/test_suites/ipc/test_i018/source.mk new file mode 100644 index 00000000..fe6e94f1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i018/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i018.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i018/test_entry.c b/psa-ff/test_suites/ipc/test_i018/test_entry.c new file mode 100644 index 00000000..ef164f1d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i018/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i018.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 18) +#define TEST_DESC "Testing psa_set_rhandle with invalid msg handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i018_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i018/test_i018.c b/psa-ff/test_suites/ipc/test_i018/test_i018.c new file mode 100644 index 00000000..726c2b9b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i018/test_i018.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i018.h" + +client_test_t test_i018_client_tests_list[] = { + NULL, + client_test_psa_set_rhandle_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_set_rhandle_with_invalid_handle(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_set_rhandle with invalid msg handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i018/test_i018.h b/psa-ff/test_suites/ipc/test_i018/test_i018.h new file mode 100644 index 00000000..0ad2487e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i018/test_i018.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST018_CLIENT_TESTS_H_ +#define _TEST018_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i018_client_tests_list[]; + +int32_t client_test_psa_set_rhandle_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i018/test_supp_i018.c b/psa-ff/test_suites/ipc/test_i018/test_supp_i018.c new file mode 100644 index 00000000..e951455f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i018/test_supp_i018.c @@ -0,0 +1,83 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_set_rhandle_with_invalid_handle(void); + +server_test_t test_i018_server_tests_list[] = { + NULL, + server_test_psa_set_rhandle_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_set_rhandle_with_invalid_handle(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + int *num = NULL; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Call psa_set_rhandle with INVALID_HANLDE. Call shouldn't return */ + *num = 5; + psa_set_rhandle(INVALID_HANDLE, num); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_set_rhandle with invalid handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return status; + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i019/source.mk b/psa-ff/test_suites/ipc/test_i019/source.mk new file mode 100644 index 00000000..5146eeeb --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i019/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i019.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i019/test_entry.c b/psa-ff/test_suites/ipc/test_i019/test_entry.c new file mode 100644 index 00000000..a2ccc6c4 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i019/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i019.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 19) +#define TEST_DESC "Testing psa_set_rhandle with NULL msg handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i019_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i019/test_i019.c b/psa-ff/test_suites/ipc/test_i019/test_i019.c new file mode 100644 index 00000000..3e81e496 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i019/test_i019.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i019.h" + +client_test_t test_i019_client_tests_list[] = { + NULL, + client_test_psa_set_rhandle_with_null_handle, + NULL, +}; + +int32_t client_test_psa_set_rhandle_with_null_handle(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_set_rhandle with null msg handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i019/test_i019.h b/psa-ff/test_suites/ipc/test_i019/test_i019.h new file mode 100644 index 00000000..ea11995f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i019/test_i019.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST019_CLIENT_TESTS_H_ +#define _TEST019_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i019_client_tests_list[]; + +int32_t client_test_psa_set_rhandle_with_null_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i019/test_supp_i019.c b/psa-ff/test_suites/ipc/test_i019/test_supp_i019.c new file mode 100644 index 00000000..a45d891e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i019/test_supp_i019.c @@ -0,0 +1,72 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_set_rhandle_with_null_handle(void); + +server_test_t test_i019_server_tests_list[] = { + NULL, + server_test_psa_set_rhandle_with_null_handle, + NULL, +}; + +int32_t server_test_psa_set_rhandle_with_null_handle(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + int *num = NULL; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Set boot flag to BOOT_EXPECTED_* to indicate- test is targeting + fatal error condition and test will expect error recovery to happen */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /*Call psa_set_rhandle with PSA_NULL_HANDLE. Call shouldn't return*/ + *num = 5; + psa_set_rhandle(PSA_NULL_HANDLE, num); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_set_rhandle with NULL handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return status; + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i020/source.mk b/psa-ff/test_suites/ipc/test_i020/source.mk new file mode 100644 index 00000000..064390b7 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i020/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i020.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i020/test_entry.c b/psa-ff/test_suites/ipc/test_i020/test_entry.c new file mode 100644 index 00000000..2647f9bd --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i020/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i020.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 20) +#define TEST_DESC "Testing psa_reply with invalid status code for PSA_IPC_CONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i020_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i020/test_i020.c b/psa-ff/test_suites/ipc/test_i020/test_i020.c new file mode 100644 index 00000000..62f588de --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i020/test_i020.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i020.h" + +client_test_t test_i020_client_tests_list[] = { + NULL, + client_test_psa_reply_with_invalid_connect_status_code, + NULL, +}; + +int32_t client_test_psa_reply_with_invalid_connect_status_code(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, + "[Check1] Test psa_reply with invalid status code for PSA_IPC_CONNECT\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + (void)(handle); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i020/test_i020.h b/psa-ff/test_suites/ipc/test_i020/test_i020.h new file mode 100644 index 00000000..74c30859 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i020/test_i020.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST020_CLIENT_TESTS_H_ +#define _TEST020_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i020_client_tests_list[]; + +int32_t client_test_psa_reply_with_invalid_connect_status_code(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i020/test_supp_i020.c b/psa-ff/test_suites/ipc/test_i020/test_supp_i020.c new file mode 100644 index 00000000..17014597 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i020/test_supp_i020.c @@ -0,0 +1,80 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_reply_with_invalid_connect_status_code(void); + +server_test_t test_i020_server_tests_list[] = { + NULL, + server_test_psa_reply_with_invalid_connect_status_code, + NULL, +}; + +int32_t server_test_psa_reply_with_invalid_connect_status_code(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + psa_status_t invalid_status_code = PSA_CONNECTION_REFUSED + 0x10; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /*Call psa_reply with invalid status code for PSA_IPC_CONNECT. Call shouldn't return*/ + psa_reply(msg.handle, invalid_status_code); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_reply with invalid status code for connect should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return status; + } + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i021/source.mk b/psa-ff/test_suites/ipc/test_i021/source.mk new file mode 100644 index 00000000..bb36ffe9 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i021/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i021.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i021/test_entry.c b/psa-ff/test_suites/ipc/test_i021/test_entry.c new file mode 100644 index 00000000..45de605d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i021/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i021.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 21) +#define TEST_DESC "Testing psa_reply with invalid status code for PSA_IPC_CALL\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i021_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i021/test_i021.c b/psa-ff/test_suites/ipc/test_i021/test_i021.c new file mode 100644 index 00000000..172fb30a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i021/test_i021.c @@ -0,0 +1,61 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i021.h" + +client_test_t test_i021_client_tests_list[] = { + NULL, + client_test_psa_reply_with_invalid_call_status_code, + NULL, +}; + +int32_t client_test_psa_reply_with_invalid_call_status_code(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_reply with invalid status code for PSA_IPC_CALL\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i021/test_i021.h b/psa-ff/test_suites/ipc/test_i021/test_i021.h new file mode 100644 index 00000000..d94bf79e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i021/test_i021.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST021_CLIENT_TESTS_H_ +#define _TEST021_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i021_client_tests_list[]; + +int32_t client_test_psa_reply_with_invalid_call_status_code(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i021/test_supp_i021.c b/psa-ff/test_suites/ipc/test_i021/test_supp_i021.c new file mode 100644 index 00000000..9e2ec16e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i021/test_supp_i021.c @@ -0,0 +1,92 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_reply_with_invalid_call_status_code(void); + +server_test_t test_i021_server_tests_list[] = { + NULL, + server_test_psa_reply_with_invalid_call_status_code, + NULL, +}; + +int32_t server_test_psa_reply_with_invalid_call_status_code(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + psa_status_t invalid_status_code = INT32_MIN+1; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, (PSA_SUCCESS - 2)); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, (PSA_SUCCESS - 3)); + } + else + { + /* Call psa_reply with invalid status code for PSA_IPC_CALL. Call shouldn't return */ + psa_reply(msg.handle, invalid_status_code); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_reply with invalid status code for CALL should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + status = VAL_STATUS_SPM_FAILED; + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i022/source.mk b/psa-ff/test_suites/ipc/test_i022/source.mk new file mode 100644 index 00000000..4d6fcff6 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i022/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i022.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i022/test_entry.c b/psa-ff/test_suites/ipc/test_i022/test_entry.c new file mode 100644 index 00000000..9b36f64c --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i022/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i022.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 22) +#define TEST_DESC "Testing psa_reply with invalid handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i022_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i022/test_i022.c b/psa-ff/test_suites/ipc/test_i022/test_i022.c new file mode 100644 index 00000000..1085cb06 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i022/test_i022.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i022.h" + +client_test_t test_i022_client_tests_list[] = { + NULL, + client_test_psa_reply_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_reply_with_invalid_handle(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Testing psa_reply with invalid handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i022/test_i022.h b/psa-ff/test_suites/ipc/test_i022/test_i022.h new file mode 100644 index 00000000..5506613d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i022/test_i022.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST022_CLIENT_TESTS_H_ +#define _TEST022_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i022_client_tests_list[]; + +int32_t client_test_psa_reply_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i022/test_supp_i022.c b/psa-ff/test_suites/ipc/test_i022/test_supp_i022.c new file mode 100644 index 00000000..6e4f43ae --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i022/test_supp_i022.c @@ -0,0 +1,80 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_reply_with_invalid_handle(void); + +server_test_t test_i022_server_tests_list[] = { + NULL, + server_test_psa_reply_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_reply_with_invalid_handle(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /*Call psa_reply with INVALID_HANLDE. Call shouldn't return*/ + psa_reply(INVALID_HANDLE, PSA_CONNECTION_REFUSED); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_reply with invalid handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return status; + } + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i023/source.mk b/psa-ff/test_suites/ipc/test_i023/source.mk new file mode 100644 index 00000000..43d511c5 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i023/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i023.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i023/test_entry.c b/psa-ff/test_suites/ipc/test_i023/test_entry.c new file mode 100644 index 00000000..30f0e5e5 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i023/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i023.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 23) +#define TEST_DESC "Testing psa_reply with NULL handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i023_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i023/test_i023.c b/psa-ff/test_suites/ipc/test_i023/test_i023.c new file mode 100644 index 00000000..08275442 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i023/test_i023.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i023.h" + +client_test_t test_i023_client_tests_list[] = { + NULL, + client_test_psa_reply_with_null_handle, + NULL, +}; + +int32_t client_test_psa_reply_with_null_handle(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Testing psa_reply with invalid handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tConnection should failed but successed\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i023/test_i023.h b/psa-ff/test_suites/ipc/test_i023/test_i023.h new file mode 100644 index 00000000..8d065f05 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i023/test_i023.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST023_CLIENT_TESTS_H_ +#define _TEST023_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i023_client_tests_list[]; + +int32_t client_test_psa_reply_with_null_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i023/test_supp_i023.c b/psa-ff/test_suites/ipc/test_i023/test_supp_i023.c new file mode 100644 index 00000000..d28aa448 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i023/test_supp_i023.c @@ -0,0 +1,82 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_reply_with_null_handle(void); + +server_test_t test_i023_server_tests_list[] = { + NULL, + server_test_psa_reply_with_null_handle, + NULL, +}; + +int32_t server_test_psa_reply_with_null_handle(void) +{ + psa_msg_t msg = {0}; + int32_t status = VAL_STATUS_SUCCESS; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Test check - Call psa_reply with PSA_NULL_HANDLE. Call shouldn't return */ + psa_reply(PSA_NULL_HANDLE, PSA_CONNECTION_REFUSED); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_reply with NULL handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return status; + } + + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + status = VAL_STATUS_SPM_FAILED; + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i024/source.mk b/psa-ff/test_suites/ipc/test_i024/source.mk new file mode 100644 index 00000000..8b9ee544 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i024/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i024.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i024/test_entry.c b/psa-ff/test_suites/ipc/test_i024/test_entry.c new file mode 100644 index 00000000..35c258db --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i024/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i024.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 24) +#define TEST_DESC "Testing psa_call with invalid handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i024_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i024/test_i024.c b/psa-ff/test_suites/ipc/test_i024/test_i024.c new file mode 100644 index 00000000..d6a8551a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i024/test_i024.c @@ -0,0 +1,89 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i024.h" + +client_test_t test_i024_client_tests_list[] = { + NULL, + client_test_psa_call_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_call_with_invalid_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + boot_state_t boot_state; + + val->print(PRINT_TEST, + "[Check1] Test psa_call with invalid handle\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Test check- psa_call with INVALID_HANDLE */ + status_of_call = psa->call(INVALID_HANDLE, NULL, 0, NULL, 0); + + /* Expectation is psa_call should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + status = VAL_STATUS_SPM_FAILED; + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i024/test_i024.h b/psa-ff/test_suites/ipc/test_i024/test_i024.h new file mode 100644 index 00000000..1d38d682 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i024/test_i024.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST024_CLIENT_TESTS_H_ +#define _TEST024_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i024_client_tests_list[]; + +int32_t client_test_psa_call_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i024/test_supp_i024.c b/psa-ff/test_suites/ipc/test_i024/test_supp_i024.c new file mode 100644 index 00000000..d3bbbf13 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i024/test_supp_i024.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_call_with_invalid_handle(void); + +server_test_t test_i024_server_tests_list[] = { + NULL, + server_test_psa_call_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_call_with_invalid_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* Control shouldn't have come here */ + val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); + psa_reply(msg.handle, -2); + + val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i025/source.mk b/psa-ff/test_suites/ipc/test_i025/source.mk new file mode 100644 index 00000000..bb0b6af4 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i025/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i025.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i025/test_entry.c b/psa-ff/test_suites/ipc/test_i025/test_entry.c new file mode 100644 index 00000000..7dec64af --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i025/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i025.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 25) +#define TEST_DESC "Testing psa_call with NULL handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i025_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i025/test_i025.c b/psa-ff/test_suites/ipc/test_i025/test_i025.c new file mode 100644 index 00000000..a8cd7264 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i025/test_i025.c @@ -0,0 +1,89 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i025.h" + +client_test_t test_i025_client_tests_list[] = { + NULL, + client_test_psa_call_with_null_handle, + NULL, +}; + +int32_t client_test_psa_call_with_null_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + boot_state_t boot_state; + + val->print(PRINT_TEST, + "[Check1] Test psa_call with NULL handle\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Test check- psa_call with NULL HANDLE */ + status_of_call = psa->call(PSA_NULL_HANDLE, NULL, 0, NULL, 0); + + /* Expectation is psa_call should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + status = VAL_STATUS_SPM_FAILED; + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i025/test_i025.h b/psa-ff/test_suites/ipc/test_i025/test_i025.h new file mode 100644 index 00000000..3b871e80 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i025/test_i025.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST025_CLIENT_TESTS_H_ +#define _TEST025_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i025_client_tests_list[]; + +int32_t client_test_psa_call_with_null_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i025/test_supp_i025.c b/psa-ff/test_suites/ipc/test_i025/test_supp_i025.c new file mode 100644 index 00000000..036a2c9e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i025/test_supp_i025.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_call_with_null_handle(void); + +server_test_t test_i025_server_tests_list[] = { + NULL, + server_test_psa_call_with_null_handle, + NULL, +}; + +int32_t server_test_psa_call_with_null_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* Control shouldn't have come here */ + val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); + psa_reply(msg.handle, -2); + + val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i026/source.mk b/psa-ff/test_suites/ipc/test_i026/source.mk new file mode 100644 index 00000000..238a1392 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i026/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i026.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i026/test_entry.c b/psa-ff/test_suites/ipc/test_i026/test_entry.c new file mode 100644 index 00000000..feeba462 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i026/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i026.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 26) +#define TEST_DESC "Testing psa_call with IOVEC > PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i026_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i026/test_i026.c b/psa-ff/test_suites/ipc/test_i026/test_i026.c new file mode 100644 index 00000000..2c41d18a --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i026/test_i026.c @@ -0,0 +1,96 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i026.h" + +client_test_t test_i026_client_tests_list[] = { + NULL, + client_test_psa_call_with_iovec_more_than_max_limit, + NULL, +}; + +int32_t client_test_psa_call_with_iovec_more_than_max_limit(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + uint8_t data = 0x11; + psa_status_t status_of_call; + boot_state_t boot_state; + psa_invec invec[PSA_MAX_IOVEC] = {{&data, sizeof(data)}, + {&data, sizeof(data)}, + {&data, sizeof(data)}, + {&data, sizeof(data)} + }; + psa_outvec outvec[1] = {{&data, sizeof(data)}}; + + val->print(PRINT_TEST, + "[Check1] Test psa_call with IOVEC > PSA_MAX_IOVEC\n", 0); + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Setting boot.state before test check */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Test check- psa_call with IOVEC > PSA_MAX_IOVEC */ + status_of_call = psa->call(handle, invec, PSA_MAX_IOVEC, outvec, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } + + status = VAL_STATUS_SPM_FAILED; + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i026/test_i026.h b/psa-ff/test_suites/ipc/test_i026/test_i026.h new file mode 100644 index 00000000..34c81c8d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i026/test_i026.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST026_CLIENT_TESTS_H_ +#define _TEST026_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +/* Redefining PSA_MAX_IOVEC as it is undefined for client */ +#ifndef PSA_MAX_IOVEC +#define PSA_MAX_IOVEC 4 +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i026_client_tests_list[]; + +int32_t client_test_psa_call_with_iovec_more_than_max_limit(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i026/test_supp_i026.c b/psa-ff/test_suites/ipc/test_i026/test_supp_i026.c new file mode 100644 index 00000000..0e5be8f0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i026/test_supp_i026.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_call_with_iovec_more_than_max_limit(); + +server_test_t test_i026_server_tests_list[] = { + NULL, + server_test_psa_call_with_iovec_more_than_max_limit, + NULL, +}; + +int32_t server_test_psa_call_with_iovec_more_than_max_limit() +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + + /* Control shouldn't have come here */ + val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); + psa_reply(msg.handle, -2); + + val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa_reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i027/source.mk b/psa-ff/test_suites/ipc/test_i027/source.mk new file mode 100644 index 00000000..69f6fc86 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i027/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i027.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i027/test_entry.c b/psa-ff/test_suites/ipc/test_i027/test_entry.c new file mode 100644 index 00000000..3f09f0b9 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i027/test_entry.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i027.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 27) +#define TEST_DESC "Testing PSA_DROP_CONNECTION\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i027_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + + /* Switch to secure side (client_partition.c) and execute list of tests available in + test[num]_client_tests_list from Secure side */ + status = val->switch_to_secure_client(TEST_NUM); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i027/test_i027.c b/psa-ff/test_suites/ipc/test_i027/test_i027.c new file mode 100644 index 00000000..28571321 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i027/test_i027.c @@ -0,0 +1,119 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i027.h" + +client_test_t test_i027_client_tests_list[] = { + NULL, + client_test_psa_drop_connection, + NULL, +}; + +int32_t client_test_psa_drop_connection(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + boot_state_t boot_state; + + val->print(PRINT_TEST, + "[Check1] Test PSA_DROP_CONNECTION\n", 0); + + /* Test algorithm: + * + * RoT service will terminate the connection by completing the message with a call + * to psa_reply() using the status code PSA_DROP_CONNECTION. + * + * The SPM can implement the required PSA_DROP_CONNECTION behavior in different ways: + * + * 1. Return PSA_DROP_CONNECTION to the client from the faulty psa_call() and mark the + * SPM connection object so that all future calls to psa_call() on this connection are + * immediately failed with PSA_DROP_CONNECTION. And SPM must deliver a PSA_IPC_DISCONNECT + * message for the connection to the RoT Service directly after receipt of the + * PSA_DROP_CONNECTION completion to allow connection resources within the RoT Service + * to be released. + * + * 2. Terminate the client task and restart if appropriate for the system + * + * + * If SPM implementation happens to be to first case, the test will expect psa_call to return + * PSA_DROP_CONNECTION for every future call to psa_call on the connection + * + * If SPM implementation happens to be second case, test will expect does not return behaviour + * for executed psa_call API + */ + + handle = psa->connect(SERVER_CONNECTION_DROP_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Setting boot flag to BOOT_EXPECTED_* to help error recovery if SPM behaviour + * matches with 2nd case + */ + boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; + if (val->set_boot_flag(boot_state)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Resetting boot.state to catch unwanted reboot */ + status = val->set_boot_flag(BOOT_NOT_EXPECTED); + if (VAL_ERROR(status)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + goto exit; + } + + /* Case 1 implementation */ + if (status_of_call == PSA_DROP_CONNECTION) + { + val->print(PRINT_DEBUG, "\tRecieved PSA_DROP_CONNECTION\n", 0); + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + if (status_of_call != PSA_DROP_CONNECTION) + { + status = VAL_STATUS_SPM_FAILED; + val->print(PRINT_ERROR, + "\tCall should have returned PSA_DROP_CONNECTION. Status = 0x%x\n", status_of_call); + } + } + /* If implementation is case 2, control shouldn't have come here*/ + else + { + /* psa_call should hang and control shouldn't have come here */ + status = VAL_STATUS_SPM_FAILED; + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + } + +exit: + psa->close(handle); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i027/test_i027.h b/psa-ff/test_suites/ipc/test_i027/test_i027.h new file mode 100644 index 00000000..c32bc9f3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i027/test_i027.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST027_CLIENT_TESTS_H_ +#define _TEST027_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i027_client_tests_list[]; + +int32_t client_test_psa_drop_connection(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i027/test_supp_i027.c b/psa-ff/test_suites/ipc/test_i027/test_supp_i027.c new file mode 100644 index 00000000..4c1ac218 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i027/test_supp_i027.c @@ -0,0 +1,65 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_drop_connection(void); + +server_test_t test_i027_server_tests_list[] = { + NULL, + server_test_psa_drop_connection, + NULL, +}; + +int32_t server_test_psa_drop_connection(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = val_process_connect_request(SERVER_CONNECTION_DROP_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_CONNECTION_DROP_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + goto exit; + } + else + { + psa_reply(msg.handle, PSA_DROP_CONNECTION); + } + +exit: + /* SPM must deliver a PSA_IPC_DISCONNECT message for the connection to the RoT Service + directly after receipt of the PSA_DROP_CONNECTION completion to allow connection resources + within the RoT Service to be released */ + status = val_process_disconnect_request(SERVER_CONNECTION_DROP_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tDisconnect request failed\n", 0); + } + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i028/source.mk b/psa-ff/test_suites/ipc/test_i028/source.mk new file mode 100644 index 00000000..51bba191 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i028/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i028.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i028/test_entry.c b/psa-ff/test_suites/ipc/test_i028/test_entry.c new file mode 100644 index 00000000..6126f471 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i028/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i028.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 28) +#define TEST_DESC "Testing psa_read at PSA_IPC_CONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if(!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i028_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i028/test_i028.c b/psa-ff/test_suites/ipc/test_i028/test_i028.c new file mode 100644 index 00000000..4d63732e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i028/test_i028.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i028.h" + +client_test_t test_i028_client_tests_list[] = { + NULL, + client_test_psa_read_at_ipc_connect, + NULL, +}; + +int32_t client_test_psa_read_at_ipc_connect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_read at PSA_IPC_CONNECT\n", 0); + + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tConnection should have failed but succeeded\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i028/test_i028.h b/psa-ff/test_suites/ipc/test_i028/test_i028.h new file mode 100644 index 00000000..de8dcb59 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i028/test_i028.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST028_CLIENT_TESTS_H_ +#define _TEST028_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i028_client_tests_list[]; + +int32_t client_test_psa_read_at_ipc_connect(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i028/test_supp_i028.c b/psa-ff/test_suites/ipc/test_i028/test_supp_i028.c new file mode 100644 index 00000000..88df3c59 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i028/test_supp_i028.c @@ -0,0 +1,78 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_read_at_ipc_connect(); + +server_test_t test_i028_server_tests_list[] = { + NULL, + server_test_psa_read_at_ipc_connect, + NULL, +}; + +int32_t server_test_psa_read_at_ipc_connect() +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /*psa_read at PSA_IPC_CONNECT */ + psa_read(msg.handle, 0, (void *)data, 0); + + /* Shouldn't have reached here */ + val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i029/source.mk b/psa-ff/test_suites/ipc/test_i029/source.mk new file mode 100644 index 00000000..2e28cf8e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i029/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i029.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i029/test_entry.c b/psa-ff/test_suites/ipc/test_i029/test_entry.c new file mode 100644 index 00000000..e548d0f5 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i029/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i029.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 29) +#define TEST_DESC "Testing psa_read at PSA_IPC_DISCONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i029_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i029/test_i029.c b/psa-ff/test_suites/ipc/test_i029/test_i029.c new file mode 100644 index 00000000..b867eb79 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i029/test_i029.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i029.h" + +client_test_t test_i029_client_tests_list[] = { + NULL, + client_test_psa_read_at_ipc_disconnect, + NULL, +}; + +int32_t client_test_psa_read_at_ipc_disconnect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_read at PSA_IPC_DISCONNECT\n", 0); + + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCheck for psa_read at PSA_IPC_DISCONNECT failed\n", 0); + + if (handle > 0) + { + psa->close(handle); + } + + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i029/test_i029.h b/psa-ff/test_suites/ipc/test_i029/test_i029.h new file mode 100644 index 00000000..a463189e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i029/test_i029.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST029_CLIENT_TESTS_H_ +#define _TEST029_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i029_client_tests_list[]; + +int32_t client_test_psa_read_at_ipc_disconnect(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i029/test_supp_i029.c b/psa-ff/test_suites/ipc/test_i029/test_supp_i029.c new file mode 100644 index 00000000..1738b102 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i029/test_supp_i029.c @@ -0,0 +1,85 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_read_at_ipc_disconnect(void); + +server_test_t test_i029_server_tests_list[] = { + NULL, + server_test_psa_read_at_ipc_disconnect, + NULL, +}; + +int32_t server_test_psa_read_at_ipc_disconnect(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, PSA_SUCCESS); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + psa_reply(msg.handle, PSA_SUCCESS); + return status; + } + + /* Test check - psa_read at PSA_IPC_DISCONNECT */ + psa_read(msg.handle, 0, (void *)data, 0); + + /* Shouldn't have reached here */ + val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i030/source.mk b/psa-ff/test_suites/ipc/test_i030/source.mk new file mode 100644 index 00000000..933df7f1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i030/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i030.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i030/test_entry.c b/psa-ff/test_suites/ipc/test_i030/test_entry.c new file mode 100644 index 00000000..bef8a99b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i030/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i030.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 30) +#define TEST_DESC "Testing psa_read with NULL handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i030_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i030/test_i030.c b/psa-ff/test_suites/ipc/test_i030/test_i030.c new file mode 100644 index 00000000..6b383ed0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i030/test_i030.c @@ -0,0 +1,61 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i030.h" + +client_test_t test_i030_client_tests_list[] = { + NULL, + client_test_psa_read_with_null_handle, + NULL, +}; + +int32_t client_test_psa_read_with_null_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_read with NULL handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i030/test_i030.h b/psa-ff/test_suites/ipc/test_i030/test_i030.h new file mode 100644 index 00000000..b06d1008 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i030/test_i030.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST030_CLIENT_TESTS_H_ +#define _TEST030_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i030_client_tests_list[]; + +int32_t client_test_psa_read_with_null_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i030/test_supp_i030.c b/psa-ff/test_suites/ipc/test_i030/test_supp_i030.c new file mode 100644 index 00000000..fb3afd7c --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i030/test_supp_i030.c @@ -0,0 +1,97 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_read_with_null_handle(void); + +server_test_t test_i030_server_tests_list[] = { + NULL, + server_test_psa_read_with_null_handle, + NULL, +}; + +int32_t server_test_psa_read_with_null_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_read with PSA_NULL_HANDLE */ + psa_read(PSA_NULL_HANDLE, 0, (void *)data, 0); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_read with NULL handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + status = VAL_STATUS_SPM_FAILED; + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i031/source.mk b/psa-ff/test_suites/ipc/test_i031/source.mk new file mode 100644 index 00000000..ab357fba --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i031/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i031.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i031/test_entry.c b/psa-ff/test_suites/ipc/test_i031/test_entry.c new file mode 100644 index 00000000..eeb9f449 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i031/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i031.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 31) +#define TEST_DESC "Testing psa_read with invalid handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i031_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i031/test_i031.c b/psa-ff/test_suites/ipc/test_i031/test_i031.c new file mode 100644 index 00000000..d802d181 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i031/test_i031.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i031.h" + +client_test_t test_i031_client_tests_list[] = { + NULL, + client_test_psa_read_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_read_with_invalid_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_read with invalid handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i031/test_i031.h b/psa-ff/test_suites/ipc/test_i031/test_i031.h new file mode 100644 index 00000000..1ba26dd4 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i031/test_i031.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST031_CLIENT_TESTS_H_ +#define _TEST031_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i031_client_tests_list[]; + +int32_t client_test_psa_read_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i031/test_supp_i031.c b/psa-ff/test_suites/ipc/test_i031/test_supp_i031.c new file mode 100644 index 00000000..3b41ba16 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i031/test_supp_i031.c @@ -0,0 +1,97 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_read_with_invalid_handle(void); + +server_test_t test_i031_server_tests_list[] = { + NULL, + server_test_psa_read_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_read_with_invalid_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_read with INVALID_HANDLE */ + psa_read(INVALID_HANDLE, 0, (void *)data, 0); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_read with invalid handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + status = VAL_STATUS_SPM_FAILED; + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i032/source.mk b/psa-ff/test_suites/ipc/test_i032/source.mk new file mode 100644 index 00000000..73756358 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i032/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i032.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i032/test_entry.c b/psa-ff/test_suites/ipc/test_i032/test_entry.c new file mode 100644 index 00000000..6e3826a8 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i032/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i032.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 32) +#define TEST_DESC "Testing psa_read with invec_idx=PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i032_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i032/test_i032.c b/psa-ff/test_suites/ipc/test_i032/test_i032.c new file mode 100644 index 00000000..f8b7adce --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i032/test_i032.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i032.h" + +client_test_t test_i032_client_tests_list[] = { + NULL, + client_test_psa_read_with_invec_equal_to_max_iovec, + NULL, +}; + +int32_t client_test_psa_read_with_invec_equal_to_max_iovec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_read with invec_idx=PSA_MAX_IOVEC\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i032/test_i032.h b/psa-ff/test_suites/ipc/test_i032/test_i032.h new file mode 100644 index 00000000..f82a29df --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i032/test_i032.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST032_CLIENT_TESTS_H_ +#define _TEST032_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i032_client_tests_list[]; + +int32_t client_test_psa_read_with_invec_equal_to_max_iovec(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i032/test_supp_i032.c b/psa-ff/test_suites/ipc/test_i032/test_supp_i032.c new file mode 100644 index 00000000..941615c3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i032/test_supp_i032.c @@ -0,0 +1,97 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_read_with_invec_equal_to_max_iovec(void); + +server_test_t test_i032_server_tests_list[] = { + NULL, + server_test_psa_read_with_invec_equal_to_max_iovec, + NULL, +}; + +int32_t server_test_psa_read_with_invec_equal_to_max_iovec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if(val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_read with invec_idx=PSA_MAX_IOVEC */ + psa_read(msg.handle, PSA_MAX_IOVEC, (void *)data, 0); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_read with invec_idx=PSA_MAX_IOVEC should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + status = VAL_STATUS_SPM_FAILED; + psa_reply(msg.handle, -4); + + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i033/source.mk b/psa-ff/test_suites/ipc/test_i033/source.mk new file mode 100644 index 00000000..fbacde75 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i033/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i033.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i033/test_entry.c b/psa-ff/test_suites/ipc/test_i033/test_entry.c new file mode 100644 index 00000000..75af2bf3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i033/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i033.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 33) +#define TEST_DESC "Testing psa_read with invec_idx > PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i033_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i033/test_i033.c b/psa-ff/test_suites/ipc/test_i033/test_i033.c new file mode 100644 index 00000000..4eed6538 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i033/test_i033.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i033.h" + +client_test_t test_i033_client_tests_list[] = { + NULL, + client_test_psa_read_with_invec_greater_than_max_iovec, + NULL, +}; + +int32_t client_test_psa_read_with_invec_greater_than_max_iovec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_read with invec_idx > PSA_MAX_IOVEC\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i033/test_i033.h b/psa-ff/test_suites/ipc/test_i033/test_i033.h new file mode 100644 index 00000000..856d5778 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i033/test_i033.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST033_CLIENT_TESTS_H_ +#define _TEST033_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i033_client_tests_list[]; + +int32_t client_test_psa_read_with_invec_greater_than_max_iovec(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i033/test_supp_i033.c b/psa-ff/test_suites/ipc/test_i033/test_supp_i033.c new file mode 100644 index 00000000..70f9308b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i033/test_supp_i033.c @@ -0,0 +1,97 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_read_with_invec_greater_than_max_iovec(void); + +server_test_t test_i033_server_tests_list[] = { + NULL, + server_test_psa_read_with_invec_greater_than_max_iovec, + NULL, +}; + +int32_t server_test_psa_read_with_invec_greater_than_max_iovec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_read with invec_idx > PSA_MAX_IOVEC */ + psa_read(msg.handle, PSA_MAX_IOVEC + 1, (void *)data, 0); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_read with invec_idx > PSA_MAX_IOVEC should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + status = VAL_STATUS_SPM_FAILED; + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i034/source.mk b/psa-ff/test_suites/ipc/test_i034/source.mk new file mode 100644 index 00000000..752c27e0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i034/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i034.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i034/test_entry.c b/psa-ff/test_suites/ipc/test_i034/test_entry.c new file mode 100644 index 00000000..6538f41d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i034/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i034.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 34) +#define TEST_DESC "Testing psa_skip at PSA_IPC_CONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i034_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i034/test_i034.c b/psa-ff/test_suites/ipc/test_i034/test_i034.c new file mode 100644 index 00000000..4fa1bbe3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i034/test_i034.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i034.h" + +client_test_t test_i034_client_tests_list[] = { + NULL, + client_test_psa_skip_at_ipc_connect, + NULL, +}; + +int32_t client_test_psa_skip_at_ipc_connect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_skip at PSA_IPC_CONNECT\n", 0); + + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tConnection should have failed but succeeded\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i034/test_i034.h b/psa-ff/test_suites/ipc/test_i034/test_i034.h new file mode 100644 index 00000000..bc119bec --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i034/test_i034.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST034_CLIENT_TESTS_H_ +#define _TEST034_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i034_client_tests_list[]; + +int32_t client_test_psa_skip_at_ipc_connect(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i034/test_supp_i034.c b/psa-ff/test_suites/ipc/test_i034/test_supp_i034.c new file mode 100644 index 00000000..6f98c12d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i034/test_supp_i034.c @@ -0,0 +1,76 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_skip_at_ipc_connect(void); + +server_test_t test_i034_server_tests_list[] = { + NULL, + server_test_psa_skip_at_ipc_connect, + NULL, +}; + +int32_t server_test_psa_skip_at_ipc_connect(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Test check- psa_skip at PSA_IPC_CONNECT */ + psa_skip(msg.handle, 0, 0); + + /* Shouldn't have reached here */ + val_print(PRINT_ERROR,"\tpsa_skip should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i035/source.mk b/psa-ff/test_suites/ipc/test_i035/source.mk new file mode 100644 index 00000000..a542e1c3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i035/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i035.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i035/test_entry.c b/psa-ff/test_suites/ipc/test_i035/test_entry.c new file mode 100644 index 00000000..be5041b2 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i035/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i035.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 35) +#define TEST_DESC "Testing psa_skip at PSA_IPC_DISCONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i035_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i035/test_i035.c b/psa-ff/test_suites/ipc/test_i035/test_i035.c new file mode 100644 index 00000000..11c58de1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i035/test_i035.c @@ -0,0 +1,50 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i035.h" + +client_test_t test_i035_client_tests_list[] = { + NULL, + client_test_psa_skip_at_ipc_disconnect, + NULL, +}; + +int32_t client_test_psa_skip_at_ipc_disconnect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_skip at PSA_IPC_DISCONNECT\n", 0); + + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + if (handle > 0) + { + psa->close(handle); + } + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCheck for psa_skip at PSA_IPC_DISCONNECT failed\n", 0); + + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i035/test_i035.h b/psa-ff/test_suites/ipc/test_i035/test_i035.h new file mode 100644 index 00000000..dca0f6d3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i035/test_i035.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST035_CLIENT_TESTS_H_ +#define _TEST035_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i035_client_tests_list[]; + +int32_t client_test_psa_skip_at_ipc_disconnect(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i035/test_supp_i035.c b/psa-ff/test_suites/ipc/test_i035/test_supp_i035.c new file mode 100644 index 00000000..1bec82bf --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i035/test_supp_i035.c @@ -0,0 +1,82 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_skip_at_ipc_disconnect(); + +server_test_t test_i035_server_tests_list[] = { + NULL, + server_test_psa_skip_at_ipc_disconnect, + NULL, +}; + +int32_t server_test_psa_skip_at_ipc_disconnect() +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, PSA_SUCCESS); + return status; + } + + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_SUCCESS); + return status; + } + + /* Test check- psa_skip at PSA_IPC_DISCONNECT */ + psa_skip(msg.handle, 0, 0); + + /* Shouldn't have reached here */ + val_print(PRINT_ERROR,"\tpsa_skip should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i036/source.mk b/psa-ff/test_suites/ipc/test_i036/source.mk new file mode 100644 index 00000000..0a4b4ea5 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i036/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i036.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i036/test_entry.c b/psa-ff/test_suites/ipc/test_i036/test_entry.c new file mode 100644 index 00000000..590cea38 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i036/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i036.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 36) +#define TEST_DESC "Testing psa_skip with NULL handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i036_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i036/test_i036.c b/psa-ff/test_suites/ipc/test_i036/test_i036.c new file mode 100644 index 00000000..9945161b --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i036/test_i036.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i036.h" + +client_test_t test_i036_client_tests_list[] = { + NULL, + client_test_psa_skip_with_null_handle, + NULL, +}; + +int32_t client_test_psa_skip_with_null_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_skip with NULL handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i036/test_i036.h b/psa-ff/test_suites/ipc/test_i036/test_i036.h new file mode 100644 index 00000000..84fa9206 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i036/test_i036.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST036_CLIENT_TESTS_H_ +#define _TEST036_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i036_client_tests_list[]; + +int32_t client_test_psa_skip_with_null_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i036/test_supp_i036.c b/psa-ff/test_suites/ipc/test_i036/test_supp_i036.c new file mode 100644 index 00000000..e7039959 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i036/test_supp_i036.c @@ -0,0 +1,94 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_skip_with_null_handle(void); + +server_test_t test_i036_server_tests_list[] = { + NULL, + server_test_psa_skip_with_null_handle, + NULL, +}; + +int32_t server_test_psa_skip_with_null_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if(val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if(val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if(val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_skip with PSA_NULL_HANDLE */ + psa_skip(PSA_NULL_HANDLE, 0, 0); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_skip with NULL handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + status = VAL_STATUS_SPM_FAILED; + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i037/source.mk b/psa-ff/test_suites/ipc/test_i037/source.mk new file mode 100644 index 00000000..4bbadb10 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i037/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i037.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i037/test_entry.c b/psa-ff/test_suites/ipc/test_i037/test_entry.c new file mode 100644 index 00000000..65d95887 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i037/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i037.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 37) +#define TEST_DESC "Testing psa_skip with invalid handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i037_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i037/test_i037.c b/psa-ff/test_suites/ipc/test_i037/test_i037.c new file mode 100644 index 00000000..537e5256 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i037/test_i037.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i037.h" + +client_test_t test_i037_client_tests_list[] = { + NULL, + client_test_psa_skip_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_skip_with_invalid_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_skip with invalid handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i037/test_i037.h b/psa-ff/test_suites/ipc/test_i037/test_i037.h new file mode 100644 index 00000000..a9ba9ef1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i037/test_i037.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST037_CLIENT_TESTS_H_ +#define _TEST037_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i037_client_tests_list[]; + +int32_t client_test_psa_skip_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i037/test_supp_i037.c b/psa-ff/test_suites/ipc/test_i037/test_supp_i037.c new file mode 100644 index 00000000..870be92c --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i037/test_supp_i037.c @@ -0,0 +1,82 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_skip_with_invalid_handle(void); + +server_test_t test_i037_server_tests_list[] = { + NULL, + server_test_psa_skip_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_skip_with_invalid_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_skip with INVALID_HANDLE */ + psa_skip(INVALID_HANDLE, 0, 0); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_skip with invalid handle should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + status = VAL_STATUS_SPM_FAILED; + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i038/source.mk b/psa-ff/test_suites/ipc/test_i038/source.mk new file mode 100644 index 00000000..11f06d3e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i038/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i038.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i038/test_entry.c b/psa-ff/test_suites/ipc/test_i038/test_entry.c new file mode 100644 index 00000000..4ab38586 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i038/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i038.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 38) +#define TEST_DESC "Testing psa_skip with invec_idx=PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i038_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i038/test_i038.c b/psa-ff/test_suites/ipc/test_i038/test_i038.c new file mode 100644 index 00000000..468d1062 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i038/test_i038.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i038.h" + +client_test_t test_i038_client_tests_list[] = { + NULL, + client_test_psa_skip_with_invec_equal_to_max_iovec, + NULL, +}; + +int32_t client_test_psa_skip_with_invec_equal_to_max_iovec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_skip with invec_idx=PSA_MAX_IOVEC\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i038/test_i038.h b/psa-ff/test_suites/ipc/test_i038/test_i038.h new file mode 100644 index 00000000..8db4d4ec --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i038/test_i038.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST038_CLIENT_TESTS_H_ +#define _TEST038_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i038_client_tests_list[]; + +int32_t client_test_psa_skip_with_invec_equal_to_max_iovec(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i038/test_supp_i038.c b/psa-ff/test_suites/ipc/test_i038/test_supp_i038.c new file mode 100644 index 00000000..536af3a1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i038/test_supp_i038.c @@ -0,0 +1,96 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_skip_with_invec_equal_to_max_iovec(void); + +server_test_t test_i038_server_tests_list[] = { + NULL, + server_test_psa_skip_with_invec_equal_to_max_iovec, + NULL, +}; + +int32_t server_test_psa_skip_with_invec_equal_to_max_iovec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_skip with invec_idx=PSA_MAX_IOVEC */ + psa_skip(msg.handle, PSA_MAX_IOVEC, 0); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_skip with invec_idx=PSA_MAX_IOVEC should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i039/source.mk b/psa-ff/test_suites/ipc/test_i039/source.mk new file mode 100644 index 00000000..957fcf63 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i039/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i039.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i039/test_entry.c b/psa-ff/test_suites/ipc/test_i039/test_entry.c new file mode 100644 index 00000000..125452a0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i039/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i039.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 39) +#define TEST_DESC "Testing psa_skip with invec_idx > PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i039_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i039/test_i039.c b/psa-ff/test_suites/ipc/test_i039/test_i039.c new file mode 100644 index 00000000..b2f4de41 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i039/test_i039.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i039.h" + +client_test_t test_i039_client_tests_list[] = { + NULL, + client_test_psa_skip_with_invec_greater_than_max_iovec, + NULL, +}; + +int32_t client_test_psa_skip_with_invec_greater_than_max_iovec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_skip with invec_idx > PSA_MAX_IOVEC\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i039/test_i039.h b/psa-ff/test_suites/ipc/test_i039/test_i039.h new file mode 100644 index 00000000..36992490 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i039/test_i039.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST039_CLIENT_TESTS_H_ +#define _TEST039_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i039_client_tests_list[]; + +int32_t client_test_psa_skip_with_invec_greater_than_max_iovec(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i039/test_supp_i039.c b/psa-ff/test_suites/ipc/test_i039/test_supp_i039.c new file mode 100644 index 00000000..58ee41e0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i039/test_supp_i039.c @@ -0,0 +1,96 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_skip_with_invec_greater_than_max_iovec(void); + +server_test_t test_i039_server_tests_list[] = { + NULL, + server_test_psa_skip_with_invec_greater_than_max_iovec, + NULL, +}; + +int32_t server_test_psa_skip_with_invec_greater_than_max_iovec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_skip with invec_idx > PSA_MAX_IOVEC */ + psa_skip(msg.handle, PSA_MAX_IOVEC + 1, 0); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_skip with invec_idx > PSA_MAX_IOVEC should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i040/source.mk b/psa-ff/test_suites/ipc/test_i040/source.mk new file mode 100644 index 00000000..c3f25dfe --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i040/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i040.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i040/test_entry.c b/psa-ff/test_suites/ipc/test_i040/test_entry.c new file mode 100644 index 00000000..ceb1e31d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i040/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i040.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 40) +#define TEST_DESC "Testing psa_write at PSA_IPC_CONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i040_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i040/test_i040.c b/psa-ff/test_suites/ipc/test_i040/test_i040.c new file mode 100644 index 00000000..05922008 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i040/test_i040.c @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i040.h" + +client_test_t test_i040_client_tests_list[] = { + NULL, + client_test_psa_write_at_ipc_connect, + NULL, +}; + +int32_t client_test_psa_write_at_ipc_connect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_write at PSA_IPC_CONNECT\n", 0); + + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tConnection should have failed but succeeded\n", 0); + + (void)(handle); + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i040/test_i040.h b/psa-ff/test_suites/ipc/test_i040/test_i040.h new file mode 100644 index 00000000..610bbf4d --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i040/test_i040.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST040_CLIENT_TESTS_H_ +#define _TEST040_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i040_client_tests_list[]; + +int32_t client_test_psa_write_at_ipc_connect(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i040/test_supp_i040.c b/psa-ff/test_suites/ipc/test_i040/test_supp_i040.c new file mode 100644 index 00000000..47433c3e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i040/test_supp_i040.c @@ -0,0 +1,79 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_write_at_ipc_connect(void); + +server_test_t test_i040_server_tests_list[] = { + NULL, + server_test_psa_write_at_ipc_connect, + NULL, +}; + +int32_t server_test_psa_write_at_ipc_connect(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + /* Test check- psa_write at PSA_IPC_CONNECT */ + psa_write(msg.handle, 0, (void *)data, 0); + + /* Shouldn't have reached here */ + val_print(PRINT_ERROR,"\tpsa_write should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i041/source.mk b/psa-ff/test_suites/ipc/test_i041/source.mk new file mode 100644 index 00000000..a77fe795 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i041/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i041.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i041/test_entry.c b/psa-ff/test_suites/ipc/test_i041/test_entry.c new file mode 100644 index 00000000..b9923d38 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i041/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i041.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 41) +#define TEST_DESC "Testing psa_write at PSA_IPC_DISCONNECT\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i041_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i041/test_i041.c b/psa-ff/test_suites/ipc/test_i041/test_i041.c new file mode 100644 index 00000000..289fbf6f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i041/test_i041.c @@ -0,0 +1,50 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i041.h" + +client_test_t test_i041_client_tests_list[] = { + NULL, + client_test_psa_write_at_ipc_disconnect, + NULL, +}; + +int32_t client_test_psa_write_at_ipc_disconnect(security_t caller) +{ + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check1] Test psa_write at PSA_IPC_DISCONNECT\n", 0); + + handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); + if (handle > 0) + { + psa->close(handle); + } + + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCheck for psa_write at PSA_IPC_DISCONNECT failed\n", 0); + + return VAL_STATUS_SPM_FAILED; +} diff --git a/psa-ff/test_suites/ipc/test_i041/test_i041.h b/psa-ff/test_suites/ipc/test_i041/test_i041.h new file mode 100644 index 00000000..677585fe --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i041/test_i041.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST041_CLIENT_TESTS_H_ +#define _TEST041_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i041_client_tests_list[]; + +int32_t client_test_psa_write_at_ipc_disconnect(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i041/test_supp_i041.c b/psa-ff/test_suites/ipc/test_i041/test_supp_i041.c new file mode 100644 index 00000000..ed483778 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i041/test_supp_i041.c @@ -0,0 +1,86 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_write_at_ipc_disconnect(void); + +server_test_t test_i041_server_tests_list[] = { + NULL, + server_test_psa_write_at_ipc_disconnect, + NULL, +}; + +int32_t server_test_psa_write_at_ipc_disconnect(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_disconnect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, PSA_SUCCESS); + return status; + } + + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, PSA_SUCCESS); + return status; + } + + /* Test check- psa_write at PSA_IPC_DISCONNECT */ + psa_write(msg.handle, 0, (void *)data, 0); + + /* Shouldn't have reached here */ + val_print(PRINT_ERROR,"\tpsa_write should failed but successed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + psa_reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_INVALID; +} diff --git a/psa-ff/test_suites/ipc/test_i042/source.mk b/psa-ff/test_suites/ipc/test_i042/source.mk new file mode 100644 index 00000000..3ea94ee8 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i042/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i042.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i042/test_entry.c b/psa-ff/test_suites/ipc/test_i042/test_entry.c new file mode 100644 index 00000000..b71eaea3 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i042/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i042.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 42) +#define TEST_DESC "Testing psa_write with NULL handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i042_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i042/test_i042.c b/psa-ff/test_suites/ipc/test_i042/test_i042.c new file mode 100644 index 00000000..ff56a636 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i042/test_i042.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i042.h" + +client_test_t test_i042_client_tests_list[] = { + NULL, + client_test_psa_write_with_null_handle, + NULL, +}; + +int32_t client_test_psa_write_with_null_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_write with NULL handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i042/test_i042.h b/psa-ff/test_suites/ipc/test_i042/test_i042.h new file mode 100644 index 00000000..86de5d5f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i042/test_i042.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST042_CLIENT_TESTS_H_ +#define _TEST042_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i042_client_tests_list[]; + +int32_t client_test_psa_write_with_null_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i042/test_supp_i042.c b/psa-ff/test_suites/ipc/test_i042/test_supp_i042.c new file mode 100644 index 00000000..712791e6 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i042/test_supp_i042.c @@ -0,0 +1,82 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +int32_t server_test_psa_write_with_null_handle(); + +server_test_t test_i042_server_tests_list[] = { + NULL, + server_test_psa_write_with_null_handle, + NULL, +}; + +int32_t server_test_psa_write_with_null_handle() +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[4] = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if(val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if(val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Set boot flag to BOOT_EXPECTED_* to indicate- test is targeting + fatal error condition and test will expect error recovery to happen */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if(val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + psa_reply(msg.handle, -3); + } + else + { + /*psa_write with PSA_NULL_HANDLE */ + psa_write(PSA_NULL_HANDLE, 0, (void *)data, 0); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED); + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_write with NULL handle should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i043/source.mk b/psa-ff/test_suites/ipc/test_i043/source.mk new file mode 100644 index 00000000..889842a2 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i043/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i043.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i043/test_entry.c b/psa-ff/test_suites/ipc/test_i043/test_entry.c new file mode 100644 index 00000000..871ee930 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i043/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i043.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 43) +#define TEST_DESC "Testing psa_write with invalid handle\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i043_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i043/test_i043.c b/psa-ff/test_suites/ipc/test_i043/test_i043.c new file mode 100644 index 00000000..24ee058c --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i043/test_i043.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i043.h" + +client_test_t test_i043_client_tests_list[] = { + NULL, + client_test_psa_write_with_invalid_handle, + NULL, +}; + +int32_t client_test_psa_write_with_invalid_handle(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_write with invalid handle\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i043/test_i043.h b/psa-ff/test_suites/ipc/test_i043/test_i043.h new file mode 100644 index 00000000..74d19e1f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i043/test_i043.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST043_CLIENT_TESTS_H_ +#define _TEST043_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i043_client_tests_list[]; + +int32_t client_test_psa_write_with_invalid_handle(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i043/test_supp_i043.c b/psa-ff/test_suites/ipc/test_i043/test_supp_i043.c new file mode 100644 index 00000000..04053f20 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i043/test_supp_i043.c @@ -0,0 +1,98 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_write_with_invalid_handle(void); + +server_test_t test_i043_server_tests_list[] = { + NULL, + server_test_psa_write_with_invalid_handle, + NULL, +}; + +int32_t server_test_psa_write_with_invalid_handle(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_write with INVALID_HANDLE */ + psa_write(INVALID_HANDLE, 0, (void *)data, 0); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_write with invalid handle should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i044/source.mk b/psa-ff/test_suites/ipc/test_i044/source.mk new file mode 100644 index 00000000..31fa6739 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i044/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i044.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i044/test_entry.c b/psa-ff/test_suites/ipc/test_i044/test_entry.c new file mode 100644 index 00000000..212e4b32 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i044/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i044.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 44) +#define TEST_DESC "Testing psa_write with invec_idx=PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i044_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i044/test_i044.c b/psa-ff/test_suites/ipc/test_i044/test_i044.c new file mode 100644 index 00000000..ff3962a0 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i044/test_i044.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i044.h" + +client_test_t test_i044_client_tests_list[] = { + NULL, + client_test_psa_write_with_invec_equal_to_max_iovec, + NULL, +}; + +int32_t client_test_psa_write_with_invec_equal_to_max_iovec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_write with invec_idx=PSA_MAX_IOVEC\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i044/test_i044.h b/psa-ff/test_suites/ipc/test_i044/test_i044.h new file mode 100644 index 00000000..428350b7 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i044/test_i044.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST044_CLIENT_TESTS_H_ +#define _TEST044_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i044_client_tests_list[]; + +int32_t client_test_psa_write_with_invec_equal_to_max_iovec(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i044/test_supp_i044.c b/psa-ff/test_suites/ipc/test_i044/test_supp_i044.c new file mode 100644 index 00000000..1219b5ef --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i044/test_supp_i044.c @@ -0,0 +1,86 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_write_with_invec_equal_to_max_iovec(void); + +server_test_t test_i044_server_tests_list[] = { + NULL, + server_test_psa_write_with_invec_equal_to_max_iovec, + NULL, +}; + +int32_t server_test_psa_write_with_invec_equal_to_max_iovec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_write with invec_idx=PSA_MAX_IOVEC */ + psa_write(msg.handle, PSA_MAX_IOVEC, (void *)data, 0); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_write with invec_idx=PSA_MAX_IOVEC should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i045/source.mk b/psa-ff/test_suites/ipc/test_i045/source.mk new file mode 100644 index 00000000..81fa6db1 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i045/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i045.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i045/test_entry.c b/psa-ff/test_suites/ipc/test_i045/test_entry.c new file mode 100644 index 00000000..e57165c8 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i045/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i045.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 45) +#define TEST_DESC "Testing psa_write with invec_idx > PSA_MAX_IOVEC\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i045_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i045/test_i045.c b/psa-ff/test_suites/ipc/test_i045/test_i045.c new file mode 100644 index 00000000..ba6a31cd --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i045/test_i045.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i045.h" + +client_test_t test_i045_client_tests_list[] = { + NULL, + client_test_psa_write_with_invec_greater_than_max_iovec, + NULL, +}; + +int32_t client_test_psa_write_with_invec_greater_than_max_iovec(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + + val->print(PRINT_TEST, + "[Check1] Test psa_write with invec_idx > PSA_MAX_IOVEC\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i045/test_i045.h b/psa-ff/test_suites/ipc/test_i045/test_i045.h new file mode 100644 index 00000000..19e35992 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i045/test_i045.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST045_CLIENT_TESTS_H_ +#define _TEST045_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i045_client_tests_list[]; + +int32_t client_test_psa_write_with_invec_greater_than_max_iovec(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i045/test_supp_i045.c b/psa-ff/test_suites/ipc/test_i045/test_supp_i045.c new file mode 100644 index 00000000..a16d5a74 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i045/test_supp_i045.c @@ -0,0 +1,98 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_write_with_invec_greater_than_max_iovec(void); + +server_test_t test_i045_server_tests_list[] = { + NULL, + server_test_psa_write_with_invec_greater_than_max_iovec, + NULL, +}; + +int32_t server_test_psa_write_with_invec_greater_than_max_iovec(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_write with invec_idx > PSA_MAX_IOVEC */ + psa_write(msg.handle, PSA_MAX_IOVEC + 1, (void *)data, 0); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_write with invec_idx > PSA_MAX_IOVEC should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i046/source.mk b/psa-ff/test_suites/ipc/test_i046/source.mk new file mode 100644 index 00000000..994a7ac5 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i046/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +CC_SOURCE = test_entry.c test_i046.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/psa-ff/test_suites/ipc/test_i046/test_entry.c b/psa-ff/test_suites/ipc/test_i046/test_entry.c new file mode 100644 index 00000000..aa29c11f --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i046/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_interfaces.h" +#include "val_target.h" +#include "test_i046.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 46) +#define TEST_DESC "Testing psa_write with size overflow\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val = NULL; +psa_api_t *psa = NULL; + +void test_entry(val_api_t *val_api, psa_api_t *psa_api) +{ + int32_t status = VAL_STATUS_SUCCESS; + + val = val_api; + psa = psa_api; + + /* test init */ + val->test_init(TEST_NUM, TEST_DESC, TEST_FIELD(TEST_ISOLATION_L1, WD_LOW_TIMEOUT)); + if (!IS_TEST_START(val->get_status())) + { + goto test_exit; + } + + /* Execute list of tests available in test[num]_client_tests_list from Non-secure side*/ + status = val->execute_non_secure_tests(TEST_NUM, test_i046_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/psa-ff/test_suites/ipc/test_i046/test_i046.c b/psa-ff/test_suites/ipc/test_i046/test_i046.c new file mode 100644 index 00000000..23c192f7 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i046/test_i046.c @@ -0,0 +1,63 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifdef NONSECURE_TEST_BUILD +#include "val_interfaces.h" +#include "val_target.h" +#else +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" +#endif + +#include "test_i046.h" + +client_test_t test_i046_client_tests_list[] = { + NULL, + client_test_psa_write_with_size_overflow, + NULL, +}; + +int32_t client_test_psa_write_with_size_overflow(security_t caller) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call; + uint8_t data = 0; + + val->print(PRINT_TEST, + "[Check1] Test psa_write with size overflow\n", 0); + + handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + if (handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + psa_outvec resp = {&data, sizeof(data)}; + + status_of_call = psa->call(handle, NULL, 0, &resp, 1); + + /* Expectation is server test should hang and control shouldn't have come here */ + val->print(PRINT_ERROR, "\tCall should failed but successed\n", 0); + + status = VAL_STATUS_SPM_FAILED; + + psa->close(handle); + (void)(status_of_call); + return status; +} diff --git a/psa-ff/test_suites/ipc/test_i046/test_i046.h b/psa-ff/test_suites/ipc/test_i046/test_i046.h new file mode 100644 index 00000000..ae771097 --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i046/test_i046.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _TEST046_CLIENT_TESTS_H_ +#define _TEST046_CLIENT_TESTS_H_ + +#ifdef NONSECURE_TEST_BUILD +#include "val.h" +#else +#include "val/common/val_client_defs.h" +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i046_client_tests_list[]; + +int32_t client_test_psa_write_with_size_overflow(security_t); +#endif diff --git a/psa-ff/test_suites/ipc/test_i046/test_supp_i046.c b/psa-ff/test_suites/ipc/test_i046/test_supp_i046.c new file mode 100644 index 00000000..c5db835e --- /dev/null +++ b/psa-ff/test_suites/ipc/test_i046/test_supp_i046.c @@ -0,0 +1,98 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +#define NUM_OF_BYTES 4 + +int32_t server_test_psa_write_with_size_overflow(void); + +server_test_t test_i046_server_tests_list[] = { + NULL, + server_test_psa_write_with_size_overflow, + NULL, +}; + +int32_t server_test_psa_write_with_size_overflow(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + uint8_t data[NUM_OF_BYTES] = {0}; + + /* Test is targeting fatal error condition and it will expect an error recovery(reboot) + * to happen. To decide, a reboot happened was intended as per test scenario or it happended + * due to other reasons, test is setting a boot signature into non-volatile memory before and + * after targeted test check. After a reboot, these boot signatures are being read by the + * VAL APIs to decide test status. + * + * Note: If SPM is not capable of rebooting (just hangs or power down) in fatal error condition, + * a watchdog timer enabled by val_test_init can reboot the system on timeout event. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + */ + + status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + return status; + } + + psa_reply(msg.handle, PSA_SUCCESS); + + status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + { + psa_reply(msg.handle, -2); + } + else + { + /* Setting boot.state before test check */ + status = val_set_boot_flag(BOOT_EXPECTED_NS); + if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa_reply(msg.handle, -3); + } + else + { + /* Test check- psa_write with size overflow */ + psa_write(msg.handle, 0, (void *)data, msg.out_size[0]+1); + + status = VAL_STATUS_SPM_FAILED; + + /* Resetting boot.state to catch unwanted reboot */ + if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + { + val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + } + + /* shouldn't have reached here */ + val_print(PRINT_ERROR, + "\tpsa_write with size overflow should failed but successed\n", 0); + + psa_reply(msg.handle, -4); + } + } + + val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + ? VAL_STATUS_ERROR : status); + psa_reply(msg.handle, PSA_SUCCESS); + return status; +} diff --git a/psa-ff/test_suites/ipc/testsuite.db b/psa-ff/test_suites/ipc/testsuite.db new file mode 100644 index 00000000..ba07652d --- /dev/null +++ b/psa-ff/test_suites/ipc/testsuite.db @@ -0,0 +1,70 @@ +#/** @file +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + + +#List of tests to be compiled and run as part of IPC suite + +(START) + +test_i001 +test_i002 +test_i003 +test_i004 +test_i005 +test_i006 +test_i007 +test_i008 +test_i009 +test_i010 +test_i011 +test_i012 +test_i013 +test_i014 +test_i015 +test_i016 +test_i017 +test_i018 +test_i019 +test_i020 +test_i021 +test_i022 +test_i023 +test_i024 +test_i025 +test_i026 +test_i027 +test_i028 +test_i029 +test_i030 +test_i031 +test_i032 +test_i033 +test_i034 +test_i035 +test_i036 +test_i037 +test_i038 +test_i039 +test_i040 +test_i041 +test_i042 +test_i043 +test_i044 +test_i045 +test_i046 + +(END) diff --git a/psa-ff/test_suites/partition/common/driver_partition.c b/psa-ff/test_suites/partition/common/driver_partition.c new file mode 100644 index 00000000..fa0d276f --- /dev/null +++ b/psa-ff/test_suites/partition/common/driver_partition.c @@ -0,0 +1,192 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val/spe/val_driver_service_apis.h" + +void driver_main(void) +{ + psa_signal_t signals = 0; + psa_msg_t msg = {0}; + uint32_t verbosity = 0, data = 0, arg1=0; + nvmem_param_t nvmem_param; + char string[256] = {0}; + uint8_t buffer[256] = {0}; + wd_param_t wd_param; + uint32_t timeout; + target_param_t target_param; + + while (1) + { + val_status_t fn_status = VAL_STATUS_SUCCESS; + signals = psa_wait_any(PSA_BLOCK); + + /* Initialise target init variables */ + if (signals & DRIVER_TARGET_INIT_SIG) + { + psa_get(DRIVER_TARGET_INIT_SIG, &msg); + switch (msg.type) + { + case PSA_IPC_CONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_CALL: + psa_read(msg.handle, 0, &target_param, msg.in_size[0]); + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + } + } + + /* Service Print functionality */ + if (signals & DRIVER_UART_SIG) + { + psa_get(DRIVER_UART_SIG, &msg); + switch (msg.type) + { + case PSA_IPC_CONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_CALL: + psa_read(msg.handle, 0, &arg1, msg.in_size[0]); + if (arg1 == UART_INIT_SIGN) + { + /* arg1=base_addr, arg2=print_level */ + psa_read(msg.handle, 1, &verbosity, msg.in_size[1]); + fn_status = val_uart_init_sf(target_param.uart_base_addr, verbosity); + } + else + { + /* arg1=verbosity, arg2=string, arg3=DATA */ + verbosity = arg1; + psa_read(msg.handle, 1, &string, msg.in_size[1]); + psa_read(msg.handle, 2, &data, msg.in_size[2]); + fn_status = val_print_sf(verbosity, string, data); + } + if (VAL_ERROR(fn_status)) + { + psa_reply(msg.handle, VAL_STATUS_ERROR); + } + else + { + psa_reply(msg.handle, PSA_SUCCESS); + } + break; + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + } + } + + /* Service Watchdog functionality */ + if (signals & DRIVER_WATCHDOG_SIG) + { + psa_get(DRIVER_WATCHDOG_SIG, &msg); + switch (msg.type) + { + case PSA_IPC_CALL: + psa_read(msg.handle, 0, &wd_param, msg.in_size[0]); + if (wd_param.wd_fn_type == WD_INIT_SEQ) + { + if (wd_param.wd_timeout_type == WD_LOW_TIMEOUT) + { + timeout = target_param.wd_time_us_low; + } + else if (wd_param.wd_timeout_type == WD_MEDIUM_TIMEOUT) + { + timeout = target_param.wd_time_us_medium; + } + else + { + timeout = target_param.wd_time_us_high; + } + fn_status = val_wd_timer_init_sf(target_param.wd_base_addr, timeout, + target_param.wd_timer_tick_us); + } + else if (wd_param.wd_fn_type == WD_ENABLE_SEQ) + { + fn_status = val_wd_timer_enable_sf(target_param.wd_base_addr); + } + else if (wd_param.wd_fn_type == WD_DISABLE_SEQ) + { + fn_status = val_wd_timer_disable_sf(target_param.wd_base_addr); + } + else if (wd_param.wd_fn_type == WD_STATUS_SEQ) + { + fn_status = val_is_wd_timer_enabled_sf(target_param.wd_base_addr); + } + if (fn_status == VAL_STATUS_SUCCESS) + { + psa_reply(msg.handle, PSA_SUCCESS); + } + else + { + psa_reply(msg.handle, VAL_STATUS_ERROR); + } + break; + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + } + } + + /* Service NVMEM functionality */ + if (signals & DRIVER_NVMEM_SIG) + { + psa_get(DRIVER_NVMEM_SIG, &msg); + switch (msg.type) + { + case PSA_IPC_CALL: + psa_read(msg.handle, 0, &nvmem_param, msg.in_size[0]); + if (nvmem_param.nvmem_fn_type == NVMEM_READ) + { + fn_status = val_nvmem_read_sf(target_param.nvmem_base_addr, + nvmem_param.offset, buffer, nvmem_param.size); + psa_write(msg.handle, 0, (const void*) buffer, msg.out_size[0]); + } + else + { + psa_read(msg.handle, 1, (void*) buffer, msg.in_size[1]); + fn_status = val_nvmem_write_sf(target_param.nvmem_base_addr, + nvmem_param.offset, buffer, nvmem_param.size); + } + if (fn_status == VAL_STATUS_SUCCESS) + { + psa_reply(msg.handle, PSA_SUCCESS); + } + else + { + psa_reply(msg.handle, VAL_STATUS_ERROR); + } + break; + case PSA_IPC_CONNECT: + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + } + } + + if (signals == 0) + { + val_print_sf(PRINT_ERROR, + "psa_wait_any returned zero for PSA_BLOCK. Entering into infinite loop\n", 0); + while (1); + } + } +} diff --git a/psa-ff/test_suites/partition/ipc/client_partition.c b/psa-ff/test_suites/partition/ipc/client_partition.c new file mode 100644 index 00000000..11ca70ee --- /dev/null +++ b/psa-ff/test_suites/partition/ipc/client_partition.c @@ -0,0 +1,99 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "client_partition.h" +val_api_t *val = &val_api; +psa_api_t *psa = &psa_api; + +void client_main(void) +{ + uint32_t test_data = 0; + psa_signal_t signals = 0; + val_status_t status, test_status; + psa_msg_t msg = {0}; + + while (1) + { + status = VAL_STATUS_SUCCESS; + signals = psa_wait_any(PSA_BLOCK); + + if (signals & CLIENT_TEST_DISPATCHER_SIG) + { + psa_get(CLIENT_TEST_DISPATCHER_SIG, &msg); + switch (msg.type) + { + case PSA_IPC_CONNECT: + if (test_data != 0) + { + val_print(PRINT_ERROR, "must clear previous dispatcher connection\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + else + { + psa_reply(msg.handle, PSA_SUCCESS); + } + break; + case PSA_IPC_CALL: + if (psa_read(msg.handle, 0, &test_data, msg.in_size[0]) != sizeof(test_data)) + { + val_print(PRINT_ERROR, "could not read dispatcher payload\n", 0); + status = VAL_STATUS_READ_FAILED; + } + if (VAL_ERROR(status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + break; + } + + if (GET_ACTION_NUM(test_data) == TEST_EXECUTE_FUNC) + { + psa_reply(msg.handle, PSA_SUCCESS); + + val_print(PRINT_INFO, "In client part, test-id %d\n", + GET_TEST_NUM(test_data)); + /* Execute client test func from secure*/ + test_status = val_execute_secure_tests(GET_TEST_NUM(test_data), + client_ipc_test_list[GET_TEST_NUM(test_data)]); + } + else if (GET_ACTION_NUM(test_data) == TEST_RETURN_RESULT) + { + psa_write(msg.handle, 0, &test_status, sizeof(test_status)); + psa_reply(msg.handle, PSA_SUCCESS); + } + else + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + break; + case PSA_IPC_DISCONNECT: + test_data=0; + status=0; + test_status=0; + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + val_print(PRINT_ERROR, "Unexpected message type %d!", (int)(msg.type)); + while (1); + } + } + else + { + val_print(PRINT_ERROR, "In client_partition, Control shouldn't have reach here\n", 0); + while (1); + } + } +} diff --git a/psa-ff/test_suites/partition/ipc/client_partition.h b/psa-ff/test_suites/partition/ipc/client_partition.h new file mode 100644 index 00000000..0d77245c --- /dev/null +++ b/psa-ff/test_suites/partition/ipc/client_partition.h @@ -0,0 +1,124 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _CLIENT_PART_H_ +#define _CLIENT_PART_H_ +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +typedef client_test_t (*client_test_list_t); + +__WEAK extern client_test_t test_i001_client_tests_list[]; +__WEAK extern client_test_t test_i002_client_tests_list[]; +__WEAK extern client_test_t test_i003_client_tests_list[]; +__WEAK extern client_test_t test_i004_client_tests_list[]; +__WEAK extern client_test_t test_i005_client_tests_list[]; +__WEAK extern client_test_t test_i006_client_tests_list[]; +__WEAK extern client_test_t test_i007_client_tests_list[]; +__WEAK extern client_test_t test_i008_client_tests_list[]; +__WEAK extern client_test_t test_i009_client_tests_list[]; +__WEAK extern client_test_t test_i010_client_tests_list[]; +__WEAK extern client_test_t test_i011_client_tests_list[]; +__WEAK extern client_test_t test_i012_client_tests_list[]; +__WEAK extern client_test_t test_i013_client_tests_list[]; +__WEAK extern client_test_t test_i014_client_tests_list[]; +__WEAK extern client_test_t test_i015_client_tests_list[]; +__WEAK extern client_test_t test_i016_client_tests_list[]; +__WEAK extern client_test_t test_i017_client_tests_list[]; +__WEAK extern client_test_t test_i018_client_tests_list[]; +__WEAK extern client_test_t test_i019_client_tests_list[]; +__WEAK extern client_test_t test_i020_client_tests_list[]; +__WEAK extern client_test_t test_i021_client_tests_list[]; +__WEAK extern client_test_t test_i022_client_tests_list[]; +__WEAK extern client_test_t test_i023_client_tests_list[]; +__WEAK extern client_test_t test_i024_client_tests_list[]; +__WEAK extern client_test_t test_i025_client_tests_list[]; +__WEAK extern client_test_t test_i026_client_tests_list[]; +__WEAK extern client_test_t test_i027_client_tests_list[]; +__WEAK extern client_test_t test_i028_client_tests_list[]; +__WEAK extern client_test_t test_i029_client_tests_list[]; +__WEAK extern client_test_t test_i030_client_tests_list[]; +__WEAK extern client_test_t test_i031_client_tests_list[]; +__WEAK extern client_test_t test_i032_client_tests_list[]; +__WEAK extern client_test_t test_i033_client_tests_list[]; +__WEAK extern client_test_t test_i034_client_tests_list[]; +__WEAK extern client_test_t test_i035_client_tests_list[]; +__WEAK extern client_test_t test_i036_client_tests_list[]; +__WEAK extern client_test_t test_i037_client_tests_list[]; +__WEAK extern client_test_t test_i038_client_tests_list[]; +__WEAK extern client_test_t test_i039_client_tests_list[]; +__WEAK extern client_test_t test_i040_client_tests_list[]; +__WEAK extern client_test_t test_i041_client_tests_list[]; +__WEAK extern client_test_t test_i042_client_tests_list[]; +__WEAK extern client_test_t test_i043_client_tests_list[]; +__WEAK extern client_test_t test_i044_client_tests_list[]; +__WEAK extern client_test_t test_i045_client_tests_list[]; +__WEAK extern client_test_t test_i046_client_tests_list[]; + + +client_test_list_t client_ipc_test_list[] = { + NULL, + test_i001_client_tests_list, + test_i002_client_tests_list, + test_i003_client_tests_list, + test_i004_client_tests_list, + test_i005_client_tests_list, + test_i006_client_tests_list, + test_i007_client_tests_list, + test_i008_client_tests_list, + test_i009_client_tests_list, + test_i010_client_tests_list, + test_i011_client_tests_list, + test_i012_client_tests_list, + test_i013_client_tests_list, + test_i014_client_tests_list, + test_i015_client_tests_list, + test_i016_client_tests_list, + test_i017_client_tests_list, + test_i018_client_tests_list, + test_i019_client_tests_list, + test_i020_client_tests_list, + test_i021_client_tests_list, + test_i022_client_tests_list, + test_i023_client_tests_list, + test_i024_client_tests_list, + test_i025_client_tests_list, + test_i026_client_tests_list, + test_i027_client_tests_list, + test_i028_client_tests_list, + test_i029_client_tests_list, + test_i030_client_tests_list, + test_i031_client_tests_list, + test_i032_client_tests_list, + test_i033_client_tests_list, + test_i034_client_tests_list, + test_i035_client_tests_list, + test_i036_client_tests_list, + test_i037_client_tests_list, + test_i038_client_tests_list, + test_i039_client_tests_list, + test_i040_client_tests_list, + test_i041_client_tests_list, + test_i042_client_tests_list, + test_i043_client_tests_list, + test_i044_client_tests_list, + test_i045_client_tests_list, + test_i046_client_tests_list, + NULL, +}; + +#endif diff --git a/psa-ff/test_suites/partition/ipc/server_partition.c b/psa-ff/test_suites/partition/ipc/server_partition.c new file mode 100644 index 00000000..5e57fbe4 --- /dev/null +++ b/psa-ff/test_suites/partition/ipc/server_partition.c @@ -0,0 +1,102 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "server_partition.h" + +void server_main(void) +{ + uint32_t test_data = 0; + int32_t test_status; + psa_signal_t signals = 0; + val_status_t status; + psa_msg_t msg = {0}; + server_test_t *test_list; + + while (1) + { + status = VAL_STATUS_SUCCESS; + signals = psa_wait_any(PSA_BLOCK); + + if (signals & SERVER_TEST_DISPATCHER_SIG) + { + psa_get(SERVER_TEST_DISPATCHER_SIG, &msg); + switch (msg.type) + { + case PSA_IPC_CONNECT: + if (test_data != 0) + { + val_print(PRINT_ERROR, "must clear previous dispatcher connection\n", 0); + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + else + { + psa_reply(msg.handle, PSA_SUCCESS); + } + break; + case PSA_IPC_CALL: + if (psa_read(msg.handle, 0, &test_data, msg.in_size[0]) != sizeof(test_data)) + { + val_print(PRINT_ERROR, "could not read dispatcher payload\n", 0); + status = VAL_STATUS_READ_FAILED; + } + if (VAL_ERROR(status)) + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + break; + } + + if (GET_ACTION_NUM(test_data) == TEST_EXECUTE_FUNC) + { + psa_reply(msg.handle, PSA_SUCCESS); + val_print(PRINT_INFO,"\tSERVER TEST FUNC START %d\n", + GET_BLOCK_NUM(test_data)); + + /* Get server test list of given test */ + test_list = server_ipc_test_list[GET_TEST_NUM(test_data)]; + + /* Execute server test func */ + test_status = test_list[GET_BLOCK_NUM(test_data)](); + } + else if (GET_ACTION_NUM(test_data) == TEST_RETURN_RESULT) + { + val_print(PRINT_INFO,"\tSERVER TEST FUNC END\n", 0); + psa_write(msg.handle, 0, &test_status, msg.out_size[0]); + psa_reply(msg.handle, PSA_SUCCESS); + } + else + { + psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + } + break; + case PSA_IPC_DISCONNECT: + test_data=0; + status=0; + test_status=0; + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + val_print(PRINT_ERROR, "Unexpected message type %d!", (int)(msg.type)); + while (1); + } + } + else + { + val_print(PRINT_ERROR, "In server_partition, Control shouldn't have reach here\n", 0); + while (1); + } + } +} diff --git a/psa-ff/test_suites/partition/ipc/server_partition.h b/psa-ff/test_suites/partition/ipc/server_partition.h new file mode 100644 index 00000000..c5bb5566 --- /dev/null +++ b/psa-ff/test_suites/partition/ipc/server_partition.h @@ -0,0 +1,122 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _SERVER_PART_H_ +#define _SERVER_PART_H_ + +#include "val/common/val_client_defs.h" +#include "val/spe/val_partition_common.h" + +typedef server_test_t (*server_test_list_t); + +__WEAK extern server_test_t test_i001_server_tests_list[]; +__WEAK extern server_test_t test_i002_server_tests_list[]; +__WEAK extern server_test_t test_i003_server_tests_list[]; +__WEAK extern server_test_t test_i004_server_tests_list[]; +__WEAK extern server_test_t test_i005_server_tests_list[]; +__WEAK extern server_test_t test_i006_server_tests_list[]; +__WEAK extern server_test_t test_i007_server_tests_list[]; +__WEAK extern server_test_t test_i008_server_tests_list[]; +__WEAK extern server_test_t test_i009_server_tests_list[]; +__WEAK extern server_test_t test_i010_server_tests_list[]; +__WEAK extern server_test_t test_i011_server_tests_list[]; +__WEAK extern server_test_t test_i012_server_tests_list[]; +__WEAK extern server_test_t test_i013_server_tests_list[]; +__WEAK extern server_test_t test_i014_server_tests_list[]; +__WEAK extern server_test_t test_i015_server_tests_list[]; +__WEAK extern server_test_t test_i016_server_tests_list[]; +__WEAK extern server_test_t test_i017_server_tests_list[]; +__WEAK extern server_test_t test_i018_server_tests_list[]; +__WEAK extern server_test_t test_i019_server_tests_list[]; +__WEAK extern server_test_t test_i020_server_tests_list[]; +__WEAK extern server_test_t test_i021_server_tests_list[]; +__WEAK extern server_test_t test_i022_server_tests_list[]; +__WEAK extern server_test_t test_i023_server_tests_list[]; +__WEAK extern server_test_t test_i024_server_tests_list[]; +__WEAK extern server_test_t test_i025_server_tests_list[]; +__WEAK extern server_test_t test_i026_server_tests_list[]; +__WEAK extern server_test_t test_i027_server_tests_list[]; +__WEAK extern server_test_t test_i028_server_tests_list[]; +__WEAK extern server_test_t test_i029_server_tests_list[]; +__WEAK extern server_test_t test_i030_server_tests_list[]; +__WEAK extern server_test_t test_i031_server_tests_list[]; +__WEAK extern server_test_t test_i032_server_tests_list[]; +__WEAK extern server_test_t test_i033_server_tests_list[]; +__WEAK extern server_test_t test_i034_server_tests_list[]; +__WEAK extern server_test_t test_i035_server_tests_list[]; +__WEAK extern server_test_t test_i036_server_tests_list[]; +__WEAK extern server_test_t test_i037_server_tests_list[]; +__WEAK extern server_test_t test_i038_server_tests_list[]; +__WEAK extern server_test_t test_i039_server_tests_list[]; +__WEAK extern server_test_t test_i040_server_tests_list[]; +__WEAK extern server_test_t test_i041_server_tests_list[]; +__WEAK extern server_test_t test_i042_server_tests_list[]; +__WEAK extern server_test_t test_i043_server_tests_list[]; +__WEAK extern server_test_t test_i044_server_tests_list[]; +__WEAK extern server_test_t test_i045_server_tests_list[]; +__WEAK extern server_test_t test_i046_server_tests_list[]; + +server_test_list_t server_ipc_test_list[] = { + NULL, + test_i001_server_tests_list, + test_i002_server_tests_list, + test_i003_server_tests_list, + test_i004_server_tests_list, + test_i005_server_tests_list, + test_i006_server_tests_list, + test_i007_server_tests_list, + test_i008_server_tests_list, + test_i009_server_tests_list, + test_i010_server_tests_list, + test_i011_server_tests_list, + test_i012_server_tests_list, + test_i013_server_tests_list, + test_i014_server_tests_list, + test_i015_server_tests_list, + test_i016_server_tests_list, + test_i017_server_tests_list, + test_i018_server_tests_list, + test_i019_server_tests_list, + test_i020_server_tests_list, + test_i021_server_tests_list, + test_i022_server_tests_list, + test_i023_server_tests_list, + test_i024_server_tests_list, + test_i025_server_tests_list, + test_i026_server_tests_list, + test_i027_server_tests_list, + test_i028_server_tests_list, + test_i029_server_tests_list, + test_i030_server_tests_list, + test_i031_server_tests_list, + test_i032_server_tests_list, + test_i033_server_tests_list, + test_i034_server_tests_list, + test_i035_server_tests_list, + test_i036_server_tests_list, + test_i037_server_tests_list, + test_i038_server_tests_list, + test_i039_server_tests_list, + test_i040_server_tests_list, + test_i041_server_tests_list, + test_i042_server_tests_list, + test_i043_server_tests_list, + test_i044_server_tests_list, + test_i045_server_tests_list, + test_i046_server_tests_list, + NULL, +}; +#endif diff --git a/psa-ff/tools/makefiles/Makefile b/psa-ff/tools/makefiles/Makefile new file mode 100644 index 00000000..f264d761 --- /dev/null +++ b/psa-ff/tools/makefiles/Makefile @@ -0,0 +1,65 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +export SUITE_IN= $(SOURCE)/test_suites/$(SUITE) +export SUITE_OUT= $(BUILD)/$(SUITE) + +include $(SOURCE)/tools/makefiles/toolchain.mk + + +all: clean target_cfg gen_linker build simulate + +#Generate target files from User provided data base +target_cfg: + mkdir -p $(BUILD)/platform/${TARGET}/ + @if [ ! -f "$(SOURCE)/platform/targets/$(TARGET)/target.cfg" ]; then { echo "Error: Target Not Found!!!"; exit 1; } fi + perl $(SOURCE)/tools/scripts/targetConfigGen.pl $(SOURCE) $(BUILD) ${TARGET} + +#Read target.cfg and update the addresses in linker script +gen_linker: + mkdir -p $(SUITE_OUT)/; + perl $(SOURCE)/tools/scripts/process_test_linker_file.pl $(SOURCE) $(SUITE_OUT) ${TARGET} $(TOOLCHAIN) + + +#Build framework archives and test_combine.elf +build: pal_nspe.a val_nspe.a test_combine.elf + +pal_nspe.a: + @echo "----------pal build start-------------" + make -f $(SOURCE)/platform/nspe/Makefile + @echo "----------pal build complete-------------" + +val_nspe.a: + @echo "----------val build start-------------" + make -f $(SOURCE)/tools/makefiles/valbuild.mk + @echo "----------val build complete-------------" + +test_combine.elf: test.elf + perl $(SOURCE)/tools/scripts/test_elf_combine.pl $(SUITE) $(SUITE_OUT)/.testlist.txt + hexdump -v -e ' 1/4 "%08X" "\n"' $(SUITE_OUT)/test_elf_combine.bin > $(SUITE_OUT)/test_elf_combine.hex + +test.elf: + @echo "----------test build start-------------" + @mkdir -p $(SUITE_OUT)/ + @$(eval TEST_LIST := $(shell grep "^test" $(SUITE_IN)/testsuite.db > $(SUITE_OUT)/.testlist.txt ; cat $(SUITE_OUT)/.testlist.txt)) + @$(foreach TEST,$(TEST_LIST), make -f $(SOURCE)/tools/makefiles/testbuild.mk TEST=$(TEST) ;) + @echo "----------test build complete-------------" + +simulate: + +clean: + @echo ">>>> Cleaning the build directory..." + rm -rf $(BUILD)/* diff --git a/psa-ff/tools/makefiles/test.linker b/psa-ff/tools/makefiles/test.linker new file mode 100644 index 00000000..75a92571 --- /dev/null +++ b/psa-ff/tools/makefiles/test.linker @@ -0,0 +1,55 @@ +/* Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +ENTRY(acs_test_info) + +TEST_START = 0x2004F000; + +MEMORY +{ + TEST_INFO (R) : ORIGIN = TEST_START, LENGTH = 0x100 + TEST_TEXT (RX) : ORIGIN = TEST_START +0x100, LENGTH = 0xC00 + TEST_DATA (RW) : ORIGIN = TEST_START +0xD00, LENGTH = 0x1100 +} + +SECTIONS +{ + .acs_test_info : + { + KEEP(*(.acs_test_info)) + } > TEST_INFO + + .text : + { + *(.text) + *(.text*) + *(.rodata) + *(.rodata*) + } > TEST_TEXT + + .data : + { + *(.data) + *(.data*) + } > TEST_DATA + + .bss : + { + *(.bss) + *(.bss.*) + *(COMMON) + } > TEST_DATA +} diff --git a/psa-ff/tools/makefiles/testbuild.mk b/psa-ff/tools/makefiles/testbuild.mk new file mode 100644 index 00000000..801f67ca --- /dev/null +++ b/psa-ff/tools/makefiles/testbuild.mk @@ -0,0 +1,51 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +include $(SOURCE)/tools/makefiles/toolchain.mk +include $(SUITE_IN)/$(TEST)/source.mk + + +INCLUDE= -I$(SOURCE)/val/common/ \ + -I$(SOURCE)/val/nspe/ \ + -I$(SOURCE)/platform/nspe/ \ + -I$(SUITE_IN)/$(TEST)/\ + -I$(SUITE_IN)/include/ + +VPATH=$(SOURCE)/val/common/:\ + $(SOURCE)/val/nspe/:\ + $(SUITE_IN)/$(TEST)/:\ + $(SUITE_IN)/include/ + + + +all: mkdir compile_c compile_asm test.elf + +mkdir: + mkdir -p $(SUITE_OUT)/$(TEST)/ + +compile_c: $(CC_SOURCE:%.c=$(SUITE_OUT)/$(TEST)/%.o) +compile_asm: $(AS_SOURCE:%.s=$(SUITE_OUT)/$(TEST)/%.o) + +$(SUITE_OUT)/$(TEST)/%.o : %.c + $(CC) -D NONSECURE_TEST_BUILD -o $@ -c $< + +$(SUITE_OUT)/$(TEST)/%.o : %.s + $(AS) -o $@ $< + +test.elf: + $(LD) -Xlinker -Map=$(SUITE_OUT)/$(TEST)/test.map -o $(SUITE_OUT)/$(TEST)/test.elf -T$(SUITE_OUT)/.test.linker $(SUITE_OUT)/$(TEST)/*.o + $(DS) $(SUITE_OUT)/$(TEST)/test.elf > $(SUITE_OUT)/$(TEST)/test.disass + diff --git a/psa-ff/tools/makefiles/toolchain.mk b/psa-ff/tools/makefiles/toolchain.mk new file mode 100644 index 00000000..bc29c671 --- /dev/null +++ b/psa-ff/tools/makefiles/toolchain.mk @@ -0,0 +1,66 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +TOOLCHAIN=GCC_ARM +PREFIX= + +ifneq (,$(findstring $(TOOLCHAIN),GNUARM-GCC)) +ifeq (${TOOLCHAIN}, GNUARM) +PREFIX=arm-none-eabi- +endif + +ifeq (${CPU_ARCH}, armv7m) +TARGET_SWITCH= -march=armv7-m +else +ifeq (${CPU_ARCH}, armv8m_ml) +TARGET_SWITCH= -march=armv8-m.main -mcmse +else +TARGET_SWITCH= -march=armv8-m.base -mcmse +endif +endif + +COMPILER= $(PREFIX)gcc +ASSEMBLER= $(PREFIX)as +AR= $(PREFIX)ar +LINKER= $(PREFIX)gcc +OBJDUMP=$(PREFIX)objdump + +COMPILER_OPTIONS= $(TARGET_SWITCH) -Wall -Werror -fdata-sections -ffunction-sections -mno-unaligned-access -DVAL_NSPE_BUILD + +ifeq (${SUITE}, crypto) +COMPILER_OPTIONS += -DCRYPTO_SUITE +endif + +ASSEMBLER_OPTIONS= $(TARGET_SWITCH) -mthumb +AR_OPTIONS= -rc +LINKER_OPTIONS= $(TARGET_SWITCH) -mthumb -Wall -Werror -O0 -fdata-sections \ + -ffunction-sections -Xlinker --fatal-warnings -Xlinker --gc-sections \ + -z max-page-size=0x400 -lgcc -lc -lnosys +OBJDUMP_OPTIONS= -d +endif #GNUARM-GCC + +ifeq (${TOOLCHAIN}, ARMCLANG) +#TBD +endif + +ifeq (${PSA_API_ELEMENTS_AVAILABLE}, 1) +COMPILER_OPTIONS += -DPSA_API_ELEMENTS_AVAILABLE +endif + +CC= $(COMPILER) $(COMPILER_OPTIONS) $(CC_OPTIONS) $(USER_INCLUDE) $(INCLUDE) +AS= $(ASSEMBLER) $(ASSEMBLER_OPTIONS) $(AS_OPTIONS) +LD= $(LINKER) $(LINKER_OPTIONS) +DS= $(OBJDUMP) $(OBJDUMP_OPTIONS) diff --git a/psa-ff/tools/makefiles/valbuild.mk b/psa-ff/tools/makefiles/valbuild.mk new file mode 100644 index 00000000..32905336 --- /dev/null +++ b/psa-ff/tools/makefiles/valbuild.mk @@ -0,0 +1,48 @@ +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +include $(SOURCE)/tools/makefiles/toolchain.mk + +INCLUDE= -I$(SOURCE)/val/common/ \ + -I$(SOURCE)/val/nspe/ \ + -I$(SOURCE)/val/spe/ \ + -I$(SOURCE)/platform/nspe/ \ + +VPATH=$(SOURCE)/val/common/:\ + $(SOURCE)/val/nspe/:\ + $(SOURCE)/val/spe/ + +SRC_COMMON= +SRC_NS= val_entry.c val_dispatcher.c val_framework.c val_crypto.c val_interfaces.c val_peripherals.c val_target.c + +all: build + +build: mkdir build_common build_ns val_nspe.a + +mkdir: + @mkdir -p $(BUILD)/val/ + +build_common: $(SRC_COMMON:%.c=$(BUILD)/val/%.o) +build_ns: $(SRC_NS:%.c=$(BUILD)/val/%.o) + +$(BUILD)/val/%.o : %.c + $(CC) $(INCLUDE) -DVERBOSE=$(VERBOSE) -o $@ -c $< + +val_nspe.a: + $(AR) $(AR_OPTIONS) $(BUILD)/val/val_nspe.a $(BUILD)/val/*.o + +clean: + @rm -rf $(BUILD)/val/*.o $(BUILD)/val/*.a diff --git a/psa-ff/tools/scripts/process_test_linker_file.pl b/psa-ff/tools/scripts/process_test_linker_file.pl new file mode 100755 index 00000000..c56b89c9 --- /dev/null +++ b/psa-ff/tools/scripts/process_test_linker_file.pl @@ -0,0 +1,65 @@ +#!/usr/bin/env perl +#/** @file +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +print "\n>>>> Updating linker file... \n"; + +#inputs +$source=$ARGV[0]; +$suite_out=$ARGV[1]; +$target=$ARGV[2]; +$toolchain=$ARGV[3]; + +$targetConfigPath = "$source/platform/targets/$target/target.cfg"; +$linker_in = ""; +$linker_out = ""; +$ns_test_start_addr = undef; + +if($toolchain eq "GNUARM") +{ + $linker_in = "$source/tools/makefiles/test.linker"; + $linker_out = "$suite_out/.test.linker"; +} + +open(IN, $targetConfigPath) or die "Unable to open $targetConfigPath $!"; +while() { + if($_ !~ /^\//) {# exclude commented lines + if($_ =~ /\.ns_test_addr(\s*)\=(\s*)(.+)(\s*)\;/) { + $ns_test_start_addr = $3; + } + } +} +close IN; +if(defined($ns_test_start_addr)) +{ + open(IN, $linker_in) or die "Unable to open $linker_in $!"; + open(OUT, '>', $linker_out) or die "Unable to open: $!"; + while() { + if($_ =~ /^TEST_START/){ + print OUT "TEST_START = $ns_test_start_addr;\n"; + }else{ + print OUT "$_"; + } + } + close IN; + close OUT; + print "linker file - $linker_out\n" +} +else +{ + die ("Error: ns_test_addr is not found in target.cfg file\n"); +} diff --git a/psa-ff/tools/scripts/setup.sh b/psa-ff/tools/scripts/setup.sh new file mode 100755 index 00000000..67b3b564 --- /dev/null +++ b/psa-ff/tools/scripts/setup.sh @@ -0,0 +1,242 @@ +#!/usr/bin/env bash + +#/** @file +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +echo "" + +declare -a INCLUDE_PATHS +export CLIENT_FILE_FOUND=0 +export SERVICE_FILE_FOUND=0 +HELP=" + +Usage: setup.sh [--source SOURCE_DIR] [--build BUILD_DIR] [--target TARGET] [--suite SUITE] + [--toolchain TOOLCHAIN] [--cpu_arch CPU_ARCH] [--clean] [--verbose PRINT_LEVEL] + [--include INCLUDE_PATH] [--help|-h] + +Arguments Info: + --source : SOURCE_DIR pointing to compliance test directory structure. + Default is current directory + --build : To select the build (output) directory. Default: BUILD/ inside current directory + --target : Provide target string as argument. + target.cfg file corresponding to input string must be avaiable at + platform/targets// + --suite : Compile tests for given suite. Support values are ipc and crypto. + Default is ipc suite. + --toolchain : Build using the given TOOLCHAIN. Supported value is GNUARM (GNU Arm Embedded). + Future release will be extended to support ARMCLANG (ARM Compiler 6.x). + --cpu_arch : Provide cpu arch string as argument. + Supported CPU arch are armv8m_ml, armv8m_bl and armv7m. + --clean : Clean the build directory + --verbose : Print verbosity level + Supported print levels are: + 1 - INFO & above. + 2 - DEBUG & above. + 3 - TEST & above.(Default) + 4 - WARN & ERROR. + 5 - ERROR. + --include : Additional directory to be included into compiler search path. + Note - You must provide PSA defined API element header files psa_client.h and psa_service.h + to compile IPC compliance tests. Provide --include where path + pointing to location of PSA defined header files. + You can specify multiple source locations using --include option. + Ex: --include --include + --help|-h : Print this help message + +" + +if [ "$#" == "0" ]; then + echo "Error: no argument to setup.sh" + echo "$HELP" + exit 1 +fi + +while [ $# -gt 0 ]; do + case $1 in + --source ) shift + export SOURCE=$1 + ;; + --build ) shift + export BUILD="$1/BUILD" + ;; + --target ) shift + export TARGET=$1 + ;; + --suite ) shift + export SUITE=$1 + ;; + --toolchain ) shift + export TOOLCHAIN=$1 + ;; + --cpu_arch ) shift + export CPU_ARCH=$1 + ;; + --clean ) + export CLEAN=1 + ;; + --verbose ) shift + export VERBOSE=$1 + ;; + --include ) shift + export INCLUDE=" -I $1/ $INCLUDE " + INCLUDE_PATHS=("${INCLUDE_PATHS[@]}" $1) + ;; + --help | -h | * ) + echo "$HELP" + exit 1 + ;; + * ) + echo "Error: Invaid argument" + echo "$HELP" + exit 1 + esac + shift +done + +echo ">>>> Processing inputs..." +if [ -z "$SOURCE" ] +then + export SOURCE=./ + echo "--source option is not provided, hence setting \$SOURCE to present dir" +else + echo "setting \$SOURCE to $SOURCE" +fi + +if [ ! -d "$SOURCE/test_suites" ] +then + echo "Error: Could not find compliance tests directories in current path $SOURCE" + exit 1 +fi + +if [ -z "$BUILD" ] +then + export BUILD="./BUILD" + echo "--build option is not provided, hence setting \$BUILD to ./BUILD" +else + echo "setting \$BUILD to $BUILD" +fi + +if [ -z "$TARGET" ] +then + echo "Provide target string as argument using --target " + exit 1 +else + echo "Using \$TARGET=$TARGET" +fi + +if [ -z "$SUITE" ] +then + export SUITE=ipc + echo "--suite option is not provided, hence using default value \$SUITE=$SUITE" +else + echo "Using \$SUITE=$SUITE" +fi + +if [ $SUITE == "ipc" ] +then + if [ -z "$INCLUDE" ] + then + echo "Error: --include option is not provided. + You must provide PSA defined API element header files psa_client.h and psa_service.h + to compile IPC compliance tests. Provide --include where path pointing to + location of PSA defined header files. + You can specify multiple source locations using --include option. + Ex: --include --include " + exit 1 + else + for path in "${INCLUDE_PATHS[@]}" + do + if [ -f "$path/psa_client.h" ] + then + export CLIENT_FILE_FOUND=1 + fi + if [ -f "$path/psa_service.h" ] + then + export SERVICE_FILE_FOUND=1 + fi + done + if [ $CLIENT_FILE_FOUND == "0" ] + then + echo "Couldn't find psa_client.h file in paths: ${INCLUDE_PATHS[@]}" + exit 1 + fi + if [ $SERVICE_FILE_FOUND == "0" ] + then + echo "Couldn't find psa_service.h file in paths: ${INCLUDE_PATHS[@]}" + exit 1 + fi + export PSA_API_ELEMENTS_AVAILABLE=1 + fi +fi + +if [ -z "$TOOLCHAIN" ] +then + export TOOLCHAIN=GNUARM + echo "--toolchain option is not provided, hence using default value \$TOOLCHAIN=$TOOLCHAIN" +else + echo "Using \$TOOLCHAIN=$TOOLCHAIN" +fi + +if [ $TOOLCHAIN != "GNUARM" ] +#if [ $TOOLCHAIN != "GNUARM" ] && [ $TOOLCHAIN != "ARMCLANG" ] +then + echo "Error: Unsupported value for --toolchain=$TOOLCHAIN. Supported toolchain is GNUARM" + #echo "Error: Unsupported value for --toolchain=$TOOLCHAIN. Supported toolchain are GNUARM and ARMCLANG" + exit 1 +fi + +if [ -z "$CPU_ARCH" ] +then + echo "Error: Provide cpu arch string as argument using --cpu_arch " + echo "Supported CPU arch are armv8m_ml, armv8m_bl, armv7m" + exit 1 +else + echo "Using \$CPU_ARCH=$CPU_ARCH" +fi + +if [ $CPU_ARCH != "armv8m_ml" ] && [ $CPU_ARCH != "armv8m_bl" ] && [ $CPU_ARCH != "armv7m" ] +then + echo "Error: Unsupported value for --cpu_arch=$CPU_ARCH. Supported CPU arch are armv8m_ml, armv8m_bl, armv7m" + exit 1 +fi + +if [ ! -z "$VERBOSE" ] +then + if [ $VERBOSE != "1" ] && [ $VERBOSE != "2" ] && [ $VERBOSE != "3" ] && [ $VERBOSE != "4" ] && [ $VERBOSE != "5" ] + then + echo "Error: Unsupported value for --verbose=$VERBOSE." + echo "Supported print levels are:" + echo "1 - INFO & above." + echo "2 - DEBUG & above." + echo "3 - TEST & above." + echo "4 - WARN & ERROR." + echo "5 - ERROR." + exit 1 + fi +else + export VERBOSE=3 +fi + + +if [ -z "$CLEAN" ] +then + #Build VAL/PAL static library and Tests ELFs + echo "make -f $SOURCE/tools/makefiles/Makefile SOURCE=$SOURCE BUILD=$BUILD TARGET=$TARGET SUITE=$SUITE TOOLCHAIN=$TOOLCHAIN CPU_ARCH=$CPU_ARCH VERBOSE=$VERBOSE PSA_HEADER_INC=\"$INCLUDE\" all" + make -f $SOURCE/tools/makefiles/Makefile SOURCE=$SOURCE BUILD=$BUILD TARGET=$TARGET SUITE=$SUITE TOOLCHAIN=$TOOLCHAIN CPU_ARCH=$CPU_ARCH VERBOSE=$VERBOSE USER_INCLUDE="$INCLUDE" PSA_API_ELEMENTS_AVAILABLE=$PSA_API_ELEMENTS_AVAILABLE all +else + make -f $SOURCE/tools/makefiles/Makefile SOURCE=$SOURCE BUILD=$BUILD TARGET=$TARGET SUITE=$SUITE TOOLCHAIN=$TOOLCHAIN CPU_ARCH=$CPU_ARCH VERBOSE=$VERBOSE USER_INCLUDE="$INCLUDE" PSA_API_ELEMENTS_AVAILABLE=$PSA_API_ELEMENTS_AVAILABLE clean +fi diff --git a/psa-ff/tools/scripts/targetConfigGen.pl b/psa-ff/tools/scripts/targetConfigGen.pl new file mode 100755 index 00000000..8ce8edec --- /dev/null +++ b/psa-ff/tools/scripts/targetConfigGen.pl @@ -0,0 +1,235 @@ +#!/usr/bin/env perl +#/** @file +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ +#--------------------------------------------------------------------- +# USAGE: +# 1) perl +# 2) gcc -o +# 3) ./ +# 4) Resulting output file is target.hex +#--------------------------------------------------------------------- +# THIS SCRIPT : +# 1) Reads the targetConfig.cfg file written in pre-defined format. +# 2) * Generates a C file based on targetConfig, complete with all +# variable declarations and C syntax formatting. +# * It will #include val_target.h header file which contains +# template info about each device described in targetConfig. +# * This header file is also used by test code to unpack the +# resulting hex file. +# 3) The autogenerated C file will then be compiled and the resulting +# executable run to generate target.hex file: which is the packed +# output of the targetConfig.cfg parameters. +#--------------------------------------------------------------------- +# NOTE: Only C-style single line commenting is permitted inside targetConfig.cfg +#--------------------------------------------------------------------- + +use List::MoreUtils qw(uniq); + +print "\n>>>> Generating targetConfig data base... \n"; +$source=$ARGV[0]; +$build=$ARGV[1]; +$target=$ARGV[2]; +$targetConfigPath = "$source/platform/targets/$target/target.cfg"; +$final_output = "$build/platform/$target/pal_database"; +$output_c = "$build/platform/$target/targetConfigGen.c"; +$input_h = "$source/val/nspe/val_target.h"; + +$final_output_file = undef; +if($final_output =~ /([0-9a-zA-Z_]+)$/) { + $final_output_file = $1; +} + +@unique_devices = undef; + +open(IN, $targetConfigPath) or die "Unable to open $targetConfigPath $!"; +open(OUT, '>', $output_c) or die "Unable to open: $!"; + +#--------------------------------------------------------------------- +# Open header file and go through enum definition to find group of +# each component; rather than making partner do it. Store in a hash. +#--------------------------------------------------------------------- +open(IN0, $input_h) or die "Unable to open: $!"; +my %comp_groups; +while() { + if($_ =~ /COMPONENT_GROUPING/) { + while($nextline !~ /\}/) { + $nextline = ; + #print "$nextline"; + if($nextline =~ /(\S+)(\s*)\=(\s*)GROUP_([0-9a-zA-Z_]+)(,*)\n/) { + $comp_groups{$1} = $4; + } + } + } +} +close IN0; +#print keys %comp_groups, "\n"; +#print values %comp_groups, "\n"; +#--------------------------------------------------------------------- + +print OUT '#include "val_target.h"',"\n"; +print OUT '#include ',"\n\n"; + +print OUT "int main\(void\) \{\n"; + +while() { + if($_ !~ /^\//) {# exclude commented lines + + if($_ =~ /(\S+)\.num(\s*)\=(\s*)(\d+)(\s*)\;/) { + print OUT lc($comp_groups{uc($1)}),"_desc_t $1\[$4\];\n"; + print OUT "int $1","_num_instances \= $4\;\n"; + + # For each instance of this device + for ($count = 0; $count < $4; $count++) { + print OUT "$1\[$count\]\.cfg_type\.cfg_id \= \(GROUP_",$comp_groups{uc($1)}," << 24\) \+ \(",$comp_groups{uc($1)},"_",uc($1)," << 16\) \+ $count\;\n"; + print OUT "$1\[$count\]\.cfg_type\.size \= sizeof\($1\)\/$1","_num_instances\;\n"; + print OUT "$1\[$count\]\.cfg_type\.size \|\= $1","_num_instances << 24\;\n"; + } + + push(@unique_devices, $1); + push(@unique_groups, $comp_groups{uc($1)}); + } + #elsif($_ =~ /(\S+)\.(\d+)\.(\S+)(\s*)\=(\s*)(\S+)(\s*)\;/) { + elsif($_ =~ /(\S+)\.(\d+)\.(\S+)(\s*)\=(\s*)(.+)\;/) { + print OUT "$1\[$2\]\.$3 \= $6\;\n"; + } + else { + print OUT $_; + } + + } +} + +# Remove empty elements from array +@unique_devices = grep { $_ ne '' } @unique_devices; +# Remove duplicate groups +@unique_groups = uniq @unique_groups; +@unique_groups = grep { $_ ne '' } @unique_groups; + +#print "@unique_devices\n"; +#print "@unique_groups\n\n"; + +foreach $thisgroup (@unique_groups) { + print "\nGROUP $thisgroup \n"; + print OUT lc($thisgroup),"_hdr_t group_",lc($thisgroup),"\;\n"; + print OUT "int group_",lc($thisgroup),"_size \= sizeof(group_",lc($thisgroup),"\)\;\n"; + print OUT "int group_",lc($thisgroup),"_count \= 0\;\n"; + + print OUT "group_",lc($thisgroup),"\.cfg_type\.cfg_id \= \(GROUP_",$thisgroup," << 24\)\;\n"; + + foreach $thisdevice (@unique_devices) { + if($comp_groups{uc($thisdevice)} eq $thisgroup) { + print "DEVICE $thisdevice \n"; + print OUT "group_",lc($thisgroup),"_size \= group_",lc($thisgroup),"_size \+ sizeof\($thisdevice\)\;\n"; + print OUT "group_",lc($thisgroup),"_count \= group_",lc($thisgroup),"_count \+ $thisdevice","_num_instances\;\n"; + + } + } + print OUT "group_",lc($thisgroup),"\.cfg_type\.size \= group_",lc($thisgroup),"_size\;\n"; + print OUT "group_",lc($thisgroup),"\.num \= group_",lc($thisgroup),"_count\;\n"; + + print OUT "\n"; +} + +print OUT "\n"; +print OUT "uint32_t\* word_ptr\;\n"; +print OUT "int byte_no \= 0\;\n"; +#print OUT "int instance_no \= 0\;\n"; +#print OUT "int instance_size \= 0\;\n"; +#print OUT "device_type_t device_id\;\n"; +print OUT "FILE \* fp\;\n"; +print OUT "fp \= fopen\(\"",$final_output,"\.h\"\, \"w\"\)\;\n\n"; + +# Printing out main header inside hex file +#print OUT "fprintf\(fp\, \"#include \\\"pal_fvp_config\.h\\\"\\n\\n\"\)\;\n"; +print OUT "fprintf\(fp\, \"#ifndef ",uc($final_output_file),"\\n\"\)\;\n"; +print OUT "fprintf\(fp\, \"#define ",uc($final_output_file),"\\n\\n\"\)\;\n"; +print OUT "fprintf\(fp\, \"__attribute__\(\(section\(\\\"\.target_config_ns_data\\\"\)\)\)\\n\"\)\;\n"; +print OUT "fprintf\(fp\, \"const uint32_t\\n\"\)\;\n"; +print OUT "fprintf\(fp\, \"database[] \= \{\\n\"\)\;\n"; + +# print OUT "fprintf\(fp\, \"0x\%08x\,\\n\"\, \"_CFG\"\)\;\n"; +# print OUT "fprintf\(fp\, \"0x\%08x\,\\n\"\, \" FVP\"\)\;\n"; +# print OUT "fprintf\(fp\, \"0x\%08x\,\\n\"\, \"_CFG\"\)\;\n"; +# TBSA_CFG header +print OUT "fprintf\(fp\, \"0x\%x\"\, \'T\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'B\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'S\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\,\\n\"\, \'A\'\)\;\n"; +print OUT "fprintf\(fp\, \"0x\%x\"\, \'_\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'C\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'F\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\,\\n\"\, \'G\'\)\;\n"; +# FVP_CFG header +print OUT "fprintf\(fp\, \"0x\%x\"\, \' \'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'F\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'V\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\,\\n\"\, \'P\'\)\;\n"; +print OUT "fprintf\(fp\, \"0x\%x\"\, \'_\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'C\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\"\, \'F\'\)\;\n"; +print OUT "fprintf\(fp\, \"\%x\,\\n\"\, \'G\'\)\;\n"; + +print OUT "uint32_t version \= 1\;\n"; +print OUT "fprintf\(fp\, \"0x\%08x\,\\n\"\, version\)\;\n"; +#print OUT "fwrite\(\&version\, 4\, 1\, fp\)\;\n"; +print OUT "uint32_t total_size \= 0\;\n"; + +foreach $thisgroup (@unique_groups) { + print OUT "total_size \= total_size \+ group_",lc($thisgroup),"_size\;\n"; +} +# foreach $thisdevice (@unique_devices) { +# print OUT "total_size \= total_size \+ sizeof\($thisdevice\) \+ \(8\* $thisdevice","_num_instances\)\;\n"; +# } +# Add main header size +print OUT "total_size \= total_size \+8 \+8 \+4 \+4 \+4\;\n"; +#print OUT "fwrite\(\&total_size\, 4\, 1\, fp\)\;\n\n"; +print OUT "fprintf\(fp\, \"0x\%08x\,\\n\"\, total_size\)\;\n"; + + +foreach $thisgroup (@unique_groups) { + print OUT "word_ptr \= \(uint32_t \*\)\&group_",lc($thisgroup),"\;\n"; + print OUT "for\(byte_no\=0\; byte_no\<\sizeof\(group_",lc($thisgroup),"\)\; byte_no\=byte_no\+4\)\{\n"; + #print OUT "fwrite\(word_ptr\, 4\, 1\, fp\)\;\n"; + #print OUT "printf\(\"\%08x\,\\n\"\, \*word_ptr\)\;\n"; + print OUT "fprintf\(fp\, \"0x\%08x\,\\n\"\, \*word_ptr\)\;\n"; + print OUT "word_ptr\+\+\;\n"; + print OUT "\}\n"; + + foreach $thisdevice (@unique_devices) { + if($comp_groups{uc($thisdevice)} eq $thisgroup) { + print OUT "\tword_ptr \= \(uint32_t \*\)\&","$thisdevice","\[0\]\;\n"; + print OUT "\tfor\(byte_no\=0\; byte_no\<\sizeof\($thisdevice\)\; byte_no\=byte_no\+4\)\{\n"; + #print OUT "\tfwrite\(word_ptr\, 4\, 1\, fp\)\;\n"; + #print OUT "\tprintf\(\"\%08x\,\\n\"\, \*word_ptr\)\;\n"; + print OUT "\tfprintf\(fp\, \"0x\%08x\,\\n\"\, \*word_ptr\)\;\n"; + print OUT "\tword_ptr\+\+\;\n"; + print OUT "\t\}\n"; + } + } +} +print OUT "fprintf\(fp\, \"0x\%08x\\n\"\, 0xffffffff\)\;\n"; +print OUT "fprintf\(fp\, \"\}\;\\n\\n\"\)\;\n"; +print OUT "fprintf\(fp\, \"#endif \\n\"\)\;\n"; + + +print OUT "\nreturn 0;\}\/\/int main"; + +#generate pal_database.h file +print "gcc $output_c -o $build/platform/$target/targetConfigGen -I$source/val/nspe -I$source/val/common\n"; +system("gcc $output_c -o $build/platform/$target/targetConfigGen -I$source/val/nspe -I$source/val/common") && die ("Failed to compile targetConfigGen.c \n"); +print "./$build/platform/$target/targetConfigGen\n"; +system("./$build/platform/$target/targetConfigGen ") && die ("Failed to generate targetConfig data base \n"); diff --git a/psa-ff/tools/scripts/test_elf_combine.pl b/psa-ff/tools/scripts/test_elf_combine.pl new file mode 100755 index 00000000..58988584 --- /dev/null +++ b/psa-ff/tools/scripts/test_elf_combine.pl @@ -0,0 +1,89 @@ +#!/usr/bin/env perl +#/** @file +# * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +use File::Find; +use File::Basename; +use List::MoreUtils 'uniq'; + +print "\n>>>> Combining test ELFs... \n"; + +#Inputs +$suite = $ARGV[0]; +$test_list_file = $ARGV[1]; + +@suite_out = split("/.testlist.txt", $test_list_file); + +# Final output elf file +$output_elf = "$suite_out[0]/test_elf_combine.bin"; + + +my @all_elf_paths; +@test_list = `cat $test_list_file`; + + +open(OUT, '>:raw', $output_elf) or die "Unable to open: $!"; + +# Collecting all elf file paths + +foreach $test (@test_list) +{ + chomp($test); + push @all_elf_paths, "$suite_out[0]/$test/test.elf"; + if (!(-e "$suite_out[0]/$test/test.elf")) + { + print "ELF not found - $suite_out[0]/$test/test.elf\n"; + exit 1; + } +} + +foreach $elf (@all_elf_paths) { + + # Find elf sizes + $elf_size = -s $elf; + + # Get readelf program header info from either elf and process it + $out = `readelf -l $elf`; + if($out =~ /LOAD(\s+)(0[xX][0-9a-fA-F]+)/) + { + $program_header = hex($2); + } + + # Determining test_id from elf at location pointed by program header + open(TEST_NS_ID, '<:raw', $elf) or die "Unable to open: $!"; + seek(TEST_NS_ID, $program_header, 0); + read TEST_NS_ID, $test_id_raw, 4; + + $test_id = unpack('L<', $test_id_raw); + close TEST_NS_ID; + + printf("test_id:\t%4d, ", $test_id); + print "ELF:$elf,\tSIZE:$elf_size\n"; + + # 'L' unsigned-32 ; '<' Little endian + print OUT pack('L<', 0xFACEFACE); + print OUT pack('L<', $test_id); + print OUT pack('L<', $elf_size); + + # Temporarily closing final ELF to concatenate ELFs + close OUT; + system("cat $elf >> $output_elf"); + # Open final ELF again + open(OUT, '>>:raw', $output_elf) or die "Unable to open: $!"; +} +print OUT pack('L<', 0xC3C3C3C3); +close OUT; diff --git a/psa-ff/val/common/val.h b/psa-ff/val/common/val.h new file mode 100644 index 00000000..b3e66f18 --- /dev/null +++ b/psa-ff/val/common/val.h @@ -0,0 +1,258 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_COMMON_H_ +#define _VAL_COMMON_H_ + +#include +#include +#include + +/* typedef's */ +typedef uint8_t bool_t; +typedef uint32_t addr_t; +typedef uint32_t test_id_t; +typedef uint32_t block_id_t; +typedef char char8_t; +typedef uint32_t cfg_id_t; + + +#ifndef VAL_NSPE_BUILD +#define STATIC_DECLARE static +#else +#define STATIC_DECLARE +#endif + +#ifndef __WEAK +#define __WEAK __attribute__((weak)) +#endif + +#ifndef __UNUSED +#define __UNUSED __attribute__((unused)) +#endif + +#ifndef TRUE +#define TRUE 0 +#endif +#ifndef FALSE +#define FALSE 1 +#endif + +/* test status defines */ +#define TEST_START 0x01 +#define TEST_END 0x02 +#define TEST_PASS 0x04 +#define TEST_FAIL 0x08 +#define TEST_SKIP 0x10 +#define TEST_PENDING 0x20 + +#define TEST_NUM_BIT 32 +#define TEST_STATE_BIT 8 +#define TEST_STATUS_BIT 0 + +#define TEST_NUM_MASK 0xFFFFFFFF +#define TEST_STATE_MASK 0xFF +#define TEST_STATUS_MASK 0xFF + +#define RESULT_START(status) (((TEST_START) << TEST_STATE_BIT) | ((status) << TEST_STATUS_BIT)) +#define RESULT_END(status) (((TEST_END) << TEST_STATE_BIT) | ((status) << TEST_STATUS_BIT)) +#define RESULT_PASS(status) (((TEST_PASS) << TEST_STATE_BIT) | ((status) << TEST_STATUS_BIT)) +#define RESULT_FAIL(status) (((TEST_FAIL) << TEST_STATE_BIT) | ((status) << TEST_STATUS_BIT)) +#define RESULT_SKIP(status) (((TEST_SKIP) << TEST_STATE_BIT) | ((status) << TEST_STATUS_BIT)) +#define RESULT_PENDING(status) (((TEST_PENDING) << TEST_STATE_BIT) | ((status) << TEST_STATUS_BIT)) + +#define IS_TEST_FAIL(status) (((status >> TEST_STATE_BIT) & TEST_STATE_MASK) == TEST_FAIL) +#define IS_TEST_PASS(status) (((status >> TEST_STATE_BIT) & TEST_STATE_MASK) == TEST_PASS) +#define IS_TEST_SKIP(status) (((status >> TEST_STATE_BIT) & TEST_STATE_MASK) == TEST_SKIP) +#define IS_TEST_PENDING(status) (((status >> TEST_STATE_BIT) & TEST_STATE_MASK) == TEST_PENDING) +#define IS_TEST_START(status) (((status >> TEST_STATE_BIT) & TEST_STATE_MASK) == TEST_START) +#define IS_TEST_END(status) (((status >> TEST_STATE_BIT) & TEST_STATE_MASK) == TEST_END) +#define VAL_ERROR(status) (status?1:0) + + + +/* Test Defines */ +#define TEST_PUBLISH(test_id, entry) \ + const val_test_info_t __attribute__((section(".acs_test_info"))) acs_test_info = {test_id, entry} + +#define VAL_MAX_TEST_PER_COMP 200 +#define VAL_FF_BASE 0 +#define VAL_CRYPTO_BASE 1 +#define VAL_GET_COMP_NUM(test_id) \ + ((test_id - (test_id % VAL_MAX_TEST_PER_COMP)) / VAL_MAX_TEST_PER_COMP) +#define VAL_GET_TEST_NUM(test_id) (test_id % VAL_MAX_TEST_PER_COMP) +#define VAL_CREATE_TEST_ID(comp,num) ((comp*VAL_MAX_TEST_PER_COMP) + num) + +#define TEST_FIELD(num1,num2) (num2 << 8 | num1) +#define GET_TEST_ISOLATION_LEVEL(num) (num & 0x3) +#define GET_WD_TIMOUT_TYPE(num) ((num >> 8) & 0x3) + +#define TEST_CHECKPOINT_NUM(n) n +#define TEST(n) n +#define BLOCK(n) n + +#define BLOCK_NUM_POS 8 +#define ACTION_POS 16 +#define GET_TEST_NUM(n) (0xff & n) +#define GET_BLOCK_NUM(n) ((n >> BLOCK_NUM_POS) & 0xff) + +#define GET_ACTION_NUM(n) ((n >> ACTION_POS) & 0xff) +#define TEST_EXECUTE_FUNC 1 +#define TEST_RETURN_RESULT 2 +#define INVALID_HANDLE 0x1234DEAD + +#define VAL_NVMEM_BLOCK_SIZE 4 +#define VAL_NVMEM_OFFSET(nvmem_idx) (nvmem_idx * VAL_NVMEM_BLOCK_SIZE) + +#define UART_INIT_SIGN 0xff + +/* enums */ +typedef enum { + NONSECURE = 0x0, + SECURE = 0x1, +} security_t; + +typedef enum { + TEST_ISOLATION_L1 = 0x1, + TEST_ISOLATION_L2 = 0x2, + TEST_ISOLATION_L3 = 0x3, +} test_isolation_level_t; + +typedef enum { + BOOT_UNKNOWN = 0x1, + BOOT_NOT_EXPECTED = 0x2, + BOOT_EXPECTED_NS = 0x3, + BOOT_EXPECTED_S = 0x4, + BOOT_EXPECTED_BUT_FAILED = 0x5, + BOOT_EXPECTED_CRYPTO = 0x6, +} boot_state_t; + +typedef enum { + NV_BOOT = 0x0, + NV_TEST_ID_PREVIOUS = 0x1, + NV_TEST_ID_CURRENT = 0x2, + NV_TEST_CNT = 0x3, +} nvmem_index_t; + +typedef enum { + WD_INIT_SEQ = 0x1, + WD_ENABLE_SEQ = 0x2, + WD_DISABLE_SEQ = 0x3, + WD_STATUS_SEQ = 0x4, +} wd_fn_type_t; + +typedef enum { + WD_LOW_TIMEOUT = 0x1, + WD_MEDIUM_TIMEOUT = 0x2, + WD_HIGH_TIMEOUT = 0x3, +} wd_timeout_type_t; + +typedef enum { + NVMEM_READ = 0x1, + NVMEM_WRITE = 0x2, +} nvmem_fn_type_t; + +/* enums to report test sub-state */ +typedef enum { + VAL_STATUS_SUCCESS = 0x0, + VAL_STATUS_INVALID = 0x10, + VAL_STATUS_ERROR = 0x11, + VAL_STATUS_NOT_FOUND = 0x12, + VAL_STATUS_LOAD_ERROR = 0x13, + VAL_STATUS_INSUFFICIENT_SIZE = 0x14, + VAL_STATUS_CONNECTION_FAILED = 0x15, + VAL_STATUS_CALL_FAILED = 0x16, + VAL_STATUS_READ_FAILED = 0x17, + VAL_STATUS_WRITE_FAILED = 0x18, + VAL_STATUS_ISOLATION_LEVEL_NOT_SUPP = 0x19, + VAL_STATUS_INIT_FAILED = 0x1A, + VAL_STATUS_SPM_FAILED = 0x1B, + VAL_STATUS_SPM_UNEXPECTED_BEH = 0x1C, + VAL_STATUS_FRAMEWORK_VERSION_FAILED = 0x1D, + VAL_STATUS_VERSION_API_FAILED = 0x1E, + VAL_STATUS_INVALID_HANDLE = 0x1F, + VAL_STATUS_INVALID_MSG_TYPE = 0x20, + VAL_STATUS_WRONG_IDENTITY = 0x21, + VAL_STATUS_MSG_INSIZE_FAILED = 0x22, + VAL_STATUS_MSG_OUTSIZE_FAILED = 0x23, + VAL_STATUS_SKIP_FAILED = 0x24, + VAL_STATUS_CRYPTO_FAILURE = 0x25, + VAL_STATUS_INVALID_SIZE = 0x26, + VAL_STATUS_DATA_MISMATCH = 0x27, + VAL_STATUS_BOOT_EXPECTED_BUT_FAILED = 0x28, +} val_status_t; + +/* verbosity enums */ +typedef enum { + PRINT_INFO = 1, + PRINT_DEBUG = 2, + PRINT_TEST = 3, + PRINT_WARN = 4, + PRINT_ERROR = 5, + PRINT_ALWAYS = 9 +} print_verbosity_t; + +/* typedef's */ +typedef struct { + boot_state_t state; +} boot_t; + +typedef struct { + uint32_t pass_cnt:8; + uint32_t skip_cnt:8; + uint32_t fail_cnt:8; + uint32_t sim_error_cnt:8; +} test_count_t; + +typedef struct { + wd_fn_type_t wd_fn_type; + wd_timeout_type_t wd_timeout_type; +} wd_param_t; + +typedef struct { + nvmem_fn_type_t nvmem_fn_type; + uint32_t offset; + int size; +} nvmem_param_t; + +typedef struct { + addr_t wd_base_addr; + uint32_t wd_time_us_low; + uint32_t wd_time_us_medium; + uint32_t wd_time_us_high; + uint32_t wd_timer_tick_us; + addr_t nvmem_base_addr; + addr_t uart_base_addr; +} target_param_t; + + +typedef struct { + uint16_t test_num; + uint8_t block_num; +} test_info_t; + + +/* struture to capture test state */ +typedef struct { + uint16_t reserved; + uint8_t state; + uint8_t status; +} test_status_buffer_t; + +typedef int32_t (*client_test_t)(security_t caller); +typedef int32_t (*server_test_t)(void); +#endif /* VAL_COMMON_H */ diff --git a/psa-ff/val/common/val_client_defs.h b/psa-ff/val/common/val_client_defs.h new file mode 100644 index 00000000..c18d8a72 --- /dev/null +++ b/psa-ff/val/common/val_client_defs.h @@ -0,0 +1,81 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_CLIENT_H_ +#define _VAL_CLIENT_H_ + +/****************** PSA Client API *****************/ + +/* Note - This header file containts the declaration of PSA defined client API elements. + * Ideally, These elements must be defined in a header file by SPM implemented + * library and provided to clients operation in NSPE and SPE as per the specification. + * If this is available in the platform, the elements declared as part of this + * file can be overwritten by passing --include to setup.sh script. + */ + +#if ((PSA_API_ELEMENTS_AVAILABLE) || (!VAL_NSPE_BUILD)) +/* : Contains the Client API elements. Accessible to all applications */ +#include "psa_client.h" + +#else + +#include "val.h" +#define PSA_FRAMEWORK_VERSION (0x000A) +#define PSA_VERSION_NONE (0) +#define PSA_SUCCESS (0) +#define PSA_CONNECTION_REFUSED (INT32_MIN + 1) +#define PSA_CONNECTION_BUSY (INT32_MIN + 2) +#define PSA_DROP_CONNECTION (INT32_MIN) +#define PSA_NULL_HANDLE ((psa_handle_t)0) + +typedef int32_t psa_status_t; +typedef int32_t psa_handle_t; + +typedef struct psa_invec { + const void *base; + size_t len; +} psa_invec; + +typedef struct psa_outvec { + void *base; + size_t len; +} psa_outvec; + +uint32_t psa_framework_version(void); +uint32_t psa_version(uint32_t sid); +psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version); +psa_status_t psa_call(psa_handle_t handle, + const psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len); +psa_status_t psa_close(psa_handle_t handle); +#endif /* #if ((PSA_API_ELEMENTS_AVAILABLE) || (!VAL_NSPE_BUILD)) */ + +#ifndef VAL_NSPE_BUILD +/* : Macro definitions derived from manifest files that provides a mapping + * from RoT service names to Service IDs (SIDs) for use with the Client API. + * Partition manifest parse build tool must provide the implementation of this file. +*/ +#include "psa_sid.h" + +#else + +#include "pal_sid.h" + +#endif /* VAL_NSPE_BUILD */ + +#define INVALID_SID 0x0000FA20 +#endif /* _VAL_CLIENT_H_ */ diff --git a/psa-ff/val/nspe/pal_interfaces_ns.h b/psa-ff/val/nspe/pal_interfaces_ns.h new file mode 100644 index 00000000..0fe60fa2 --- /dev/null +++ b/psa-ff/val/nspe/pal_interfaces_ns.h @@ -0,0 +1,47 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ +#ifndef _PAL_INTERFACES_NS_H_ +#define _PAL_INTERFACES_NS_H_ + +#include "val.h" +#include + +/** + @brief - This function will read peripherals using SPI commands + @param - addr : address of the peripheral + data : read buffer + len : length of the read buffer in bytes + @return - error status +**/ +int pal_spi_read(addr_t addr, uint8_t *data, uint32_t len); + +/* Target Config API */ +/** + @brief - provides the database source location. + @param - void + @return - Returns base address of database +**/ +void *pal_target_get_cfg_start(void); + +/** + @brief - This API will call the requested crypto function + @param - type : function code + valist : variable argument list + @return - error status +**/ +uint32_t pal_crypto_function(int type, va_list valist); +#endif diff --git a/psa-ff/val/nspe/val_crypto.c b/psa-ff/val/nspe/val_crypto.c new file mode 100644 index 00000000..8843b779 --- /dev/null +++ b/psa-ff/val/nspe/val_crypto.c @@ -0,0 +1,53 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_crypto.h" +#include "val_target.h" +#include "pal_interfaces_ns.h" +#include "val_framework.h" +#include "val_client_defs.h" + +/** + @brief - This API will call the requested crypto function + @param - type : function code + ... : variable number of arguments + @return - Error status +**/ +val_status_t val_crypto_function(int type, ...) +{ + va_list valist; + val_status_t status; + + va_start(valist, type); + status = pal_crypto_function(type, valist); + va_end(valist); + return status; +} + +/** + @brief - Checks if the key type is of raw bits + @param - type : type of the key + @return - True : If key type is raw bits + False: If key type is not raw bits +**/ +int32_t val_crypto_key_type_is_raw(psa_key_type_t type) +{ + psa_key_type_t category = type & PSA_KEY_TYPE_CATEGORY_MASK; + + return (category == PSA_KEY_TYPE_RAW_DATA || + category == PSA_KEY_TYPE_CATEGORY_SYMMETRIC); +} diff --git a/psa-ff/val/nspe/val_crypto.h b/psa-ff/val/nspe/val_crypto.h new file mode 100644 index 00000000..7b96da0e --- /dev/null +++ b/psa-ff/val/nspe/val_crypto.h @@ -0,0 +1,305 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_CRYPTO_H_ +#define _VAL_CRYPTO_H_ + +#include "val.h" +#include + +#define BYTES_TO_BITS(byte) (byte * 8) +/* Size */ +#define AES_16B_KEY_SIZE 16 +#define AES_24B_KEY_SIZE 24 +#define AES_32B_KEY_SIZE 32 +#define AES_18B_KEY_SIZE 18 +#define AES_34B_KEY_SIZE 34 +#define DES_8B_KEY_SIZE 8 +#define DES3_2KEY_SIZE 16 +#define DES3_3KEY_SIZE 24 +#define SIZE_128B 128 +#define SIZE_256B 256 +#define SIZE_512B 512 +#define BUFFER_SIZE 1200 +#define HASH_64B 64 + +/* Key Slot */ +#define INVALID_KEY_SLOT 0xDEAD +#define ZERO_KEY_SLOT 0 +#define OCCUPIED_KEY_SLOT 1 +#define MAX_KEY_SLOT 32 + +/* Key Type */ +#define PSA_KEY_TYPE_RAW_DATA ((psa_key_type_t)0x50000001) +#define PSA_KEY_TYPE_CATEGORY_MASK ((psa_key_type_t)0x70000000) +#define PSA_KEY_TYPE_CATEGORY_SYMMETRIC ((psa_key_type_t)0x40000000) +#define PSA_KEY_TYPE_PAIR_FLAG ((psa_key_type_t)0x10000000) +#define PSA_KEY_TYPE_AES ((psa_key_type_t)0x40000001) +#define PSA_KEY_TYPE_DES ((psa_key_type_t)0x40000002) +#define PSA_KEY_TYPE_RSA_PUBLIC_KEY ((psa_key_type_t)0x60010000) +#define PSA_KEY_TYPE_RSA_KEYPAIR ((psa_key_type_t)0x70010000) +#define PSA_KEY_TYPE_VENDOR_FLAG ((psa_key_type_t)0x80000000) +#define PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE ((psa_key_type_t)0x60030000) +#define PSA_KEY_TYPE_ECC_KEYPAIR_BASE ((psa_key_type_t)0x70030000) +#define PSA_KEY_TYPE_ECC_CURVE_MASK ((psa_key_type_t)0x0000ffff) + +/* Key Lifetime */ +#define PSA_KEY_LIFETIME_VOLATILE ((psa_key_lifetime_t)0x00000000) +#define PSA_KEY_LIFETIME_PERSISTENT ((psa_key_lifetime_t)0x00000001) +#define PSA_KEY_LIFETIME_WRITE_ONCE ((psa_key_lifetime_t)0x7fffffff) +#define PSA_KEY_LIFETIME_INVALID ((psa_key_lifetime_t)0xffffffff) + +/* Algorithm */ +#define PSA_ALG_BLOCK_CIPHER_PAD_NONE ((psa_algorithm_t)0x00000000) +#define PSA_ALG_BLOCK_CIPHER_BASE ((psa_algorithm_t)0x04000000) +#define PSA_KEY_USAGE_EXPORT ((psa_key_usage_t)0x00000001) +#define PSA_KEY_USAGE_ENCRYPT ((psa_key_usage_t)0x00000100) +#define PSA_KEY_USAGE_DECRYPT ((psa_key_usage_t)0x00000200) +#define PSA_KEY_USAGE_SIGN ((psa_key_usage_t)0x00000400) +#define PSA_KEY_USAGE_VERIFY ((psa_key_usage_t)0x00000800) +#define PSA_KEY_USAGE_DERIVE ((psa_key_usage_t)0x00001000) +#define PSA_ALG_RSA_PKCS1V15_SIGN_BASE ((psa_algorithm_t)0x10020000) +#define PSA_ALG_ECDSA_BASE ((psa_algorithm_t)0x10060000) +#define PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION ((psa_algorithm_t)0x12000000) +#define PSA_ALG_RSA_PKCS1V15_SIGN_RAW PSA_ALG_RSA_PKCS1V15_SIGN_BASE +#define PSA_KEY_USAGE_INVALID 0xFFFFFFFF +#define PSA_ALG_INVALID 0xFFFFFFFF + +/* Hash Algorithm */ +#define PSA_ALG_CATEGORY_HASH ((psa_algorithm_t)0x01000000) +#define PSA_ALG_HASH_MASK ((psa_algorithm_t)0x000000ff) +#define PSA_ALG_MD2 ((psa_algorithm_t)0x01000001) +#define PSA_ALG_MD4 ((psa_algorithm_t)0x01000002) +#define PSA_ALG_MD5 ((psa_algorithm_t)0x01000003) +#define PSA_ALG_RIPEMD160 ((psa_algorithm_t)0x01000004) +#define PSA_ALG_SHA_1 ((psa_algorithm_t)0x01000005) +#define PSA_ALG_SHA_224 ((psa_algorithm_t)0x01000008) +#define PSA_ALG_SHA_256 ((psa_algorithm_t)0x01000009) +#define PSA_ALG_SHA_384 ((psa_algorithm_t)0x0100000a) +#define PSA_ALG_SHA_512 ((psa_algorithm_t)0x0100000b) +#define PSA_ALG_SHA_512_224 ((psa_algorithm_t)0x0100000c) +#define PSA_ALG_SHA_512_256 ((psa_algorithm_t)0x0100000d) +#define PSA_ALG_SHA3_224 ((psa_algorithm_t)0x01000010) +#define PSA_ALG_SHA3_256 ((psa_algorithm_t)0x01000011) +#define PSA_ALG_SHA3_384 ((psa_algorithm_t)0x01000012) +#define PSA_ALG_SHA3_512 ((psa_algorithm_t)0x01000013) + +/* Error codes */ +#define PSA_ERROR_UNKNOWN_ERROR ((psa_status_t)1) +#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)2) +#define PSA_ERROR_NOT_PERMITTED ((psa_status_t)3) +#define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)4) +#define PSA_ERROR_OCCUPIED_SLOT ((psa_status_t)5) +#define PSA_ERROR_EMPTY_SLOT ((psa_status_t)6) +#define PSA_ERROR_BAD_STATE ((psa_status_t)7) +#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)8) +#define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)9) +#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)10) +#define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)11) +#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)12) +#define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)13) +#define PSA_ERROR_TAMPERING_DETECTED ((psa_status_t)14) +#define PSA_ERROR_INSUFFICIENT_ENTROPY ((psa_status_t)15) +#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)16) +#define PSA_ERROR_INVALID_PADDING ((psa_status_t)17) +#define PSA_ERROR_INSUFFICIENT_CAPACITY ((psa_status_t)18) + +/* Encoding of curve identifiers */ +#define PSA_ECC_CURVE_SECT163K1 ((psa_ecc_curve_t) 0x0001) +#define PSA_ECC_CURVE_SECT163R1 ((psa_ecc_curve_t) 0x0002) +#define PSA_ECC_CURVE_SECT163R2 ((psa_ecc_curve_t) 0x0003) +#define PSA_ECC_CURVE_SECT193R1 ((psa_ecc_curve_t) 0x0004) +#define PSA_ECC_CURVE_SECT193R2 ((psa_ecc_curve_t) 0x0005) +#define PSA_ECC_CURVE_SECT233K1 ((psa_ecc_curve_t) 0x0006) +#define PSA_ECC_CURVE_SECT233R1 ((psa_ecc_curve_t) 0x0007) +#define PSA_ECC_CURVE_SECT239K1 ((psa_ecc_curve_t) 0x0008) +#define PSA_ECC_CURVE_SECT283K1 ((psa_ecc_curve_t) 0x0009) +#define PSA_ECC_CURVE_SECT283R1 ((psa_ecc_curve_t) 0x000a) +#define PSA_ECC_CURVE_SECT409K1 ((psa_ecc_curve_t) 0x000b) +#define PSA_ECC_CURVE_SECT409R1 ((psa_ecc_curve_t) 0x000c) +#define PSA_ECC_CURVE_SECT571K1 ((psa_ecc_curve_t) 0x000d) +#define PSA_ECC_CURVE_SECT571R1 ((psa_ecc_curve_t) 0x000e) +#define PSA_ECC_CURVE_SECP160K1 ((psa_ecc_curve_t) 0x000f) +#define PSA_ECC_CURVE_SECP160R1 ((psa_ecc_curve_t) 0x0010) +#define PSA_ECC_CURVE_SECP160R2 ((psa_ecc_curve_t) 0x0011) +#define PSA_ECC_CURVE_SECP192K1 ((psa_ecc_curve_t) 0x0012) +#define PSA_ECC_CURVE_SECP192R1 ((psa_ecc_curve_t) 0x0013) +#define PSA_ECC_CURVE_SECP224K1 ((psa_ecc_curve_t) 0x0014) +#define PSA_ECC_CURVE_SECP224R1 ((psa_ecc_curve_t) 0x0015) +#define PSA_ECC_CURVE_SECP256K1 ((psa_ecc_curve_t) 0x0016) +#define PSA_ECC_CURVE_SECP256R1 ((psa_ecc_curve_t) 0x0017) +#define PSA_ECC_CURVE_SECP384R1 ((psa_ecc_curve_t) 0x0018) +#define PSA_ECC_CURVE_SECP521R1 ((psa_ecc_curve_t) 0x0019) +#define PSA_ECC_CURVE_BRAINPOOL_P256R1 ((psa_ecc_curve_t) 0x001a) +#define PSA_ECC_CURVE_BRAINPOOL_P384R1 ((psa_ecc_curve_t) 0x001b) +#define PSA_ECC_CURVE_BRAINPOOL_P512R1 ((psa_ecc_curve_t) 0x001c) +#define PSA_ECC_CURVE_CURVE25519 ((psa_ecc_curve_t) 0x001d) +#define PSA_ECC_CURVE_CURVE448 ((psa_ecc_curve_t) 0x001e) +#define PSA_ECC_CURVE_FFDHE_2048 ((psa_ecc_curve_t) 0x0100) +#define PSA_ECC_CURVE_FFDHE_3072 ((psa_ecc_curve_t) 0x0101) +#define PSA_ECC_CURVE_FFDHE_4096 ((psa_ecc_curve_t) 0x0102) +#define PSA_ECC_CURVE_FFDHE_6144 ((psa_ecc_curve_t) 0x0103) +#define PSA_ECC_CURVE_FFDHE_8192 ((psa_ecc_curve_t) 0x0104) + +#define PSA_ALG_HMAC_HASH(hmac_alg) \ + (PSA_ALG_CATEGORY_HASH | ((hmac_alg) & PSA_ALG_HASH_MASK)) + +/* The size of the output hash */ +#define PSA_HASH_SIZE(alg) \ + ( \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_MD2 ? 16 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_MD4 ? 16 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_MD5 ? 16 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_RIPEMD160 ? 20 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_1 ? 20 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_224 ? 28 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_256 ? 32 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_384 ? 48 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_512 ? 64 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_512_224 ? 28 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA_512_256 ? 32 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA3_224 ? 28 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA3_256 ? 32 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA3_384 ? 48 : \ + PSA_ALG_HMAC_HASH(alg) == PSA_ALG_SHA3_512 ? 64 : \ + 0) + +/** The public key type corresponding to a key pair type. */ +#define PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type) \ + ((type) & ~PSA_KEY_TYPE_PAIR_FLAG) + +/** Whether a key type is an RSA key (pair or public-only). */ +#define PSA_KEY_TYPE_IS_RSA(type) \ + (PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY) + +/** Whether a key type is an elliptic curve key (pair or public-only). */ +#define PSA_KEY_TYPE_IS_ECC(type) \ + ((PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type) & \ + ~PSA_KEY_TYPE_ECC_CURVE_MASK) == PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE) +#define PSA_KEY_TYPE_IS_ECC_KEYPAIR(type) \ + (((type) & ~PSA_KEY_TYPE_ECC_CURVE_MASK) == \ + PSA_KEY_TYPE_ECC_KEYPAIR_BASE) +#define PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(type) \ + (((type) & ~PSA_KEY_TYPE_ECC_CURVE_MASK) == \ + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE) + +typedef uint16_t psa_ecc_curve_t; +typedef uint32_t psa_key_usage_t; +typedef uint32_t psa_algorithm_t; +typedef int32_t psa_status_t; +typedef uint32_t psa_key_type_t; +typedef uint32_t psa_key_slot_t; +typedef uint32_t psa_key_lifetime_t; + +enum crypto_function_code { + VAL_CRYPTO_INIT = 0x1, + VAL_CRYPTO_GENERATE_RANDOM = 0x2, + VAL_CRYPTO_IMPORT_KEY = 0x3, + VAL_CRYPTO_EXPORT_KEY = 0x4, + VAL_CRYPTO_EXPORT_PUBLIC_KEY = 0x5, + VAL_CRYPTO_DESTROY_KEY = 0x6, + VAL_CRYPTO_GET_KEY_INFO = 0x7, + VAL_CRYPTO_KEY_POLICY_INIT = 0x8, + VAL_CRYPTO_KEY_POLICY_SET_USAGE = 0x9, + VAL_CRYPTO_KEY_POLICY_GET_USAGE = 0xA, + VAL_CRYPTO_KEY_POLICY_GET_ALGORITHM = 0xB, + VAL_CRYPTO_SET_KEY_POLICY = 0xC, + VAL_CRYPTO_GET_KEY_POLICY = 0xD, + VAL_CRYPTO_GET_KEY_INFORMATION = 0xE, + VAL_CRYPTO_GET_KEY_LIFETIME = 0xF, + VAL_CRYPTO_SET_KEY_LIFETIME = 0x10, + VAL_CRYPTO_HASH_SETUP = 0x11, + VAL_CRYPTO_HASH_UPDATE = 0x12, + VAL_CRYPTO_HASH_VERIFY = 0x13, + VAL_CRYPTO_HASH_FINISH = 0x14, + VAL_CRYPTO_HASH_ABORT = 0x15, +}; + +struct psa_key_policy_s { + psa_key_usage_t usage; + psa_algorithm_t alg; +}; + +typedef struct { + unsigned char cksum[16]; /*!< checksum of the data block */ + unsigned char state[48]; /*!< intermediate digest state */ + unsigned char buffer[16]; /*!< data block being processed */ + size_t left; /*!< amount of data in buffer */ +} mbedtls_md2_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} mbedtls_md4_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} mbedtls_md5_context; + +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} mbedtls_ripemd160_context; + +typedef struct { + uint32_t total[2]; /*!< The number of Bytes processed. */ + uint32_t state[5]; /*!< The intermediate digest state. */ + unsigned char buffer[64]; /*!< The data block being processed. */ +} mbedtls_sha1_context; + +typedef struct { + uint32_t total[2]; /*!< The number of Bytes processed. */ + uint32_t state[8]; /*!< The intermediate digest state. */ + unsigned char buffer[64]; /*!< The data block being processed. */ + int is224; /*!< Determines which function to use: + 0: Use SHA-256, or 1: Use SHA-224. */ +} mbedtls_sha256_context; + +typedef struct { + uint64_t total[2]; /*!< The number of Bytes processed. */ + uint64_t state[8]; /*!< The intermediate digest state. */ + unsigned char buffer[128]; /*!< The data block being processed. */ + int is384; /*!< Determines which function to use: + 0: Use SHA-512, or 1: Use SHA-384. */ +} mbedtls_sha512_context; + +struct psa_hash_operation_s +{ + psa_algorithm_t alg; + union + { + unsigned dummy; /* Make the union non-empty even with no supported algorithms. */ + mbedtls_md2_context md2; + mbedtls_md4_context md4; + mbedtls_md5_context md5; + mbedtls_ripemd160_context ripemd160; + mbedtls_sha1_context sha1; + mbedtls_sha256_context sha256; + mbedtls_sha512_context sha512; + } ctx; +}; + +typedef struct psa_hash_operation_s psa_hash_operation_t; +typedef struct psa_key_policy_s psa_key_policy_t; + +val_status_t val_crypto_function(int type, ...); +int32_t val_crypto_key_type_is_raw(psa_key_type_t type); +#endif /* _VAL_CRYPTO_H_ */ diff --git a/psa-ff/val/nspe/val_dispatcher.c b/psa-ff/val/nspe/val_dispatcher.c new file mode 100644 index 00000000..ae222cb0 --- /dev/null +++ b/psa-ff/val/nspe/val_dispatcher.c @@ -0,0 +1,436 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_framework.h" +#include "val_dispatcher.h" +#include "val_interfaces.h" +#include "val_peripherals.h" +#include "val_target.h" + +extern val_api_t val_api; +extern psa_api_t psa_api; + +/* gloabls */ +addr_t g_test_info_addr; +uint32_t combine_test_binary_in_ram; +addr_t combine_test_binary_addr; + +static const unsigned char elf_magic_header[ELF_IDENT] = { + /* 0x7f, 'E', 'L', 'F' */ + 0x7f, 0x45, 0x4c, 0x46, + /* Only 32-bit objects */ + 0x01, + /* Only LSB data. */ + 0x01, + /* Only ELF version 1. */ + 0x01, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0 +}; + +/** + @brief - This API will copy the length of data from addr to *data + @param - addr : address to be read + data pointer : address to which data will be copied + len : length of data to be copy in bytes + @return - error status +**/ +val_status_t val_mem_copy(addr_t addr, uint8_t *data, uint32_t len) +{ + if (combine_test_binary_in_ram) + { + memcpy((void*)data, (void *)addr, len); + return VAL_STATUS_SUCCESS; + } + else + { + return val_spi_read(addr, data, len); + } +} + + +/** + @brief - This function parses ELF header, entry address(test info addreess) + and program headers. Copies the loadable segments to system memory. + @return - Returns Success/Failure +**/ +int val_copy_elf(uint32_t saddr, uint32_t *info_addr) +{ + elf_header_t test_elfh; + elf_pheader_t test_ph; + int i; + + if (0 != val_mem_copy(saddr, (uint8_t *)&test_elfh, sizeof(elf_header_t))) + { + val_print(PRINT_ERROR, "Error: read failure for Test ELF header\n", 0); + return -1; + } + + /* validate ELF header */ + if (0 != memcmp(&test_elfh.e_ident, &elf_magic_header, ELF_IDENT)) + { + val_print(PRINT_ERROR, "Fail: Test ELF header validation\n", 0); + return -1; + } + + for (i = 0; i < test_elfh.e_phnum; i++) + { + /* Read the program header */ + if (0 != val_mem_copy((saddr + test_elfh.e_phoff + (sizeof(elf_pheader_t)*i)), + (uint8_t *)&test_ph, sizeof(elf_pheader_t))) + { + val_print(PRINT_ERROR, "Error: reading Test program header\n", 0); + return -1; + } + + /* Load the program to physical RAM */ + if (0 != val_mem_copy((saddr + test_ph.p_offset), + (uint8_t *)test_ph.p_paddr, test_ph.p_filesz)) + { + val_print(PRINT_ERROR, "Error: reading Test program header\n", 0); + return -1; + } + } + + *info_addr = test_elfh.e_entry; + return 0; +} + +/** + @brief - This function reads the test ELFs from RAM or secondary storage and loads into + system memory + @param - test_id : Returns the current test ID + - test_id_prev : Previous test ID. + @return - Error code +**/ +val_status_t val_test_load(test_id_t *test_id, test_id_t test_id_prev) +{ + test_header_t test_header; + addr_t flash_addr = combine_test_binary_addr; + + /* + * The combined Test ELF binary: + * + * ---------------------- + * | Custom Test Header*| + * |--------------------| + * | Test-1 Image | + * ---------------------- + * | Custom Test Header*| + * ---------------------- + * | Test-2 Image | + * |--------------------| + * | Custom Test Header*| + * : + * : + * ---------------------- + * | END Marker | + * ---------------------- + * + */ + + if (test_id_prev != VAL_INVALID_TEST_ID) + { + /* Jump to last test run + 1 */ + do + { + if (val_mem_copy(flash_addr, (uint8_t *)&test_header, sizeof(test_header_t))) + { + val_print(PRINT_ERROR, "Error: reading Test program header\n", 0); + return VAL_STATUS_LOAD_ERROR; + } + + if (test_header.start_marker == VAL_TEST_END_MARKER) + { + val_print(PRINT_ERROR, "\n\nNo more valid tests found. Exiting..", 0); + *test_id = VAL_INVALID_TEST_ID; + return VAL_STATUS_SUCCESS; + } + + if (test_header.start_marker != VAL_TEST_START_MARKER) + { + flash_addr += 0x4; + continue; + } + + if ((test_header.start_marker == VAL_TEST_START_MARKER) + && (test_header.test_id == test_id_prev)) + { + flash_addr += (sizeof(test_header_t) + test_header.elf_size); + break; + } + + flash_addr += (sizeof(test_header_t) + test_header.elf_size); + } while(1); + } + + if (val_mem_copy(flash_addr, (uint8_t *)&test_header, sizeof(test_header_t))) + { + val_print(PRINT_ERROR, "\n\nError: reading custom Test header", 0); + return VAL_STATUS_LOAD_ERROR; + } + + if (test_header.start_marker == VAL_TEST_END_MARKER) + { + val_print(PRINT_ERROR, "\n\nNo more valid tests found. Exiting.", 0); + *test_id = VAL_INVALID_TEST_ID; + return VAL_STATUS_SUCCESS; + } + + if (test_header.start_marker != VAL_TEST_START_MARKER) + { + val_print(PRINT_ERROR, "\n\nNo valid test binary found. Exiting.", 0); + *test_id = VAL_INVALID_TEST_ID; + return VAL_STATUS_LOAD_ERROR; + } + + flash_addr += sizeof(test_header_t); + if (val_copy_elf(flash_addr, &g_test_info_addr)) + { + val_print(PRINT_ERROR, "Error: loading Test program\n", 0); + return VAL_STATUS_LOAD_ERROR; + } + + *test_id = test_header.test_id; + return VAL_STATUS_SUCCESS; +} + +/** + @brief - This function reads the function pointer addresses for + test_entry + @param - paddr : Returns the Test function address + @return - Returns val_status_t +**/ +val_status_t val_get_test_entry_addr(addr_t *paddr) +{ + *paddr = (addr_t)(((val_test_info_t *)g_test_info_addr)->entry_addr); + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Execute the function pointer which was given to us by the test + @param - void +**/ +void val_execute_test_fn(void) +{ + test_fptr_t fn_ptr; + addr_t addr; + + val_get_test_entry_addr(&addr); + fn_ptr = (test_fptr_t)addr; + fn_ptr(&val_api, &psa_api); + return; +} + +/* + @brief - Reads the pre-defined component name against given test_id + @param - test_id : Current Test ID + @return - Component name +*/ +char * val_get_comp_name(test_id_t test_id) +{ + switch (VAL_GET_COMP_NUM(test_id)) + { + case VAL_FF_BASE: + return "\nRunning... IPC Suite"; + case VAL_CRYPTO_BASE: + return "\nRunning... Crypto Suite"; + default: + return "No Component"; + } +} + +/** + @brief - This function is responsible for setting up VAL infrastructure. + Loads test one by one from combine binary and calls test_entry + function of each test image. + @return - none +**/ +void val_dispatcher(test_id_t test_id_prev) +{ + + test_id_t test_id; + val_status_t status; + miscellaneous_desc_t *misc_desc; + boot_t boot; + test_count_t test_count; + uint32_t test_result; + + status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MISCELLANEOUS, + MISCELLANEOUS_DUT, 0), + (uint8_t **)&misc_desc, + (uint32_t *)sizeof(miscellaneous_desc_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\ttarget config read failed", 0); + return; + } + + combine_test_binary_addr = misc_desc->ns_start_addr_of_combine_test_binary; + combine_test_binary_in_ram = misc_desc->combine_test_binary_in_ram; + do + { + status = val_get_boot_flag(&boot.state); + if (VAL_ERROR(status)) + { + break; + } + + /* Did last run test hang and system re-booted due to watchdog timeout and + boot.state was set to BOOT_NOT_EXPECTED ? If yes, set the test status + to SIM ERROR and go to next test. */ + if (boot.state == BOOT_NOT_EXPECTED) + { + val_set_status(RESULT_PENDING(VAL_STATUS_ERROR)); + status = val_nvmem_read(VAL_NVMEM_OFFSET(NV_TEST_ID_CURRENT), + &test_id, sizeof(test_id_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM read error", 0); + } + } + /* Did last run test hang and system reset due to watchdog timeout but + boot.state was set to BOOT_EXPECTED_BUT_FAILED ? If yes, set the test status + to FAIL and go to next test. This condition will hit when test was expecting + re-boot on perticular scenario but it didn't happen and system re-booted due + to other reason. */ + else if (boot.state == BOOT_EXPECTED_BUT_FAILED) + { + val_set_status(RESULT_FAIL(VAL_STATUS_BOOT_EXPECTED_BUT_FAILED)); + status = val_nvmem_read(VAL_NVMEM_OFFSET(NV_TEST_ID_CURRENT), + &test_id, sizeof(test_id_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM read error", 0); + } + } + else + { + status = val_test_load(&test_id, test_id_prev); + + if (test_id == VAL_INVALID_TEST_ID || VAL_ERROR(status)) + { + break; + } + + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_TEST_ID_CURRENT), + &test_id, sizeof(test_id_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM write error", 0); + break; + } + + if (VAL_GET_COMP_NUM(test_id_prev) != VAL_GET_COMP_NUM(test_id)) + { + val_print(PRINT_ALWAYS, val_get_comp_name(test_id), 0); + val_print(PRINT_ALWAYS, "\n******************************************\n", 0); + } + + if (boot.state == BOOT_UNKNOWN) + { + /* Set boot.state to BOOT_NOT_EXPECTED to catch unexpected test hang */ + status = val_set_boot_flag(BOOT_NOT_EXPECTED); + if (VAL_ERROR(status)) + { + break; + } + } + val_execute_test_fn(); + } + + test_result = val_report_status(); + + /* Reset boot.state to UNKNOWN before lunching next test */ + status = val_set_boot_flag(BOOT_UNKNOWN); + if (VAL_ERROR(status)) + { + break; + } + + /* Prepare suite summary data structure */ + status = val_nvmem_read(VAL_NVMEM_OFFSET(NV_TEST_CNT), &test_count, sizeof(test_count_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM read error", 0); + break; + } + + switch (test_result) + { + case TEST_PASS: + test_count.pass_cnt += 1; + break; + case TEST_FAIL: + test_count.fail_cnt += 1; + break; + case TEST_SKIP: + test_count.skip_cnt += 1; + break; + case TEST_PENDING: + test_count.sim_error_cnt += 1; + break; + } + + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_TEST_CNT), &test_count, sizeof(test_count_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM write error", 0); + break; + } + + test_id_prev = test_id; + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_TEST_ID_PREVIOUS), + &test_id, sizeof(test_id_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM write error", 0); + break; + } + + } while(1); + + status = val_nvmem_read(VAL_NVMEM_OFFSET(NV_TEST_CNT), &test_count, sizeof(test_count_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM read error", 0); + return; + } + + val_print(PRINT_ALWAYS, "\n\n************ REGRESSION SUMMARY **********\n", 0); + val_print(PRINT_ALWAYS, "TOTAL TESTS : %d\n", test_count.pass_cnt + test_count.fail_cnt + + test_count.skip_cnt + test_count.sim_error_cnt); + val_print(PRINT_ALWAYS, "TOTAL PASSED : %d\n", test_count.pass_cnt); + val_print(PRINT_ALWAYS, "TOTAL SIM ERROR : %d\n", test_count.sim_error_cnt); + val_print(PRINT_ALWAYS, "TOTAL FAILED : %d\n", test_count.fail_cnt); + val_print(PRINT_ALWAYS, "TOTAL SKIPPED : %d\n", test_count.skip_cnt); + val_print(PRINT_ALWAYS, "\n******************************************\n", 0); +} + + + + + + + diff --git a/psa-ff/val/nspe/val_dispatcher.h b/psa-ff/val/nspe/val_dispatcher.h new file mode 100644 index 00000000..9adc98f8 --- /dev/null +++ b/psa-ff/val/nspe/val_dispatcher.h @@ -0,0 +1,70 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_DISPATCHER_H_ +#define _VAL_DISPATCHER_H_ + +#include "val.h" + +#define ELF_IDENT 16 +#define VAL_INVALID_TEST_ID 0xffffffff +#define VAL_TEST_START_MARKER 0xfaceface +#define VAL_TEST_END_MARKER 0xc3c3c3c3 + +/* typedef's */ +typedef uint32_t elf32_word; +typedef int32_t elf32_sword; +typedef uint16_t elf32_half; +typedef uint32_t elf32_off; +typedef uint32_t elf32_addr; + +typedef struct { + unsigned char e_ident[ELF_IDENT]; /* ident bytes */ + elf32_half e_type; /* file type */ + elf32_half e_machine; /* target machine */ + elf32_word e_version; /* file version */ + elf32_addr e_entry; /* start address */ + elf32_off e_phoff; /* phdr file offset */ + elf32_off e_shoff; /* shdr file offset */ + elf32_word e_flags; /* file flags */ + elf32_half e_ehsize; /* sizeof ehdr */ + elf32_half e_phentsize; /* sizeof phdr */ + elf32_half e_phnum; /* number phdrs */ + elf32_half e_shentsize; /* sizeof shdr */ + elf32_half e_shnum; /* number shdrs */ + elf32_half e_shstrndx; /* shdr string index */ +} elf_header_t; + +typedef struct { + elf32_word p_type; /* Segment type */ + elf32_off p_offset; /* Segment file offset */ + elf32_addr p_vaddr; /* Segment virtual address */ + elf32_addr p_paddr; /* Segment physical address */ + elf32_word p_filesz; /* Segment size in file */ + elf32_word p_memsz; /* Segment size in memory */ + elf32_word p_flags; /* Segment flags */ + elf32_word p_align; /* Segment alignment */ +} elf_pheader_t; + +typedef struct { + uint32_t start_marker; + test_id_t test_id; + uint32_t elf_size; +} test_header_t; + +void val_dispatcher(test_id_t); +#endif diff --git a/psa-ff/val/nspe/val_entry.c b/psa-ff/val/nspe/val_entry.c new file mode 100644 index 00000000..3f9d119a --- /dev/null +++ b/psa-ff/val/nspe/val_entry.c @@ -0,0 +1,63 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_entry.h" +#include "val_framework.h" +#include "val_peripherals.h" +#include "val_dispatcher.h" + +/** + @brief - PSA C main function, does VAL init and calls test dispatcher + @param - None + @return - void +**/ +void val_entry(void) +{ + test_id_t test_id; + + if (VAL_ERROR(val_target_init())) + { + goto exit; + } + + if (VAL_ERROR(val_uart_init())) + { + goto exit; + } + + if (VAL_ERROR(val_get_last_run_test_id(&test_id))) + { + goto exit; + } + + /* Compliance header print */ + if (test_id == VAL_INVALID_TEST_ID) + { + val_print(PRINT_ALWAYS, "\n***** PSA Compliance Suite - Version %d.", PSA_ACS_MAJOR_VER); + val_print(PRINT_ALWAYS, "%d *****\n", PSA_ACS_MINOR_VER); + } + + /* Call dispatcher routine*/ + val_dispatcher(test_id); + +exit: + val_print(PRINT_ALWAYS, "\n\nEntering standby\n", 0); + while(1) + { + asm volatile("WFI"); + } +} diff --git a/psa-ff/val/nspe/val_entry.h b/psa-ff/val/nspe/val_entry.h new file mode 100644 index 00000000..0293113b --- /dev/null +++ b/psa-ff/val/nspe/val_entry.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_ENTRY_H_ +#define _VAL_ENTRY_H_ + +#include "val_framework.h" + +#define PSA_ACS_MAJOR_VER 0 +#define PSA_ACS_MINOR_VER 5 + +/** + @brief - PSA C main function, does VAL init and calls test dispatcher + @param - None + @return - void +**/ +extern void val_entry(void); +#endif diff --git a/psa-ff/val/nspe/val_framework.c b/psa-ff/val/nspe/val_framework.c new file mode 100644 index 00000000..bb50d805 --- /dev/null +++ b/psa-ff/val/nspe/val_framework.c @@ -0,0 +1,590 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_framework.h" +#include "val_interfaces.h" +#include "val_dispatcher.h" +#include "val_peripherals.h" +#include "pal_interfaces_ns.h" +#include "val_target.h" + +extern val_api_t val_api; +extern psa_api_t psa_api; + +/* globals */ +test_status_buffer_t g_status_buffer; + +/** + * @brief Connect to given sid + @param -sid : RoT service id + @param -minor_version : minor_version of RoT service + @param -handle - return connection handle + * @return val_status_t + */ +val_status_t val_ipc_connect(uint32_t sid, uint32_t minor_version, psa_handle_t *handle ) +{ + *handle = psa_connect(sid, minor_version); + + if (*handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +/** + * @brief Call a connected Root of Trust Service.@n + * The caller must provide an array of ::psa_invec_t structures as the input payload. + * + * @param handle Handle for the connection. + * @param in_vec Array of psa_invec structures. + * @param in_len Number of psa_invec structures in in_vec. + * @param out_vec Array of psa_outvec structures for optional Root of Trust Service response. + * @param out_len Number of psa_outvec structures in out_vec. + * @return val_status_t + */ +val_status_t val_ipc_call(psa_handle_t handle, psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len) +{ + psa_status_t call_status = PSA_SUCCESS; + + call_status = psa_call(handle, in_vec, in_len, out_vec, out_len); + + if (call_status != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +/** + * @brief Close a connection to a Root of Trust Service. + * Sends the PSA_IPC_DISCONNECT message to the Root of Trust Service so it can clean up resources. + * + * @param handle Handle for the connection. + * @return void + */ +void val_ipc_close(psa_handle_t handle) +{ + psa_close(handle); +} +/** + @brief - This function executes given list of tests from non-secure sequentially + This covers non-secure to secure IPC API scenario + @param - test_num : Test_num + @param - tests_list : list of tests to be executed + @param - server_hs : Initiate a server handshake + @return - val_status_t +**/ +val_status_t val_execute_non_secure_tests(uint32_t test_num, client_test_t *tests_list, + bool_t server_hs) +{ + val_status_t status = VAL_STATUS_SUCCESS; + val_status_t test_status = VAL_STATUS_SUCCESS; + boot_t boot; + psa_handle_t handle; + uint32_t i = 1; + test_info_t test_info; + + test_info.test_num = test_num; + + status = val_get_boot_flag(&boot.state); + if (VAL_ERROR(status)) + { + val_set_status(RESULT_FAIL(status)); + return status; + } + + if (boot.state == BOOT_NOT_EXPECTED || boot.state == BOOT_EXPECTED_CRYPTO) + { + val_print(PRINT_TEST,"[Info] Executing tests form non-secure\n", 0); + while (tests_list[i] != NULL) + { + if (server_hs == TRUE) + { + /* Handshake with server tests */ + test_info.block_num = i; + status = val_execute_secure_test_func(&handle, test_info, + SERVER_TEST_DISPATCHER_SID); + if (VAL_ERROR(status)) + { + val_set_status(RESULT_FAIL(status)); + val_print(PRINT_ERROR,"[Check%d] START\n", i); + return status; + } + else + { + val_print(PRINT_DEBUG,"[Check%d] START\n", i); + } + } + + /* Execute client tests */ + test_status = tests_list[i](NONSECURE); + + if (server_hs == TRUE) + { + /* Retrive Server test status */ + status = val_get_secure_test_result(&handle); + } + + status = test_status ? test_status:status; + if (VAL_ERROR(status)) + { + val_set_status(RESULT_FAIL(status)); + val_print(PRINT_ERROR,"[Check%d] FAILED\n", i); + return status; + } + else + { + val_print(PRINT_DEBUG,"[Check%d] PASSED\n", i); + } + i++; + } + } + else + { + /* If we are here means, we are in second run of this test */ + status = VAL_STATUS_SUCCESS; + if (boot.state != BOOT_EXPECTED_S) + { + val_print(PRINT_DEBUG,"[Check1] PASSED\n", 0); + } + } + return status; +} +/** + @brief - This function is used to switch to client_partition.c + where client tests will be executed to cover secure to secure + IPC scenario. + @param - test_num : Test_num + @return - val_status_t +**/ +val_status_t val_switch_to_secure_client(uint32_t test_num) +{ + val_status_t status = VAL_STATUS_SUCCESS; + boot_t boot; + psa_handle_t handle; + test_info_t test_info; + + test_info.test_num = test_num; + test_info.block_num = 1; + + status = val_get_boot_flag(&boot.state); + if (VAL_ERROR(status)) + { + goto exit; + } + + if (boot.state != BOOT_EXPECTED_S) + { + status = val_set_boot_flag(BOOT_NOT_EXPECTED); + if (VAL_ERROR(status)) + { + goto exit; + } + + /* switch to secure client */ + status = val_execute_secure_test_func(&handle, test_info, CLIENT_TEST_DISPATCHER_SID); + if (VAL_ERROR(status)) + { + goto exit; + } + + /* Retrive secure client test status */ + status = val_get_secure_test_result(&handle); + if (VAL_ERROR(status)) + { + goto exit; + } + return status; + } + else + { + /* If we are here means, we are in third run of this test */ + val_print(PRINT_DEBUG,"[Check1] PASSED\n", 0); + return VAL_STATUS_SUCCESS; + } + +exit: + val_set_status(RESULT_FAIL(status)); + return status; +} + +/** + @brief - This function is used to handshake between: + - nonsecure client fn to server test fn + - secure client fn and server test fn + - nonsecure client fn to secure client test fn + @param - handle : handle returned while connecting given sid + @param - test_info : Test_num and block_num to be executed + @param - sid : RoT service to be connected. Partition dispatcher sid + @return - val_status_t +**/ +val_status_t val_execute_secure_test_func(psa_handle_t *handle, test_info_t test_info, uint32_t sid) +{ + uint32_t test_data; + val_status_t status = VAL_STATUS_SUCCESS; + psa_status_t status_of_call = PSA_SUCCESS; + + *handle = psa_connect(sid, 0); + + if (*handle < 0) + { + val_print(PRINT_ERROR, "Could not connect SID. Handle=%x\n", *handle); + return VAL_STATUS_CONNECTION_FAILED; + } + + test_data = ((uint32_t)(test_info.test_num) |((uint32_t)(test_info.block_num) << BLOCK_NUM_POS) + | ((uint32_t)(TEST_EXECUTE_FUNC) << ACTION_POS)); + psa_invec data[1] = {{&test_data, sizeof(test_data)}}; + + status_of_call = psa_call(*handle, data, 1, NULL, 0); + + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + val_print(PRINT_ERROR, "Call to dispatch SF failed. Status=%x\n", status_of_call); + psa_close(*handle); + } + return status; +} + +/** + @brief - This function is used to retrive the status of previously connected test function + using val_execute_secure_test_func + @param - handle : handle of server function. Handle of Partition dispatcher sid + @return - The status of test functions +**/ +val_status_t val_get_secure_test_result(psa_handle_t *handle) +{ + uint32_t test_data; + val_status_t status = VAL_STATUS_SUCCESS; + psa_status_t status_of_call = PSA_SUCCESS; + + test_data = (TEST_RETURN_RESULT << ACTION_POS); + + psa_outvec resp = {&status, sizeof(status)}; + psa_invec data[1] = {{&test_data, sizeof(test_data)}}; + + status_of_call = psa_call(*handle, data, 1, &resp, 1); + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + val_print(PRINT_ERROR, "Call to dispatch SF failed. Status=%x\n", status_of_call); + } + + psa_close(*handle); + return status; +} + + +/** + @brief - Parses input status for a given test and + outputs appropriate information on the console + @return - Test state +**/ +uint32_t val_report_status(void) +{ + uint32_t status, state; + + status = val_get_status(); + + state = (status >> TEST_STATE_BIT) & TEST_STATE_MASK; + status = status & TEST_STATUS_MASK; + + switch (state) + { + case TEST_START: + state = TEST_FAIL; + val_print(PRINT_ALWAYS, "TEST RESULT: FAILED (Error Code=0x%x)\n", + VAL_STATUS_INIT_FAILED); + break; + + case TEST_END: + state = TEST_PASS; + val_print(PRINT_ALWAYS, "TEST RESULT: PASSED \n", 0); + break; + + case TEST_FAIL: + val_print(PRINT_ALWAYS, "TEST RESULT: FAILED (Error Code=0x%x) \n", status); + break; + + case TEST_SKIP: + val_print(PRINT_ALWAYS, "TEST RESULT: SKIPPED (Skip Code=0x%x)\n", status); + break; + + case TEST_PENDING: + val_print(PRINT_ALWAYS, "TEST RESULT: SIM ERROR (Error Code=0x%x)\n", status); + break; + + default: + state = TEST_FAIL; + val_print(PRINT_ALWAYS, "TEST RESULT: FAILED(Error Code=0x%x)\n", VAL_STATUS_INVALID); + break; + + } + + val_print(PRINT_ALWAYS, "\n******************************************\n", 0); + return state; +} + +/** + @brief - Records the state and status of test + @return - val_status_t +**/ +val_status_t val_set_status(uint32_t status) +{ + g_status_buffer.state = ((status >> TEST_STATE_BIT) & TEST_STATE_MASK); + g_status_buffer.status = (status & TEST_STATUS_MASK); + + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Updates the state and status for a given test + @return - test status +**/ +uint32_t val_get_status(void) +{ + return ((g_status_buffer.state) << TEST_STATE_BIT) | (g_status_buffer.status); +} + +/* + @brief - This function checks if the input status argument is an error. + On error, we print the checkpoint value and set the status. + @param - checkpoint : Test debug checkpoint + - val_status_t : Test status + @return - returns the input status back to the program. +*/ + +val_status_t val_err_check_set(uint32_t checkpoint, val_status_t status) +{ + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\tCheckpoint %d : ", checkpoint); + val_print(PRINT_ERROR, "Error Code=0x%x \n", status); + val_set_status(RESULT_FAIL(status)); + } + else + { + status = val_get_status(); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\tCheckpoint %d : ", checkpoint); + val_print(PRINT_ERROR, "Error Code=0x%x \n", status); + } + else + { + val_print(PRINT_DEBUG, "\tCheckpoint %d \n", checkpoint); + } + } + return status; +} + +/** + @brief This API prints the test number, description and + sets the test state to TEST_START on successful execution. + @param test_num :unique number identifying this test + @param desc :brief description of the test + @param test_bitfield :Addition test info such as + - test isolation level requirement + - Watchdog timeout type + @return void +**/ + +void val_test_init(uint32_t test_num, char8_t *desc, uint32_t test_bitfield) +{ + val_status_t status = VAL_STATUS_SUCCESS; + miscellaneous_desc_t *misc_desc; + + /*global init*/ + g_status_buffer.state = 0; + g_status_buffer.status = VAL_STATUS_INVALID; + + val_print(PRINT_ALWAYS, "\nTEST: %d | DESCRIPTION: ", test_num); + val_print(PRINT_ALWAYS, desc, 0); + + /* common skip logic */ + status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MISCELLANEOUS, + MISCELLANEOUS_DUT, 0), + (uint8_t **)&misc_desc, + (uint32_t *)sizeof(miscellaneous_desc_t)); + if (VAL_ERROR(status)) + { + return; + } + + if (misc_desc->implemented_psa_firmware_isolation_level < + GET_TEST_ISOLATION_LEVEL(test_bitfield)) + { + val_set_status(RESULT_SKIP(VAL_STATUS_ISOLATION_LEVEL_NOT_SUPP)); + val_print(PRINT_ALWAYS, "Skipping test. Required isolation level is not supported\n", 0); + return; + } + + /* Initialise watchdog */ + status = val_wd_timer_init(GET_WD_TIMOUT_TYPE(test_bitfield)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "val_wd_timer_init failed Error=0x%x\n", status); + return; + } + + /* Enable watchdog Timer */ + status = val_wd_timer_enable(); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "val_wd_timer_enable failed Error=0x%x\n", status); + return; + } + + val_set_status(RESULT_START(VAL_STATUS_SUCCESS)); + return; +} + +/** + @brief This API sets the test state to TEST_END if test is successfuly passed. + @param none + @return none +**/ + +void val_test_exit(void) +{ + val_wd_timer_disable(); + + /* return if test skipped or failed */ + if (IS_TEST_SKIP(val_get_status()) || IS_TEST_FAIL(val_get_status())) + { + return; + } + val_set_status(RESULT_END(VAL_STATUS_SUCCESS)); +} + +/** + @brief - This function returns the test ID of the last test that was run + @param - test_id address + @return - val_status_t +**/ +val_status_t val_get_last_run_test_id(test_id_t *test_id) +{ + val_status_t status; + test_count_t test_count; + boot_t boot; + int i = 0, intermediate_boot = 0; + boot_state_t boot_state[] = {BOOT_NOT_EXPECTED, BOOT_EXPECTED_NS, + BOOT_EXPECTED_S, BOOT_EXPECTED_BUT_FAILED, + BOOT_EXPECTED_CRYPTO}; + + status = val_get_boot_flag(&boot.state); + if (VAL_ERROR(status)) + { + return status; + } + + for (i = 0; i < (sizeof(boot_state)/sizeof(boot_state[0])); i++) + { + if (boot.state == boot_state[i]) + { + intermediate_boot = 1; + break; + } + } + + if (!intermediate_boot) + { + /* First boot. Initiliase necessary data structure */ + status = val_set_boot_flag(BOOT_UNKNOWN); + if (VAL_ERROR(status)) + { + return status; + } + + *test_id = VAL_INVALID_TEST_ID; + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_TEST_ID_PREVIOUS), + test_id, sizeof(test_id_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ALWAYS, "\n\tNVMEM write error", 0); + return status; + } + + test_count.pass_cnt = 0; + test_count.fail_cnt = 0; + test_count.skip_cnt = 0; + test_count.sim_error_cnt = 0; + + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_TEST_CNT), + &test_count, sizeof(test_count_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM write error", 0); + return status; + } + } + + status = val_nvmem_read(VAL_NVMEM_OFFSET(NV_TEST_ID_PREVIOUS), test_id, sizeof(test_id_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n\tNVMEM read error", 0); + } + + val_print(PRINT_INFO, "In val_get_last_run_test_id, test_id=%x\n", *test_id); + return status; +} + +/** + @brief - This function sets the given boot.state value to corresponding + boot NVMEM location + @param - state: boot_state_t + @return - val_status_t +**/ +val_status_t val_set_boot_flag(boot_state_t state) +{ + boot_t boot; + val_status_t status; + + boot.state = state; + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_BOOT), &boot, sizeof(boot_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "val_nvmem_write failed. Error=0x%x\n", status); + return status; + } + return status; +} + +/** + @brief - This function returns boot.state value available in boot NVMEM location + @param - state address + @return - val_status_t +**/ +val_status_t val_get_boot_flag(boot_state_t *state) +{ + boot_t boot; + val_status_t status; + + status = val_nvmem_read(VAL_NVMEM_OFFSET(NV_BOOT), &boot, sizeof(boot_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "val_nvmem_read failed. Error=0x%x\n", status); + return status; + } + *state = boot.state; + return status; +} diff --git a/psa-ff/val/nspe/val_framework.h b/psa-ff/val/nspe/val_framework.h new file mode 100644 index 00000000..84e2bd0c --- /dev/null +++ b/psa-ff/val/nspe/val_framework.h @@ -0,0 +1,46 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + + +#ifndef _VAL_INFRA_H_ +#define _VAL_INFRA_H_ + +#include "val.h" +#include "val_client_defs.h" +#include "val_interfaces.h" + +/* prototypes */ +uint32_t val_report_status(void); +val_status_t val_set_status(uint32_t status); +uint32_t val_get_status(void); +val_status_t val_err_check_set(uint32_t checkpoint, val_status_t status); +void val_test_init(uint32_t test_num, char8_t *desc, uint32_t test_bitfield); +void val_test_exit(void); +val_status_t val_get_last_run_test_id(test_id_t *test_id); +val_status_t val_execute_non_secure_tests(uint32_t test_num, client_test_t *tests_list, + bool_t server_hs); +val_status_t val_switch_to_secure_client(uint32_t test_num); +val_status_t val_execute_secure_test_func(psa_handle_t *handle, test_info_t test_info, + uint32_t sid); +val_status_t val_get_secure_test_result(psa_handle_t *handle); +val_status_t val_ipc_connect(uint32_t sid, uint32_t minor_version, psa_handle_t *handle); +val_status_t val_ipc_call(psa_handle_t handle, psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len); +void val_ipc_close(psa_handle_t handle); +val_status_t val_set_boot_flag(boot_state_t state); +val_status_t val_get_boot_flag(boot_state_t *state); +#endif diff --git a/psa-ff/val/nspe/val_interfaces.c b/psa-ff/val/nspe/val_interfaces.c new file mode 100644 index 00000000..34b325c3 --- /dev/null +++ b/psa-ff/val/nspe/val_interfaces.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + + +#include "val_framework.h" +#include "val_interfaces.h" +#include "val_peripherals.h" +#include "val_target.h" +#include "val_crypto.h" + +/*VAL APIs to be used by test */ +const val_api_t val_api = { + .print = val_print, + .set_status = val_set_status, + .get_status = val_get_status, + .test_init = val_test_init, + .test_exit = val_test_exit, + .err_check_set = val_err_check_set, + .target_get_config = val_target_get_config, + .execute_non_secure_tests = val_execute_non_secure_tests, + .switch_to_secure_client = val_switch_to_secure_client, + .execute_secure_test_func = val_execute_secure_test_func, + .get_secure_test_result = val_get_secure_test_result, + .ipc_connect = val_ipc_connect, + .ipc_call = val_ipc_call, + .ipc_close = val_ipc_close, + .nvmem_read = val_nvmem_read, + .nvmem_write = val_nvmem_write, + .wd_timer_init = val_wd_timer_init, + .wd_timer_enable = val_wd_timer_enable, + .wd_timer_disable = val_wd_timer_disable, + .is_wd_timer_enabled = val_is_wd_timer_enabled, + .set_boot_flag = val_set_boot_flag, + .get_boot_flag = val_get_boot_flag, + .crypto_function = val_crypto_function, + .crypto_key_type_is_raw = val_crypto_key_type_is_raw, +}; + +const psa_api_t psa_api = { + .framework_version = psa_framework_version, + .version = psa_version, + .connect = psa_connect, + .call = psa_call, + .close = psa_close, +}; diff --git a/psa-ff/val/nspe/val_interfaces.h b/psa-ff/val/nspe/val_interfaces.h new file mode 100644 index 00000000..74362bba --- /dev/null +++ b/psa-ff/val/nspe/val_interfaces.h @@ -0,0 +1,82 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_INTERFACES_H_ +#define _VAL_INTERFACES_H_ + +#include "val.h" +#include "val_client_defs.h" + +/* typedef's */ +typedef struct { + val_status_t (*print) (print_verbosity_t verbosity, + char *string, uint32_t data); + val_status_t (*set_status) (uint32_t status); + uint32_t (*get_status) (void); + void (*test_init) (uint32_t test_num, char8_t *desc, + uint32_t test_bitfield); + void (*test_exit) (void); + val_status_t (*err_check_set) (uint32_t checkpoint, val_status_t status); + val_status_t (*target_get_config) (cfg_id_t cfg_id, uint8_t **data, uint32_t *size); + val_status_t (*execute_non_secure_tests) (uint32_t test_num, client_test_t *tests_list, + bool_t server_hs); + val_status_t (*switch_to_secure_client) (uint32_t test_num); + val_status_t (*execute_secure_test_func) (psa_handle_t *handle, test_info_t test_info, + uint32_t sid); + val_status_t (*ipc_connect) (uint32_t sid, uint32_t minor_version, + psa_handle_t *handle ); + val_status_t (*ipc_call) (psa_handle_t handle, psa_invec *in_vec, + size_t in_len, psa_outvec *out_vec, + size_t out_len); + void (*ipc_close) (psa_handle_t handle); + val_status_t (*get_secure_test_result) (psa_handle_t *handle); + val_status_t (*nvmem_read) (uint32_t offset, void *buffer, int size); + val_status_t (*nvmem_write) (uint32_t offset, void *buffer, int size); + val_status_t (*wd_timer_init) (wd_timeout_type_t timeout_type); + val_status_t (*wd_timer_enable) (void); + val_status_t (*wd_timer_disable) (void); + val_status_t (*is_wd_timer_enabled) (void); + val_status_t (*set_boot_flag) (boot_state_t state); + val_status_t (*get_boot_flag) (boot_state_t *state); + val_status_t (*crypto_function) (int type, ...); + int32_t (*crypto_key_type_is_raw) (uint32_t type); +} val_api_t; + +typedef struct { + uint32_t (*framework_version) (void); + uint32_t (*version) (uint32_t sid); + psa_handle_t (*connect) (uint32_t sid, uint32_t minor_version); + psa_status_t (*call) (psa_handle_t handle, + const psa_invec *in_vec, + size_t in_len, + psa_outvec *out_vec, + size_t out_len + ); + psa_status_t (*close) (psa_handle_t handle); +} psa_api_t; + +typedef void (*test_fptr_t)(val_api_t *val, psa_api_t *psa); + +typedef struct { + test_id_t test_id; + test_fptr_t entry_addr; +} val_test_info_t; + +void test_entry(val_api_t *val, psa_api_t *psa); +void test_payload(val_api_t *val, psa_api_t *psa); + +#endif diff --git a/psa-ff/val/nspe/val_peripherals.c b/psa-ff/val/nspe/val_peripherals.c new file mode 100644 index 00000000..3ffbe570 --- /dev/null +++ b/psa-ff/val/nspe/val_peripherals.c @@ -0,0 +1,395 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_peripherals.h" +#include "val_target.h" +#include "pal_interfaces_ns.h" +#include "val_framework.h" +#include "val_client_defs.h" + +/* Global */ +uint32_t is_uart_init_done = 0; + +/** + @brief - This API will read the necessary target config info + and pass it to driver partition to initialise the driver partition + global variables + @param - void + @return - error status +**/ +val_status_t val_target_init(void) +{ + target_param_t target_param; + val_status_t status = VAL_STATUS_SUCCESS; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + soc_peripheral_desc_t *uart_desc; + soc_peripheral_desc_t *soc_per_desc; + memory_desc_t *memory_desc; + + status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_SOC_PERIPHERAL, + SOC_PERIPHERAL_UART, 0), + (uint8_t **)&uart_desc, + (uint32_t *)sizeof(soc_peripheral_desc_t)); + if (VAL_ERROR(status)) + { + return status; + } + + target_param.uart_base_addr = uart_desc->base; + + status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_SOC_PERIPHERAL, + SOC_PERIPHERAL_WATCHDOG, 0), + (uint8_t **)&soc_per_desc, + (uint32_t *)sizeof(soc_peripheral_desc_t)); + if (VAL_ERROR(status)) + { + return status; + } + target_param.wd_base_addr = soc_per_desc->base; + target_param.wd_time_us_low = soc_per_desc->timeout_in_micro_sec_low; + target_param.wd_time_us_medium = soc_per_desc->timeout_in_micro_sec_medium; + target_param.wd_time_us_high = soc_per_desc->timeout_in_micro_sec_high; + target_param.wd_timer_tick_us = soc_per_desc->num_of_tick_per_micro_sec; + + status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, MEMORY_NVMEM, 0), + (uint8_t **)&memory_desc, + (uint32_t *)sizeof(memory_desc_t)); + + target_param.nvmem_base_addr = memory_desc->start; + + if (VAL_ERROR(status)) + { + return status; + } + + psa_invec invec[1] = {{&target_param, sizeof(target_param)}}; + handle = psa_connect(DRIVER_TARGET_INIT_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/* + @brief - Initialize UART. + This is client interface API of secure partition UART INIT API. + @param - None + @return - val_status_t +*/ +val_status_t val_uart_init(void) +{ + psa_handle_t print_handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + uint32_t uart_init_sign = UART_INIT_SIGN; + uint32_t verbosity = VERBOSE; + + psa_invec data[3] = {{&uart_init_sign, sizeof(uart_init_sign)}, + {&verbosity, sizeof(verbosity)}}; + + print_handle = psa_connect(DRIVER_UART_SID, 0); + if (print_handle < 0) + { + return(VAL_STATUS_CONNECTION_FAILED); + } + + status_of_call = psa_call(print_handle, data, 3, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + return(VAL_STATUS_CALL_FAILED); + } + + is_uart_init_done = 1; + psa_close(print_handle); + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Print module. This is client interface API of secure partition + val_print_sf API for nspe world + @param - verbosity: Print verbosity level + - string : Input string + - data : Value for format specifier + @return - val_status_t +**/ +val_status_t val_print(print_verbosity_t verbosity, char *string, uint32_t data) +{ + int string_len = 0; + char *p = string; + psa_handle_t print_handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + val_status_t status = VAL_STATUS_SUCCESS; + + if ((is_uart_init_done == 0) || (verbosity < VERBOSE)) + { + return 0; + } + while (*p != '\0') + { + string_len++; + p++; + } + + psa_invec data1[3] = {{&verbosity, 4}, {string, string_len+1}, {&data, 4}}; + print_handle = psa_connect(DRIVER_UART_SID, 0); + + if (print_handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(print_handle, data1, 3, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + } + } + psa_close(print_handle); + return status; +} + +/** + @brief - This API will read from slave address via SPI + @param - addr : Slave address + data : value read from Slave address + len : length of data to be read in bytes + @return - error status +**/ +val_status_t val_spi_read(addr_t addr, uint8_t *data, uint32_t len) +{ + return pal_spi_read(addr, data, len); +} + +/* Watchdog APIs */ +/** + @brief - Initializes the WatchDog Timer instance. This is client interface API of + secure partition val_wd_timer_init_sf API for nspe world. + @param timeout: watchdog timeout value to be programmed + Defines to be used are WD_LOW_TIMEOUT, WD_MEDIUM_TIMEOUT and WD_HIGH_TIMEOUT + @return - error status +**/ +val_status_t val_wd_timer_init(wd_timeout_type_t timeout_type) +{ + wd_param_t wd_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + wd_param.wd_fn_type = WD_INIT_SEQ; + wd_param.wd_timeout_type = timeout_type; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = psa_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Enable WatchDog Timer instance. This is client interface API of + secure partition val_wd_timer_enable_sf API for nspe world. + @return - error status +**/ +val_status_t val_wd_timer_enable(void) +{ + wd_param_t wd_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + wd_param.wd_fn_type = WD_ENABLE_SEQ; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = psa_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + }else + { + status_of_call = psa_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Disable Watch Dog Timer instance. This is client interface API of + secure partition val_wd_timer_disable_sf API for nspe world. + @return - error status +**/ +val_status_t val_wd_timer_disable(void) +{ + wd_param_t wd_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + wd_param.wd_fn_type = WD_DISABLE_SEQ; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = psa_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Checks if watchdog enabled. This is client interface API of + secure partition val_is_wd_timer_enabled_sf API for nspe world. + @return - error status +**/ +val_status_t val_is_wd_timer_enabled(void) +{ + wd_param_t wd_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + wd_param.wd_fn_type = WD_STATUS_SEQ; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = psa_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/* + @brief - Reads 'size' bytes from Non-volatile memory at a given. This is client interface + API of secure partition val_nvmem_read_sf API for nspe world. + 'base + offset' into given buffer. + - offset : Offset from NV MEM base address + - buffer : Pointer to source address + - size : Number of bytes + @return - val_status_t +*/ +val_status_t val_nvmem_read(uint32_t offset, void *buffer, int size) +{ + nvmem_param_t nvmem_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + nvmem_param.nvmem_fn_type = NVMEM_READ; + nvmem_param.offset = offset; + nvmem_param.size = size; + psa_invec invec[1] = {{&nvmem_param, sizeof(nvmem_param)}}; + psa_outvec outvec[1] = {{buffer, size}}; + + handle = psa_connect(DRIVER_NVMEM_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 1, outvec, 1); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/* + @brief - Writes 'size' bytes from buffer into non-volatile memory at a given + 'base + offset'. This is client interface API of secure partition + val_nvmem_write_sf API for nspe world. + - offset : Offset + - buffer : Pointer to source address + - size : Number of bytes + @return - val_status_t +*/ +val_status_t val_nvmem_write(uint32_t offset, void *buffer, int size) +{ + nvmem_param_t nvmem_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + nvmem_param.nvmem_fn_type = NVMEM_WRITE; + nvmem_param.offset = offset; + nvmem_param.size = size; + psa_invec invec[2] = {{&nvmem_param, sizeof(nvmem_param)}, {buffer, size}}; + + handle = psa_connect(DRIVER_NVMEM_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 2, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} diff --git a/psa-ff/val/nspe/val_peripherals.h b/psa-ff/val/nspe/val_peripherals.h new file mode 100644 index 00000000..f3db9f69 --- /dev/null +++ b/psa-ff/val/nspe/val_peripherals.h @@ -0,0 +1,33 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_PERIPHERALS_H_ +#define _VAL_PERIPHERALS_H_ + +#include "val.h" + +val_status_t val_uart_init(void); +val_status_t val_print(print_verbosity_t verbosity, char *string, uint32_t data); +val_status_t val_spi_read(addr_t addr, uint8_t *data, uint32_t len); +val_status_t val_target_init(void); +val_status_t val_nvmem_read(uint32_t offset, void *buffer, int size); +val_status_t val_nvmem_write(uint32_t offset, void *buffer, int size); +val_status_t val_wd_timer_init(wd_timeout_type_t timeout_type); +val_status_t val_wd_timer_enable(void); +val_status_t val_wd_timer_disable(void); +val_status_t val_is_wd_timer_enabled(void); +#endif diff --git a/psa-ff/val/nspe/val_target.c b/psa-ff/val/nspe/val_target.c new file mode 100644 index 00000000..fe697599 --- /dev/null +++ b/psa-ff/val/nspe/val_target.c @@ -0,0 +1,146 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#include "val_framework.h" +#include "val_target.h" +#include "val_peripherals.h" +#include "pal_interfaces_ns.h" + +/** + @brief - Returns the base address of target configuration block database. + @param - blob : Populates the base address + @return - val_status_t +**/ +val_status_t val_target_cfg_get_next(void **blob) +{ + val_status_t status = VAL_STATUS_SUCCESS; + target_cfg_hdr_t *hdr; + uint32_t size; + + if (*blob == NULL) + { + *blob = pal_target_get_cfg_start(); + if (blob == NULL) + { + return VAL_STATUS_NOT_FOUND; + } + hdr = *blob; + + /* Sanity check signature and version here */ + if ((hdr->version != 1) || (hdr->size == 0)) + { + val_print(PRINT_ERROR, "Target config database Error. \n", 0); + return status; + } + hdr++; + *blob = hdr; // skip the header. start with the first record. + return status; + } + + size = (((cfg_type_t *)*blob)->size) & 0xFFFFFF; + if (size) + { + *blob = (void *)((uint8_t *)*blob + size); + return VAL_STATUS_SUCCESS; + } + return VAL_STATUS_ERROR; +} + +/** + @brief - This function checks for the given configuration ID with the block in + target configuration database. + @param - cfg_id : Configuration ID of a block + - data : Returns block base address + - size : Block size + @return - val_status_t +**/ +val_status_t val_target_get_cfg_blob(cfg_id_t cfg_id, uint8_t **data, uint32_t *size) +{ + val_status_t status; + void *config_blob = NULL; + + val_print(PRINT_INFO, "Input id is %x \n", cfg_id); + do + { + + status = val_target_cfg_get_next(&config_blob); + + if (VAL_ERROR(status)) + { + break; + } + + if (((cfg_type_t *)config_blob)->cfg_id == cfg_id) + { + *data = (uint8_t *)config_blob; + status = VAL_STATUS_SUCCESS; + break; + } + else if (((((cfg_type_t *)config_blob)->cfg_id & VAL_TEST_MAJOR_GROUP_MASK) == \ + (cfg_id & VAL_TEST_MAJOR_GROUP_MASK)) && \ + !(((cfg_type_t *)config_blob)->cfg_id & \ + (VAL_TEST_MINOR_GROUP_MASK | VAL_TEST_CFG_INSTANCE_MASK))) + { + config_blob = (void *)((uint8_t *)config_blob + sizeof(memory_hdr_t)); + if (((cfg_type_t *)config_blob)->cfg_id == cfg_id) + { + *data = (uint8_t *)config_blob; + status = VAL_STATUS_SUCCESS; + break; + } + } + else if (((cfg_type_t *)config_blob)->cfg_id == VAL_TEST_INVALID_CFG_ID) + { + status = VAL_STATUS_NOT_FOUND; + break; + } + } while(1); + + return status; +} + +/** + + @brief - This function returns the data associated with a given + config ID. + @param - size - if the input size is less than the data size to + returned, the size is updated with the actual data size and + error is returned. + @return - data contains the information of type specific to a + config id. +**/ +val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **data, uint32_t *size) +{ + val_status_t status; + + if ((cfg_id < TARGET_MIN_CFG_ID) || (cfg_id > TARGET_MAX_CFG_ID)) + { + val_print(PRINT_ERROR, "Invalid Target data config ID = %x \n", cfg_id); + return VAL_STATUS_INSUFFICIENT_SIZE; + } + + status = val_target_get_cfg_blob(cfg_id, data, size); + + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\n Get Config failed with status = %x", status); + val_print(PRINT_ERROR, " for cfg_id = %x", cfg_id); + return status; + } + return VAL_STATUS_SUCCESS; +} + diff --git a/psa-ff/val/nspe/val_target.h b/psa-ff/val/nspe/val_target.h new file mode 100644 index 00000000..50a7e3f6 --- /dev/null +++ b/psa-ff/val/nspe/val_target.h @@ -0,0 +1,198 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _TARGET_INFO_DATA_H_ +#define _TARGET_INFO_DATA_H_ + +#include "val.h" + +#define TARGET_CONFIG_CREATE_ID(major, minor, index) \ + (((major & 0xFF) << 24) | ((minor & 0xFF) << 16) | (index & 0xFFFF)) +#define TARGET_CONFIG_GET_MAJOR(config_id) ((config_id >> 24) & 0xFF) +#define TARGET_CONFIG_GET_MINOR(config_id) ((config_id >> 16) & 0xFF) +#define TARGET_CONFIG_INCREMENT_INDEX(config_id) \ + ((config_id & 0xFFFF0000) | ((config_id & 0xFFFF) + 1)) +#define GET_NUM_INSTANCE(struct_type) (struct_type->cfg_type.size >> 24) +#define VAL_TEST_MAJOR_GROUP_MASK 0xFF000000UL +#define VAL_TEST_MINOR_GROUP_MASK 0x00FF0000UL +#define VAL_TEST_CFG_INSTANCE_MASK 0x0000FFFFUL +#define VAL_TEST_INVALID_CFG_ID 0xFFFFFFFFUL +#define TARGET_MIN_CFG_ID TARGET_CONFIG_CREATE_ID(GROUP_SOC_PERIPHERAL, 0, 0) +#define TARGET_MAX_CFG_ID TARGET_CONFIG_CREATE_ID(GROUP_MAX, 0, 0) + +/** + Config IDs for each group/component + 31:24 : MAJOR (group) + 23:16 : MINOR (component) + 16:8 : SUB-component + 7:0 : INSTANCE (instance of same component) +**/ + +/* + MAJOR IDs +*/ +typedef enum _GROUP_CONFIG_ID_ { + GROUP_SOC_PERIPHERAL = 0x1, + GROUP_MEMORY = 0x2, + GROUP_MISCELLANEOUS = 0x3, + GROUP_MAX = 0xFF, +} group_cfg_id_t; + +/* + MINOR IDs + */ +typedef enum _SOC_PERIPHERAL_CONFIG_ID_ { + SOC_PERIPHERAL_UART = 0x1, + SOC_PERIPHERAL_TIMER = 0x2, + SOC_PERIPHERAL_WATCHDOG = 0x3, +} soc_peripheral_cfg_id_t; + +typedef enum _MEMORY_CONFIG_ID_ { + MEMORY_NVMEM = 0x2 +} memory_cfg_id_t; + +typedef enum _MISCELLANEOUS_CONFIG_ID_ { + MISCELLANEOUS_BOOT = 0x1, + MISCELLANEOUS_DUT = 0x2 +} miscellaneous_cfg_id_t; + +/** + Assign group type to each system component +**/ +typedef enum _COMPONENT_GROUPING_{ + UART = GROUP_SOC_PERIPHERAL, + TIMER = GROUP_SOC_PERIPHERAL, + WATCHDOG = GROUP_SOC_PERIPHERAL, + NVMEM = GROUP_MEMORY, + BOOT = GROUP_MISCELLANEOUS, + DUT = GROUP_MISCELLANEOUS, +} comp_group_assign_t; + +/** + Target Configuration Header +**/ +typedef struct _TARGET_CFG_HDR_ { + /* PSA_CFG */ + uint32_t signature[2]; + /* 8 byte String describing the Target platform */ + uint32_t target_string[2]; + /* version = 1 for now */ + uint32_t version; + /* Header Size */ + uint32_t size; +}target_cfg_hdr_t; + +typedef enum { + LEVEL1 = 0x1, + LEVEL2, + LEVEL3, +} firmware_level_t; + +typedef enum { + NOT_AVAILABLE = 0x0, + AVAILABLE = 0x1, +} is_available_t; + +typedef enum { + SECURE_ACCESS = 0x100, + NONSECURE_ACCESS, + SECURE_PROGRAMMABLE, + NONSECURE_PROGRAMMABLE +} dev_attr_t; + +typedef enum { + MEM_SECURE = 0x100, + MEM_NONSECURE, + MEM_NSC, +} mem_tgt_attr_t; + +typedef enum { + TYPE_READ_ONLY = 0x10, + TYPE_WRITE_ONLY, + TYPE_READ_WRITE, + TYPE_EXECUTE, + TYPE_RESERVED, +} perm_type_t; + +typedef struct _CFG_HDR_TYPE_ { + cfg_id_t cfg_id; + /* size inclusive of this header */ + uint32_t size; +} cfg_type_t; + +/** + Memory Information +**/ +typedef struct _MEM_INFO_DESC_ { + cfg_type_t cfg_type; + uint32_t num; +} memory_hdr_t; + +typedef struct _MEM_REGION_ { + cfg_type_t cfg_type; + addr_t start; + addr_t end; + mem_tgt_attr_t attribute; + perm_type_t permission; +} memory_desc_t; + +/* + SOC Peripheral description structures +*/ +typedef struct _SOC_PER_INFO_NUM_ { + cfg_type_t cfg_type; + uint32_t num; +} soc_peripheral_hdr_t; + +typedef struct _SOC_PER_INFO_DESC_ { + cfg_type_t cfg_type; + uint32_t vendor_id; + uint32_t device_id; + addr_t base; + uint32_t size; + uint32_t intr_id; + perm_type_t permission; + uint32_t timeout_in_micro_sec_low; + uint32_t timeout_in_micro_sec_medium; + uint32_t timeout_in_micro_sec_high; + uint32_t num_of_tick_per_micro_sec; + dev_attr_t attribute; +} soc_peripheral_desc_t; + +/** + System Miscellaneous Information +**/ + +typedef struct _MISCELLANEOUS_INFO_HDR_ { + cfg_type_t cfg_type; + uint32_t num; +} miscellaneous_hdr_t; + +typedef struct _MISCELLANEOUS_INFO_DESC_ { + cfg_type_t cfg_type; + firmware_level_t implemented_psa_firmware_isolation_level; + addr_t ns_start_addr_of_combine_test_binary; + is_available_t combine_test_binary_in_ram; + addr_t ns_test_addr; +} miscellaneous_desc_t; + +/*val target config read apis */ +val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **data, uint32_t *size); +val_status_t val_target_cfg_get_next(void **blob); +val_status_t val_target_get_cfg_blob(cfg_id_t cfg_id, uint8_t **data, uint32_t *size); +val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **data, uint32_t *size); +#endif diff --git a/psa-ff/val/spe/pal_interfaces_s.h b/psa-ff/val/spe/pal_interfaces_s.h new file mode 100644 index 00000000..a9edd70a --- /dev/null +++ b/psa-ff/val/spe/pal_interfaces_s.h @@ -0,0 +1,93 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_PAL_INTERFACE_APIS_H_ +#define _VAL_PAL_INTERFACE_APIS_H_ + + +#include "val/common/val.h" + +/* Peripherals APIs */ + +/* + @brief - This function initializes the uart + @param - uart_base_addr : Base address of the UART + @return - +*/ +void pal_uart_init(addr_t uart_base_addr); + +/* + @brief - This function parses the input string and writes byte by byte to print + the input string + @param - str : Input String + - data : Value for Format specifier + @return - void + */ +void pal_print(char *str, uint32_t data); + +/** + @brief - Initializes an hardware watchdog timer + @param - base_addr : Base address of the watchdog module + - time_us : Time in micro seconds + - timer_tick_us : Number of ticks per micro second + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); + +/** + @brief - Enables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_enable(addr_t base_addr); + +/** + @brief - Disables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_disable(addr_t base_addr); + + +/** + @brief - Checks whether hardware watchdog timer is enabled + @param - base_addr : Base address of the watchdog module + @return - Enabled : 1, Disabled : 0 +**/ +int pal_wd_timer_is_enabled(addr_t base_addr); + +/* + @brief - Writes 'size' bytes from buffer into non-volatile memory at a given 'base + offset' + @param - base : Base address of NV MEM + - offset : Offset + - buffer : Pointer to source address + - size : Number of bytes + @return - error status 0:SUCCESS, 1:FAIL +*/ +int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); + +/* + @brief - Reads 'size' bytes from non-volatile memory at a given + 'base + offset' into given buffer + @param - base : Base address of NV MEM + - offset : Offset + - buffer : Pointer to source address + - size : Number of bytes + @return - error status 0:SUCCESS, 1:FAIL +*/ +int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); +#endif diff --git a/psa-ff/val/spe/val_driver_service_apis.c b/psa-ff/val/spe/val_driver_service_apis.c new file mode 100644 index 00000000..ba56f3dd --- /dev/null +++ b/psa-ff/val/spe/val_driver_service_apis.c @@ -0,0 +1,142 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + + +#include "val_driver_service_apis.h" + +print_verbosity_t g_print_level = PRINT_INFO; + +/* UART APIs */ +/* + @brief - Initialise the UART + @param - uart_base_addr: address of the uart base + @param - print_level: g_print_level verbosity + @return - val_status_t +*/ +val_status_t val_uart_init_sf(addr_t uart_base_addr, print_verbosity_t print_level) +{ + val_status_t status; + + g_print_level = print_level; + status = VAL_STATUS_SUCCESS; + pal_uart_init(uart_base_addr); + return status; +} +/* + @brief - This function parses the input string and writes byte by byte to + print the input string + @param - pointer : Input String + - data : Value for Format specifier + @return - error status + */ +val_status_t val_print_sf(print_verbosity_t verbosity, char *string, uint32_t data) +{ + if (verbosity >= g_print_level) + { + pal_print(string, data); + } + return VAL_STATUS_SUCCESS; +} + +/* Watchdog APIs */ +/** + @brief - Initializes the WatchDog Timer instance + @param - base address of the given timer instance + - time_us : Time in micro seconds + - timer_tick_us : Number of ticks per micro second + @return - error status +**/ +val_status_t val_wd_timer_init_sf(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us) +{ + return pal_wd_timer_init(base_addr, time_us, timer_tick_us); +} + +/** + @brief - Enable WatchDog Timer instance + @param - base address of the given timer instance + @return - error status +**/ +val_status_t val_wd_timer_enable_sf(addr_t base_addr) +{ + return pal_wd_timer_enable(base_addr); +} + +/** + @brief - Disable Watch Dog Timer instance + @param - base address of the given timer instance + @return - error status +**/ +val_status_t val_wd_timer_disable_sf(addr_t base_addr) +{ + return pal_wd_timer_disable(base_addr); +} + +/** + @brief - Checks if watchdog enabled + @param - base address of the given timer instance + @return - error status +**/ +val_status_t val_is_wd_timer_enabled_sf(addr_t base_addr) +{ + if (pal_wd_timer_is_enabled(base_addr)) + { + return VAL_STATUS_SUCCESS; + } + else + { + return VAL_STATUS_ERROR; + } +} + +/* + @brief - Reads 'size' bytes from Non-volatile memory at a given 'base + offset' + into given buffer. + - offset : Offset from NV MEM base address + - buffer : Pointer to source address + - size : Number of bytes + @return - val_status_t +*/ +val_status_t val_nvmem_read_sf(addr_t base, uint32_t offset, void *buffer, int size) +{ + if (pal_nvmem_read(base, offset, buffer, size)) + { + return VAL_STATUS_SUCCESS; + } + else + { + return VAL_STATUS_ERROR; + } +} + +/* + @brief - Writes 'size' bytes from buffer into non-volatile memory at a given 'base + offset' + - offset : Offset + - buffer : Pointer to source address + - size : Number of bytes + @return - val_status_t +*/ +val_status_t val_nvmem_write_sf(addr_t base, uint32_t offset, void *buffer, int size) +{ + if (pal_nvmem_write(base, offset, buffer, size)) + { + return VAL_STATUS_SUCCESS; + } + else + { + return VAL_STATUS_ERROR; + } +} diff --git a/psa-ff/val/spe/val_driver_service_apis.h b/psa-ff/val/spe/val_driver_service_apis.h new file mode 100644 index 00000000..066a8592 --- /dev/null +++ b/psa-ff/val/spe/val_driver_service_apis.h @@ -0,0 +1,41 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_DRIVER_PARTITION_APIS_H_ +#define _VAL_DRIVER_PARTITION_APIS_H_ + +#include "val/common/val.h" +#include "val/common/val_client_defs.h" +#include "val_service_defs.h" +#include "pal_interfaces_s.h" + +/* Manifest definitions. Only accessible to Secure Partition. + * The file name is based on the name of the Secure Partitions manifest file. + * The name must not collide with other header files. + * Compliance tests expect the below manifest output files implementation from build tool. + */ +#include "driver_partition_psa.h" + +val_status_t val_uart_init_sf(addr_t uart_base_addr, print_verbosity_t print_level); +val_status_t val_print_sf(print_verbosity_t verbosity, char *string, uint32_t data); +val_status_t val_wd_timer_init_sf(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); +val_status_t val_wd_timer_enable_sf(addr_t base_addr); +val_status_t val_wd_timer_disable_sf(addr_t base_addr); +val_status_t val_is_wd_timer_enabled_sf(addr_t base_addr); +val_status_t val_nvmem_read_sf(addr_t base, uint32_t offset, void *buffer, int size); +val_status_t val_nvmem_write_sf(addr_t base, uint32_t offset, void *buffer, int size); +#endif diff --git a/psa-ff/val/spe/val_partition_common.h b/psa-ff/val/spe/val_partition_common.h new file mode 100644 index 00000000..25a32c93 --- /dev/null +++ b/psa-ff/val/spe/val_partition_common.h @@ -0,0 +1,496 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +/* Note- This file contains the functions and variables definition which are common to + all partitions defined by acs. These functions and variables are declared with static + keyword because some fully isolated system may not allow to share code and data segment + between partitions and static will help each partition to have its own copy of code and data. + Moreover it can prevents symbol names conflict if these functions are separately compiled and + linked with each of partitions in fully isolated environment. +*/ + +#ifndef _VAL_COMMON_SP_APIS_H_ +#define _VAL_COMMON_SP_APIS_H_ + +#include "val/common/val.h" +#include "val_service_defs.h" + +/* Manifest definitions. Only accessible to Secure Partition. + * The file name is based on the name of the Secure Partitions manifest file. + * The name must not collide with other header files. + * Compliance tests expect the below manifest output files implementation from build tool. + */ +#include "client_partition_psa.h" +#include "server_partition_psa.h" + +__UNUSED STATIC_DECLARE val_status_t val_print + (print_verbosity_t verbosity, char *string, uint32_t data); +__UNUSED STATIC_DECLARE val_status_t val_ipc_connect + (uint32_t sid, uint32_t minor_version, psa_handle_t *handle ); +__UNUSED STATIC_DECLARE val_status_t val_ipc_call + (psa_handle_t handle, psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len); +__UNUSED STATIC_DECLARE void val_ipc_close + (psa_handle_t handle); +__UNUSED STATIC_DECLARE val_status_t val_process_connect_request(psa_signal_t sig, psa_msg_t *msg); +__UNUSED STATIC_DECLARE val_status_t val_process_call_request(psa_signal_t sig, psa_msg_t *msg); +__UNUSED STATIC_DECLARE val_status_t val_process_disconnect_request + (psa_signal_t sig, psa_msg_t *msg); +__UNUSED STATIC_DECLARE val_status_t val_execute_secure_tests + (uint32_t test_num, client_test_t *tests_list); +__UNUSED STATIC_DECLARE val_status_t val_execute_secure_test_func + (psa_handle_t *handle, test_info_t test_info, uint32_t sid); +__UNUSED STATIC_DECLARE val_status_t val_get_secure_test_result(psa_handle_t *handle); +__UNUSED STATIC_DECLARE val_status_t val_err_check_set(uint32_t checkpoint, val_status_t status); +__UNUSED STATIC_DECLARE val_status_t val_nvmem_write(uint32_t offset, void *buffer, int size); +__UNUSED STATIC_DECLARE val_status_t val_set_boot_flag(boot_state_t state); + +__UNUSED static val_api_t val_api = { + .print = val_print, + .err_check_set = val_err_check_set, + .execute_secure_test_func = val_execute_secure_test_func, + .get_secure_test_result = val_get_secure_test_result, + .ipc_connect = val_ipc_connect, + .ipc_call = val_ipc_call, + .ipc_close = val_ipc_close, + .set_boot_flag = val_set_boot_flag, +}; + +__UNUSED static psa_api_t psa_api = { + .framework_version = psa_framework_version, + .version = psa_version, + .connect = psa_connect, + .call = psa_call, + .close = psa_close, +}; + +/** + @brief - Print module. This is client interface API of secure partition + val_print_sf API for spe world + @param - verbosity: Print verbosity level + - string : Input string + - data : Value for format specifier + @return - val_status_t +**/ +STATIC_DECLARE val_status_t val_print(print_verbosity_t verbosity, char *string, uint32_t data) +{ + int string_len = 0; + char *p = string; + psa_handle_t print_handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + val_status_t status = VAL_STATUS_SUCCESS; + + while (*p != '\0') + { + string_len++; + p++; + } + + psa_invec data1[3] = {{&verbosity, 4}, {string, string_len+1}, {&data, 4}}; + print_handle = psa_connect(DRIVER_UART_SID, 0); + + if (print_handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(print_handle, data1, 3, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + } + } + psa_close(print_handle); + return status; +} + +/** + * @brief Connect to given sid + @param -sid : RoT service id + @param -minor_version : minor_version of RoT service + @param -handle - return connection handle + * @return val_status_t + */ +STATIC_DECLARE val_status_t val_ipc_connect(uint32_t sid, uint32_t minor_version, + psa_handle_t *handle ) +{ + *handle = psa_connect(sid, minor_version); + + if (*handle < 0) + { + return(VAL_STATUS_CONNECTION_FAILED); + } + + return VAL_STATUS_SUCCESS; +} + +/** + * @brief Call a connected Root of Trust Service.@n + * The caller must provide an array of ::psa_invec_t structures as the input payload. + * @param handle: Handle for the connection. + * @param in_vec: Array of psa_invec structures. + * @param in_len: Number of psa_invec structures in in_vec. + * @param out_vec: Array of psa_outvec structures for optional Root of Trust Service response. + * @param out_len: Number of psa_outvec structures in out_vec. + * @return val_status_t + */ +STATIC_DECLARE val_status_t val_ipc_call(psa_handle_t handle, psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len) +{ + psa_status_t call_status = PSA_SUCCESS; + + call_status = psa_call(handle, in_vec, in_len, out_vec, out_len); + + if (call_status != PSA_SUCCESS) + { + return(VAL_STATUS_CALL_FAILED); + } + + return VAL_STATUS_SUCCESS; +} + +/** + * @brief Close a connection to a Root of Trust Service. + * Sends the PSA_IPC_DISCONNECT message to the Root of Trust Service so + it can clean up resources. + * @param handle: Handle for the connection. + * @return void + */ +STATIC_DECLARE void val_ipc_close(psa_handle_t handle) +{ + psa_close(handle); +} + +/** + * @brief Proccess a generic connect message to given rot signal. + @param -sig : signal to be processed + @param -msg : return msg info of given signal + * @return val_status_t. + */ +STATIC_DECLARE val_status_t val_process_connect_request(psa_signal_t sig, psa_msg_t *msg) +{ + val_status_t res = VAL_STATUS_ERROR; + psa_signal_t signals; + +wait1: + signals = psa_wait_any(PSA_BLOCK); + if (signals & sig) + { + if (psa_get(sig, msg) != PSA_SUCCESS) + { + goto wait1; + } + + if ((msg->type != PSA_IPC_CONNECT) || (msg->handle <= 0)) + { + val_print(PRINT_ERROR, "\npsa_get failed for PSA_IPC_CONNECT", 0); + res = VAL_STATUS_ERROR; + } + else + { + res = VAL_STATUS_SUCCESS; + } + } + else + { + val_print(PRINT_ERROR, "\npsa_wait_any returned with invalid signal value = 0x%x", signals); + res = VAL_STATUS_ERROR; + } + return res; +} + +/** + * @brief Proccess a generic call message to given rot signal. + @param -sig : signal to be processed + @param -msg : return msg info of given signal + * @return val_status_t + */ +STATIC_DECLARE val_status_t val_process_call_request(psa_signal_t sig, psa_msg_t *msg) +{ + val_status_t res = VAL_STATUS_ERROR; + psa_signal_t signals; + +wait2: + signals = psa_wait_any(PSA_BLOCK); + if (signals & sig) + { + if (psa_get(sig, msg) != PSA_SUCCESS) + { + goto wait2; + } + + if ((msg->type != PSA_IPC_CALL) || (msg->handle <= 0)) + { + val_print(PRINT_ERROR, "\npsa_get failed for PSA_IPC_CALL", 0); + res = VAL_STATUS_ERROR; + } + else + { + res = VAL_STATUS_SUCCESS; + } + } + else + { + val_print(PRINT_ERROR, "\npsa_wait_any returned with invalid signal value = 0x%x", signals); + res = VAL_STATUS_ERROR; + } + return res; +} + +/** + * @brief Proccess a generic disconnect message to given rot signal. + @param -sig : signal to be processed + @param -msg : return msg info of given signal + * @return val_status_t + */ +STATIC_DECLARE val_status_t val_process_disconnect_request(psa_signal_t sig, psa_msg_t *msg) +{ + val_status_t res = VAL_STATUS_ERROR; + psa_signal_t signals; + +wait3: + signals = psa_wait_any(PSA_BLOCK); + if (signals & sig) + { + if (psa_get(sig, msg) != PSA_SUCCESS) + { + goto wait3; + } + + if ((msg->type != PSA_IPC_DISCONNECT) || (msg->handle <= 0)) + { + val_print(PRINT_ERROR, "\npsa_get failed for PSA_IPC_DISCONNECT", 0); + res = VAL_STATUS_ERROR; + } + else + { + res = VAL_STATUS_SUCCESS; + } + } + else + { + val_print(PRINT_ERROR, "\npsa_wait_any returned with invalid signal value = 0x%x", signals); + res = VAL_STATUS_ERROR; + } + return res; +} + +/** + @brief - This function executes given list of tests from secure sequentially + This covers secure to secure IPC API scenario + @param - test_num : Test_num + @param - tests_list : list of tests to be executed + @return - val_status_t +**/ +STATIC_DECLARE val_status_t val_execute_secure_tests(uint32_t test_num, client_test_t *tests_list) +{ + val_status_t status = VAL_STATUS_SUCCESS; + val_status_t test_status = VAL_STATUS_SUCCESS; + psa_handle_t handle; + int i = 1; + test_info_t test_info; + + test_info.test_num = test_num; + val_print(PRINT_TEST, "[Info] Executing tests form secure\n", 0); + + while (tests_list[i] != NULL) + { + + /* Handshake with server tests */ + test_info.block_num = i; + status = val_execute_secure_test_func(&handle, test_info, SERVER_TEST_DISPATCHER_SID); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR,"[Check%d] START\n", i); + return status; + } + else + { + val_print(PRINT_DEBUG,"[Check%d] START\n", i); + } + + /* Execute client tests */ + test_status = tests_list[i](SECURE); + + /* Retrive Server test status */ + status = val_get_secure_test_result(&handle); + + status = test_status ? test_status:status; + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR,"[Check%d] FAILED\n", i); + return status; + } + else + { + val_print(PRINT_DEBUG,"[Check%d] PASSED\n", i); + } + i++; + } + return status; +} + +/** + @brief - This function is used to handshake between: + - nonsecure client to server test fn + - secure client and server test fn + - nonsecure client to secure client test fn + @param - handle : handle returned while connecting given sid + @param - test_info : Test_num and block_num to be executed + @param - sid : RoT service to be connected. Partition dispatcher sid + @return - val_status_t +**/ +STATIC_DECLARE val_status_t val_execute_secure_test_func(psa_handle_t *handle, + test_info_t test_info, + uint32_t sid) +{ + uint32_t test_data; + val_status_t status = VAL_STATUS_SUCCESS; + psa_status_t status_of_call = PSA_SUCCESS; + + *handle = psa_connect(sid, 0); + + if (*handle < 0) + { + val_print(PRINT_ERROR, "Could not connect SID. Handle=%x\n", *handle); + status = VAL_STATUS_CONNECTION_FAILED; + } + + test_data = ((uint32_t)(test_info.test_num) | ((uint32_t)(test_info.block_num) << BLOCK_NUM_POS) + | ((uint32_t)(TEST_EXECUTE_FUNC) << ACTION_POS)); + psa_invec data[1] = {{&test_data, sizeof(test_data)}}; + + status_of_call = psa_call(*handle, data, 1, NULL, 0); + + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + val_print(PRINT_ERROR, "Call to dispatch SF failed. Status=%x\n", status_of_call); + psa_close(*handle); + } + return status; +} + +/** + @brief - This function is used to retrive the status of previously connected test function + using val_execute_secure_test_func + @param - handle : handle of server function. Handle of Partition dispatcher sid + @return - The status of test functions +**/ +STATIC_DECLARE val_status_t val_get_secure_test_result(psa_handle_t *handle) +{ + uint32_t test_data; + val_status_t status = VAL_STATUS_SUCCESS; + psa_status_t status_of_call = PSA_SUCCESS; + + test_data = (TEST_RETURN_RESULT << ACTION_POS); + + psa_outvec resp = {&status, sizeof(status)}; + psa_invec data[1] = {{&test_data, sizeof(test_data)}}; + + status_of_call = psa_call(*handle, data, 1, &resp, 1); + if (status_of_call != PSA_SUCCESS) + { + status = VAL_STATUS_CALL_FAILED; + val_print(PRINT_ERROR, "Call to dispatch SF failed. Status=%x\n", status_of_call); + } + + psa_close(*handle); + return status; +} + +/* + @brief - This function checks if the input status argument is an error. + On error, print the checkpoint value + @param - checkpoint : Test debug checkpoint + - val_status_t : Test status + @return - returns the input status back to the program. +*/ +STATIC_DECLARE val_status_t val_err_check_set(uint32_t checkpoint, val_status_t status) +{ + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\tCheckpoint %d : ", checkpoint); + val_print(PRINT_ERROR, "Error Code=0x%x \n", status); + } + else + { + val_print(PRINT_DEBUG, "\tCheckpoint %d \n", checkpoint); + } + return status; +} + +/* + @brief - Writes 'size' bytes from buffer into non-volatile memory at a given + 'base + offset'. This is client interface API of secure partition + val_nvmem_write_sf API for spe world + - offset : Offset + - buffer : Pointer to source address + - size : Number of bytes + @return - val_status_t +*/ +STATIC_DECLARE val_status_t val_nvmem_write(uint32_t offset, void *buffer, int size) +{ + nvmem_param_t nvmem_param; + psa_handle_t handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + + nvmem_param.nvmem_fn_type = NVMEM_WRITE; + nvmem_param.offset = offset; + nvmem_param.size = size; + psa_invec invec[2] = {{&nvmem_param, sizeof(nvmem_param)}, {buffer, size}}; + + handle = psa_connect(DRIVER_NVMEM_SID, 0); + if (handle < 0) + { + return VAL_STATUS_CONNECTION_FAILED; + } + else + { + status_of_call = psa_call(handle, invec, 2, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + psa_close(handle); + return VAL_STATUS_CALL_FAILED; + } + } + psa_close(handle); + return VAL_STATUS_SUCCESS; +} + +/** + @brief - This function sets the given boot.state value to corresponding + boot NVMEM location + @param - state: boot_state_t + @return - val_status_t +**/ +STATIC_DECLARE val_status_t val_set_boot_flag(boot_state_t state) +{ + boot_t boot; + val_status_t status; + + boot.state = state; + status = val_nvmem_write(VAL_NVMEM_OFFSET(NV_BOOT), &boot, sizeof(boot_t)); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "val_nvmem_write failed Error=0x%x\n", status); + return status; + } + return status; +} +#endif diff --git a/psa-ff/val/spe/val_service_defs.h b/psa-ff/val/spe/val_service_defs.h new file mode 100644 index 00000000..6a0e03dc --- /dev/null +++ b/psa-ff/val/spe/val_service_defs.h @@ -0,0 +1,104 @@ +/** @file + * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +#ifndef _VAL_PSA_SERVICE_H_ +#define _VAL_PSA_SERVICE_H_ + +/***************** PSA Secure Function API *****************/ + +/* Note - This header file containts the declaration of PSA defined secure partition service API + elements. Ideally, These elements must be defined in a header file by SPM + implemented library and provided to clients operation in NSPE and SPE as per the specification. + If this is available in the platform, the elements declared as part of this + file can be overwritten by passing --include to setup.sh script. + */ + +#if 1 +/* : Secure Partition API elements. Only accessible to Secure Partition */ +#include "psa_service.h" + +#else +#include "val/common/val.h" + +#define PSA_POLL 0x00000000U +#define PSA_BLOCK 0x80000000U +#define PSA_DOORBELL 0x00000008U +#define PSA_IPC_CONNECT 1 +#define PSA_IPC_CALL 2 +#define PSA_IPC_DISCONNECT 3 +#define PSA_MAX_IOVEC 4 +#define PSA_ERR_NOMSG (INT32_MIN + 3) + +typedef uint32_t psa_signal_t; +typedef struct psa_msg_t psa_msg_t; + +typedef struct psa_msg_t { + uint32_t type; + psa_handle_t handle; + int32_t client_id; + void *rhandle; + size_t in_size[PSA_MAX_IOVEC]; + size_t out_size[PSA_MAX_IOVEC]; +} psa_msg_t; + + +/* The implementation for following PSA service APIs should come from SPM */ +psa_signal_t psa_wait_any(uint32_t timeout); +psa_signal_t psa_wait_interrupt(psa_signal_t signal_mask, uint32_t timeout); +void psa_set_rhandle(psa_handle_t msg_handle, void *rhandle); +psa_status_t psa_get(psa_signal_t signal, psa_msg_t *msg); +size_t psa_read(psa_handle_t msg_handle, uint32_t invec_idx, + void *buffer, size_t num_bytes); +size_t psa_skip(psa_handle_t msg_handle, uint32_t invec_idx, + size_t num_bytes); +void psa_write(psa_handle_t msg_handle, uint32_t outvec_idx, + const void *buffer, size_t num_bytes); +void psa_reply(psa_handle_t msg_handle, psa_status_t status); +void psa_notify(int32_t partition_id); +void psa_clear(psa_signal_t doorbell_signal); +void psa_eoi(psa_signal_t irq_signal); +#endif /* #if 1 */ + +/* struct of function pointers to uniqify nspe and spe client interface for test */ +typedef struct { + uint32_t (*framework_version) (void); + uint32_t (*version) (uint32_t sid); + psa_handle_t (*connect) (uint32_t sid, uint32_t minor_version); + psa_status_t (*call) (psa_handle_t handle, + const psa_invec *in_vec, + size_t in_len, + psa_outvec *out_vec, + size_t out_len + ); + psa_status_t (*close) (psa_handle_t handle); +} psa_api_t; + +typedef struct { + val_status_t (*print) (print_verbosity_t verbosity, + char *string, uint32_t data); + val_status_t (*err_check_set) (uint32_t checkpoint, val_status_t status); + val_status_t (*execute_secure_test_func) (psa_handle_t *handle, test_info_t test_info, + uint32_t sid); + val_status_t (*get_secure_test_result) (psa_handle_t *handle); + val_status_t (*ipc_connect) (uint32_t sid, uint32_t minor_version, + psa_handle_t *handle ); + val_status_t (*ipc_call) (psa_handle_t handle, psa_invec *in_vec, + size_t in_len, psa_outvec *out_vec, size_t out_len); + void (*ipc_close) (psa_handle_t handle); + val_status_t (*set_boot_flag) (boot_state_t state); +} val_api_t; +#endif

    TD1}7m6AswO?Jg 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/wK#~1yUYJpZT8vjh6{NQ>tGkNV>O7bfTQEw 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